pax_global_header00006660000000000000000000000064121122241720014504gustar00rootroot0000000000000052 comment=7b558ae1822fa333822d2c676e797d06020b54a9 padevchooser-0.9.4/000077500000000000000000000000001211222417200142005ustar00rootroot00000000000000padevchooser-0.9.4/.gitignore000066400000000000000000000003321211222417200161660ustar00rootroot00000000000000*.o .deps/ /aclocal.m4 /autom4te.cache/ /compile /config.h /config.h.in /config.log /config.status /configure /depcomp /doc/README /doc/README.html /install-sh /missing /src/padevchooser /stamp-h1 Makefile Makefile.in padevchooser-0.9.4/LICENSE000066400000000000000000000431031211222417200152060ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. padevchooser-0.9.4/Makefile.am000066400000000000000000000024231211222417200162350ustar00rootroot00000000000000# This file is part of padevchooser. # # padevchooser is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # padevchooser is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with padevchooser; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA. EXTRA_DIST = bootstrap.sh LICENSE SUBDIRS=src doc homepage: all dist test -d $$HOME/homepage/private mkdir -p $$HOME/homepage/private/projects/padevchooser cp padevchooser-@PACKAGE_VERSION@.tar.gz $$HOME/homepage/private/projects/padevchooser cp doc/README.html doc/screenshot.png doc/style.css $$HOME/homepage/private/projects/padevchooser ln -sf README.html $$HOME/homepage/private/projects/padevchooser/index.html fedora-snapshot: dist cp $(distdir).tar.gz $$HOME/cvs.fedora/padevchooser/devel/$(distdir).svn`date +%Y%m%d`.tar.gz .PHONY: homepage padevchooser-0.9.4/README000066400000000000000000000003431211222417200150600ustar00rootroot00000000000000This is a clone of git://git.0pointer.de/padevchooser.git I've add a few pieces from pulse-audio that I need to be able to build. The main piece is browser.c. Everything in pulse-core was added to be able to build browser.c padevchooser-0.9.4/autogen.sh000077500000000000000000000014761211222417200162110ustar00rootroot00000000000000#!/bin/sh # This file is part of PulseAudio. # # PulseAudio is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # PulseAudio is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with PulseAudio; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. # Only there to make jhbuild happy NOCONFIGURE=1 ./bootstrap.sh exec ./configure "$@" padevchooser-0.9.4/bootstrap.sh000077500000000000000000000026201211222417200165540ustar00rootroot00000000000000#!/bin/bash # This file is part of padevchooser. # # padevchooser is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # padevchooser is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with padevchooser; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. VERSION=1.9 run_versioned() { local P local V V=$(echo "$2" | sed -e 's,\.,,g') if [ -e "`which $1$V`" ] ; then P="$1$V" else if [ -e "`which $1-$2`" ] ; then P="$1-$2" else P="$1" fi fi shift 2 "$P" "$@" } set -ex if [ "x$1" = "xam" ] ; then run_versioned automake "$VERSION" -a -c --foreign ./config.status else rm -rf autom4te.cache rm -f config.cache run_versioned aclocal "$VERSION" run_versioned autoconf 2.59 -Wall run_versioned autoheader 2.59 run_versioned automake "$VERSION" -a -c --foreign CFLAGS="-g -O0" ./configure --sysconfdir=/etc "$@" make clean fi padevchooser-0.9.4/configure.ac000066400000000000000000000055201211222417200164700ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. # This file is part of padevchooser. # # padevchooser is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # padevchooser is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with padevchooser; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. AC_PREREQ([2.69]) AC_INIT([padevchooser],[0.9.4],[mzcnqripubbfre (at) 0pointer (dot) de],,[http://0pointer.de/lennart/projects/padevchooser/]) AC_CONFIG_SRCDIR([src/padevchooser.c]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([foreign 1.9 -Wall]) if type -p stow > /dev/null && test -d /usr/local/stow ; then AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***]) ac_default_prefix="/usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION}" fi # Checks for programs. AC_PROG_CC AC_PROG_LN_S AC_HEADER_STDC AM_PROG_CC_C_O PKG_CHECK_MODULES(GUILIBS, [ gtk+-2.0 >= 2.10 libnotify libglade-2.0 gconf-2.0 libgnomeui-2.0 gnome-desktop-2.0 x11 ]) if test -d ../pulseaudio ; then PULSE_CFLAGS='-I$(top_srcdir)/../pulseaudio/src' PULSE_LIBS='-L$(top_srcdir)/../pulseaudio/src/.libs -lpulse -lpulse-mainloop-glib' echo "*** Found pulseaudio in ../pulseaudio, using that version ***" AC_SUBST(PULSE_LIBS) AC_SUBST(PULSE_CFLAGS) else PKG_CHECK_MODULES(PULSE, [ libpulse >= 0.9.2 libpulse-mainloop-glib >= 0.9.2 ]) fi PKG_CHECK_MODULES(AVAHI, [avahi-client]) LIBS="${GUILIBS_LIBS} ${PULSE_LIBS} ${AVAHI_LIBS} ${LIBS}" AM_CPPFLAGS="${GUILIBS_CFLAGS} ${PULSE_CFLAGS} ${AVAHI_CFLAGS}" AC_SUBST(AM_CPPFLAGS) # If using GCC specifiy some additional parameters if test "x$GCC" = "xyes" ; then AM_CFLAGS="-pipe -Wall -W -Wno-unused-parameter" fi AC_SUBST(AM_CFLAGS) # LYNX documentation generation AC_ARG_ENABLE(lynx, AS_HELP_STRING(--disable-lynx,Turn off lynx usage for documentation generation), [case "${enableval}" in yes) lynx=yes ;; no) lynx=no ;; *) AC_MSG_ERROR(bad value ${enableval} for --disable-lynx) ;; esac],[lynx=yes]) if test x$lynx = xyes ; then AC_CHECK_PROG(have_lynx, lynx, yes, no) if test x$have_lynx = xno ; then AC_MSG_ERROR([*** Sorry, you have to install lynx or use --disable-lynx ***]) fi fi AM_CONDITIONAL([USE_LYNX], [test "x$lynx" = xyes]) AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile doc/README.html]) AC_OUTPUT padevchooser-0.9.4/doc/000077500000000000000000000000001211222417200147455ustar00rootroot00000000000000padevchooser-0.9.4/doc/Makefile.am000066400000000000000000000020611211222417200170000ustar00rootroot00000000000000# This file is part of padevchooser. # # padevchooser is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # padevchooser is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with padevchooser; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. noinst_DATA = README.html README EXTRA_DIST = $(noinst_DATA) style.css README.html.in MAINTAINERCLEANFILES = README README.html CLEANFILES = if USE_LYNX README: README.html lynx --dump $^ | sed 's,file://localhost/.*/doc/README.html,README,' > $@ CLEANFILES += README endif tidy: README.html tidy -e < README.html .PHONY: tidy padevchooser-0.9.4/doc/README.html.in000066400000000000000000000124561211222417200172050ustar00rootroot00000000000000 PulseAudio Device Chooser @PACKAGE_VERSION@

PulseAudio Device Chooser @PACKAGE_VERSION@

Copyright 2006 Lennart Poettering <@PACKAGE_BUGREPORT@>

License

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

News

Sat Aug 26 2006:

Version 0.9.3 released; add menu item for starting the PulseAudio Preferences

Sat Jul 8 2006:

Version 0.9.2 released; initial release

Overview

PulseAudio Device Chooser (padevchooser) is a simple GTK tool which registers an icon in the tray area and allows quick access to some features of the PulseAudio sound server. Specifically it can do for you:

Everybody loves screenshots.

Current Status

Works perfectly.

Documentation

There is not much to say. Just run padevchooser and see for yourself.

Requirements

Currently, padevchooser is tested on Linux only.

padevchooser was developed and tested on Debian GNU/Linux "testing" from June 2006, it should work on most other Linux distributions (and maybe Unix versions) since it uses GNU autoconf for source code configuration.

padevchooser requires GTK 2.4, libnotify, GConf installed.

Obviously padevchooser requires an installation of PulseAudio (version 0.9.2), compiled with ZeroConf support. For being discovered correctly PulseAudio servers need to load module-zeroconf-publish and module-native-protocol-tcp.

Installation

As this package is made with the GNU autotools you should run ./configure inside the distribution directory for configuring the source tree. After that you should run make for compilation and make install (as root) for installation of padevchooser.

Acknowledgements

None so far.

Download

The newest release is always available from @PACKAGE_URL@

The current release is @PACKAGE_VERSION@

Get padevchooser's development sources from the Subversion repository (viewcvs):

svn checkout svn://svn.0pointer.net/padevchooser/trunk padevchooser

If you want to be notified whenever I release a new version of this software use the subscription feature of Freshmeat.


Lennart Poettering <@PACKAGE_BUGREPORT@>, August 2006
padevchooser-0.9.4/doc/screenshot.png000066400000000000000000003262721211222417200176440ustar00rootroot00000000000000PNG  IHDRaQ pHYs+tIMEt̡tEXtCommentCreated with GIMPW IDATx}w\{fYQ5=vb|ܘ1MboFA@DM&Ƃ"v"F3;,Xz<?8;)ϼ!A:@M~rխǂ;4%C>)ɸW 6ZJ># -~|~gG;?=xoivXaKN֌<\֘闿 jl(E2}&sK]3xi寞75!OqQG'#ŠqciIwHZ5 Z') hG%' e' t\ztf8J _cy.mx{Mor@v3o ;!CB! ! ! !ĒDcܐnIT#oIfwJN\mok%as@nHw1z:B ;?ɧm5 ($5OEduG;g>pB^xo0TUC'7OarC,S%ޛnD>:vx]$*@ 2bo(7nWOZ,o%B(G_(|]͌ kK2K SEWz9/ 8 %56ym=r8UyISkw1vpaݻf겐qi";|2猠]ϳgD(OcmY+`;Ԝ]W$$'BkoBnCH8 ƛs`ݔԐz;dh"[k;/'X,t]tȗOSܖ {oU̥FՉs(5`Xڵ3Ig=꺌0D4  T"=Wv׊7% m 80"Ri e] G8i8G]쨀뷞G5jʏ{hW1?i x.[Ʋ_,4'vi"!l7HP"*!Ƹ [-E!y'TC 7HR[&a\x`BFRX_Ǜ4l/gC9ɳFt"!_G4uFÀrlLΦ1.H:[ta|*,O{:ZOt|U=֌B߸|vyğ? Vyxr!Xea״I$X(2z5PH`}u<_:@(J**ڝ+zJOPP(^\$֖fr-iSNoLzYkfl9W'p/.M$c}u֬ttP4~jIJtl\ܗ@ZpPlUNURŢ|VsD$bC]JWaDy1Jh5,29QT{E2۱îD&]Zod!oo}vypdD}[%Ϗfy!Bl!99Pi؈n|kBdOrM8"an\`,.ywS~΍2s F^ Y9cYӑ}xZgJ kCmn~mrP43!tҭ2=$#kEדXT#?/jYӷC?szzy0O8')ƽ6ȗ891e!G; 1+13X]][\L9kzdW17=pY#%#_PW]gpwSA&O86R0?tqٝi؉ؓo4Mw-SqXv^lx2;Ec}Us!9P^5dX5҆:y ݢZ[GĸSf~1)Ɋ` ufjPmנrBмiKL< h ʷw'IE;\CHvsq JE+*o9"&5am4`o'(D"hHgBI)WcQx =-?D5j,tmv=9HAgԙ_wϦ6ɝ~?7"Fl%! ƊؐixBṔxH bC'K,]:m$ ndS)6!PuՉS+=" )1/qQ6y4 $ርRI M@Zp[#nln$>I{ xOgCB@QQB5X5Td/9Ԑ.wjX :?z#]͌ k3^<[&5nIo !XVH 90GdE z]UIe5 vt+~0:vx]$)}7t}cY|]͌ kK2[;G "O9_PhU${#7|:vvi TYxo-،"Y] rg_w"76K^f_e"1!A_awwFf/ *{'}_6KVeOl"/ ;s2dZ]z]mt\ݗ 9ǹ;K_vfϧ~a5۬ο{r'^YBҬp|[E+3~Jރ.|}-s4 }yr0>;?|)dgݫ7%"`NyAvz5D77EfT׺ۻ"cfw~]y9Ƶ>ooy/ ;s^bVIF$8v/{FڮoA!6KV;gC۱Y.?uEظ|wyagq^CeH)G!!,5Fo mŠ # 1;A떱l_BZSiJD\*,7&?01\xx@=I4T6TR6,9T'28-Cre._iKGEIeUAs!^7p$U U( HOWB QFW%f]. !X˗U\lNB؅v$9FntE$.DpDC)_E֦j<33֭Y'9Q axvuAF#reϜ𥇃 iSv̬E [i&NϏrp6PK^qy0oau5CnG>t-L!T? cU73UitxYtXP76{Sq+m|>{GL*ׄlWŐEw6s%b!HmԞd$6I'e5H@5Rɯǭ7uL;C9%tax"V\RxGԐ:n8ތbe|3LsK}k%o쨪U+Sw]w=x}]VZmyjX3A&I퍴 tPɀ5%E|͖-h2KH"xg@8";- }~lΝ=k|痚_ޫjтoΟ=e7NĦ1x5OEdxs%zo h%@I! rr/y`nV9ޛ>GVl뎰_fflX[C ye/Y(%_<=12-|ס2V"#X PER聰rC7+{jAy+U}x{!0-,+̈ }Q$0?kk=Aq뎰_(ҡ*p{+T$>$[Vϼ/GqX=mm ! ym8SOԐuD8{`]-,+|w/.x^EW#¹ 6|m7̇AGߔ7 @`0n|G^BSPh9 {kU?sқr '" 2)g93s%2͜+QY] rw?0#héS':#s5cX[WrʆXw[u'Dd2QnEZXV{D\fW~彊V^#?Cw3וxx>0)~PW"*pr~?|ːYV>ة ;&Py9Ԍm;1c7ކbhaéS~^_ۏ@ER{rC5 lFIՃk'"z"cջ>qYE^_*ReG "gv+{Ctq?G&duFN-N k}@}NEۏ@ERObeBE彊 vg=]ބYs rb?jȩ6-{Y6 -Ҵ;H!TD e^sk x}>r[i;|r˻gzJG_zS&vnֆ=b㾢+ C\ef>"sg۲bH뾣fm+w_Iַt1Y.잦SV&?{rv%#hbY.잦S]W&c3Oi۹ {EQ~?mqt@;:xڽGGEv_Ib>fٺS{LqvYpP=)Mc;%kÞl[6=9˾ˣʼ늼&/`.Kflu7ve42) 0}]7Ӳ{Lqq^Doo켔c0| D͘bziEE"(4nOmڤiwOH-..d9dh"׈m'@l 9^_W75XUw:&sljn7w r^$ PvHWwZ 'dJNOqZQt{lAJ_q3G&y쾕x ǎOv7> M( ?ru4Vm_ɛ :/X#? tY$YOJ& !;rjv qcg|N1LpYWVFvO)NV&?RoG-{@ݳNisuZ{/zP1}]=ۙ)U"al6k6Yܱ ǵ߇nBG wYpIs=bK0i]]2{MqZ*0ӂg|X}'靳>'5ع:x݋N^(ԾWK͋W kq>?,ZYjjLyI흷^d4`U‰ò= PP@R[qz:ZBӌT+H\ 4z_0.H9 P[Zc>\pY! 4'i x[Ʋ)Ues%Hd9cs彋bЃBqA8Zcsbo =Q/lǎ+y;ptȳD%f$-ZԍgY0f;znRWPy@i3ʢ2iL\? v}޵lLHIa?Mc\r7 Jc({JNz!¸ tmDC nl]=?Iv9jid? TNtqzZrÖUJΤ1.H+x ,˜W{@I.Nˢ1Oسq` 8D\{ x Xn\!5X\Ri eB(5w%k6nfSďd-mՔ4ix8}ϩR>O1d.˔W(( MANhNa\tqQ;#?b%Z6O:1ẹ`u<>thP$78ݰNj?AI46T[U5mjo^MtY\rYIeUA3s$4E:Nk_:@(bd5#5rC E4ҋ9Ɍ}SFW%}',]}EDb _F!' T[r_o[.u\փvU=<D e2BQ/ ;x)!!)!Hm9 ]AP|VMeddJ{Ɣ^*!#3s]ԙMɯuM':Kie725WbQS')Q): :;Ef p4?"AO'/j*pmQj򳾁6@\vcFC?Ewi¼/qۑO&/f*#kl/<j=b5G-v1QWxӄy5EQRjުg#O-6ٮFBnߟyhh27,5+xd6[TZً𘯑XvǾ.eе/jCjM)aW.JeqP_|yZ;[Z|ypn/D0!QXT]cL n8~b:%bC M}P;c|I.?BeSYk@h11[bP,3aqyޫq]8;skkWeD\8ܓܬĈg/Xc{ *RN09.sSxZx KgGȄy^y<&ȝGO'9mʌGʶ8yaC̈Ȭxh]鋈 ^lm6P\C]~h:?5sJ(jE{.Y~|ZhZJ5Ens)Z̸0UŅ;,yUwɓImQeceߘ:=.ό뀲f\zsgNE3eZ+#qQ)BGu?2SD 9ԬĆIۢʌ'lW쏿\3Ssiu^Vű+^(tp': foJ2nŕgHiu:G~q[&Xqv4KgswϊܪsR5f2HxCy~qg0 cj=:zb VvaR'eCD:f[|A, 9@u U1הgXߏf[,?R OfqMIVoZY&t~K-Liឈ߈~ yo%+g_sg`nƦIT1 b'HkƸ4X@ak2\I\o"#vXA12]8.Գ"y%O_ IGQܙy2M8T(HմQq2pVďy9 "G$ hL@+jcܘ2 4N,+ C@ЅA8"a*hĊXcC$,q;ea"5@ *׼n;F ZDcu:+`Q?xxC)Ҿ APfb5zeYL@@N.XxQ"!£Si e١́ue򎍗УHzuG$hiCSG b"3, gB W6ztESϥ.ʿ;9C!֊dځ#?mv0C,QdQXe}Xhq;nR1T?k9~1{l&Oem+L@@!._lQkBQfq`OƭZH$ xY|#unv2nŕ%ޑ ӆkw@L׊©:~M!ͪi`6InQȸ wLV\ݣ㎬VY-9Q3(ڸ|/SC`O((x&=n>;lhVO2ˀpD HYk[͸1B{t;̞"qH31wرԐpDj*=[XYvȬV0enXC,}b>u+#@xF6찲QZ[q# z g&?l~#ھoSyUngMHJ2o;$~DB=#֖d>K~ FWz9/ 8(7wE[NUE<`jn3V=@<̹5'ϚvUaÿ&.t;~U$>8[}h|eEOs}Yo\DrOs7i$mC@Q{Oiع-[huvd4de¾#*S6tY$ "vn>Ͳe!Dg.sɸyV1 teQ+.`|]=M8LIN ͟9<(ބQ_7k3LjR\Togm]_q99uҤC :iltm-KG*dCHPLqA>Z/f6uC7ΥtxfEm8E>?7JI#@@@@8"aJc( r._c􇓦s̎~*j/_cC-\9z\qse,0͉y@cH Eެek~\Pw%R~cIS:jkf?*lイ?x=>wيTRGf._J@u砚"7st_ 9{|  sG6Yu7"I@@@@8"aMݘ _L&^>WE-  쳷bKEIUw%gJ)3 H:TP4@O#ZI"-'[:iSNDl˲^!8|4D'֙wXH6TZpPll ˪ v%g*qb{ٵ|^kH$„ b;XrU%lc`$fMWѯ&t?tWW١xPI0tk3:<>@mK}pUX@"OGTH!AS2s E%yq7%ef%2C@Qj򳾁>v =LxB>v <]šz@HyއfyOxmrP43Adm2=2ԌEחXT#?/zY@P BG~קDBsnܛol|) Ō)vu$vf%vf 3Vpm;{G2L5ӎU{26H*ұNKP?B6{> -61Hn#zM YnҎ[!%-2wwwA7+]-Od1Aׇ&qMIV:Gj5)1;d i!(ܒFq]vWM]'t G|}i mDщcĭ";ľC CKܸLIҎfO@8b7 NJ) b9( W(}˸%t߽kI$ >~"@qrDo1 d*&,M$)#:HЩqnԟ2m610Gp[,W-s!\/a>.qw'QXBiˣ*79Qe;!|]9{Μ0eQoS]Q(, %b[KZL\=q&.~rqӌ76D&}R!AK:q&q:,t:C 9ē{>(5Dq762ݸqvLj{66t5+ ҢC34Fد`0- u^/Y} )wh+[~V% PB PCs&ӹ휖|cKυրSG?6xGw#ϊMsexl%xVDBv>H^ @m{_7`)Ӿ/3^*PCI^ȑžs~8 G/o|%jzWB=q<O6[Ȥ.@u%7AL E&{{n{)qkGא'|aϱJ}s4G@]c̋51wH[1pa<;?w1BLjQ:Kdx33A\L4o7A@J7䋚"c3#֖d>PB^xQTUdoBm")@lPXAfb?_pC@]LQ<:~>g|_ig=VaGßx~3wt sohv9y?tC\u0z4sz!سNv{hU? I~]>{y/?}<Uu)i LzP]֖d=r$oKeHm&d\|zott^v7~v}a]#:HiaA$Tu,ӨDpm xaMGJ;|LAeS5&` wEFn0֌` rS~Ӽm )h5MEFb)adlϯZt?gJy}VϴQ2%!F}-S|Q ͨyO{y٥ RNDu3]2v>*Ni:ueO_7k33Oi۹ {E*TO Q Rc!8s~eEeUӭ:Swm4֓A2c6yRڝ;E1] n?ikQ?Emq~, 6+t2>;P{ཷrzLrr\pgEyF/9Q%o =>/~ĪUG =H|z]MmĿ,f.훝CZ )$Ji~ڼk5seoLSPv[XVaC\Sj9eso< *ѽyϘ;o`E)|{! {^Q-?1{CS`iPGV-M@'E:9OݸmoJ6qAEXF3b$Rz%" o0^O: v\Y'rS&yiO=*@j-bOS|tvc~K'YzipG$k`9awnUD"U=`*!xc{"0~*gc% :$|UK#(ʷZr%!& Pl˯!#[SE,0zRB!F9~"@bI 8V[RJZUFi%` E!s(RLw{.;1oo|5/_ %UJʭ5)ϗϤ|q(fP4@O#"F-s;xQCW$ ҋ@1qVX(jlru]l4d 0C 9%ݼti.Y|U%7,29`bQU yA|^YoNLf3*6ǧsd4ڞ<ɼsԐcM]Eٌ+`򉛸;qn=/7Q_x pDN n QkvQQ>U}pt0r6R{*=+bhZ Tlyw 5kG0:{Rnx9ԇX`1/eeL#Gen~ XM@ ]J.Dy)tռUF[imb*쌃[9as%0XPSSD!ӷâa<>d.R Py`* u7~g̶jl<K_>} ϟ95 _DBFv*SL{1{=n>bԙS>r0f.3`ڲ=aJ ']<5}6yϮu}%rU ѨYe\w:z\z4A]~NG'u.fł:gv gHi[^ezX,]dI߼²Nj4qk.~$R9S1+*|DSySXh%C剪p"hjѰ[WB}ŏNH-}JlpU5,]@;SYAjG$:Õ^_>eɟ;A=h]GHi*lx=9 ˜ƘnJK bN5ܲ{mdQ3AWD∄#'g"E?%X5v0qҴHܰfW4u= #$ x!v!"㶭y G(x0m!;l A2TS7k!G :G I)%V xLU$ P1^J E:HX^& IDATcΛTuUbQ,ޙb&!2JƕqKh BX݀1 Bi1>,팺:% tD>}pD2 S9r$)s)-//LD"HD4WP9BY BȀA∄ e8b]]H$:ur򰰰JMMMb=vƸ|DƏhgg) iN# FAYYd Dq2nr%III̴03nF 111SH;$/u=~ ūbfd1g=sE0} 5(@r@jߊK}_Q=2j+Ȍ{Z Y**(C{%pDgW OdP# c9HWU“<0=RIk)86|Lr9~xTޤĤTYUƉMyQ9A-f^꩗d'Fsd[o_QcspxfDq~~˗/%$$$|0?vhL)G&m߹ib E²/R2X"DY3({\`1# Dikӊ ~Y/ћ,D*7TEU"0:| cV_(O*keþ0R4rī(RoDT׬\LEmioD`4:|H)Jc)UA޽,((x!'BhْOSRR{ |>p6Ddv!  ̀KU޽+ MӀxS:6:@K "Uy5PCV66-ڐY,Fľ XH$E4H#EY%"V +iM>jX ˣ\(Ehd{ec\3~gê9\$#rbRbTO8*Q%PI-L{oz\s(ZMׯ_4ݿ555PzzH$*,,dzq(RE92PWyE4 AsDb;(Tt IQjJ,a:QɫĘW)M?4iEC 2э,xq?PS5}h3T_ЯINE%M(}5\O2Z,d&&/ŵs-^62ڒ 4bM0T_ϐ80V3b6{_"K8LӋkdp}tB)ԃre)fuRr)a# 6Տ̯"9R21L6_F1مqI0׺ %xאAY(XD"G隚Jfl %O#CؼpD O#:pDP0,ő+&{'.9o@&˕ab6m,7-2IXϭ[̵Mr>#6Pk3-s3QXe={PmKsIo7ىsD [Fœb^}GM 0YH¯CE?R41&Gx?{cm'swjuϊ'\QiT$/rň)S~q9Ó,gH,aa&&r~qI,pa܍-dLi ]D@mm-s1}B;QŝyǪ&wNym.NfNT>S[.??D(1]*zU߾}MLL@M5-̙3͛R7nh 4@)ޕ\ŀܲzɟsrrՙ;|-˗,1c0ӏ=(&1c|J:JKK ޾}/@ߺ;GMH*ሄ#5kWIA.6j K\`Ȑ!7o1B?*--Р(J(1B&C+[8yQvcff=YB@@yЀ#[X&Hܺ7{"\Σ-MecǖӋ0nBիMOvxw/x 1Ư_VWWWTT`y<S?:444G$ f@GqZP| ύk}ج G333P( rDG$`HEQ!+bBaUU)Of"݅G$ xw9bgFZp@wd:\TҡsL|HЁ)+5=P@I;sǴ0OB!MB= :GY[Oܒqf~_n?J*n-0 ͈Z$a*& l9d?`5FLsu335-|2 W'>BM'O?VOG6 M~W0ФX^Wx}C\R B|.:WVV2c͊r+ GT &%pDRB{#\- ({f)MS%kFn]rΆb9i^Y?ʆbfU^nf˻o&g;eG&Y]v}LVhL!))uJ˝* uͪys9ӧ<_cǯ70s%\ϟDНkfiffޞDϢ1Ώ\pbn׊>03xS{''剓 5{HorKtMtX.7ذZ`qa椒3r9w`zp$K+kU?ߌN^v \n%Ǡ* 49#5Ey0' }!qM0 G|fDH}``qU dʚmPezX<73sɽ߼g=ϺqXesZr:8p;#1|D*ڨK!驫B@ &蜕7DpJuuu||<>֌4"x`X1119+X=39+} aDJw-39+D@ @ 4"@ >N04?@@ ;҈@ >iD{5,NoM WtAiAP(V[ԡ<ܖ!IhD|YJ=jl 4O.h hkݕ:vA>h|5/^ WƠW1kSeMs𜣩,|wP& _ z3v 4<R.gbKI*JD"𡨌@OW߁*i^Oރ@ SOtxHU=h֯?kW*U5a;/KX\kYʓ۫[2D)+߾<jWN[S-~tDH{ig GG _{/OXXf:@ >a$5'f0h,M,>/T+HK~)/}Z˺XU?$Tk{sfQ"0ɋ/#Rx>bss&lx(+˖'ވK\FZܭR"ˆiXރ@ }"F}YlLQm[/yʭo; 8p8OiEu-K:$\ž2zȃgў ҰGQsgOa=40xڒU.bZ@ Fz#2+Q۱QI`Y<9nkLJ&tm휖)oHv?ü jKs+.i-9^L)H =ϺqK:kOS^_>yz@ ` =1ad TWWgPQF@HO]Ml~ǁX,VzzzLL >>[#z2˻<?[.*}߈T^[@ cŋmmm)((h4%%%]]]EEEDVA!߃geeEEE jjjD"d*((?~Ĉ 5 @@ qS__bTUUDbrr2F4it:@p8<H$ڪcǎUWW[ZZgeeUWW+))1b:::@F,aә%%W;/;w9z|sssqq1۶mV[[[MMMiiirr2b0*JPTCsssFFFddAUUUlv~~ƍ.]=1 GE68ucn+E k'ﺑJdIn$㚸|$Pz*88x˖-9J-..ӧcVVV\VVс?::::::kkkB 6lSNr<O]]}ҤI{5jԤIPG 10T_w(P~Z&GD4FجȂ~r=f6vIDIII",㕔<} $I\ \x'Ol&񪫫Gtc7@ 6|keeG-= %=wMt|O]-:@;yƚkZq V/%s\%4"PxJpсt7nBijjT*hhh囻鏙kʐNuWmPEC6ITc.i-2bF%ѵ&Nqp튞PV/K~шdb-(<6>Ef]+,~arecBn{kIQxڬ_wUt~)8''J><-- 0|4ݕ(.=zd``0|p99+W;v(AQփ/'ݘ ?PS^Y DWZz㳏VVgi΄mسR?=M%[=G V.˻}o{*VZyz.>|쓨GI/^upte(d4ɥY[Al<7ަ8\QұXle1YEQH$`L~Z:lZ?h̫Wر9--ӧu׍8 >q„ 4MQQٳgVVVo'_0xY-:]w\ҭn9p{G;tXgqYe)򦧗wbw ao4 x c6|2 ^GXC{ H]!dDw>+<*KFɩϭ=hD[SYs[ GS}멬fx};~z_Oř{p8Ǐ_v-BIOOuH$$"XZZZRRr~:l;x/"Nԡ+Q$y%Slܶ XiWB VPZSf,SSc0qǏ?GϛENw+ Ӓվ*&A/S<&7MgGYw9ztXOoqS{urG}וpܠ|Oh}c>iDvsUae[ l6E 7g73cl5V|ѕ.#^h៞KY!˜褎goط#RB9Ja+қ::uU`˥ pȪmyuA&Nu<͊.\=b3fSzш=BFiD?z 3D 翿E 0?RYYI dZo/$]^^L&WVVu#K3nn? vkڧ̈@MEQJ)7['= _;bj(C&˙+x൯WZeNq+WJ%ɑ<{*~柳'C'89[Be ?kce?/e7ɚʺJ\gA\ Xb_{h*ȐeMuBUD#d]U\1@<6N&Nu<[oF4DFa#OE#GE/粔f|aa?[Jdai(.E0/dy_|+&:] :CgvԷ5AF~2#'{ly,pU ~je_:l5MCc)ifŚV0T*mu\_ۜӚ_hC K"/[!/#T֩j~;I>QʢEBʖ T:%pm_Oϔ{߆\H#~rX,f"./H$ FP(ي;I$@ <OQQd}-ÌPVa!5qXjvp교=,jrs3φtdMi,ᲙTVa7@$Xqxe7;; m0ԗo n|8WՑ? R;^-A>?T}+- O[.p{/Oer!~ԞT_#҈hh$J DzFihhʒd2,h+6-AҴTw?:zjif`#/ M&q:^ķu!BB :::{r"x<TXX8dȐwT5{U+^obso{ۿ}󹻏I)knei~:_,Zz>>q:_?#-:\ܫx+nU/:4vn7kYlFǩȊ[a e72-~Z/g,t*eѳ?aHU^ݼg~l{vn{Svʵu3iZX8o>@jsI@ 8`25_~222O<5 C_eee**)b=CdOUUȨ:t(""ǡD)O7hC'9@$Hn=3iG|SYNc@yؖ=gp(3)ĸMC9븝yv~ciF9Ğ9+7mm>~??ݙ\K- 1yޤOճfo3u^I2Е~=?xf0IЅg a_՛3ЛF܃)"uX?X}+|?ݻ```O+=\l6r<㉏|la'"B0aBCCF{葝:ژ1w&D6ax6Ui74 HVP՞h񘔀0ށ7 ^H_WAJ$T4L6{ngT*6w) VHc'oSh&'!Kv~O7[SmUE25vΞ[Wz")4uQl>ϳKEAc^A!>׆=Ⱥ6@W]JQPNڸ'qt㙘l"veootRć?7 )F]iktԗ~шo 1GWr^6dN;ؖ!me7UZ[},e`䗥/F<]WN$pߡ]cxE*DAȷЅq |a3q QtP&CxFVI:<#jc*θ==Ӿ8vvCf3M}<(} gW~IS&Ӡ% m+;9 E1GsB I-&tb/EitggW52ے#ȏh}}}%%%YYY-- &쉍k;((p+*((C ƏMsnz}Λ@ ƞBc7ST!k4vWD}y(n?J֩dž,=jnί>sanP. #/c,}ivQ˛g Ngk9mZ%u,7.󪰅ё\=r2 5'zyyż%57wGlqqdcc#L􌌌|)Sн@ 6]OC} !_}EUs "-h)< c_$k;`,%*a̼ $K5Tv+K_)$%`+AAϨ"-7SN.**>GXk)y<LҒ)jMM YYJTLSt:ʺ7y7]w'"%Wq[B)ǡm͐Xtnc++vo^yWY-lQ%fq-dʍ (X-Z7B+^rA\nkYOO9]zA@I+C7t'n|Ub%n .BBa ybem]9s2:C'T =mʋ @PжZp%O^Q)(]b'S[2p. T[[fL&HTRR--aÆ1̢&AL=V!H4M^^rUTT d2_|wtH&9hРW^@ d3rI|ĔB|Lutd0ze#yIH6;uޮ mA㤜v1Y>f9^"Ϋd E 5xz7$9Zo2QҤ ޣ4;BKRF`L0aΝ/_>raVRRR~GUTTt6 ,bֶp\SSĪ*|cEm۶M8 _y#M]xg?m̉f>9E@c҈PQQQKKիWeeed2F/uOU5jԠA={>x`ggA)**˷fԃyOR$=0R*HT͡F~?~<- Ijh𻝿{\C ҈7!\,>C=z/?V^i䴵Gٳ0 ײ(**r\a-Rh0xy~3GQDaV [vzo/l8;~ j0o@ >,ș67͂+**ٳ,--nj3dȐkjjiii :tȑ&MrppXdKJJ=z@&KwHoQyNlѧ9?'< _.[R GJ ?Ǔ*N2rʞ E>6-5Y[Cs5p#騪D>31Fnsz7??s%.@ "_)m{ݭlͫ=Ե[͚h=s=UPhM˕jlrQWkcFYrx㰬tcmG\~9~_n Vm i?o.҈1LK`Cuu|/^(**hiiQRR2003f̤I,,,LLLFKP222222*** qH$@.67uLW4[vou])|zvq~Yj*ŪIs/6'lu!UUA^zmA],aD> \7 ]Q^UNo=ECUYEŮڿ>z1R<۴߷3v O)ܷtEEF=UP^b #=+M {󯣫|*¿|_IMYnò}]l,a^g 5[hﵿNN:H15Ynn>p:]Y @yxC6hAͥ^ln0Um:!=N}G|ޝҝ/ BM!Ad„ ׮]322E\---MMMef񂔔pq6WީkwP@{m@3OåW d,yd͕l*|JQxB^4/ .s&u,N:Wĺ\bΟD#&_Z=z3"d_v^DԮ(?dTPdkOQ%sl-4+_"Eڡr_[5<ώ7/xn{=  w_?Mz}el$Za#xZΞ.:O8h/53!طJx u,4m㵶nfVbbY('rˌCN1~(7El_3m݇4⇁2lbbèK/BYY*33jQ>i_qbyzl%_A83)Lu R0o*qm ҝ?_`vlO(qo9+PƏ_¢u1'_|H&ַP5v58]VUlh 6CqDɶƲ%[z⹹uU1TPhmmO徶jN]55|RbO-L9NC>t}~ZföY_N)|>kRޥTבdDv;ndd yZR{9eb[JDpG{yZ*!&Úu܆_rv:/ZZ$DB2<2gur{wэ]uMWNL+dh*ͭn|դ^{UU w(Jc;{iit4ήj|fjD#NCɣ#9M>c7<-ٻRU 1q4vSg>gW5[P1Gr{Sے#B¡sYJvf"pF$$n-κw"}oFGCi@!{F=B~o{SFۓ#NhMZ[z#(I)Jc^/F`vU#n:cH T_UxbӨ!ZoHͮ돋p`Y#Iv]< h∗;=5"n;?K+p-%Y뉥pTPh=,LO徶jY9>?d_Y (s<+?#LJsEڿ ä+Ymk{W ~8>9_ b:uxmB|,-M ?)aϏdD|qFeݝv/#\O(p_ t/n:eB~SwhN/4nzͪ#02F_mF } 096myT_< "DpZ: IDAT,$~m.D32"_eᱲ1#

#Z =>(p.Hv$!N:ԩ~tO(QNw+sieCk^Wx6F{/K6,ɹYt3mQ0'ȹ -'S_>F.ODp$ 9#p;q)@$6xR13]u&-4@$0kRA|f؝SQrK̻`7`ӿ<{(1Uo5<- g\& ]SPPƁPD58 )A;D"*C*(a3əmgT@MK%PFr?c@>)y< c\ EƓP_W0fy|#naWΝ@|V?K'IWYR/^E&UD0 4=]Nn'9t4AmXX0f9eɋ/G):>91V6=dBD| KyYx3qDS&K*,aXMYpC؞(ҌThԋkZ[à6Px,"[SUUU^^^RRrǎ;c uuuYYYYYY999Y!Dhű!̚f O ',h{uK7?9NXU#ɤ63 de)5lM_罬Sd9obA4^f#&喫 t6K:371޿4uoV=~A[MM|&HئztCwn[arq{+9BM +U rn潬Wd1oR~|>4)\e8KnS++vO^E|g"r^`Wlk9/NWiSt3o}ִvt'B̞nL%Vw綥Im:|HraS ʺ[-l9\{ϔִ-dʏ (hO[4ց *T-jּze]yC`=*wXyކŭl,?C*ua߸5m<H\ʺխti2#בMBL8q%%%onjjRPPРd2Y6~:3~J}}=ֶ266VRR d|f""B\9];X&z Z 隌(~ds|f L=JTBK^ וOҹszϖya};D4WOa->+"?^i\΅=[5ؿJE'N…v]j.I\4rak:WJ"c/Tr_6{4B KMTh)N (ގ2O`-fX;tsW]9_|`ăP~'a ,YyvⳲWHx3 k_SN{;zߞK} N9vRl-N$=nmk:G_! Wک #IUG$ E 5>zD'"TX[x=ybTBKcV5IO;nvUfE*SN{nŒe'3;F,/ 7wSvvg+'] > rPS`kߏ&! TWWgPQ~:::ZZZZ[[:PN,..~lhhhoo0BdyyyUUU555ÇPT`=ߩ8x LOOgy-X0GƟ'6/AIeHQ)p1wb*ax-^aCzՈ{-~OJQ S JzCÎeo-k6`}zaBrbugS(4e蓗,8qO^ȹ-MH#meG$Eި U+9^Q0sQxC^6N܄e",BA@|JKYoPĉ҈"('''''fqpTQx !!>-Јh!@oA4' .?Y0=S?5#@ @1ߵ D"h4|ClUUUuuu MMM_uuu:(##C&@D >a \۾~m'|uөЁnᆰh7.M#4Vn4M?BL *E Rr 㣰6{ EgoջXwzH1:#ˎpXwSte)x-P6ڂ?[&CtK~B`TO*`bX}ӟxsƻe^ aa͉mͦ/C~_Tn~ 'u3T<hŹ&Vuv#lk[!֏kۣFi:;}h[AS[ORQR$ z79؈ؚ'a:kH и͍8%)g$Mgw;ʺ0>nq^]Vօaw!<{0.bD7@Vgu+s}o |NSACo|.ӍOћn^K>x}zt'#igiϊW#Y"'#Cm~9 kn.x- 9KCwvS\.m9gE6㰅wSpc~ÄNiaSz4ND꫑Gý\,9`]c_,ܖ_tQBgJ嵭=]WdNO,pbEunpnJf!7;ղ:!sš;._$74{ BȉWϞ315PV-O ?^j6?pJxkagI^=nڙYà6u < / +H{1X,@$v ]ða-zi!FRv!wE0T_wrP|RrKy̿Hr=ڕ ~r.1.H~}&K*(aXM9pSP0f>}yJ@qEǜ73fL㰸tO63̀4ED{ h?=%lyJdvWLtyc6ݕdO9olde3C"Oo5<-! }\TIɯZ ơ` {j1H)a:*C*,a<R5-@T⷏"퓒Wڙ*t[OVQP <}9JT^ Øf|ďK/a3Nl~xwH3,u/ ]Fr!=%< c^ ky F0YpcY.җDFDǞ3ee3CYAyM *韻s[3>uYJM+|L +U ݸs.jf[b㲒V^rA%н˞ܞŨ"3Yqr :V j'񴭼]0+շ=y9=[^1[yqmhJʋ @HcW<!!DLf8Bq j2(ئ +ܶLZܦVVܞJaWJۖvVSl˫Wֵ7o)>W2sZY7w#>p~ν*yuWSv㹴Pk:%jLDC +Tɞ<ۖ!B~YpWʶMXʿq+AAkڢy>$D4\T!h1;K۔s[us_%xp.{~V'谷os -?]9u n|zw"|[*:{u]M }3?J& FG]"'J" P0n[ sk*Ir=yb5Jr+oZr.ԥӞ+K[8SUx>hݯQ8jX![[E_9=fYhҥ+bO=|L555TԵk @HO]Ml~LfzzzLL >>_O3i&, Vb2$,v.8WKc@ iZ>}pٸwxE~O8+ eo{k1*A\_MRGPstvV}uٵ瑩o f)q[Em=aS(aeJ=:^=L*xǺ0nG]CO\>$AϾTGD o{#~gϐ0`{Ϸ~āX>ӏH}'|~O,>@ W@ >Y#޵&r @ ?D&qv>C6Ufގ y)iC/1g*ޙŸygZ(J,⺶PIJJ+3.͚]K-*s+k6K-RJiժy4L$-O͜U\Yk7IHpɬ^h/;BhuEY jIz;qiĀvн@ Nued0!}.1s` JZby ^2R D>kV/ˎ~>=钂QiԂ< 8|48؈770$C$&NAX|P@JTyB$F$$D=*1md nbdA6:GowRFL5ۊymG23I;.GTL]{']0nQ ~< {d%TmroS'"c Sw2BcK CefVw'tc@ @?E_ P6UQ=\HDb;B#ᾜOմ IDATҷ7qcp_N)xٻ@!(C-6|I4TQNUbb1U؞޼⳴,ntDH,Յ9]EmްaOBp]L}#Lqj9V4=wߙ^T5l:l*H+~tn]1R EqC7;@ Tj@qt VE?B;"2Pؓ8F:Oho _)tci`gm,P(5R6'!ٔ$6ח&/~R'ͫV/ '~ke:,.$.88)^W+vp$2݃@ A nŧls^PJY|xvwlDϨkc"u5/ ޻giSW Yp6GS<쩼&ʡq @ >r5h9׀,UTA` U@ 1[z$h@  /쪍@ 枰Njnݳ/j祟âHk͛x/ ˆB柨A9bfL}?78B :k=fq<M[Ӷ.nޮ0AŖ[k3Bb7%8a6W}goLU 5vZ&5\wPW3t}<* '"q M]@zh㗼,'t}{%.\>^>=#$Ι2ⲻJ1W.0'Dn3Wdz#EHqZ_"DrIdKq gLi>kZI Ik.Mz♜W+toAo>چt}KX`_3qX[)z֒jPP'@CSGLX" %5!L~c*i+nDzp6c58Ga K-%ՖDIeÅ>,n6E^QbBoJ%',?/?8v_=z&mIX@8[kFV׋R#dNdK)s<<`Y+Ȃ3m6'tdO6bGb8%mzD3ϷI-7\efvú!nRE3utE9r;D~< {d!qKl+^r^bLQ*e~ɅƖ6Q/5fz=̬,+o}LTV] B.lxCU▏|2 @\$?vYcqDnh{J*^ Ҥ`Q!ܗS h8^.lxp7V5цs8IƛJ9ʩJ O :f 9^YZf ~7^a:96Cu#)ܽ8sa zAvc~Dk~8(D ;/h8^y4,cY 0z,3J{*/yޙL]:Uiz6CV#Geӯ5v Wu$f+l;iuѵ'¥U4Ya`,?8䵪M^2C} D 9cz ^{EaF ˲K4Q1[4,MYOuU=yxn*m0EçiY4?<&|$kp w R{ uXjSi8dEDtB8Jv> ҌC›5Zw&#i{KY]I GL/ :Sa @e#UAZ]aدDܺ9c(':R(OOd6 [|VQ&dP|}9aFe+G{W|b%*lmԙ#˓CÓC0 XBhX*x2)CmJa@q@eg ˮ'…/ݓr#S!i/c H#<䟌6.NDv{?n9iƦb/T*h.|;S0)5kh4|$?wi}S>YiDʼnϳeWSp'#"PXH/kWn]Llj6HG7;ЈPL@\߰;[5ڐ E/:xߪ]+ln.#ꐣ, *cHRY@L|zM}y19 gq"veX|_ߤ8aB% oi|}䴹s|=so7|< 1~\y3UF/Kp5sKpmxU+g.'X!"2P@b}YX dJ46l(͍=15R6'!ٔ$悛 1ΪK@}Iuߚ:bYND,3}U>Or8Nm,N "Ujiξ50|hަ.pLTղV\ݢ vhyO81A˶X6> :В 'mb,ѦXhgG+qCr+G'"]-G{t"_QY4r_"mx^!Z֊-G1-V$&uw2Y~OCr`CE[Cs襈:/U5WIs21̼(e,% UZ+lj.7#),?R}LJN]nC&wq3uԹ?A+6Q;Rrr9`g|',U)JiiiIIITT1֝n= ݭkwN[0N}&l*2,e^ݰcw=(`v꺱dQZb"nzˋ E:鲲`~`}b{U Y3Ճ7mqΞ7v:ԦK]bw.wwѧƮ'2kzKgv4FöKuȳןmD3YEFaVީ{B س^2$:c\猀{ܠYW`M"+;u9忘F* j:Z^فV3%ɘPJBy٨i_cunXF}w__쁋O)wy[|'*əw yءoy}(dCtS)n-6ڛCĖ@7Kwv+%\/=9. `ǽj*ȿ)X9ۓQ2槊!υb)lDDdI4[%5-ޑi'm$ϋ0I?l_2Bc+9xAD`qϮU]cavHl(wg j,+Ȃ3-7'9q8tw{YKts TMlb wgPQ7lAWH(筘Y<9e t1${z*js /xf/y,cH((.(@"Q"y׵9RD⻘WGru+ " Ŧ*D˩] ^BW՗:[LotsϝU*Z&VɹuMs|BNT͍`PW1̲*=S*K]aE&fٝi\s߳(4BF17V+CmOu"Σg[jV8:?Pz:Iy}VcU-UoBHDef6#.9O/@ h`FFeV;3; .G6|=ݛ}f'bC3oՎug]'b~6{"v˳v}J@ D {.@ 4Dwc7mDԌlCUd#uow%ڒF@-cm,k!a!+U#w釲X.gdnf|=9Cn")0U'.35Cb=v^u3v?@d9s jmO1x/I #ܗeozߎag4="錄&G!CnK@bP^@ b036bv]n6{өupQʆPNl!mհ_󛒚pV陛Í3g qf1R尗Cf,)S57:8yG~tvOPR'1fZGhQI@q0I-XǺmD^D+{XmI|\Z76S#B(^G7S;ʼn,s ?I7k'`֓җ׉i$/3CNl'9i. JZ`) nU1k*RtJly3j:daQ?aQ / ǙC2n.=j$_(,*!"&9fʂEkˮ]G32P'!.Ŝ"%`,Q_ 1gR $/hBuLpyCMA$X`ʮ e: MګL Ā@ρ߃C77%[JoʭJf]RZ>lү%OޕM5&ZvqcŘkV13~" 曙0K| 1:Sx鄰aB3$9lhfMÙj*@e3l|Q0 DŽ}?sq"پf  ۋɎr3nd3å3_\k'vɣ0vz{`@XEߖk^Z?RExB Y9eF #^O yEK cl>/BصeWff > 3+QNS"$VR]!9% r+l۷byC2΢4aWФD _}yG~@yR Rn$!^:Y΁^Jn{ruT>/pL2v $aGJy~TIiMI$MUf ᠡS{rW|_]->)maf OPCk*g[4%aߪ:}dK9ߗSNUw*+%s}"%|_r>V4p 4njX :OZ!9)Gb7[edRvk.Gjk[m{ ~jZ,%9 e(~=MؕXjҕD ĀI0G^GR@zU}K{v[i/n+'6ðʺ ak9&ɫk#?G(Svs @SsQӥb}oCx:.^+ժaN [oէ*#[4GUr?{-y}>qSl:F># db{+:0z1-FZWExBHUkj$q_[6y/*ytǛ9 aψ]iF=x}?˚.V`DA,a.4nÆ+K2F%sp(~=MؕXjҕD '" P#2dܐ\i*/ jC溴fl,o-OX47\U|j鬂D{RWػpuuWF_\cgm$P{ϣg[jp C06kޢ8_iDj IDAT?L OwoW8{Ѣ#uίh7T6Rv^ 8ڔZp&9iOI^lBN+-nv0Gy,zh8^ùKldži&" aψb(-]W#o6Xo"i1keR7]?)6mHilǥٯ 2KVMR`ׯ_q|}L.a1B)---)):|,b#b›lN^.hmbgӧ uB:#ϸmzN+eG饦(墳oaS̈1*vmDD$`=q1C Z@8؛Q{@ # "4!P!zv#2?&e@pov$hF:Ů|0v81H$ Ud]\3@ vfS mW!PF1(2?׌, n\9M?G2p%J*ϛ5\HDˆp֐=V2p%RŪ*AmzKQs5M G>ӑhޔ/ΥO'Ν1T75+F֗e]?V\C8*|R 4[G/F߿JN4ЩqAq IX4wJsϥpHZR2/ٔj)eS平_D_Ck6w6/ԥGyS'#&"P_O5!Mf.(H0ǖ{ؔE+ G6V vMP ;<"Ι@jxqv7zcP+2{HF$輥+Ąx W8|*Z+D+r^ݻ^R+2{'h"; p@ ;#/]QFfeYy3 o l !C2UQ~N[D=wP0+^rŢ>/23N;B_"1q:{@#wRD WpkEq",?ƟZ$"gjIQqn]>%ٺ33vs/xAdiG=)g j,别P/C CC7 KÎ,FG8'{H'YymYĉمC%U֥^w~.U )տRQ24:퓒MmGicGb0"Q5* ?v/HDvqO%>{+&Б Uu @iMIcUvZ<95dN4NoOzlbV3V]-2C%Қ˒q(^ċ<9F$|Df:cpH)ѨeC8'8WHIL$'|i{M2 $;栧Z24{# ďijv3N.~B/3 h8,#<7!񏝿Pom.{~ezDf:+]@z&gu(Ӕ6fx~Y44="Ke%8hm zѫYam''`XBz ^v%nᘐfήm7elɺ@9RDuo8ʫk#?YJ>>lCx>5Ƒo7OJW-. ݚ"9z_Ko-bK˓CÓC'@PX&EYE"tn=<=,"# P111m!d7\^E`c ®Ln]LlJ6%&ʼnϳDdu+{|Bܺ&%9VD?4`18<˜efJ$889%zȃs ˪6}|LZ=ji*/ Ѿ~vƂ W+v"W4Y 6^*|KL+o*/xPzZI.28z@DDN $&0@c˕ B옃Ԇ/~ˬu%N[vV[O7|mq%7Xh gG|z銭y3{ e[,usLO2D Lmwf2!-1qPN4Ȣ FH8zRU)JiiiIIITT1fB ~ӧO%$$@^qNX'6 [飷"[qβWU{PpZ.?7ro(Mw! QbpnߣmN} >kY@ `&P Pq,sV:mJ#TW, t?"}cQǯ/+32}q"'0^Vj ]RT] # 0Bm$^C Ckɲ*B#͞HkGRVT,pYT-T6=%yAnq#̏0QTs0*.1ԗ%B0fT]GRvYÅI$ HaՅ 'y@|cW;=&|ɺq뉸frqY!+_ƾWt~ >ʛ?=CxYK[w nhf#/7o;"C# '_e0 :rӥ؅kˮLȉm~O> mj psHƫw$,6Yԁ!$73% rڢ _"њ+uf-{\ێF% JMyi" E\eׄ2f"rkz6u˱} ɹjsgB]z䙘7|*:b"9]LT"9Diႂ$ lMY@zhcU``#2>q)|xF,'eu+:ooA*Ʋb"9]H/xHך U#ܷ,x9 >`"f(?'k0"bT>OT̬.~ict4Xrst'ihOd.Df/Z]Iݴj*@Ң:k }Vnls<Y@ք_qϝӓϟ\Fc)ȸsvIJ~uJ~}\}Y+aޱ`Sf2B+n~m;F`nfjNFrE(][vev{G;y'> p'@GsGcL~ΒKAX .(r=l t8(~=M:(/K@w`ךB)dDrzoX}Иe}c! >n#Uۖ=tԦ[F+։F ֖Fv#,gTsИ|"/%K>60,”_~SѣQ~Dvbh8T<"qr0IaQI8*C歃m-vB$AЩ@qj}U]N1 7˰eCn6K((.ت36WH؛ҴCGϵ?4j M_fk]"$&M\f4^] T`|л"$}{gˉ"S!]~YCd¥^ +l2W6ޯrn]Z]s${>ΒKA؅5ӫKeutHGd<̲*ȸ;]yDg X]/IQGǫZ:`u9:Z\ #RWػp} !ks]ZMc3D6RW7@%+=az1*X]N ]Cb!S*Bζ8>S>ߴ*Wͯ=ivw{mŽ ξg@@r !xȘNN8/v!zͨiv0yƭNYҌ㹙,vv4؅gG@TxȾ,77)^,(ꄎkAp~ǁ9f66ۊ@iHXI5+,pɼ}[,W 4xccS|H~r^y.Rԕ kb'"< _brޢīokhol(}z<ܸd Vv. _`Esϵ]Թh"ї& ~GS!#gDU RZZZRRu*Ym:FmpCp@_?}tYYYBBBpp0d!ęm|g&{ryMOLߠ j`.x.Eo`,>b~!W6l6C<,tvT޾?+5H3"@ c 'Xl#:etC8 xŕņ%B.64mȋ~&hޔ/ΦH_ qܘg"ġ &{?B(O Oj!Yw/TaoAe!oKzg m殗$'"kn(Oʋu7GW'=8 TԪ+.8&^ V@}̕3ꧩH47Y}y)Fdf8z@DD{D6Ṕ,$TuG`j\SۘQ;od#R 79QՄBDEEs>n;=?}tYYYBBBpp0dެnf`ՐXgezXg^U讀\'uu%z⛆tG죿>^݂dW&@w!m\}ԉ9w:@ hFD @ 2]k1A΃=6ѧ ߕhKVB ?@$"A˩=Q% g؈ kk 3twƹ\bN쿇w(j\nfj/If|=9Cn")uը!5OOlD^1mc^3At밷nՕpAs<.0BX,.cs6h&)ܒao޽y?ˎaߗ G 3O7AMbHǷ>54Jkx>N&[8ҧ&_.6SD yĠe0O4bO3L x~gb3H3?Y6ķ !{> ] NSvr mO>,GH},X|y'75]#5[G/PeY0cdI"M9njsDa"V"/C;yO^rI-Xew:%`wi>n +Rk rwV].4J]w̙>MnC7K=S텓&my7 ׄڦ _OdT5־ejfe:2^VxC8sȗW,y#.3I~G8 aז]gHȬ6Cr3+,ND>6_o n{-ZNyƛq^ż$=Oaל)љsęK)_o _$7]P  uϝӓϟ\Fc)ȸsvIJ~uJ~}\}Y+aޱbB673yf9n)'s2C.ڜ{G!ڲ+;;9-e^Yr2TcNh(brb̖/Y`)p^a>@n¼[53 IDAT`}"19!!د 2#/ٳn>Zk$#\7KjIcs5D FLc'F˟~êwޫȏhc@^ϩ3}֢B ݠhzȸTnlj_*{Juz*js />ϓp,%lxSBTEIq8m@.ֳ"_pȼ}ṜO!Ҙ<ѹկ?WwoΜ}n:zP[h2]+V HM7iw7+Vjإpץ)$i+;;0^Ntv 銆W%ӷy`faWr̈E+N{WU9p.ٹx=ڊ_h`gɂ WrZfw]槺݈G4aWö11bQ='Xnq4ӆsOGe-,c2jYJx܆))hԌ O-*Zgdz{UTT&HdhIIC233t9cU||~G195Mʿɐ۞Jl*H[l#8ayUFcRH'wy\7ߊȏ!ao@SS-)J #\:l2=q=> (Kxˆ7j<9$ߴ;\iLZ-,sNn6%;'ړwޗ$\}Ӿ.R֗Nr_&w- @9u\7Wx3G!+3Ewӕwn֜p_hY]sY̲,,F ;Ww)Wӄ2lO'eXgϺ+A0?̟)GƸG4n"VT6ī%Ud{L"pNz<#D?Cgׯ_Guqy[e\Ƴǎӕ]@ :#2}yF+}as"F>]fyXj3x,iqX?EfyxC> }mwNM6T'=8۞>SbxU*X]N7s 1Wp9Vk#ثg5}oڧ%_ }iTZ|_%2zҦE{mŽ ξg@@r !xȘNN8/v!zͨiv0AyƭNYҌ㹙,vv4!jwyF[9&e7˂ [q_s:{80Ϭf`{\j{+Þy#3˥hZ9\سJ}"z[cט[|JYszRY0z d]JBCV'>2k&$|p8755 6jܸqN(Ԓ#s#5|?H+fr AטԘE4y蝐IhT?zgTM4s~ݜhy5ч{~UM(JiiiIIITT1?o/1x^.hmbgӧ 7-zq{<%:_dFEqS~-xcۋ,9zlqa7YmD VmRR_҉}ꚊϾa=g낍?&N#b?]c=aHb]-4 /-}2*dx~D"i/V5# @XXÇ_G=~WHPKWZN#OY<2_pX)W]}SF.4g K=Vvbh;2,KOILas77f\CyȸI*[T,4AyCح;9 Q~z O3^B?%ZK\tIXX˗7o|ݶm899xjϾMMM];\AM)2r2ݷ~'-cQ޿w{IXc>!yr0PPv{9@|">Q@͟8hu)Z͉~]?j@ AOX qWW 6deeTUUS~*N2c!_+p7(+0=^s?Up֩cUpt#qd{j+|%ܽLdt4+E6b?-4D R_aة'L(/ %""U嫛Lk,|sfǮ_L.!Cq4F݊Z3U !W>*n=AH_Nj2&-S:ȑ8k3z G،4 D4yC< n\60=A}·s" &+ ĥƐx.Ymw"mv%k3rVc0-CDZc_ho8ˈypoR%8-ڻ^#}1ڕrv]?X DĀw*92PZGO7k5#Ok&MPӷ#.RK0obK9y> {"2#&6rmYv#M? s͝p=0{cQӝ;#皻>Skƻ_Xb$naE&܃b3AŸs򿩣j5gf/~pmvJ/ӧ3wҞ.edV$iS"yxXFcy,?D[()ۣM Ѫ4S5f+_םs9;w>s=.K۽!(`P>@PM8]=d|Fۆ/>pܾ#D#z%2ͩ|ތOv_ LyU]21TI<'#oBPTVb#-ۨ*˲ʲG(>0iSeo3)pc2&1iޢMYp~%7L%1;MYN/nd`3kFT4'xl8wIm 3~dv;/*8zDDlK6` tڵ]T6)j.F{u6 oO6=(jdi7z|VQ+Co(8@7!-_`* ,,WeTw2s;3C;NԷ Jv\V2 .yoOwj<%fJ4EF{|w(Uz~/ۘWZAc^_GV]%s*؍_D|m3!6 ?zZF(xQҥ|#WNe*T `5jqaߤ)l;р[6>wsJmNlGNśDŽ/ z~\gYvнa33UM#qˮ,L G:SSNBOCNYN_2Q]:>Ld;b#6Ҕ܉Is8HLs-t!ɫ Az1W)I23/SUņ¤B&[.;F.'!)BG7U;gD3k݂oq2M+}؞ӈ_XN4 h-Z0Mk?&wPU]psUca.4{ ^s%KD*h$W|w̛.Egݧ5 ۔4W[i|xb4 u-/NqԶ=:޼)_"׹jtdZP?Ƿ^M&q*k{.a58R_>.!^X qq fQp#_g555`0ddd455{쩥%'''!!!%%%))ٱcG.KFp t!_ +6G_dڮ_qZ}1}^; @ZI.8qV] LNTuXG@ /݋LgƯ.;5#f[2QC[7 ݅iDD;Эk34Ծ Wۮ[ڋ#oe?.QrY|YԔ`3|װőu7:R|-'*e-,P.hעBB`ߦ0Sc95}׆M˛`y7/Q玗e>Bk7NѩeBWysZyi˼φF0Q*UCCCIINKII1:8.%%*--"C%^D@yWTtDD:SN/IG9X8na'gPNx Nx8=_v">1m8=x݋cX.UF:=c>/ǐ6.}e*t߽jS?eC^SdJa)' \\Kv7'["+_=:گW9ih-o|ⶎkbjF\ yM_1w7ob r_ c/|B 4"~&4 DD$=hXuҽemӍtA7J; tiMo6]=>ջ[? f1`f~yvQ_fYH;_ePEM%Ue\K*DI_i _,{QrM? >6УKTȒ˯W0">~` tڵ]T)mjEd ^IwxFFD8};"M!TSSQWWgX999߿/((`٪>|57di[Y4=cya|-7^vu6q'}B g>aNLlA$]H/3vt+=^N}dUd,䋱VDn~S\oG$rF6jGqBQm3́> vwm,Eij̸g7 z# ZAu:Aw! 4" 3;\ XC~z/x(ImxzGJy@NͬK|ԿIw-.K(^LVK(Dha(:qL߉G$߆AZR!ܘ2黑H%8=>qNi7YIj ܆.m o^BKE\R}'A1z6rfD:.S-R88T /+/*qǏrrr}իW=ӹsguugϞ%$&\P(/ &6cLan )>cS㿗98satIۼe/Ѕ?`5qg>+pKߦ$;>*b )j,ւCsy٥͒Jx'*I6R5IYr'O4#,=QnNz]Ɍ#n|B Ƭ̚@E *]BMx7%1GkaYw;">.'ӱ]빼]mLY% .Y]?1 "0|l?s#tYZZݻ~n^:=z#1 њ(؈y4Vry:+43Nbպw.++KJJvd?mIOg5ibJ֨⅖#22^|r@Là3q Ů}Y#/QJ=7p[QQ,)5Kb\3S^x c=Lt j+}E:8Qc&lАSƻw3- )1?Tߺu*28_J.<׾3[]uYV]h}聿Dž}-#df8VZQ.~DᇠAДm ?jn0+E4ؠ+UP__OlpXڛ|u1am\ؕ{E?yݔz#pDx!O}i8_Hk2;-:^CkD}l9ֹ5+iI!Z15Iv8v9Q6` E7K1#F)w.H7if9 p'ZX4@TNرC~:([{G[5qĤ32ZzZЋR$rp:^ez={\o1r$|̼;6QȪD1.5afd{xϑlbf2HANJD1Ny<_8k4b4yyDE$!낏(o0/K=Kw|!) MK9&e Bx4ɐw*;zq9EW n}8v-psJnzaB>)bczAi}K!_x%^=gF5"7oW*kE76MHW2eS}3%"=e;X$*rE"\C}>+TSd dVJ/l{#::Gf}r %JFW n$T320Cș]E$J(NfKA 888UBĂ.߯?XQQKoi9n:QUڻFl MZ? :MTws|)+ǿ:2 6.M΀C>MHl8]YSUD\&g}*Nś{Ṽ 74 #)AASg4X]63~:wu ;e9}\GYv@0^]қ>gbMIʭ'Db?6|WTT kL "GyI—wpҲkij\nCCË)\.kŪۑFDUA(d8vrgOOwnۄ.&uZqeeeIII׮]mq`]-P?W9v̘1]v.IIt!G)гC҈-}聿Dž}u*!F a)jjjػwoiiaBBBBBBft700hŢ$lYn kkjhu`f!xA^=Եt'Ԍ*뒝nׁ^Vq2ƹwb`:tZe3ٽ_ M|6+PիcMtuh|Ut; =F>w!ikFD Wr򥘘?c;vusssss ;yJrA]KWSívGϽHܹ/eH0)L1|:ߙܢwCػċL\*-y~W9ih- 4 o鋬\{1qML͈ <~!+5y捘4bSN(gQ:hm@h)ƍԩ:ta233߼y]PXXRR]T; VD)[?+NwsQye8e%m1Y;:ټM[\% P>)3-0s 0Iɕrv&2և?cjlOسY{1mL6Pq);n$MIOYxӧ >w!⎺M~{ @ Kێcwwe}r S[;'{gevp\T`&j2չR2(5 f.BnA2킻m;t9''36?3Wn=^)ƌd g|@_Tq^Hҩ/?ԑ.M%&^TFb$e5͝")"]@ha~r9H\:V._&T"ܗ@OGlMPhEysx`Z r=57{ޞu :꥽y#M܏e K襽yӡ[PhvuHV]knksS*'t ?@N=aǣ-8qbra=\'Ii2RGFF@VVZXXkW]~*n f=۳rMODƓ }T<n:\(=ג=!Mx߲td޾ĹD"ы7W}|1ɎB[p &lFڕkC~7 žuT+>>'-36JLň!13fwlbSAMpDq "׈=4׍1%=wq:Cօo1 á:"*E>.RpqY1-<0=ici92\z3M(Pco_ ^?AWhS >LN9M\ưazI![eh1TVk4G(+׬ݺO^MuU?EUL$B4bfܶ9o\C}J?6o+ƫEQ2qa iJv\V2|l0~r6)EUUzf3+Ub9̼TRV Ҳ *j'51nypA qCaFfh9KDi۩{)HPsJh}@$ҥAFee y=x8HGKi#ӈ # )A5n5h9ni>=g5@X_3*a 7'?s+ }jWK4v45-pnffoܿqߥþ^7Owz?罫bP5gFsm\=%`ẊH#"H"?TT??H͕Rh4t9Q8ڠE囎|d#F̠Xó<0-P#`e\Hhuop l+t#FƭqmwM hycA EApɶԈh!j4X}y,6f駏미MsyeUBsD BDU@S=9_o_iD(58 {׮0toE ?d7 9S .a7eP{CKzN3BIC}uA%^7BD'DK߼i7:҈2*m ˔U&?D祫TNرC~:.[{G[`IIgh wX4S}RJg~pm\]KBȨt 8|&x}`3A3K*Uf}B[\u-:,gCpx{1u >;lue[2 1(oDn5}#x|B  `6@jmw-s3A rT*uOCa dLP4ʤ73{3 kɴn1b$|̌G!&2#RNRFЬ7'Ӭ(fn0@hVCgƥ4S"*]zh}C4" Ϝfn1kM--f7pY| #>yBȔo0[v&m$+>êPyTڽ'r!qo|WT?0ar P$4aZ.qJS"ĕpK)NxC!#aE|kt ShDa 9}RĆGn43g圜?_gzRD%I.J)[4#,=GN}ti,4%D#8IYr'uMH0SSDVˆ ToGl|*)7S(2]^7e';3dT{S~}w|ܐ zZF("QZEvj5[W/^/ Z.88T =~~jftA@{Ęl9m.˔ 8A.N#I[ LWq!O4dhbPZ ~_uY1Nj r3_9|_TbUqu&̣'j$pˮ,|%+jC:i)~ 76V}t:y#} :/z& }FhJiph[q GcRjsBY,=MRsQG !5sYi]ZۤApnoN kIu=}bMIʿ `ⱗ!6RTp焗# c zyx}6Z%/ZXfecV(]e,9w-xin(iD5 su}~ϴ4"҈?P~ 4iD!}͏}%DZoݺj@ ZZZ+[ލF8@cЄnTTu~iD@@A345 m)he9<{n !=}CK-zt}GE@ ЯZ4j/4ngR)x~XP AFpx1r̚CmTO4$]v rSّytyQe +xaZ;;0]gI'7n_>ݞ/{J5 rv{Ԉd7 9S .a7P(}F2P(AB&V565Q^cYN#io+ym+5 7ZwB&D;+!q2dâf jc5}ҋӛ{q-*bPὤf.+xwsL>}je|Tż;f?at^}u9)\Z.Dڦ DDS*̱]Kr 1kp=(7 !;܈0]FHC1ꐔmIA4~{,#:xn DImT;ʲʲٟQX#6@ST.͋;.5|>q p$48{*\:˜,E0<ڴ ~!Ӕr%&bI;61Ok 9Y*ֻ ;o`y t\Ժ.5yWx(c{rG ~j܎LbܼR US¶׊:,QFOǿ~ IDAT"o턭&7wJ[!p K]KBȨt 8|n >;lu.hSzzƼmϋFgqAnMޝElJˡunL۪rTJkGl#yUYVp4D^~gդ/I4 .+kECR70X44D+W(Jm ^lѹ{C6D>b|$. 5  t[|Y:;3Y|xuu;v&uܽWk|wtZaZO؎NC/ ~J||/QT!+Ϻ/ܕR1ѕdfC!-GNp/BWe-F}pVqKf;/xخ"o0>ymw NqA. i'ඎkb& }bm'DW|9l7.hiP4|ծ) bSv}tE} Jhop٭Г8xYYƶ4kSӮJJ>w:0;N58OT ꃬf< {BO~Pwp+g[ &//Lx_W7wx1pٝWC_U=fJgR6ދ|{PE?N'OVK;cBSiyu'EA0\7xL2Hy-஖ӽRΌlôKڐ[;< @fh?ǯQf7u-{]P<8v-psJ^ wâG١,jdi7z| ^+[OVLpp#_Tp0Bij7 m4[͵rvTʀް("k<B#xGz79hшok uR d^iH\Xmnfx56]l]WOnA4}ųhBo\}3%"=e;,z Wx_[/EQ2BoKݟCWm  x͔+J*yoGs=/J?eMZ3BS\Im"CߩV~6^c h pn`mYtMCk39r`?[Jg G@n%Ì =2rfDҏ8z飌4]\]X>s?3 ߹:vo۶bQ}(r:"$hc$s%ob+_7{ iՍ''#ɛNAH2O%;eid,u|^nY'vwXS<#WJMVy~W3Z]c \ 6쑽|O% 8]YxMF8 mD`q4a΍O%z@I'[Ut%sYD C}79 Tj cgnuZ~Ua.~^l68Q)0M]#thS bdvIqBHz. ]X{{8/Xc%ǯQ䖨T˞׷ DkB9skJRnE<GL[fPS%d0eh8sM bt39KvǥuIq]V؏ZMr,QfKs];/p]GuM/^ϗ5uX\Wp@ga^ ngn>=} eb_o9c)s{, hI6dznk]7PZ/kI& u1zgD >GE3{V1^c=^g bcE++ xQBFj^, >~E\6ucwJ* .++&l-OΩI]\cfSx}>jl2a]ЄuMY3ϘQ4tXUU@Z`dLm A*#z)|S~lJ+gOOwnFsѓEMpCy53X ޳v˼n.jݻw%%%]v 6;A=i5yD]K%KcR(4%kVDW׷??8}聿dž}"#lݕTVļ(NuAPE!ژ_d=hC mozYrCMQ="Ak7"9:4i6C/LJQ=D+x ҈>|="ⷸ0 Uogsk9skA'O_oG=h@ ~,^a%Yܦ?FđF5"/ָ}8Tѳi),dg铞ȫ4rLBVp/Պ=,Hݴusk.Ї@ @~S?U7BF~G/.Ah8t}K_fVNv̼%[NQUee?>?F0MٛGS7/D\a8&6@F<{WN0&7h|ڞ~!Wb-F 1ci s1:HANJ*̍c&P;QGK{=}Cu@ CX ̄,_R}8ɪJ'Wdhajd +KP (`.CϨI:*Uo\ݗ^T ^K6ӛNzMָ,]NW2p=#?P0ZN\̢/D[n.M0:)1'mD<΍o2.UNm9*5#q#ʪ,+`8)yMy^~e^Ӷ;d"t{z1)?hfXjVRvΙ L3bD!Ϊ('nt&n9nf5<ޘO6"ڻv5PYAE}]ǖdJtTA'҈ݭU}]Uw[BE>{B*L8̛Y>H +K:[S#vfKpy8Z5ғ8xYYcBR{XՀ)iWAe BT%;|'LLAVNyژ\#P_a8Tݡ2\JYCK=LxCIs8Po.}E4EF{;X"-'3X|]prO=f@ ms,xe5 鮓Uc\hJy[7(23gլ򨀗y{ Odq1>T|q1Ç2̆sP\_OcIcG o(WV]g>3}q t[|i 4bSVNudFyGd^)I\٘56S-`1NlYEUsb};|yNUP&?6e$0u{/hYk醭Yy(ˆFQ.&kN=|FT)C MgW9'aԍQMѯ q㉄1q+qW7ֈhtՔygbjZ=N`0T5Ul6q4Fh)>e\)ASeZ{?nc&SZq;?l3u{^0pM DZ= 9skʧ5;J Fme'L9bSv/-Ϫ1r҈IdiI:UK/p8S6Kq@[Fhie]WݞO$^)g`R)m}XOJe)@vq*tYQ\[a(`el(4vFj{>/ob}~"_FN/xuE@YRGӮkhPMA ܫ܎q{nRv^DD%I.J)[Ҍ"[Fa|g˃.z (5.C8M+r+ 5_XY:出O -MG/ؿFgyF]d%b q>\@:4C"~DAsNv#X}OZ*Ҝ)"`(T]WWװuKyBmV|k׽){)[No}g^ν8S|]8arXKgӥckwS% $1\pbts'wnYuUeu}b|IKO#z,V9_2ԪoϵpvKAMⷦ)ׯ]aױ:HHKu(,(LWNDDd[q6}w?kD c0^d: ﶴheXd5_ymN+ʗrp Qd,JwvAX-ebȩ|xrOfG?{WNMFVҐIڊ\܋ȸfl(K)픖iqt:sJ[|s` cYpĜYR:Uƹ6 /fMn+js~ Qf֖|~\y436Xm-Uv@3ۧhe~rqYeչ~_ -jV{ OA +vϤ+-λ{u c ўJP9mȈYI͍EQUg;F/)sZ}3UQᦓ-DYa^{5<'_%$󉓚x O22f>8#=+#u5qxqϡ+BrZ&6vAa{$YmцFj%?*!(gU[{cih&ɍ%.1TQ3{꼇GfJM0fZq|Du b3$Y9@-qb!.fxiMWo!M##7=m#yr9 RHD(2 %$$$ :sVUeetlLfzZuMM0l,UAA(FäDƌFZYPUVVמR\\(ӧ=.,*_|)M LbPD5o2T&C6vЈqTu+ŅC<-F#;N2jf*b"$=0_0a՘4&ӣn6ߴ ~c|Fs`:g@t!8@hL45j֔ g]ILV|Sdl`01 @`XySLD^n&>PX wq oc2$U|l*v&d&Q j奾dH+2Z^!ϿhŕRY,>Tڂo)54Uo~ %Yj&>@H]7ׯ8.e Z\ժ4x+;>6vm7#^] 2鹹AAAǜƄ@ Ǝݎ...=|XޛE L:](Eri{W^}lm0A܇h_ v`<'Q zWfT\pR3je K QtCH?7@ H "=p> ;6NFD ΁4P[;%uꍈ;* EU4"@ Mq&QSҰ!_%.=n _NI>5-UiЈRV/qmmw">Mz8셮=.9.CPW߫Kz;j0]BEٲ"ngW[2Da{ _~.PDVֈ. `zJD|ל?31}%c9ttsqU.F@[Znq pfͼR6+2waa IDATшsK-*~h݄(PRݔ41q?)%5;3{F Hpw\Q;U}~Fk>@rjQUwqrEpa [CDυdVWQi$R;ed;tMZʛcseh}Oiz; DoC]aM҆XmSad cwۼjA/jv6s]]tUJ@LCݿѬ%K|}m&PV]<8/D;_v5reR̄l5P&o͹9/:D+o*#<~/힁#g(M U]pHN@j8ίe+hޝTK{9j_;ѱ "OoO`8jKIF@?3pfMb_sXnNzŭQv^sвFǸo.Ϊa wr pkBq܀/ 7iڠBhV4Al8#5fqG wg:;t^K' v=w^bȢ2SMl7F8Ċ5居ӹsniw>?fqGy^:R4ZO/JL,'#d" "}_:m*j<D%:}!iBd4m|tT30;%eJ6\f`C/efrW@JbQaщ2]~nJ{sy{qU ϜtN-nv\ gVE(NoȻ$3^b8=>XN{ۊOJ(n(~9~b G?Ovy)ԏ 1ԁY/? wP,¶fg9XN|ϻTWnJ4FF">7XN0#߰d~xBE Lc̓#ணkT}$Bm/[nѷqY5L]E9oZ=[~Y@ЈCHu[#"K#EYBճcCVj՘4&%7P!~OzY:XG˨HH3[Sŋ*f -SϾCY9l#' ~QPs\CB>G9C^#" wttF-șo<#4ꠌ[~{VmW{{V[L$/匂#,r}ނէ>S}cƪ  oθesu%΀3[PGNBJc}1ESȍ 2@~o.W)$fƧ̶ooМSjJs [tGLG {2;'L$}爱sKuU)@/:4?bFJrQDitjY `*jfSx!9ހQvoj^,q"0CՌl,j@afrihC)e /IagF&̂ϣ`"hYF>9k %1q)_(F{큫GMU%)9z%sKbKAxLDFl-~/,xr_yT3debS(®1q ţ'c ?cvPo2QU& ^!@7KQCD, FeLu]E\YM0W Gph=%L }Q:V5ȋgFc(?.@s5EF[l i^CqQUO(d, TF91y=aXGgMVzU)g\78{o%6*222xፙˈwo.Jq `n'Rmqk\ x+'J IwlCBR؞"+I~zx9q2Ҏ?ID)+N[8h3h_%hMɻvȳ5BV)Ia%j.m#XqPPr1PR{~p6F#|}7߭:ë_*X~іY{H3=w<zX*y5+tznnnPP11!m?w9SYV;JDNnqUE+0vvtqqϏx$>sk}OE L:](shY/qXwOΗ1mwE b7ᦣAj%w^=x&Y ˻&;|È%]B.shb-+/K1EV᭮2^i9 ٵ;oPe'6'8::gg8Pq[682%眶wGt*eyO<֚>G];t%$2iDCtO'ҢQWH#6 ZSx?HVq4:@l9,ę-,]g\FGhUv%~";TN}ڐ̶4U[{zuZy BosuK"Oש @M0Xf 3\Ng>$h\f5-3򔪺A42m!^MvC5!5Ho%ggW>n]!þ_g1sT5։a2mP]UJ{"T;WkY(vԞ/9B8>,џB#aSWn lJp-mmubID)f\5 "ϊk=kDozej5o2PC :ci;J4Ss1JՕ팇{LU%)9z%sKbKAxLDFl-~/,xrVGԱ88Ψ*ʊzr&!Fw9 o>jmzV1Be1JRTWvlo|},d'ZU{eyvOVGW=Yxw?n3BZq `.j2"vX`#DK23SejooOsss9@@]\\#""=z|: IDAT7kA1ʕq#FbӭN+Jꃔy?vMNk8t @uKB"ݮ iH#"zJ.CT'H#"zJtDz{%ǠJ@ @ H10I{%u5U}UQJjYBVnrE;yq@ =lCNmW( n`f˯ōNyfb崫^ D $ȩE@ @NOQ"F{G( )~5&m`>Rxtjs9(ɨ-21ڞ,;y= 3kB]Q[gXG1kKR>?vJ*6ϙ985or"`@X̩Ha^'. iIKV'9U=Zxm.>)7\m4 1Q {՗kL @t3jRAy|73e4,Y&-)ZY||N`nf$ 6Tq%m]#k~/u=?FP@ r MjxxMMVYf -FKFb߄Ξ~~kr`e뢃Y|sS"$ebcWΞ#n׮λAlq;|x9ɱz&qG/bdQ&6ٌWg4%"Ll/[=p~!9H,=2db}w/}Hy ^~XO[jkOxyBz_4 -qC@t Ko42}Q엔)9pi95c3gDKYyI^S X:/3j2u;}y{ډ~Cpu3燉RFCЕ@ h-sCB"8"Q`Q<ÄWcҘ8N|ۓ˰T&C6v$f"!&B"a<|~exo?`0DXO~/ Ѓ R_ިC>Tu+ŅC=-F#s׏a/^@ :И$&YIT3"DTڢuf;? PXdc<~POG?D~0ŕ{ )Z NMU%)9z%sKbfQ,8Z_ZxB:w';N*3$G @oV 孻LKY|/Gf%r]kLxvYx,2bcL|,9Cc > ?jZQ*{qYk40+Hlc,ZEO v_Shq9oVVa/qFUQVԓ3]kڃw#_K7YVmpY.ZU{dub%ߣ_=̐!:16.#"Z 0o~b⣣c@HؘQ&ORBA!]R1{vtYFjܠ <D{pqqϏlIoO:ø#FbN+-ڻ{ZMIGRV}W;H:Aaݣ۞ 3RA笴2HKKۣzA t:ӧOSN߿7ϟrwaaa B"A(pצ6D`2Tjzz:aRd$8Ft^SPiQtB>Ennn]]]yyyqq1]A +++۩;P .%KXٯgyy`#$}TkDߤ,==HHH!ĝJH6k# ll8zzzH$ ۥ- u43U ҈Q/jD_&??o? v$ ) H )]jZjY;snWO"L3|kqI ekF'=[m\V #-",$H :ju`X&O jKT6wlϕH<὜ = ~J'e@XԔuɼvZ3a)`$48_) #v,vFD6^ʯycCFvCfdo=Z^xC25"zqE٦ >O__VZau{/0kKSyD}-Z߫Pm4u:CF rU_lHe.s & ʨ/ Bԇ}yXִu%eQŕ0XSÿgcJv_{J|5h+iv/ۦ(;Tu( ㈭.=].yR35v~ȯ7tNE_hȵ8 GUu? ўBNi 56C]G^+{b17;}cGqYTTaD5]HogWT-9J͇=j0em^xH|:Ay[~}j}NFdkD+ 斥\-F4bCq'Vs[HVxmGIo!YMcs鏏=ϔT\$a5ӟϔT\$%}UˌN_eaebF?9 cS`xI?d5lȧ\&ⳤwp%d"9/|M,*(/l cV-32s3KadAS6D8Bo>K&[es)Fgr + q46 Wb2=~Sð_LPFX'rŢ+%gi47D-  H#"w4"\Mlw!B!@f%DH,ġ45j .CLϏ9"D]7&\%J#ګ̪|ÄY1c|Ao_?~ޝ:q4}q1^^G4J}i>QFDMehC^∴БhZnr1)rġ45jXw['S(x]MaFka$`lI}pǶMM(c{~%psz[p<;371&"t+σ}y/$R^9V;jJD%3Y8oλ+6'b]!O4{^`-Out{v煪G^x+4G[a1/_n$ ƌh{V::D=V%=;;6qc}\(M4as)N#7Ky~YFAwk @D}u'%)*@ \?FdDb8S{iJᗑO[no4"nO̡g77m~Yó9~u>8 qBP.T㟶{.Zn;m S5b缉F9aj3aΝ$>wxW^/"!jkL4ehk(zQԼ:lIÍo6`G[-[Zێ2ӤZD/ѓP7bﴮ-|iÿ,:zKqtmY=" 7ך=S;uݫD>i; kq%E z@3 |D_':vU188ɬ2qp3C.tEGmW Pk X &g]e|]FNvPX!D `#:p7Y|*(Q *t~ @Fj0DQQ&I䝌1df0%Λ,aSdR>_-R+@I1I/Ws4,m k+Bx<\W:gasM+I}l\v_䌉HUaW^XLZR 92(3۔HWg$?x`MbJiR.7(I0g7".*fb$ c; ^A.BKyB]Ijq9e,c$ %B+h YOC讀U^NH7Mjec%-7s,!(prV)rS1SU&!*qk1+u3K`ˁ#g(M U]źRK@VYfY ^ҿƣ޶W"?| z| ޽ł]e}XBfx~:Fx_21'\[D3޹ğ$QHge1fM1WPPcmk?XHLAq:$~#J{TP=}TemYUL.2>ŵS$/T}m`w*,"NpNuHh1tjU{RPDhd .5{4֒wՉC>= W%&-ۛlՓ$GZnhwG&.. O&dJ1\ndƃg^eRF?9 CTM֖ngl!4_{|M,*,<q7ʡ$GZfdw>,*=lCaU#s_b"?fώH'jEsɺ&N~܀j4WLć46 o LFWƟupuJC)TMϏHN'2֍uĎLϜ:oOc#N⻚g3٥e$JjIa{E =z&ՆrMkXZǜt+w+$u-dN.3IPz,`9?2qZ%;Kye*1>7;$2 @N}^ۏ XizI$#TcBu>@N 0v#+XwKECmP:1!Q[j"l°Qy-$GWL6`ӅQ7x1qqswdM.!*B0"aBkL݁espF+MOIun ٿܻCWcsҬW$LO%/wT%9SW i(uV~e׈H;1U ]ퟬ$^Ȏ訖unIm#HiIT9vd#f/>W reA`4O=xY  .[jՅyTr^97 p \RSB0 `rd+di9jKV1N/=1︟)gHU!IGyq JD) @qtq\] r? )nDģ 0fPjwJ*跰 Io&`jlD&dQv0AZehL1+_ZYxBZ&RQ:~xȗdGT5L|5_"Q7]>Hs漻ᐐ],*ZQ2J>,1SrJv~OHd3d,XS`׊Zq4!dLU!9ڀQץ EZ=҈h2[:yąK"0ˣ&*i[Mԓ(yGr2BY|kk/kcK`cRb %ՓeD~H\r"a/i&|}_/35~N=,+ `$칆8*;(K6̝$>v_LҸQlXOڱbmFDȑ׷\>Y-o,dB %Y~]ԵQY%#@!Q77/_D="cnp9g(8{|+ol,[3XEʻ+,ͅ~ZFt `.Zj3Àba7lu#.GBnz_foDs>"w}}.d'ZUeݎˬRdEvݵ̐e~ܚϵhlD>:Y2#R ߈zѯcCKU7 F"4EOT:<6;'v|t o_F=9Xp Uj1t?deV$;1@vvS<=|Ϣgk WOdu.6/#jw /mnQSҵ9}=Ҿ ㈮y@|A*SH2n ⎧#/%ďe#n6]V;@ @ hae0tcJAb!Ѝ*i TE/@u0Ȏ@ @X3V*B zF[;8%[I=T2Ě88a IDATcXQa$2&,<ht,2kc3˱{{] Ξiv<.ιZ#\׌shpt4ͳ=R9faVwUͭթ?V`GۢfZ\\_if@yiTuvݚnւeL>3Z #00bɨԙ /xw4>13gv Y{؍8AGuؙ5ag' 'F|,%p# F#?BBMQqJį!c-.3^vfR}7ǻ(""@DH߻dt'Opjl5ayكC vV#?wBa}]ɓyyy:RPXTN+ݿ}vwl%Ћz.[x/JA{'LD[20>o|gvCK 5J Vwn1-{w5~ i%Ν;aGZZ:Єhdrƈ%0 Э.8 `a8dg[/_vv7:jbdGgMV@orJCd8鏙0%՘[f @ii9s@XD;MqlEFmel3 0;3 ~۶.#b/X7amf2 Rslz~^_IYHr&kN0  |UUUJH$H@T =Ef@vyC1u8ɡ9_ܩ+yDR|!`ṗx_ƙA_,WsFa┦5sK-*~wEa굈LF:rJ3ݿ=V=Ȉ4b,CpOYڌ5'=x[=Kv49갗 .mx,/&>lJT[[[\\d*Y䀕ʾQKUWwC殦 gx$h֒Ɋhush!sw蜕.MޤgzcE Sci,itJK7œ@#[pnT ?8'!ޝҨv]66w1-钺}ʓ8ޚ:&?x43x;wӅR X8%5kNN^?m$1)1/?ˍ-\K^F93d eyO( &<.:-թ]UpGM4Zdu<1~O ~<W'>kfi1=t,t bM$ONRDcccuUk  P(d.u*x Զ Q7kE_BhCQcf2:UB_RDzazM4?{]E#vjٹo ʊ h"b"6ژCנ@83Lb8K %5 !~JT4~z}(+(PE1eqq1aaaFRѽ衝I'KSx5`@#SsibBBBٹeYYOzK0v4&)*TFN9屖08Džh)DP 1 'XpgG;oxKAYsS5ÇjKn&(4Q_Ԗ 4H\LM |MIOLL,((a!aIȡge6=ߩj,X>lX^wsDgB\O蝥<{_U̜s-WH躾ꪋeߵE\캢b_^O'ĐF@T^>'sg{9>4X/l1Z~ʀ*ͤE䑣Fǟ{i\{NGe H Ui1E%¨e?vdY e* C!]=eek֯g E#bWBuI;zLFxP] kB ?k}~]G)̫b'0*p{$}jj2{?Y{M0<{unw$@H'z!11QM }pȀ;ޱ{Ok?328y7 # [M*!S 8(;ғ UPPoH J2 F&"Ra._ uJ,#j4R Z0"!\~YV~ k֯;t<"cFcgq_9_uɑi\Wjx:b4OiIT$QE DdAUU{Svܱk.BcQԣC *ilhsis 1 XIɃ%^s])o@cPw l62E !Y\yKo7lVk,645a 홨)$'_GDeuy145x*SǏOD@lҭ aQEQףB0D!YV,6ǁ '0~ZCS~Sk#O)_Gg؇ 4De5,GDT*Lv?:tv8N1!bcfM4w\xF޼U)=?"P~֞?QK]Lnkq3.fњV8"BQmi[}/? 2t7Ow;d% 9l&4@u>b `^Wrr\=ҋ3W_p-]Qͪ-Ј{*ꮻ̊ڦmN-uw^~W".g5q~i^=$`Dҿ[ t9bY~GڠQ !5߯^_[S[P><3sv^ār9f Z__e֩M1 ‘H[VniRҶ 2`cHi$HHR( P x}F@@TRJ`psc !D)=9*`q /_c^!TSy&><KYVԎbCuDg@Ep*)>oO-e@!{m))2eg}4vq> CG8\WWwхo޲#&$1_P@dMfRv-.S_ۋn[:oy/kUw:p;}ck #sn(:dۿLCUTV>x~[mcE['j6UɊ4D/d.sc=$/a ;9`hŗ1ePYn;D{k^8sؓ+ ܳow.=0VJw ?+sGz8S1(bEg0HA 2 )YVǘ1*PDE҅1'K2So_B)X4`0`U~ԣJ(*,Yf&f&-]*&'OKWϿ{YΚ&c&}}gI0y gE9Ǯ)cc tj.5|P EMsauNuu 5Ayw戌1כVp!؞L5i>Pg Yi&T/[V^Uٽ& Ύ#xО/?FcS`hmm8s[s(]X~(:uCKIK2d /0cƌK.3?OGUi?m\ݴb\YQZ*6EsWM'{rS3#?uwYٛniSNKHJ6zߜ&n+Xv䈔ҽǤ_~{rb浫vgOQYx^zs ȑHTԉ{Ū8b$TBbi ,{Qp& E<ª(]Ynw(UA[<@$I8A@^dр Á$%KP$Dn,VP:y<Љ#/eck~|埖_pn1U]_>f֒8_5G a/#",E{._!&XіFRu{jBEdYy7F1,˄1M)]&&LM-L|yUYgۥ\mYþ)7nXTTtw&녥*' ?sν뮻l'G|48sj}cˎMuyrݷ?nXqӎGjJi$"77{B $zQY:, ctBW;G (5!wC /V{0tl6˭'i ڶ _ ַ{!zU~G TXrZc rRq&6ԙ<9rȓdtetLRs t2KII.++VdtjDLIIill>}e-udۧx 8m9@}tNΐsxK[`=L/l9YW<#?ȝ=~$%p(%e^ccPOPzZו=PH8?;+OttԚ=sl.Kt-->|{vt5^{K7s^`۳μj]lAg1 -ayNCowGjsݵ/GZPhr E%74jPd)X!n$E I2"!`2Τq`$R[]^nnGUIp8v Q4 % cܞ=o3pC,h6G‹"r0iP:ܭ7GT~k׽X+y}NxpFS8Rqo~>v|t㋿f#1.)rHX Fz rl-N.-˰a***DPB!Rt:c,>>d2Pp'"GrfA) 3:(7*aRCEmõ=GmPyuiU4n\wSO?>;|$e}F7L ͭ^{-B<ϧ%',aӖ{ܑH1f5_>[K{=#֓cI PEsr FO⫓DAzC7׽ 57=e[rD@/Zn0r̾qܹM6kx91e0Sk\pEmY\DU s^sXUծ.:(8cL%J(p?Eobʁy^HNLY?bv#DQ,a$1dvKMt 77) +咋n9N81!qq=A`f3g2I&SgRY/9v賂#گ:5~}{ٷ n?8W8甦Dž_p",h3Z)kCW!1lʸn V JNGH$th4J)|ӄ/ZV^?`Ƙ125Qck9U-un*47_79SOƂܷׯT(bW_}j7xdz'|}31&Ia̢Jvy|VW4*'sR ~7| ?n}/y!MmXj$ keu^A?Xj{,y˦];v:Ν=8sD #-ĨL=w&qx~x[ؾy5\p(F^Ō{Y|68B"2񅈢RBaWT빔ֶ8?I wؑ$5Bâhz!+VdVݔRX ֬ E2J84cd"JwX %J1!u!ж߼0ڷ%@VN4IxBB(ZKQ|>_fffZZvQJyX,)))c\UUnXL&SWWX5ӆRRA6G5_sOqp47p(v޾csg[!?a3u:媮5kqskνwqV-Cg [f[Lpw+~G.c4׿`@m9PUs&ˊ*jSKB#RVZ&M:"FP8j'@Q)C\~~>Ƹ gN$BDQ@:;D^lg׫(R&RJE'˰qSS=4`1牪Ι3Go!UF*rvN.B%O,L!3G^=o\?s}9᜛u_[c˛}tD*R*3fmQ]bz-gt FE8){3qqԴ􌼼<٬(J hii.-]^Q󞩓'J_]}:!DepgyUe+)^rLں iШakKJ].Ww~ 1tўBC1`1y>+G~ ?#"4Vjjj81͔1}x=`GlC '(LPϢ2 $@I*A%s|bB€,Azq IDAT: _zs9OUՆ榪ʨګK64^}CuF*?B+ѢJ{(RgG:#,V4u]8VS}O?{6:~d%GY7|駉yyy!H+\!p55 խWEX0W◬6(f0×Co4sXǚUs_~qٍx1e鯼9@[3/ob0tr=6qx^nQ%tb`LMU.ot햪.01$YVGZLтK{sDU@5]4B )t:1v c)20;K:u*v;h5;fW.΂l6_`JsϼҥKKKK}>ߢE:x>(3'󔩛yǞ]e,='⣲>pp87]>]D(xS222!hcUU֬Yc4oda5] mB^&z} @KM}/ԎBczKAKK˘&wu'7/? m۶m'V]s5)I}v~ [cyAQ(|af-;O 1z⹗n޼|)Dz哥}@|}ۏ5sPSShӉg]fJ2eU J*W 2,x{ijjl% <Ǎ)ܼs׮۶;㝂kc(Pd4g`OO BmV 0F NH0(_W  ZG QUsVTT477kj7S//bXtDʜ4(J*>FY*Rm/>ל)gP[[z(J :Jx}PEQl,U)Ģ!* ]꺤in &JowQeȋ|`&]q޽s$LzNgX,qfd.fLHHHJLwO(+U9|8{ ;kxݏz1,#zv|wH8%T8pn+-)\N2qDQc|;X}.Ȳb2FiT</Fбcaw:zkj^D"&S<ˆ #gpZp!iJNNӏ;;LcǎR~?pc18xD8UokAWz~? 3+4Dh~^$2u<78{w߯v pME{;NXywhwfugNvVP^ާ>>pPED (0%S Ǥq*/>0=}zmMɓhcfsJrȄP"( NweE9/މ;r/xLG@q!sgHRt;vaȽhvWۣPJI]_WN23 hX0b (OHOIK:ع`4`KK`GH)-ٱRf0 Ftv$%$ yho42%Z6"ƀQF)œ^{{1pT:ÙdS(06}ީ Ub $wF\8zJW}gvaI(TUUJ"f7j4]E5޳/wKIKK+;Mavt)E~=O=gy"ex'v՟N?/ ^QV5D5'?'yf]p?BP{pD~ "8"g]8a!Cr;r1#L` ,2*1!m!$:]?MN$"ˊh YgS06PEsn}hСyCƞ)E :|h^1rpm6[ф۶oQBPHHG=@UO(fɔ7lfKINNII>^Ԇ.O$K&Q BFۦ NQ|@wgU wmmOJ3q歱{}+ ֬˛}[l^r@I-_XW_0i4LC䤥6ltӡc^QYU*Y*p,XxU2zīkjҳ!<ϩ*E g bӶϷ \DTG-:SX9 }qj׼?[ż'F~M̛I)^___W_n޴)o~M_!Hɧ]WMMM3ϸ 7vt8$| U+*==c݌nL?[ZYU]S[-C]ah2pGcW&߫^LW@8nni1 iiiCyEQ|>PtvϭEQUUI0 #include #include #include #include #include #include #include #include #include #include "browser.h" #define SERVICE_TYPE_SINK "_pulse-sink._tcp." #define SERVICE_TYPE_SOURCE "_pulse-source._tcp." #define SERVICE_TYPE_SERVER "_pulse-server._tcp." struct pa_browser { PA_REFCNT_DECLARE; pa_mainloop_api *mainloop; AvahiPoll* avahi_poll; pa_browse_cb_t callback; void *userdata; pa_browser_error_cb_t error_callback; void *error_userdata; AvahiClient *client; AvahiServiceBrowser *server_browser, *sink_browser, *source_browser; }; int atou(const char* str, unsigned *out) { int tmp; tmp=atoi(str); if(tmp < 0) return -1; *out=tmp; return 0; } static int map_to_opcode(const char *type, int new) { if (avahi_domain_equal(type, SERVICE_TYPE_SINK)) return new ? PA_BROWSE_NEW_SINK : PA_BROWSE_REMOVE_SINK; else if (avahi_domain_equal(type, SERVICE_TYPE_SOURCE)) return new ? PA_BROWSE_NEW_SOURCE : PA_BROWSE_REMOVE_SOURCE; else if (avahi_domain_equal(type, SERVICE_TYPE_SERVER)) return new ? PA_BROWSE_NEW_SERVER : PA_BROWSE_REMOVE_SERVER; return -1; } static void resolve_callback( AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *aa, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) { pa_browser *b = userdata; pa_browse_info i; char ip[256], a[256]; int opcode; int device_found = 0; uint32_t cookie; pa_sample_spec ss; int ss_valid = 0; char *key = NULL, *value = NULL; pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) >= 1); memset(&i, 0, sizeof(i)); i.name = name; if (event != AVAHI_RESOLVER_FOUND) goto fail; if (!b->callback) goto fail; opcode = map_to_opcode(type, 1); pa_assert(opcode >= 0); if (aa->proto == AVAHI_PROTO_INET) snprintf(a, sizeof(a), "tcp:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port); else { pa_assert(aa->proto == AVAHI_PROTO_INET6); snprintf(a, sizeof(a), "tcp6:%s:%u", avahi_address_snprint(ip, sizeof(ip), aa), port); } i.server = a; while (txt) { if (avahi_string_list_get_pair(txt, &key, &value, NULL) < 0) break; if (!strcmp(key, "device")) { device_found = 1; pa_xfree((char*) i.device); i.device = value; value = NULL; } else if (!strcmp(key, "server-version")) { pa_xfree((char*) i.server_version); i.server_version = value; value = NULL; } else if (!strcmp(key, "user-name")) { pa_xfree((char*) i.user_name); i.user_name = value; value = NULL; } else if (!strcmp(key, "fqdn")) { size_t l; pa_xfree((char*) i.fqdn); i.fqdn = value; value = NULL; l = strlen(a); pa_assert(l+1 <= sizeof(a)); strncat(a, " ", sizeof(a)-l-1); strncat(a, i.fqdn, sizeof(a)-l-2); } else if (!strcmp(key, "cookie")) { if (atou(value, &cookie) < 0) goto fail; i.cookie = &cookie; } else if (!strcmp(key, "description")) { pa_xfree((char*) i.description); i.description = value; value = NULL; } else if (!strcmp(key, "channels")) { uint32_t ch; if (atou(value, &ch) < 0 || ch <= 0 || ch > 255) goto fail; ss.channels = (uint8_t) ch; ss_valid |= 1; } else if (!strcmp(key, "rate")) { if (atou(value, &ss.rate) < 0) goto fail; ss_valid |= 2; } else if (!strcmp(key, "format")) { if ((ss.format = pa_parse_sample_format(value)) == PA_SAMPLE_INVALID) goto fail; ss_valid |= 4; } pa_xfree(key); pa_xfree(value); key = value = NULL; txt = avahi_string_list_get_next(txt); } /* No device txt record was sent for a sink or source service */ if (opcode != PA_BROWSE_NEW_SERVER && !device_found) goto fail; if (ss_valid == 7) i.sample_spec = &ss; b->callback(b, opcode, &i, b->userdata); fail: pa_xfree((void*) i.device); pa_xfree((void*) i.fqdn); pa_xfree((void*) i.server_version); pa_xfree((void*) i.user_name); pa_xfree((void*) i.description); pa_xfree(key); pa_xfree(value); avahi_service_resolver_free(r); } static void handle_failure(pa_browser *b) { const char *e = NULL; pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) >= 1); if (b->sink_browser) avahi_service_browser_free(b->sink_browser); if (b->source_browser) avahi_service_browser_free(b->source_browser); if (b->server_browser) avahi_service_browser_free(b->server_browser); b->sink_browser = b->source_browser = b->server_browser = NULL; if (b->client) { e = avahi_strerror(avahi_client_errno(b->client)); avahi_client_free(b->client); } b->client = NULL; if (b->error_callback) b->error_callback(b, e, b->error_userdata); } static void browse_callback( AvahiServiceBrowser *sb, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void *userdata) { pa_browser *b = userdata; pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) >= 1); switch (event) { case AVAHI_BROWSER_NEW: { if (!avahi_service_resolver_new( b->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, b)) handle_failure(b); break; } case AVAHI_BROWSER_REMOVE: { if (b->callback) { pa_browse_info i; int opcode; memset(&i, 0, sizeof(i)); i.name = name; opcode = map_to_opcode(type, 0); pa_assert(opcode >= 0); b->callback(b, opcode, &i, b->userdata); } break; } case AVAHI_BROWSER_FAILURE: { handle_failure(b); break; } default: ; } } static void client_callback(AvahiClient *s, AvahiClientState state, void *userdata) { pa_browser *b = userdata; pa_assert(s); pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) >= 1); if (state == AVAHI_CLIENT_FAILURE) handle_failure(b); } static void browser_free(pa_browser *b); pa_browser *pa_browser_new(pa_mainloop_api *mainloop) { return pa_browser_new_full(mainloop, PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES, NULL); } pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string) { pa_browser *b; int error; pa_assert(mainloop); if (flags & ~(PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES) || flags == 0) return NULL; b = pa_xnew(pa_browser, 1); b->mainloop = mainloop; PA_REFCNT_INIT(b); b->callback = NULL; b->userdata = NULL; b->error_callback = NULL; b->error_userdata = NULL; b->sink_browser = b->source_browser = b->server_browser = NULL; b->avahi_poll = pa_avahi_poll_new(mainloop); if (!(b->client = avahi_client_new(b->avahi_poll, 0, client_callback, b, &error))) { if (error_string) *error_string = avahi_strerror(error); goto fail; } if ((flags & PA_BROWSE_FOR_SERVERS) && !(b->server_browser = avahi_service_browser_new( b->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, SERVICE_TYPE_SERVER, NULL, 0, browse_callback, b))) { if (error_string) *error_string = avahi_strerror(avahi_client_errno(b->client)); goto fail; } if ((flags & PA_BROWSE_FOR_SINKS) && !(b->sink_browser = avahi_service_browser_new( b->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE_SINK, NULL, 0, browse_callback, b))) { if (error_string) *error_string = avahi_strerror(avahi_client_errno(b->client)); goto fail; } if ((flags & PA_BROWSE_FOR_SOURCES) && !(b->source_browser = avahi_service_browser_new( b->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE_SOURCE, NULL, 0, browse_callback, b))) { if (error_string) *error_string = avahi_strerror(avahi_client_errno(b->client)); goto fail; } return b; fail: if (b) browser_free(b); return NULL; } static void browser_free(pa_browser *b) { pa_assert(b); pa_assert(b->mainloop); if (b->sink_browser) avahi_service_browser_free(b->sink_browser); if (b->source_browser) avahi_service_browser_free(b->source_browser); if (b->server_browser) avahi_service_browser_free(b->server_browser); if (b->client) avahi_client_free(b->client); if (b->avahi_poll) pa_avahi_poll_free(b->avahi_poll); pa_xfree(b); } pa_browser *pa_browser_ref(pa_browser *b) { pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) >= 1); PA_REFCNT_INC(b); return b; } void pa_browser_unref(pa_browser *b) { pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) >= 1); if (PA_REFCNT_DEC(b) <= 0) browser_free(b); } void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) { pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) >= 1); b->callback = cb; b->userdata = userdata; } void pa_browser_set_error_callback(pa_browser *b, pa_browser_error_cb_t cb, void *userdata) { pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) >= 1); b->error_callback = cb; b->error_userdata = userdata; } padevchooser-0.9.4/src/browser.h000066400000000000000000000073751211222417200166370ustar00rootroot00000000000000#ifndef foobrowserhfoo #define foobrowserhfoo /*** This file is part of PulseAudio. Copyright 2004-2006 Lennart Poettering PulseAudio 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.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #include #include #include #include #include /** \file * An abstract interface for Zeroconf browsing of PulseAudio servers */ PA_C_DECL_BEGIN /** An opaque Zeroconf service browser object */ typedef struct pa_browser pa_browser; /** Opcodes for pa_browser_cb_t callbacks */ typedef enum pa_browse_opcode { PA_BROWSE_NEW_SERVER = 0, /**< New server found */ PA_BROWSE_NEW_SINK, /**< New sink found */ PA_BROWSE_NEW_SOURCE, /**< New source found */ PA_BROWSE_REMOVE_SERVER, /**< Server disappeared */ PA_BROWSE_REMOVE_SINK, /**< Sink disappeared */ PA_BROWSE_REMOVE_SOURCE /**< Source disappeared */ } pa_browse_opcode_t; typedef enum pa_browse_flags { PA_BROWSE_FOR_SERVERS = 1, /**< Browse for servers */ PA_BROWSE_FOR_SINKS = 2, /**< Browse for sinks */ PA_BROWSE_FOR_SOURCES = 4 /** Browse for sources */ } pa_browse_flags_t; /** Create a new browser object on the specified main loop */ pa_browser *pa_browser_new(pa_mainloop_api *mainloop); /** Same pa_browser_new, but pass additional flags parameter. */ pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string); /** Increase reference counter of the specified browser object */ pa_browser *pa_browser_ref(pa_browser *z); /** Decrease reference counter of the specified browser object */ void pa_browser_unref(pa_browser *z); /** Information about a sink/source/server found with Zeroconf */ typedef struct pa_browse_info { const char *name; /**< Unique service name; always available */ const char *server; /**< Server name; always available */ const char *server_version; /**< Server version string; optional */ const char *user_name; /**< User name of the server process; optional */ const char *fqdn; /* Server version; optional */ const uint32_t *cookie; /* Server cookie; optional */ const char *device; /* Device name; always available when this information is of a sink/source */ const char *description; /* Device description; optional */ const pa_sample_spec *sample_spec; /* Sample specification of the device; optional */ } pa_browse_info; /** Callback prototype */ typedef void (*pa_browse_cb_t)(pa_browser *z, pa_browse_opcode_t c, const pa_browse_info *i, void *userdata); /** Set the callback pointer for the browser object */ void pa_browser_set_callback(pa_browser *z, pa_browse_cb_t cb, void *userdata); /** Callback prototype for errors */ typedef void (*pa_browser_error_cb_t)(pa_browser *z, const char *error_string, void *userdata); /** Set a callback function that is called whenever the browser object * becomes invalid due to an error. After this function has been * called the browser object has become invalid and should be * freed. */ void pa_browser_set_error_callback(pa_browser *z, pa_browser_error_cb_t, void *userdata); PA_C_DECL_END #endif padevchooser-0.9.4/src/padevchooser.c000066400000000000000000000733771211222417200176360ustar00rootroot00000000000000/*** This file is part of padevchooser. padevchooser is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. padevchooser is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with padevchooser; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "x11prop.h" #include "browser.h" #define GCONF_PREFIX "/apps/padevchooser" struct menu_item_info { GtkWidget *menu_item; char *name, *server, *device, *description; pa_sample_spec sample_spec; int sample_spec_valid; }; static NotifyNotification *notification = NULL; static gchar *last_events = NULL; static GtkStatusIcon *tray_icon = NULL; static gchar *current_server = NULL, *current_sink = NULL, *current_source = NULL; static struct menu_item_info *current_source_menu_item_info = NULL, *current_sink_menu_item_info = NULL, *current_server_menu_item_info = NULL; static GtkMenu *menu = NULL, *sink_submenu = NULL, *source_submenu = NULL, *server_submenu = NULL; static GHashTable *server_hash_table = NULL, *sink_hash_table = NULL, *source_hash_table = NULL; static GtkWidget *no_servers_menu_item = NULL, *no_sinks_menu_item = NULL, *no_sources_menu_item = NULL; static GtkWidget *default_server_menu_item = NULL, *default_sink_menu_item = NULL, *default_source_menu_item = NULL; static GtkWidget *other_server_menu_item = NULL, *other_sink_menu_item = NULL, *other_source_menu_item = NULL; static GtkTooltips *menu_tooltips = NULL; static int updating = 0; static time_t startup_time = 0; static GConfClient *gconf = NULL; static GladeXML *glade_xml = NULL; static gboolean notify_on_server_discovery = FALSE, notify_on_sink_discovery = FALSE, notify_on_source_discovery = FALSE, no_notify_on_startup = FALSE; static void set_sink(const char *server, const char *device); static void set_source(const char *server, const char *device); static void set_server(const char *server); static void set_x11_props(void); static gboolean find_predicate(const gchar* name, const struct menu_item_info *m, gpointer userdata) { return strcmp(m->server, current_server) == 0 && (!m->device || strcmp(m->device, userdata) == 0); } static void look_for_current_menu_item( GHashTable *h, const char *device, int look_for_device, struct menu_item_info **current_menu_item_info, GtkWidget *default_menu_item, GtkWidget *other_menu_item) { struct menu_item_info *m; if (!current_server || (look_for_device && !device)) m = NULL; else if (*current_menu_item_info && (strcmp(current_server, (*current_menu_item_info)->server) == 0 && (!look_for_device || strcmp(device, (*current_menu_item_info)->device) == 0))) m = *current_menu_item_info; else /* Look for the right entry */ m = g_hash_table_find(h, (GHRFunc) find_predicate, (gpointer) device); /* Deactivate the old item */ if (*current_menu_item_info) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM((*current_menu_item_info)->menu_item), FALSE); /* Update item */ *current_menu_item_info = m; /* Activate the new item */ if (*current_menu_item_info) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM((*current_menu_item_info)->menu_item), TRUE); /* Enable/Disable the "Default" menu item */ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(default_menu_item), !*current_menu_item_info && (look_for_device ? !device : !current_server)); /* Enable/Disable the "Other..." menu item and set the tooltip appriately */ if (!*current_menu_item_info && (look_for_device ? device : current_server)) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(other_menu_item), TRUE); gtk_tooltips_set_tip(GTK_TOOLTIPS(menu_tooltips), other_menu_item, look_for_device ? device : current_server, NULL); } else { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(other_menu_item), FALSE); gtk_tooltips_set_tip(GTK_TOOLTIPS(menu_tooltips), other_menu_item, NULL, NULL); } } static void look_for_current_menu_items(void) { updating = 1; look_for_current_menu_item(server_hash_table, NULL, FALSE, ¤t_server_menu_item_info, default_server_menu_item, other_server_menu_item); look_for_current_menu_item(sink_hash_table, current_sink, TRUE, ¤t_sink_menu_item_info, default_sink_menu_item, other_sink_menu_item); look_for_current_menu_item(source_hash_table, current_source, TRUE, ¤t_source_menu_item_info, default_source_menu_item, other_source_menu_item); updating = 0; } static void menu_item_info_free(struct menu_item_info *i) { if (i->menu_item) gtk_widget_destroy(i->menu_item); g_free(i->name); g_free(i->server); g_free(i->device); g_free(i->description); g_free(i); if (current_sink_menu_item_info == i) current_sink_menu_item_info = NULL; if (current_source_menu_item_info == i) current_source_menu_item_info = NULL; if (current_server_menu_item_info == i) current_server_menu_item_info = NULL; } static void notification_closed(void) { if (notification) { g_object_unref(G_OBJECT(notification)); notification = NULL; } } static void notify_event(const char *title, const char*text) { char *s; if (no_notify_on_startup && time(NULL)-startup_time <= 5) return; if (!notify_is_initted()) return; if (!notification) { s = g_strdup_printf("%s\n%s", title, text); notification = notify_notification_new(title, s, NULL); notify_notification_set_category(notification, "device.added"); notify_notification_set_urgency(notification, NOTIFY_URGENCY_LOW); g_signal_connect_swapped(G_OBJECT(notification), "closed", G_CALLBACK(notification_closed), NULL); } else { s = g_strdup_printf("%s\n\n%s\n%s", last_events, title, text); notify_notification_update(notification, title, s, "audio-card"); } g_free(last_events); last_events = s; notify_notification_show(notification, NULL); } static GtkWidget *append_radio_menu_item(GtkMenu *menu, const gchar *label, gboolean mnemonic, gboolean prepend) { GtkWidget *item; if (mnemonic) item = gtk_check_menu_item_new_with_mnemonic(label); else item = gtk_check_menu_item_new_with_label(label); gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE); gtk_widget_show_all(item); if (prepend) gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), item); else gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); return item; } static void sink_change_cb(struct menu_item_info *m) { set_sink(m->server, m->device); } static void source_change_cb(struct menu_item_info *m) { set_source(m->server, m->device); } static void server_change_cb(struct menu_item_info *m) { set_server(m->server); } static struct menu_item_info* add_menu_item_info(GHashTable *h, GtkMenu *menu, const pa_browse_info *i, GCallback callback) { struct menu_item_info *m; gchar *c; const gchar *title; gboolean b; m = g_new(struct menu_item_info, 1); m->name = g_strdup(i->name); m->server = g_strdup(i->server); m->device = g_strdup(i->device); m->description = g_strdup(i->description); if ((m->sample_spec_valid = !!i->sample_spec)) m->sample_spec = *i->sample_spec; m->menu_item = append_radio_menu_item(menu, m->name, FALSE, TRUE); g_signal_connect_swapped(G_OBJECT(m->menu_item), "activate", callback, m); if (!m->device) c = g_strdup_printf( "Name: %s\n" "Server: %s", i->name, i->server); else { char t[PA_SAMPLE_SPEC_SNPRINT_MAX]; c = g_strdup_printf( "Name: %s\n" "Server: %s\n" "Device: %s\n" "Description: %s\n" "Sample Specification: %s", i->name, i->server, i->device, i->description ? i->description : "n/a", m->sample_spec_valid ? pa_sample_spec_snprint(t, sizeof(t), &m->sample_spec) : "n/a"); } gtk_tooltips_set_tip(GTK_TOOLTIPS(menu_tooltips), m->menu_item, c, NULL); if (menu == sink_submenu) { title = "Networked Audio Sink Discovered"; b = notify_on_sink_discovery; } else if (menu == source_submenu) { title = "Networked Audio Source Discovered"; b = notify_on_source_discovery; } else { title = "Networked Audio Server Discovered"; b = notify_on_server_discovery; } if (b) notify_event(title, c); g_free(c); g_hash_table_insert(h, m->name, m); return m; } static void remove_menu_item_info(GHashTable *h, const pa_browse_info *i) { struct menu_item_info *m; const gchar *title; gchar *c; gboolean b; if (!(m = g_hash_table_lookup(h, i->name))) return; if (h == sink_hash_table) { title = "Networked Audio Sink Disappeared"; b = notify_on_sink_discovery; } else if (h == source_hash_table) { title = "Networked Audio Source Disappeared"; b = notify_on_source_discovery; } else { title = "Networked Audio Server Disappeared"; b = notify_on_server_discovery; } c = g_strdup_printf("Name: %s", i->name); if (b) notify_event(title, c); g_free(c); g_hash_table_remove(h, i->name); } static void update_no_devices_menu_items(void) { if (g_hash_table_size(server_hash_table) == 0) gtk_widget_show_all(no_servers_menu_item); else gtk_widget_hide(no_servers_menu_item); if (g_hash_table_size(source_hash_table) == 0) gtk_widget_show_all(no_sources_menu_item); else gtk_widget_hide(no_sources_menu_item); if (g_hash_table_size(sink_hash_table) == 0) gtk_widget_show_all(no_sinks_menu_item); else gtk_widget_hide(no_sinks_menu_item); } static void browse_cb(pa_browser *z, pa_browse_opcode_t c, const pa_browse_info *i, void *userdata) { switch (c) { case PA_BROWSE_NEW_SERVER: add_menu_item_info(server_hash_table, server_submenu, i, (GCallback) server_change_cb); break; case PA_BROWSE_NEW_SINK: add_menu_item_info(sink_hash_table, sink_submenu, i, (GCallback) sink_change_cb); break; case PA_BROWSE_NEW_SOURCE: add_menu_item_info(source_hash_table, source_submenu, i, (GCallback) source_change_cb); break; case PA_BROWSE_REMOVE_SERVER: remove_menu_item_info(server_hash_table, i); break; case PA_BROWSE_REMOVE_SINK: remove_menu_item_info(sink_hash_table, i); break; case PA_BROWSE_REMOVE_SOURCE: remove_menu_item_info(source_hash_table, i); break; } update_no_devices_menu_items(); look_for_current_menu_items(); } static void tray_icon_on_click(GtkStatusIcon *status_icon, void * user_data) { gtk_menu_popup(menu, NULL, NULL, gtk_status_icon_position_menu, status_icon, 0, gtk_get_current_event_time()); } static void run(const char *name) { GnomeDesktopItem* di; char *p; p = g_strdup_printf(DESKTOP_DIR "/%s.desktop", name); di = gnome_desktop_item_new_from_file(p, GNOME_DESKTOP_ITEM_LOAD_ONLY_IF_EXISTS, NULL); if (di) { if (gnome_desktop_item_launch(di, NULL, 0, NULL)) { gnome_desktop_item_unref(di); g_free(p); return; } } g_message("Failed to launch desktop item '%s'.", di ? gnome_desktop_item_get_location(di) : p); if (di) gnome_desktop_item_unref(di); g_free(p); if (strcmp(name, "pavumeter-record") == 0) g_spawn_command_line_async("pavumeter --record", NULL); else g_spawn_command_line_async(name, NULL); } static void start_manager_cb(void) { run("paman"); } static void start_vucontrol_cb(void) { run("pavucontrol"); } static void start_vumeter_playback_cb(void) { run("pavumeter"); } static void start_vumeter_record_cb(void) { run("pavumeter-record"); } static void start_server_preferences_cb(void) { run("paprefs"); } static void show_preferences(void) { GtkWidget *w, *eb; GdkColor white; eb = glade_xml_get_widget(glade_xml, "titleEventBox"); gdk_color_white(gtk_widget_get_colormap(eb), &white); gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &white); w = glade_xml_get_widget(glade_xml, "preferencesDialog"); gtk_widget_show_all(w); gtk_window_present(GTK_WINDOW(w)); gtk_dialog_run(GTK_DIALOG(w)); gtk_widget_hide(w); } static void set_props(const char *server, const char *sink, const char *source) { if (server != current_server) { g_free(current_server); current_server = g_strdup(server); } if (sink != current_sink) { g_free(current_sink); current_sink = g_strdup(sink); } if (source != current_source) { g_free(current_source); current_source = g_strdup(source); } set_x11_props(); } static gboolean pstrequal(const char *a, const char *b) { if (!a && !b) return TRUE; if (!a || !b) return FALSE; return strcmp(a, b) == 0; } static void set_sink(const char *server, const char *sink) { if (updating) return; if (server) { if (!pstrequal(server, current_server) || !pstrequal(sink, current_sink)) set_props(server, sink, pstrequal(server, current_server) ? current_source : NULL); } else { if (!pstrequal(sink, current_sink)) set_props(current_server, sink, current_source); } look_for_current_menu_items(); } static void set_source(const char *server, const char *source) { if (updating) return; if (server) { if (!pstrequal(server, current_server) || !pstrequal(source, current_source)) set_props(server, pstrequal(server, current_server) ? current_sink : NULL, source); } else { if (!pstrequal(source, current_source)) set_props(current_server, current_sink, source); } look_for_current_menu_items(); } static void set_server(const char *server) { if (updating) return; if (!pstrequal(server, current_server)) set_props(server, NULL, NULL); look_for_current_menu_items(); } static void sink_default_cb(void) { set_sink(NULL, NULL); } static void source_default_cb(void) { set_source(NULL, NULL); } static void server_default_cb(void) { set_server(NULL); } static const gchar *input_dialog(const gchar *title, const gchar *text, const gchar *value) { GtkWidget *w, *entry, *label; gint response; w = glade_xml_get_widget(glade_xml, "inputDialog"); if (GTK_WIDGET_VISIBLE(w)) { gtk_window_present(GTK_WINDOW(w)); return value; } gtk_window_set_title(GTK_WINDOW(w), title); entry = glade_xml_get_widget(glade_xml, "inputEntry"); gtk_entry_set_text(GTK_ENTRY(entry), value ? value : ""); label = glade_xml_get_widget(glade_xml, "inputLabel"); gtk_label_set_markup(GTK_LABEL(label), text); gtk_widget_show_all(w); gtk_window_present(GTK_WINDOW(w)); response = gtk_dialog_run(GTK_DIALOG(w)); gtk_widget_hide(w); if (response != GTK_RESPONSE_OK) return value; return gtk_entry_get_text(GTK_ENTRY(entry)); } static void sink_other_cb(void) { if (updating) return; set_sink(NULL, input_dialog("Other Sink", "Please enter sink name:", current_sink)); } static void source_other_cb(void) { if (updating) return; set_source(NULL, input_dialog("Other Source", "Please enter source name:", current_source)); } static void server_other_cb(void) { if (updating) return; set_server(input_dialog("Other Server", "Please enter server name:", current_server)); } static GtkStatusIcon *create_tray_icon(void) { GtkStatusIcon *tray_icon; tray_icon = gtk_status_icon_new(); g_signal_connect_object(G_OBJECT(tray_icon), "activate", G_CALLBACK(tray_icon_on_click), tray_icon, 0); g_signal_connect_object(G_OBJECT(tray_icon), "popup-menu", G_CALLBACK(tray_icon_on_click), tray_icon, 0); gtk_status_icon_set_from_icon_name(tray_icon, "audio-card"); gtk_status_icon_set_tooltip(tray_icon, "PulseAudio Applet"); gtk_status_icon_set_visible(tray_icon, TRUE); return tray_icon; } static GtkWidget *append_menuitem(GtkMenu *m, const char *text, const char *icon_name) { GtkWidget *item; item = gtk_image_menu_item_new_with_mnemonic(text); gtk_menu_shell_append(GTK_MENU_SHELL(m), item); if (icon_name) { GtkWidget *i; i = gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), i); } return item; } static GtkWidget* append_submenu(GtkMenu *m, const char *text, GtkMenu *sub, const char *icon_name) { GtkWidget *item; item = append_menuitem(m, text, icon_name); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), GTK_WIDGET(sub)); return item; } static void append_default_device_menu_items(GtkMenu *m, GtkWidget **empty_menu_item, GtkWidget **default_menu_item, GtkWidget**other_menu_item, GCallback default_callback, GCallback other_callback) { *empty_menu_item = append_menuitem(m, "No Network Devices Found", NULL); gtk_widget_set_sensitive(*empty_menu_item, FALSE); gtk_menu_shell_append(GTK_MENU_SHELL(m), gtk_separator_menu_item_new()); *default_menu_item = append_radio_menu_item(m, "_Default", TRUE, FALSE); g_signal_connect_swapped(G_OBJECT(*default_menu_item), "activate", default_callback, NULL); *other_menu_item = append_radio_menu_item(m, "_Other...", TRUE, FALSE); g_signal_connect_swapped(G_OBJECT(*other_menu_item), "activate", other_callback, NULL); } static GtkMenu *create_menu(void) { GtkWidget *item; gchar *c; menu = GTK_MENU(gtk_menu_new()); menu_tooltips = gtk_tooltips_new(); sink_submenu = GTK_MENU(gtk_menu_new()); source_submenu = GTK_MENU(gtk_menu_new()); server_submenu = GTK_MENU(gtk_menu_new()); append_default_device_menu_items(sink_submenu, &no_sinks_menu_item, &default_sink_menu_item, &other_sink_menu_item, sink_default_cb, sink_other_cb); append_default_device_menu_items(source_submenu, &no_sources_menu_item, &default_source_menu_item, &other_source_menu_item, source_default_cb, source_other_cb); append_default_device_menu_items(server_submenu, &no_servers_menu_item, &default_server_menu_item, &other_server_menu_item, server_default_cb, server_other_cb); append_submenu(menu, "Default S_erver", server_submenu, "network-wired"); append_submenu(menu, "Default S_ink", sink_submenu, "audio-card"); append_submenu(menu, "Default S_ource", source_submenu, "audio-input-microphone"); gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new()); item = append_menuitem(menu, "_Manager...", NULL); gtk_widget_set_sensitive(item, !!(c = g_find_program_in_path("paman"))); g_free(c); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(start_manager_cb), NULL); item = append_menuitem(menu, "_Volume Control...", "multimedia-volume-control"); gtk_widget_set_sensitive(item, !!(c = g_find_program_in_path("pavucontrol"))); g_free(c); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(start_vucontrol_cb), NULL); item = append_menuitem(menu, "_Volume Meter (Playback)...", NULL); gtk_widget_set_sensitive(item, !!(c = g_find_program_in_path("pavumeter"))); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(start_vumeter_playback_cb), NULL); item = append_menuitem(menu, "_Volume Meter (Recording)...", NULL); gtk_widget_set_sensitive(item, !!c); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(start_vumeter_record_cb), NULL); g_free(c); item = append_menuitem(menu, "_Configure Local Sound Server...", NULL); gtk_widget_set_sensitive(item, !!(c = g_find_program_in_path("paprefs"))); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(start_server_preferences_cb), NULL); g_free(c); gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new()); item = append_menuitem(menu, "_Preferences...", "gtk-preferences"); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(show_preferences), NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new()); item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_main_quit), NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show_all(GTK_WIDGET(menu)); return menu; } static void get_x11_props(void) { char t[256]; g_free(current_server); g_free(current_sink); g_free(current_source); current_server = g_strdup(x11_get_prop(GDK_DISPLAY(), "PULSE_SERVER", t, sizeof(t))); current_sink = g_strdup(x11_get_prop(GDK_DISPLAY(), "PULSE_SINK", t, sizeof(t))); current_source = g_strdup(x11_get_prop(GDK_DISPLAY(), "PULSE_SOURCE", t, sizeof(t))); } static void set_x11_props(void) { if (current_server) x11_set_prop(GDK_DISPLAY(), "PULSE_SERVER", current_server); else x11_del_prop(GDK_DISPLAY(), "PULSE_SERVER"); if (current_sink) x11_set_prop(GDK_DISPLAY(), "PULSE_SINK", current_sink); else x11_del_prop(GDK_DISPLAY(), "PULSE_SINK"); if (current_source) x11_set_prop(GDK_DISPLAY(), "PULSE_SOURCE", current_source); else x11_del_prop(GDK_DISPLAY(), "PULSE_SOURCE"); /* This is used by module-x11-publish to detect whether the * properties have been altered. We delete this property here to * make sure that the module notices that it is no longer in * control */ x11_del_prop(GDK_DISPLAY(), "PULSE_ID"); } static void start_on_login_cb(GtkCheckButton *w) { gchar *c; mkdir(g_get_user_config_dir(), 0777); c = g_build_filename(g_get_user_config_dir(), "autostart", NULL); mkdir(c, 0777); g_free(c); c = g_build_filename(g_get_user_config_dir(), "autostart", "padevchooser.desktop", NULL); if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) { if (symlink(DESKTOP_FILE, c) < 0 && errno != EEXIST) g_warning("symlink() failed: %s", strerror(errno)); } else { if (unlink(c) < 0 && errno != ENOENT) g_warning("unlink() failed: %s", strerror(errno)); } g_free(c); } static void init_start_on_login_check_button(GtkToggleButton *w) { struct stat st; gchar *c; c = g_build_filename(g_get_user_config_dir(), "autostart", "padevchooser.desktop", NULL); gtk_toggle_button_set_active(w, lstat(c, &st) >= 0); g_free(c); } static void check_button_cb(GtkCheckButton *w, const gchar *key) { gboolean b, *ptr; ptr = g_object_get_data(G_OBJECT(w), "ptr"); b = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); if (*ptr == b) return; *ptr = b; gconf_client_set_bool(gconf, key, b, NULL); gtk_widget_set_sensitive(glade_xml_get_widget(glade_xml, "startupCheckButton"), notify_on_server_discovery||notify_on_sink_discovery||notify_on_source_discovery); } static void gconf_notify_cb(GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer userdata) { gboolean b; b = gconf_value_get_bool(gconf_entry_get_value(entry)); if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(userdata)) != b) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(userdata), b); gtk_widget_set_sensitive(glade_xml_get_widget(glade_xml, "startupCheckButton"), notify_on_server_discovery||notify_on_sink_discovery||notify_on_source_discovery); } static void setup_gconf(void) { GtkWidget *server_check_button, *sink_check_button, *source_check_button, *startup_check_button, *start_on_login_check_button; gconf = gconf_client_get_default(); g_assert(gconf); gconf_client_add_dir(gconf, GCONF_PREFIX, GCONF_CLIENT_PRELOAD_NONE, NULL); server_check_button = glade_xml_get_widget(glade_xml, "serverCheckButton"); sink_check_button = glade_xml_get_widget(glade_xml, "sinkCheckButton"); source_check_button = glade_xml_get_widget(glade_xml, "sourceCheckButton"); startup_check_button = glade_xml_get_widget(glade_xml, "startupCheckButton"); start_on_login_check_button = glade_xml_get_widget(glade_xml, "loginCheckButton"); g_object_set_data(G_OBJECT(server_check_button), "ptr", ¬ify_on_server_discovery); g_object_set_data(G_OBJECT(sink_check_button), "ptr", ¬ify_on_sink_discovery); g_object_set_data(G_OBJECT(source_check_button), "ptr", ¬ify_on_source_discovery); g_object_set_data(G_OBJECT(startup_check_button), "ptr", &no_notify_on_startup); notify_on_server_discovery = gconf_client_get_bool(gconf, GCONF_PREFIX"/notify_on_server_discovery", NULL); notify_on_sink_discovery = gconf_client_get_bool(gconf, GCONF_PREFIX"/notify_on_sink_discovery", NULL); notify_on_source_discovery = gconf_client_get_bool(gconf, GCONF_PREFIX"/notify_on_source_discovery", NULL); no_notify_on_startup = gconf_client_get_bool(gconf, GCONF_PREFIX"/no_notify_on_startup", NULL); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(server_check_button), notify_on_server_discovery); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sink_check_button), notify_on_sink_discovery); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(source_check_button), notify_on_source_discovery); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(startup_check_button), no_notify_on_startup); init_start_on_login_check_button(GTK_TOGGLE_BUTTON(start_on_login_check_button)); gtk_widget_set_sensitive(startup_check_button, notify_on_server_discovery||notify_on_sink_discovery||notify_on_source_discovery); gconf_client_notify_add(gconf, GCONF_PREFIX"/notify_on_server_discovery", gconf_notify_cb, server_check_button, NULL, NULL); gconf_client_notify_add(gconf, GCONF_PREFIX"/notify_on_sink_discovery", gconf_notify_cb, sink_check_button, NULL, NULL); gconf_client_notify_add(gconf, GCONF_PREFIX"/notify_on_source_discovery", gconf_notify_cb, source_check_button, NULL, NULL); gconf_client_notify_add(gconf, GCONF_PREFIX"/no_notify_on_startup", gconf_notify_cb, startup_check_button, NULL, NULL); g_signal_connect(G_OBJECT(server_check_button), "toggled", G_CALLBACK(check_button_cb), GCONF_PREFIX"/notify_on_server_discovery"); g_signal_connect(G_OBJECT(sink_check_button), "toggled", G_CALLBACK(check_button_cb), GCONF_PREFIX"/notify_on_sink_discovery"); g_signal_connect(G_OBJECT(source_check_button), "toggled", G_CALLBACK(check_button_cb), GCONF_PREFIX"/notify_on_source_discovery"); g_signal_connect(G_OBJECT(startup_check_button), "toggled", G_CALLBACK(check_button_cb), GCONF_PREFIX"/no_notify_on_startup"); g_signal_connect(G_OBJECT(start_on_login_check_button), "toggled", G_CALLBACK(start_on_login_cb), NULL); } int main(int argc, char *argv[]) { pa_browser *b = NULL; pa_glib_mainloop *m = NULL; GnomeProgram *program; startup_time = time(NULL); program = gnome_program_init("padevchoose", VERSION, LIBGNOMEUI_MODULE, argc, argv, NULL); glade_xml = glade_xml_new(GLADE_FILE, NULL, NULL); g_assert(glade_xml); m = pa_glib_mainloop_new(NULL); g_assert(m); server_hash_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify) menu_item_info_free); sink_hash_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify) menu_item_info_free); source_hash_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify) menu_item_info_free); create_menu(); update_no_devices_menu_items(); setup_gconf(); notify_init("PulseAudio Applet"); get_x11_props(); if (!(b = pa_browser_new(pa_glib_mainloop_get_api(m)))) { GtkWidget *dialog; dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "The Avahi Zeroconf service is not running. Please make sure that Avahi is installed and activated before starting the PulseAudio Device Chooser."); gtk_window_set_title(GTK_WINDOW(dialog), "Error"); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); goto fail; } pa_browser_set_callback(b, browse_cb, NULL); tray_icon = create_tray_icon(); gtk_main(); fail: if (b) pa_browser_unref(b); if (m) pa_glib_mainloop_free(m); if (server_hash_table) g_hash_table_destroy(server_hash_table); if (sink_hash_table) g_hash_table_destroy(sink_hash_table); if (source_hash_table) g_hash_table_destroy(source_hash_table); if (notification) g_object_unref(G_OBJECT(notification)); g_free(last_events); if (program) g_object_unref(program); return 0; } padevchooser-0.9.4/src/padevchooser.desktop000066400000000000000000000003611211222417200210440ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Name=PulseAudio Device Chooser GenericName=Device Chooser Comment=An Applet for Quick Device Changing Exec=padevchooser Icon=audio-card StartupNotify=true Type=Application Categories=Application;AudioVideo;Audio; padevchooser-0.9.4/src/padevchooser.glade000066400000000000000000000415221211222417200204530ustar00rootroot00000000000000 PulseAudio Device Chooser False gtk-preferences GDK_WINDOW_TYPE_HINT_DIALOG False True True True True 12 12 True 6 audio-card False False True True 0 1 <span size="18000" color="black"><b>PulseAudio Device Chooser</b></span> True True 0 0 <span color="black">Quick access to your sound server</span> True 1 1 False False True False False 1 True 6 True 6 0 GTK_SHADOW_NONE True 6 12 True 6 True True True Show notifications for discovered _servers True 0 True False False True True Show notifications for discovered s_inks True 0 True False False 1 True True Show notifications for discovered s_ources True 0 True False False 2 True True Do_n't show notifications during startup True 0 True False False 3 True <b>Notifications</b> True label_item False False True 6 0 GTK_SHADOW_NONE True 6 12 True 6 True True True Start applet on session _login True 0 True False False True <b>Startup</b> True label_item False False 1 2 1 True GTK_BUTTONBOX_END True True True 6 gtk-close True -7 False GTK_PACK_END 6 Other Server 300 gtk-edit GDK_WINDOW_TYPE_HINT_DIALOG False True True 6 6 True 0 Please enter server name: True False False True 12 True True * False False 1 1 True GTK_BUTTONBOX_END True True True gtk-ok True -5 False GTK_PACK_END padevchooser-0.9.4/src/padevchooser.gladep000066400000000000000000000004251211222417200206300ustar00rootroot00000000000000 paapplet paapplet FALSE padevchooser-0.9.4/src/pulsecore/000077500000000000000000000000001211222417200167705ustar00rootroot00000000000000padevchooser-0.9.4/src/pulsecore/atomic.h000066400000000000000000000361301211222417200204200ustar00rootroot00000000000000#ifndef foopulseatomichfoo #define foopulseatomichfoo /*** This file is part of PulseAudio. Copyright 2006-2008 Lennart Poettering Copyright 2008 Nokia Corporation PulseAudio 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.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #include /* * atomic_ops guarantees us that sizeof(AO_t) == sizeof(void*). It is * not guaranteed however, that sizeof(AO_t) == sizeof(size_t). * however very likely. * * For now we do only full memory barriers. Eventually we might want * to support more elaborate memory barriers, in which case we will add * suffixes to the function names. * * On gcc >= 4.1 we use the builtin atomic functions. otherwise we use * libatomic_ops */ #ifndef PACKAGE #error "Please include config.h before including this file!" #endif #ifdef HAVE_ATOMIC_BUILTINS /* __sync based implementation */ typedef struct pa_atomic { volatile int value; } pa_atomic_t; #define PA_ATOMIC_INIT(v) { .value = (v) } static inline int pa_atomic_load(const pa_atomic_t *a) { __sync_synchronize(); return a->value; } static inline void pa_atomic_store(pa_atomic_t *a, int i) { a->value = i; __sync_synchronize(); } /* Returns the previously set value */ static inline int pa_atomic_add(pa_atomic_t *a, int i) { return __sync_fetch_and_add(&a->value, i); } /* Returns the previously set value */ static inline int pa_atomic_sub(pa_atomic_t *a, int i) { return __sync_fetch_and_sub(&a->value, i); } /* Returns the previously set value */ static inline int pa_atomic_inc(pa_atomic_t *a) { return pa_atomic_add(a, 1); } /* Returns the previously set value */ static inline int pa_atomic_dec(pa_atomic_t *a) { return pa_atomic_sub(a, 1); } /* Returns TRUE when the operation was successful. */ static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { return __sync_bool_compare_and_swap(&a->value, old_i, new_i); } typedef struct pa_atomic_ptr { volatile unsigned long value; } pa_atomic_ptr_t; #define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) } static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { __sync_synchronize(); return (void*) a->value; } static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) { a->value = (unsigned long) p; __sync_synchronize(); } static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { return __sync_bool_compare_and_swap(&a->value, (long) old_p, (long) new_p); } #elif defined(__NetBSD__) && defined(HAVE_SYS_ATOMIC_H) /* NetBSD 5.0+ atomic_ops(3) implementation */ #include typedef struct pa_atomic { volatile unsigned int value; } pa_atomic_t; #define PA_ATOMIC_INIT(v) { .value = (unsigned int) (v) } static inline int pa_atomic_load(const pa_atomic_t *a) { membar_sync(); return (int) a->value; } static inline void pa_atomic_store(pa_atomic_t *a, int i) { a->value = (unsigned int) i; membar_sync(); } /* Returns the previously set value */ static inline int pa_atomic_add(pa_atomic_t *a, int i) { int nv = (int) atomic_add_int_nv(&a->value, i); return nv - i; } /* Returns the previously set value */ static inline int pa_atomic_sub(pa_atomic_t *a, int i) { int nv = (int) atomic_add_int_nv(&a->value, -i); return nv + i; } /* Returns the previously set value */ static inline int pa_atomic_inc(pa_atomic_t *a) { int nv = (int) atomic_inc_uint_nv(&a->value); return nv - 1; } /* Returns the previously set value */ static inline int pa_atomic_dec(pa_atomic_t *a) { int nv = (int) atomic_dec_uint_nv(&a->value); return nv + 1; } /* Returns TRUE when the operation was successful. */ static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { unsigned int r = atomic_cas_uint(&a->value, (unsigned int) old_i, (unsigned int) new_i); return (int) r == old_i; } typedef struct pa_atomic_ptr { volatile void *value; } pa_atomic_ptr_t; #define PA_ATOMIC_PTR_INIT(v) { .value = (v) } static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { membar_sync(); return (void *) a->value; } static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) { a->value = p; membar_sync(); } static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { void *r = atomic_cas_ptr(&a->value, old_p, new_p); return r == old_p; } #elif defined(__GNUC__) && (defined(__amd64__) || defined(__x86_64__)) //#warn "The native atomic operations implementation for AMD64 has not been tested thoroughly. libatomic_ops is known to not work properly on AMD64 and your gcc version is too old for the gcc-builtin atomic ops support. You have three options now: test the native atomic operations implementation for AMD64, fix libatomic_ops, or upgrade your GCC." /* Addapted from glibc */ typedef struct pa_atomic { volatile int value; } pa_atomic_t; #define PA_ATOMIC_INIT(v) { .value = (v) } static inline int pa_atomic_load(const pa_atomic_t *a) { return a->value; } static inline void pa_atomic_store(pa_atomic_t *a, int i) { a->value = i; } static inline int pa_atomic_add(pa_atomic_t *a, int i) { int result; __asm __volatile ("lock; xaddl %0, %1" : "=r" (result), "=m" (a->value) : "0" (i), "m" (a->value)); return result; } static inline int pa_atomic_sub(pa_atomic_t *a, int i) { return pa_atomic_add(a, -i); } static inline int pa_atomic_inc(pa_atomic_t *a) { return pa_atomic_add(a, 1); } static inline int pa_atomic_dec(pa_atomic_t *a) { return pa_atomic_sub(a, 1); } static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { int result; __asm__ __volatile__ ("lock; cmpxchgl %2, %1" : "=a" (result), "=m" (a->value) : "r" (new_i), "m" (a->value), "0" (old_i)); return result == old_i; } typedef struct pa_atomic_ptr { volatile unsigned long value; } pa_atomic_ptr_t; #define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) } static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { return (void*) a->value; } static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) { a->value = (unsigned long) p; } static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { void *result; __asm__ __volatile__ ("lock; cmpxchgq %q2, %1" : "=a" (result), "=m" (a->value) : "r" (new_p), "m" (a->value), "0" (old_p)); return result == old_p; } #elif defined(ATOMIC_ARM_INLINE_ASM) /* These should only be enabled if we have ARMv6 or better. */ typedef struct pa_atomic { volatile int value; } pa_atomic_t; #define PA_ATOMIC_INIT(v) { .value = (v) } static inline void pa_memory_barrier(void) { #ifdef ATOMIC_ARM_MEMORY_BARRIER_ENABLED asm volatile ("mcr p15, 0, r0, c7, c10, 5 @ dmb"); #endif } static inline int pa_atomic_load(const pa_atomic_t *a) { pa_memory_barrier(); return a->value; } static inline void pa_atomic_store(pa_atomic_t *a, int i) { a->value = i; pa_memory_barrier(); } /* Returns the previously set value */ static inline int pa_atomic_add(pa_atomic_t *a, int i) { unsigned long not_exclusive; int new_val, old_val; pa_memory_barrier(); do { asm volatile ("ldrex %0, [%3]\n" "add %2, %0, %4\n" "strex %1, %2, [%3]\n" : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val) : "r" (&a->value), "Ir" (i) : "cc"); } while(not_exclusive); pa_memory_barrier(); return old_val; } /* Returns the previously set value */ static inline int pa_atomic_sub(pa_atomic_t *a, int i) { unsigned long not_exclusive; int new_val, old_val; pa_memory_barrier(); do { asm volatile ("ldrex %0, [%3]\n" "sub %2, %0, %4\n" "strex %1, %2, [%3]\n" : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val) : "r" (&a->value), "Ir" (i) : "cc"); } while(not_exclusive); pa_memory_barrier(); return old_val; } static inline int pa_atomic_inc(pa_atomic_t *a) { return pa_atomic_add(a, 1); } static inline int pa_atomic_dec(pa_atomic_t *a) { return pa_atomic_sub(a, 1); } static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { unsigned long not_equal, not_exclusive; pa_memory_barrier(); do { asm volatile ("ldrex %0, [%2]\n" "subs %0, %0, %3\n" "mov %1, %0\n" "strexeq %0, %4, [%2]\n" : "=&r" (not_exclusive), "=&r" (not_equal) : "r" (&a->value), "Ir" (old_i), "r" (new_i) : "cc"); } while(not_exclusive && !not_equal); pa_memory_barrier(); return !not_equal; } typedef struct pa_atomic_ptr { volatile unsigned long value; } pa_atomic_ptr_t; #define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) } static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { pa_memory_barrier(); return (void*) a->value; } static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) { a->value = (unsigned long) p; pa_memory_barrier(); } static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { unsigned long not_equal, not_exclusive; pa_memory_barrier(); do { asm volatile ("ldrex %0, [%2]\n" "subs %0, %0, %3\n" "mov %1, %0\n" "strexeq %0, %4, [%2]\n" : "=&r" (not_exclusive), "=&r" (not_equal) : "r" (&a->value), "Ir" (old_p), "r" (new_p) : "cc"); } while(not_exclusive && !not_equal); pa_memory_barrier(); return !not_equal; } #elif defined(ATOMIC_ARM_LINUX_HELPERS) /* See file arch/arm/kernel/entry-armv.S in your kernel sources for more information about these functions. The arm kernel helper functions first appeared in 2.6.16. Apply --disable-atomic-arm-linux-helpers flag to confugure if you prefere inline asm implementation or you have an obsolete Linux kernel. */ /* Memory barrier */ typedef void (__kernel_dmb_t)(void); #define __kernel_dmb (*(__kernel_dmb_t *)0xffff0fa0) static inline void pa_memory_barrier(void) { #ifndef ATOMIC_ARM_MEMORY_BARRIER_ENABLED __kernel_dmb(); #endif } /* Atomic exchange (__kernel_cmpxchg_t contains memory barriers if needed) */ typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr); #define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0) /* This is just to get rid of all warnings */ typedef int (__kernel_cmpxchg_u_t)(unsigned long oldval, unsigned long newval, volatile unsigned long *ptr); #define __kernel_cmpxchg_u (*(__kernel_cmpxchg_u_t *)0xffff0fc0) typedef struct pa_atomic { volatile int value; } pa_atomic_t; #define PA_ATOMIC_INIT(v) { .value = (v) } static inline int pa_atomic_load(const pa_atomic_t *a) { pa_memory_barrier(); return a->value; } static inline void pa_atomic_store(pa_atomic_t *a, int i) { a->value = i; pa_memory_barrier(); } /* Returns the previously set value */ static inline int pa_atomic_add(pa_atomic_t *a, int i) { int old_val; do { old_val = a->value; } while(__kernel_cmpxchg(old_val, old_val + i, &a->value)); return old_val; } /* Returns the previously set value */ static inline int pa_atomic_sub(pa_atomic_t *a, int i) { int old_val; do { old_val = a->value; } while(__kernel_cmpxchg(old_val, old_val - i, &a->value)); return old_val; } /* Returns the previously set value */ static inline int pa_atomic_inc(pa_atomic_t *a) { return pa_atomic_add(a, 1); } /* Returns the previously set value */ static inline int pa_atomic_dec(pa_atomic_t *a) { return pa_atomic_sub(a, 1); } /* Returns TRUE when the operation was successful. */ static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { pa_bool_t failed; do { failed = !!__kernel_cmpxchg(old_i, new_i, &a->value); } while(failed && a->value == old_i); return !failed; } typedef struct pa_atomic_ptr { volatile unsigned long value; } pa_atomic_ptr_t; #define PA_ATOMIC_PTR_INIT(v) { .value = (unsigned long) (v) } static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { pa_memory_barrier(); return (void*) a->value; } static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) { a->value = (unsigned long) p; pa_memory_barrier(); } static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { pa_bool_t failed; do { failed = !!__kernel_cmpxchg_u((unsigned long) old_p, (unsigned long) new_p, &a->value); } while(failed && a->value == (unsigned long) old_p); return !failed; } #else /* libatomic_ops based implementation */ #include typedef struct pa_atomic { volatile AO_t value; } pa_atomic_t; #define PA_ATOMIC_INIT(v) { .value = (AO_t) (v) } static inline int pa_atomic_load(const pa_atomic_t *a) { return (int) AO_load_full((AO_t*) &a->value); } static inline void pa_atomic_store(pa_atomic_t *a, int i) { AO_store_full(&a->value, (AO_t) i); } static inline int pa_atomic_add(pa_atomic_t *a, int i) { return (int) AO_fetch_and_add_full(&a->value, (AO_t) i); } static inline int pa_atomic_sub(pa_atomic_t *a, int i) { return (int) AO_fetch_and_add_full(&a->value, (AO_t) -i); } static inline int pa_atomic_inc(pa_atomic_t *a) { return (int) AO_fetch_and_add1_full(&a->value); } static inline int pa_atomic_dec(pa_atomic_t *a) { return (int) AO_fetch_and_sub1_full(&a->value); } static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { return AO_compare_and_swap_full(&a->value, (unsigned long) old_i, (unsigned long) new_i); } typedef struct pa_atomic_ptr { volatile AO_t value; } pa_atomic_ptr_t; #define PA_ATOMIC_PTR_INIT(v) { .value = (AO_t) (v) } static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { return (void*) AO_load_full((AO_t*) &a->value); } static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) { AO_store_full(&a->value, (AO_t) p); } static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { return AO_compare_and_swap_full(&a->value, (AO_t) old_p, (AO_t) new_p); } #endif #endif padevchooser-0.9.4/src/pulsecore/avahi-wrap.c000066400000000000000000000121521211222417200211740ustar00rootroot00000000000000/*** This file is part of PulseAudio. Copyright 2006 Lennart Poettering PulseAudio 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.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "avahi-wrap.h" typedef struct { AvahiPoll api; pa_mainloop_api *mainloop; } pa_avahi_poll; struct AvahiWatch { pa_io_event *io_event; pa_avahi_poll *avahi_poll; AvahiWatchEvent current_event; AvahiWatchCallback callback; void *userdata; }; static AvahiWatchEvent translate_io_flags_back(pa_io_event_flags_t e) { return (e & PA_IO_EVENT_INPUT ? AVAHI_WATCH_IN : 0) | (e & PA_IO_EVENT_OUTPUT ? AVAHI_WATCH_OUT : 0) | (e & PA_IO_EVENT_ERROR ? AVAHI_WATCH_ERR : 0) | (e & PA_IO_EVENT_HANGUP ? AVAHI_WATCH_HUP : 0); } static pa_io_event_flags_t translate_io_flags(AvahiWatchEvent e) { return (e & AVAHI_WATCH_IN ? PA_IO_EVENT_INPUT : 0) | (e & AVAHI_WATCH_OUT ? PA_IO_EVENT_OUTPUT : 0) | (e & AVAHI_WATCH_ERR ? PA_IO_EVENT_ERROR : 0) | (e & AVAHI_WATCH_HUP ? PA_IO_EVENT_HANGUP : 0); } static void watch_callback(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { AvahiWatch *w = userdata; pa_assert(a); pa_assert(e); pa_assert(w); w->current_event = translate_io_flags_back(events); w->callback(w, fd, w->current_event, w->userdata); w->current_event = 0; } static AvahiWatch* watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void *userdata) { pa_avahi_poll *p; AvahiWatch *w; pa_assert(api); pa_assert(fd >= 0); pa_assert(callback); pa_assert_se(p = api->userdata); w = pa_xnew(AvahiWatch, 1); w->avahi_poll = p; w->current_event = 0; w->callback = callback; w->userdata = userdata; w->io_event = p->mainloop->io_new(p->mainloop, fd, translate_io_flags(event), watch_callback, w); return w; } static void watch_update(AvahiWatch *w, AvahiWatchEvent event) { pa_assert(w); w->avahi_poll->mainloop->io_enable(w->io_event, translate_io_flags(event)); } static AvahiWatchEvent watch_get_events(AvahiWatch *w) { pa_assert(w); return w->current_event; } static void watch_free(AvahiWatch *w) { pa_assert(w); w->avahi_poll->mainloop->io_free(w->io_event); pa_xfree(w); } struct AvahiTimeout { pa_time_event *time_event; pa_avahi_poll *avahi_poll; AvahiTimeoutCallback callback; void *userdata; }; static void timeout_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { AvahiTimeout *to = userdata; pa_assert(a); pa_assert(e); to->callback(to, to->userdata); } static AvahiTimeout* timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback callback, void *userdata) { pa_avahi_poll *p; AvahiTimeout *t; pa_assert(api); pa_assert(callback); pa_assert_se(p = api->userdata); t = pa_xnew(AvahiTimeout, 1); t->avahi_poll = p; t->callback = callback; t->userdata = userdata; t->time_event = tv ? p->mainloop->time_new(p->mainloop, tv, timeout_callback, t) : NULL; return t; } static void timeout_update(AvahiTimeout *t, const struct timeval *tv) { pa_assert(t); if (t->time_event && tv) t->avahi_poll->mainloop->time_restart(t->time_event, tv); else if (!t->time_event && tv) t->time_event = t->avahi_poll->mainloop->time_new(t->avahi_poll->mainloop, tv, timeout_callback, t); else if (t->time_event && !tv) { t->avahi_poll->mainloop->time_free(t->time_event); t->time_event = NULL; } } static void timeout_free(AvahiTimeout *t) { pa_assert(t); if (t->time_event) t->avahi_poll->mainloop->time_free(t->time_event); pa_xfree(t); } AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *m) { pa_avahi_poll *p; pa_assert(m); p = pa_xnew(pa_avahi_poll, 1); p->api.userdata = p; p->api.watch_new = watch_new; p->api.watch_update = watch_update; p->api.watch_get_events = watch_get_events; p->api.watch_free = watch_free; p->api.timeout_new = timeout_new; p->api.timeout_update = timeout_update; p->api.timeout_free = timeout_free; p->mainloop = m; return &p->api; } void pa_avahi_poll_free(AvahiPoll *api) { pa_avahi_poll *p; pa_assert(api); pa_assert_se(p = api->userdata); pa_xfree(p); } padevchooser-0.9.4/src/pulsecore/avahi-wrap.h000066400000000000000000000017671211222417200212130ustar00rootroot00000000000000#ifndef fooavahiwrapperhfoo #define fooavahiwrapperhfoo /*** This file is part of PulseAudio. Copyright 2006 Lennart Poettering PulseAudio 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.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #include #include AvahiPoll* pa_avahi_poll_new(pa_mainloop_api *api); void pa_avahi_poll_free(AvahiPoll *p); #endif padevchooser-0.9.4/src/pulsecore/core-util.h000066400000000000000000000153761211222417200210600ustar00rootroot00000000000000#ifndef foocoreutilhfoo #define foocoreutilhfoo /*** This file is part of PulseAudio. Copyright 2004-2006 Lennart Poettering Copyright 2006-2007 Pierre Ossman for Cendio AB PulseAudio 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.1 of the License, or (at your option) any later version. PulseAudio 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 PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #include #include #include #include #include #ifdef HAVE_SYS_RESOURCE_H #include #endif #include #include #ifndef PACKAGE #error "Please include config.h before including this file!" #endif struct timeval; /* These resource limits are pretty new on Linux, let's define them * here manually, in case the kernel is newer than the glibc */ #if !defined(RLIMIT_NICE) && defined(__linux__) #define RLIMIT_NICE 13 #endif #if !defined(RLIMIT_RTPRIO) && defined(__linux__) #define RLIMIT_RTPRIO 14 #endif #if !defined(RLIMIT_RTTIME) && defined(__linux__) #define RLIMIT_RTTIME 15 #endif void pa_make_fd_nonblock(int fd); void pa_make_fd_cloexec(int fd); int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid); int pa_make_secure_parent_dir(const char *fn, mode_t, uid_t uid, gid_t gid); ssize_t pa_read(int fd, void *buf, size_t count, int *type); ssize_t pa_write(int fd, const void *buf, size_t count, int *type); ssize_t pa_loop_read(int fd, void*data, size_t size, int *type); ssize_t pa_loop_write(int fd, const void*data, size_t size, int *type); int pa_close(int fd); void pa_check_signal_is_blocked(int sig); char *pa_sprintf_malloc(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); char *pa_vsprintf_malloc(const char *format, va_list ap); char *pa_strlcpy(char *b, const char *s, size_t l); char *pa_parent_dir(const char *fn); int pa_make_realtime(int rtprio); int pa_raise_priority(int nice_level); void pa_reset_priority(void); int pa_parse_boolean(const char *s) PA_GCC_PURE; static inline const char *pa_yes_no(pa_bool_t b) { return b ? "yes" : "no"; } static inline const char *pa_strnull(const char *x) { return x ? x : "(null)"; } static inline const char *pa_strempty(const char *x) { return x ? x : ""; } static inline const char *pa_strna(const char *x) { return x ? x : "n/a"; } char *pa_split(const char *c, const char*delimiters, const char **state); char *pa_split_spaces(const char *c, const char **state); char *pa_strip_nl(char *s); char *pa_strip(char *s); const char *pa_sig2str(int sig) PA_GCC_PURE; int pa_own_uid_in_group(const char *name, gid_t *gid); int pa_uid_in_group(uid_t uid, const char *name); gid_t pa_get_gid_of_group(const char *name); int pa_check_in_group(gid_t g); int pa_lock_fd(int fd, int b); int pa_lock_lockfile(const char *fn); int pa_unlock_lockfile(const char *fn, int fd); char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength); size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength); pa_bool_t pa_startswith(const char *s, const char *pfx) PA_GCC_PURE; pa_bool_t pa_endswith(const char *s, const char *sfx) PA_GCC_PURE; FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result); char* pa_find_config_file(const char *global, const char *local, const char *env); char *pa_get_runtime_dir(void); char *pa_get_state_dir(void); char *pa_get_home_dir_malloc(void); char *pa_get_binary_name_malloc(void); char *pa_runtime_path(const char *fn); char *pa_state_path(const char *fn, pa_bool_t prepend_machine_id); int pa_atoi(const char *s, int32_t *ret_i); int pa_atou(const char *s, uint32_t *ret_u); int pa_atod(const char *s, double *ret_d); size_t pa_snprintf(char *str, size_t size, const char *format, ...); size_t pa_vsnprintf(char *str, size_t size, const char *format, va_list ap); char *pa_truncate_utf8(char *c, size_t l); int pa_match(const char *expr, const char *v); char *pa_getcwd(void); char *pa_make_path_absolute(const char *p); pa_bool_t pa_is_path_absolute(const char *p); void *pa_will_need(const void *p, size_t l); static inline int pa_is_power_of_two(unsigned n) { return !(n & (n - 1)); } static inline unsigned pa_ulog2(unsigned n) { if (n <= 1) return 0; #if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) return 8U * (unsigned) sizeof(unsigned) - (unsigned) __builtin_clz(n) - 1; #else { unsigned r = 0; for (;;) { n = n >> 1; if (!n) return r; r++; } } #endif } static inline unsigned pa_make_power_of_two(unsigned n) { if (pa_is_power_of_two(n)) return n; return 1U << (pa_ulog2(n) + 1); } void pa_close_pipe(int fds[2]); char *pa_readlink(const char *p); int pa_close_all(int except_fd, ...); int pa_close_allv(const int except_fds[]); int pa_unblock_sigs(int except, ...); int pa_unblock_sigsv(const int except[]); int pa_reset_sigs(int except, ...); int pa_reset_sigsv(const int except[]); void pa_set_env(const char *key, const char *value); void pa_set_env_and_record(const char *key, const char *value); void pa_unset_env_recorded(void); pa_bool_t pa_in_system_mode(void); #define pa_streq(a,b) (!strcmp((a),(b))) char *pa_get_host_name_malloc(void); char *pa_get_user_name_malloc(void); char *pa_machine_id(void); char *pa_session_id(void); char *pa_uname_string(void); #ifdef HAVE_VALGRIND_MEMCHECK_H pa_bool_t pa_in_valgrind(void); #else static inline pa_bool_t pa_in_valgrind(void) { return FALSE; } #endif unsigned pa_gcd(unsigned a, unsigned b); void pa_reduce(unsigned *num, unsigned *den); unsigned pa_ncpus(void); char *pa_replace(const char*s, const char*a, const char *b); char *pa_unescape(char *p); char *pa_realpath(const char *path); void pa_disable_sigpipe(void); void pa_xfreev(void**a); static inline void pa_xstrfreev(char **a) { pa_xfreev((void**) a); } char **pa_split_spaces_strv(const char *s); char* pa_maybe_prefix_path(const char *path, const char *prefix); /* Returns size of the specified pipe or 4096 on failure */ size_t pa_pipe_buf(int fd); void pa_reset_personality(void); #if defined(__linux__) && !defined(__OPTIMIZE__) pa_bool_t pa_run_from_build_tree(void); #endif const char *pa_get_temp_dir(void); char *pa_read_line_from_file(const char *fn); pa_bool_t pa_running_in_vm(void); #endif padevchooser-0.9.4/src/pulsecore/log.h000066400000000000000000000101241211222417200177200ustar00rootroot00000000000000#ifndef foologhfoo #define foologhfoo /*** This file is part of PulseAudio. Copyright 2004-2006 Lennart Poettering Copyright 2006 Pierre Ossman for Cendio AB PulseAudio 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.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #include #include #include #include /* A simple logging subsystem */ /* Where to log to */ typedef enum pa_log_target { PA_LOG_STDERR, /* default */ PA_LOG_SYSLOG, PA_LOG_NULL, /* to /dev/null */ PA_LOG_TARGET_MAX } pa_log_target_t; typedef enum pa_log_level { PA_LOG_ERROR = 0, /* Error messages */ PA_LOG_WARN = 1, /* Warning messages */ PA_LOG_NOTICE = 2, /* Notice messages */ PA_LOG_INFO = 3, /* Info messages */ PA_LOG_DEBUG = 4, /* debug message */ PA_LOG_LEVEL_MAX } pa_log_level_t; typedef enum pa_log_flags { PA_LOG_COLORS = 0x01, /* Show colorful output */ PA_LOG_PRINT_TIME = 0x02, /* Show time */ PA_LOG_PRINT_FILE = 0x04, /* Show source file */ PA_LOG_PRINT_META = 0x08, /* Show extended locaton information */ PA_LOG_PRINT_LEVEL = 0x10, /* Show log level prefix */ } pa_log_flags_t; typedef enum pa_log_merge { PA_LOG_SET, PA_LOG_UNSET, PA_LOG_RESET } pa_log_merge_t; /* Set an identification for the current daemon. Used when logging to syslog. */ void pa_log_set_ident(const char *p); /* Set a log target. */ void pa_log_set_target(pa_log_target_t t); /* Maximal log level */ void pa_log_set_level(pa_log_level_t l); /* Set flags */ void pa_log_set_flags(pa_log_flags_t flags, pa_log_merge_t merge); /* Enable backtrace */ void pa_log_set_show_backtrace(unsigned nlevels); /* Skip the first backtrace frames */ void pa_log_set_skip_backtrace(unsigned nlevels); void pa_log_level_meta( pa_log_level_t level, const char*file, int line, const char *func, const char *format, ...) PA_GCC_PRINTF_ATTR(5,6); void pa_log_levelv_meta( pa_log_level_t level, const char*file, int line, const char *func, const char *format, va_list ap); void pa_log_level( pa_log_level_t level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3); void pa_log_levelv( pa_log_level_t level, const char *format, va_list ap); #if __STDC_VERSION__ >= 199901L /* ISO varargs available */ #define pa_log_debug(...) pa_log_level_meta(PA_LOG_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__) #define pa_log_info(...) pa_log_level_meta(PA_LOG_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__) #define pa_log_notice(...) pa_log_level_meta(PA_LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__) #define pa_log_warn(...) pa_log_level_meta(PA_LOG_WARN, __FILE__, __LINE__, __func__, __VA_ARGS__) #define pa_log_error(...) pa_log_level_meta(PA_LOG_ERROR, __FILE__, __LINE__, __func__, __VA_ARGS__) #define pa_logl(level, ...) pa_log_level_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__) #else #define LOG_FUNC(suffix, level) \ PA_GCC_UNUSED static void pa_log_##suffix(const char *format, ...) { \ va_list ap; \ va_start(ap, format); \ pa_log_levelv_meta(level, NULL, 0, NULL, format, ap); \ va_end(ap); \ } LOG_FUNC(debug, PA_LOG_DEBUG) LOG_FUNC(info, PA_LOG_INFO) LOG_FUNC(notice, PA_LOG_NOTICE) LOG_FUNC(warn, PA_LOG_WARN) LOG_FUNC(error, PA_LOG_ERROR) #endif #define pa_log pa_log_error pa_bool_t pa_log_ratelimit(pa_log_level_t level); #endif padevchooser-0.9.4/src/pulsecore/macro.h000066400000000000000000000255001211222417200202440ustar00rootroot00000000000000#ifndef foopulsemacrohfoo #define foopulsemacrohfoo /*** This file is part of PulseAudio. Copyright 2004-2006 Lennart Poettering PulseAudio 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.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #include #include #include #include #include #include #include #include #include #ifndef PACKAGE #error "Please include config.h before including this file!" #endif #ifndef PA_LIKELY #ifdef __GNUC__ #define PA_LIKELY(x) (__builtin_expect(!!(x),1)) #define PA_UNLIKELY(x) (__builtin_expect(!!(x),0)) #else #define PA_LIKELY(x) (x) #define PA_UNLIKELY(x) (x) #endif #endif #if defined(PAGE_SIZE) #define PA_PAGE_SIZE ((size_t) PAGE_SIZE) #elif defined(PAGESIZE) #define PA_PAGE_SIZE ((size_t) PAGESIZE) #elif defined(HAVE_SYSCONF) #define PA_PAGE_SIZE ((size_t) (sysconf(_SC_PAGE_SIZE))) #else /* Let's hope it's like x86. */ #define PA_PAGE_SIZE ((size_t) 4096) #endif /* Rounds down */ static inline void* PA_ALIGN_PTR(const void *p) { return (void*) (((size_t) p) & ~(sizeof(void*) - 1)); } /* Rounds up */ static inline size_t PA_ALIGN(size_t l) { return ((l + sizeof(void*) - 1) & ~(sizeof(void*) - 1)); } /* Rounds down */ static inline void* PA_PAGE_ALIGN_PTR(const void *p) { return (void*) (((size_t) p) & ~(PA_PAGE_SIZE - 1)); } /* Rounds up */ static inline size_t PA_PAGE_ALIGN(size_t l) { return (l + PA_PAGE_SIZE - 1) & ~(PA_PAGE_SIZE - 1); } #define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) #if defined(__GNUC__) #define PA_DECLARE_ALIGNED(n,t,v) t v __attribute__ ((aligned (n))) #else #define PA_DECLARE_ALIGNED(n,t,v) t v #endif /* The users of PA_MIN and PA_MAX, PA_CLAMP, PA_ROUND_UP should be * aware that these macros on non-GCC executed code with side effects * twice. It is thus considered misuse to use code with side effects * as arguments to MIN and MAX. */ #ifdef __GNUC__ #define PA_MAX(a,b) \ __extension__ ({ \ typeof(a) _a = (a); \ typeof(b) _b = (b); \ _a > _b ? _a : _b; \ }) #else #define PA_MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #ifdef __GNUC__ #define PA_MIN(a,b) \ __extension__ ({ \ typeof(a) _a = (a); \ typeof(b) _b = (b); \ _a < _b ? _a : _b; \ }) #else #define PA_MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #ifdef __GNUC__ #define PA_CLAMP(x, low, high) \ __extension__ ({ \ typeof(x) _x = (x); \ typeof(low) _low = (low); \ typeof(high) _high = (high); \ ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \ }) #else #define PA_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) #endif #ifdef __GNUC__ #define PA_CLAMP_UNLIKELY(x, low, high) \ __extension__ ({ \ typeof(x) _x = (x); \ typeof(low) _low = (low); \ typeof(high) _high = (high); \ (PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \ }) #else #define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x))) #endif /* We don't define a PA_CLAMP_LIKELY here, because it doesn't really * make sense: we cannot know if it is more likely that the values is * lower or greater than the boundaries.*/ #ifdef __GNUC__ #define PA_ROUND_UP(a, b) \ __extension__ ({ \ typeof(a) _a = (a); \ typeof(b) _b = (b); \ ((_a + _b - 1) / _b) * _b; \ }) #else #define PA_ROUND_UP(a, b) ((((a) + (b) - 1) / (b)) * (b)) #endif #ifdef __GNUC__ #define PA_ROUND_DOWN(a, b) \ __extension__ ({ \ typeof(a) _a = (a); \ typeof(b) _b = (b); \ (_a / _b) * _b; \ }) #else #define PA_ROUND_DOWN(a, b) (((a) / (b)) * (b)) #endif #ifdef __GNUC__ #define PA_CLIP_SUB(a, b) \ __extension__ ({ \ typeof(a) _a = (a); \ typeof(b) _b = (b); \ _a > _b ? _a - _b : 0; \ }) #else #define PA_CLIP_SUB(a, b) ((a) > (b) ? (a) - (b) : 0) #endif /* This type is not intended to be used in exported APIs! Use classic "int" there! */ #ifdef HAVE_STD_BOOL typedef _Bool pa_bool_t; #else typedef int pa_bool_t; #endif #ifndef FALSE #define FALSE ((pa_bool_t) 0) #endif #ifndef TRUE #define TRUE (!FALSE) #endif #ifdef __GNUC__ #define PA_PRETTY_FUNCTION __PRETTY_FUNCTION__ #else #define PA_PRETTY_FUNCTION "" #endif #define pa_return_if_fail(expr) \ do { \ if (PA_UNLIKELY(!(expr))) { \ pa_log_debug("Assertion '%s' failed at %s:%u, function %s.\n", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \ return; \ } \ } while(FALSE) #define pa_return_val_if_fail(expr, val) \ do { \ if (PA_UNLIKELY(!(expr))) { \ pa_log_debug("Assertion '%s' failed at %s:%u, function %s.\n", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \ return (val); \ } \ } while(FALSE) #define pa_return_null_if_fail(expr) pa_return_val_if_fail(expr, NULL) /* pa_assert_se() is an assert which guarantees side effects of x, * i.e. is never optimized away, regardless of NDEBUG or FASTPATH. */ #define pa_assert_se(expr) \ do { \ if (PA_UNLIKELY(!(expr))) { \ pa_log_error("Assertion '%s' failed at %s:%u, function %s(). Aborting.", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \ abort(); \ } \ } while (FALSE) /* Does exactly nothing */ #define pa_nop() do {} while (FALSE) /* pa_assert() is an assert that may be optimized away by defining * NDEBUG. pa_assert_fp() is an assert that may be optimized away by * defining FASTPATH. It is supposed to be used in inner loops. It's * there for extra paranoia checking and should probably be removed in * production builds. */ #ifdef NDEBUG #define pa_assert(expr) pa_nop() #define pa_assert_fp(expr) pa_nop() #elif defined (FASTPATH) #define pa_assert(expr) pa_assert_se(expr) #define pa_assert_fp(expr) pa_nop() #else #define pa_assert(expr) pa_assert_se(expr) #define pa_assert_fp(expr) pa_assert_se(expr) #endif #ifdef NDEBUG #define pa_assert_not_reached() pa_nop() #else #define pa_assert_not_reached() \ do { \ pa_log_error("Code should not be reached at %s:%u, function %s(). Aborting.", __FILE__, __LINE__, PA_PRETTY_FUNCTION); \ abort(); \ } while (FALSE) #endif /* A compile time assertion */ #define pa_assert_cc(expr) \ do { \ switch (0) { \ case 0: \ case !!(expr): \ ; \ } \ } while (FALSE) #define PA_PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) #define PA_UINT_TO_PTR(u) ((void*) ((uintptr_t) (u))) #define PA_PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) #define PA_UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u))) #define PA_PTR_TO_INT(p) ((int) ((intptr_t) (p))) #define PA_INT_TO_PTR(u) ((void*) ((intptr_t) (u))) #define PA_PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p))) #define PA_INT32_TO_PTR(u) ((void*) ((intptr_t) (u))) #ifdef OS_IS_WIN32 #define PA_PATH_SEP "\\" #define PA_PATH_SEP_CHAR '\\' #else #define PA_PATH_SEP "/" #define PA_PATH_SEP_CHAR '/' #endif #if defined(__GNUC__) && defined(__ELF__) #define PA_WARN_REFERENCE(sym, msg) \ __asm__(".section .gnu.warning." #sym); \ __asm__(".asciz \"" msg "\""); \ __asm__(".previous") #else #define PA_WARN_REFERENCE(sym, msg) #endif #if defined(__i386__) || defined(__x86_64__) #define PA_DEBUG_TRAP __asm__("int $3") #else #define PA_DEBUG_TRAP raise(SIGTRAP) #endif #define pa_memzero(x,l) (memset((x), 0, (l))) #define pa_zero(x) (pa_memzero(&(x), sizeof(x))) #define PA_INT_TYPE_SIGNED(type) (!!((type) 0 > (type) -1)) #define PA_INT_TYPE_MAX(type) \ ((type) (PA_INT_TYPE_SIGNED(type) \ ? ~(~(type) 0 << (8*sizeof(type)-1)) \ : (type) -1)) #define PA_INT_TYPE_MIN(type) \ ((type) (PA_INT_TYPE_SIGNED(type) \ ? (~(type) 0 << (8*sizeof(type)-1)) \ : (type) 0)) /* We include this at the very last place */ #include #endif padevchooser-0.9.4/src/pulsecore/refcnt.h000066400000000000000000000045261211222417200204310ustar00rootroot00000000000000#ifndef foopulserefcnthfoo #define foopulserefcnthfoo /*** This file is part of PulseAudio. Copyright 2006 Lennart Poettering PulseAudio 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.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #include #include #include /* #define DEBUG_REF */ #define PA_REFCNT_DECLARE \ pa_atomic_t _ref #define PA_REFCNT_VALUE(p) \ pa_atomic_load(&(p)->_ref) #define PA_REFCNT_INIT_ZERO(p) \ pa_atomic_store(&(p)->_ref, 0) #ifndef DEBUG_REF #define PA_REFCNT_INIT(p) \ pa_atomic_store(&(p)->_ref, 1) #define PA_REFCNT_INC(p) \ pa_atomic_inc(&(p)->_ref) #define PA_REFCNT_DEC(p) \ (pa_atomic_dec(&(p)->_ref)-1) #else /* If you need to debug ref counting problems define DEBUG_REF and * set $PULSE_LOG_BACKTRACE=5 or suchlike in the shell when running * PA */ #define PA_REFCNT_INIT(p) \ do { \ pa_atomic_store(&(p)->_ref, 1); \ pa_log("REF: Init %p", p); \ } while (FALSE) #define PA_REFCNT_INC(p) \ do { \ pa_atomic_inc(&(p)->_ref); \ pa_log("REF: Inc %p", p); \ } while (FALSE) \ #define PA_REFCNT_DEC(p) \ ({ \ int _j = (pa_atomic_dec(&(p)->_ref)-1); \ if (_j <= 0) \ pa_log("REF: Done %p", p); \ else \ pa_log("REF: Dec %p", p); \ _j; \ }) #endif #endif padevchooser-0.9.4/src/stubs.c000066400000000000000000000003751211222417200163000ustar00rootroot00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include void pa_log_levelv_meta( pa_log_level_t level, const char*file, int line, const char *func, const char *format, va_list ap) { } padevchooser-0.9.4/src/x11prop.c000066400000000000000000000035561211222417200164560ustar00rootroot00000000000000/*** This file is part of pulseaudio. pulseaudio 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. pulseaudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with pulseaudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "x11prop.h" void x11_set_prop(Display *d, const char *name, const char *data) { Atom a = XInternAtom(d, name, False); XChangeProperty(d, RootWindow(d, 0), a, XA_STRING, 8, PropModeReplace, (const unsigned char*) data, strlen(data)+1); } void x11_del_prop(Display *d, const char *name) { Atom a = XInternAtom(d, name, False); XDeleteProperty(d, RootWindow(d, 0), a); } char* x11_get_prop(Display *d, const char *name, char *p, size_t l) { Atom actual_type; int actual_format; unsigned long nitems; unsigned long nbytes_after; unsigned char *prop = NULL; char *ret = NULL; Atom a = XInternAtom(d, name, False); if (XGetWindowProperty(d, RootWindow(d, 0), a, 0, (l+2)/4, False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop) != Success) goto finish; if (actual_type != XA_STRING) goto finish; memcpy(p, prop, nitems); p[nitems] = 0; ret = p; finish: if (prop) XFree(prop); return ret; } padevchooser-0.9.4/src/x11prop.h000066400000000000000000000020201211222417200164440ustar00rootroot00000000000000#ifndef foox11prophfoo #define foox11prophfoo /*** This file is part of pulseaudio. pulseaudio 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. pulseaudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with pulseaudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #include #include void x11_set_prop(Display *d, const char *name, const char *data); void x11_del_prop(Display *d, const char *name); char* x11_get_prop(Display *d, const char *name, char *p, size_t l); #endif