diff options
132 files changed, 10441 insertions, 7122 deletions
diff --git a/ABOUT-NLS b/ABOUT-NLS new file mode 100644 index 0000000..4f50fb5 --- /dev/null +++ b/ABOUT-NLS @@ -0,0 +1,1327 @@ +1 Notes on the Free Translation Project +*************************************** + +Free software is going international! The Free Translation Project is +a way to get maintainers of free software, translators, and users all +together, so that free software will gradually become able to speak many +languages. A few packages already provide translations for their +messages. + + If you found this `ABOUT-NLS' file inside a distribution, you may +assume that the distributed package does use GNU `gettext' internally, +itself available at your nearest GNU archive site. But you do _not_ +need to install GNU `gettext' prior to configuring, installing or using +this package with messages translated. + + Installers will find here some useful hints. These notes also +explain how users should proceed for getting the programs to use the +available translations. They tell how people wanting to contribute and +work on translations can contact the appropriate team. + + When reporting bugs in the `intl/' directory or bugs which may be +related to internationalization, you should tell about the version of +`gettext' which is used. The information can be found in the +`intl/VERSION' file, in internationalized packages. + +1.1 Quick configuration advice +============================== + +If you want to exploit the full power of internationalization, you +should configure it using + + ./configure --with-included-gettext + +to force usage of internationalizing routines provided within this +package, despite the existence of internationalizing capabilities in the +operating system where this package is being installed. So far, only +the `gettext' implementation in the GNU C library version 2 provides as +many features (such as locale alias, message inheritance, automatic +charset conversion or plural form handling) as the implementation here. +It is also not possible to offer this additional functionality on top +of a `catgets' implementation. Future versions of GNU `gettext' will +very likely convey even more functionality. So it might be a good idea +to change to GNU `gettext' as soon as possible. + + So you need _not_ provide this option if you are using GNU libc 2 or +you have installed a recent copy of the GNU gettext package with the +included `libintl'. + +1.2 INSTALL Matters +=================== + +Some packages are "localizable" when properly installed; the programs +they contain can be made to speak your own native language. Most such +packages use GNU `gettext'. Other packages have their own ways to +internationalization, predating GNU `gettext'. + + By default, this package will be installed to allow translation of +messages. It will automatically detect whether the system already +provides the GNU `gettext' functions. If not, the included GNU +`gettext' library will be used. This library is wholly contained +within this package, usually in the `intl/' subdirectory, so prior +installation of the GNU `gettext' package is _not_ required. +Installers may use special options at configuration time for changing +the default behaviour. The commands: + + ./configure --with-included-gettext + ./configure --disable-nls + +will, respectively, bypass any pre-existing `gettext' to use the +internationalizing routines provided within this package, or else, +_totally_ disable translation of messages. + + When you already have GNU `gettext' installed on your system and run +configure without an option for your new package, `configure' will +probably detect the previously built and installed `libintl.a' file and +will decide to use this. This might not be desirable. You should use +the more recent version of the GNU `gettext' library. I.e. if the file +`intl/VERSION' shows that the library which comes with this package is +more recent, you should use + + ./configure --with-included-gettext + +to prevent auto-detection. + + The configuration process will not test for the `catgets' function +and therefore it will not be used. The reason is that even an +emulation of `gettext' on top of `catgets' could not provide all the +extensions of the GNU `gettext' library. + + Internationalized packages usually have many `po/LL.po' files, where +LL gives an ISO 639 two-letter code identifying the language. Unless +translations have been forbidden at `configure' time by using the +`--disable-nls' switch, all available translations are installed +together with the package. However, the environment variable `LINGUAS' +may be set, prior to configuration, to limit the installed set. +`LINGUAS' should then contain a space separated list of two-letter +codes, stating which languages are allowed. + +1.3 Using This Package +====================== + +As a user, if your language has been installed for this package, you +only have to set the `LANG' environment variable to the appropriate +`LL_CC' combination. If you happen to have the `LC_ALL' or some other +`LC_xxx' environment variables set, you should unset them before +setting `LANG', otherwise the setting of `LANG' will not have the +desired effect. Here `LL' is an ISO 639 two-letter language code, and +`CC' is an ISO 3166 two-letter country code. For example, let's +suppose that you speak German and live in Germany. At the shell +prompt, merely execute `setenv LANG de_DE' (in `csh'), +`export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash'). +This can be done from your `.login' or `.profile' file, once and for +all. + + You might think that the country code specification is redundant. +But in fact, some languages have dialects in different countries. For +example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The +country code serves to distinguish the dialects. + + The locale naming convention of `LL_CC', with `LL' denoting the +language and `CC' denoting the country, is the one use on systems based +on GNU libc. On other systems, some variations of this scheme are +used, such as `LL' or `LL_CC.ENCODING'. You can get the list of +locales supported by your system for your language by running the +command `locale -a | grep '^LL''. + + Not all programs have translations for all languages. By default, an +English message is shown in place of a nonexistent translation. If you +understand other languages, you can set up a priority list of languages. +This is done through a different environment variable, called +`LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG' +for the purpose of message handling, but you still need to have `LANG' +set to the primary language; this is required by other parts of the +system libraries. For example, some Swedish users who would rather +read translations in German than English for when Swedish is not +available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'. + + Special advice for Norwegian users: The language code for Norwegian +bokma*l changed from `no' to `nb' recently (in 2003). During the +transition period, while some message catalogs for this language are +installed under `nb' and some older ones under `no', it's recommended +for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and +older translations are used. + + In the `LANGUAGE' environment variable, but not in the `LANG' +environment variable, `LL_CC' combinations can be abbreviated as `LL' +to denote the language's main dialect. For example, `de' is equivalent +to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT' +(Portuguese as spoken in Portugal) in this context. + +1.4 Translating Teams +===================== + +For the Free Translation Project to be a success, we need interested +people who like their own language and write it well, and who are also +able to synergize with other translators speaking the same language. +Each translation team has its own mailing list. The up-to-date list of +teams can be found at the Free Translation Project's homepage, +`http://translationproject.org/', in the "Teams" area. + + If you'd like to volunteer to _work_ at translating messages, you +should become a member of the translating team for your own language. +The subscribing address is _not_ the same as the list itself, it has +`-request' appended. For example, speakers of Swedish can send a +message to `[email protected]', having this message body: + + subscribe + + Keep in mind that team members are expected to participate +_actively_ in translations, or at solving translational difficulties, +rather than merely lurking around. If your team does not exist yet and +you want to start one, or if you are unsure about what to do or how to +get started, please write to `[email protected]' to +reach the coordinator for all translator teams. + + The English team is special. It works at improving and uniformizing +the terminology in use. Proven linguistic skills are praised more than +programming skills, here. + +1.5 Available Packages +====================== + +Languages are not equally supported in all packages. The following +matrix shows the current state of internationalization, as of May 2010. +The matrix shows, in regard of each package, for which languages PO +files have been submitted to translation coordination, with a +translation percentage of at least 50%. + + Ready PO files af am ar as ast az be be@latin bg bn_IN bs ca crh + +---------------------------------------------------+ + a2ps | [] [] | + aegis | | + ant-phone | | + anubis | | + aspell | [] [] | + bash | | + bfd | | + bibshelf | [] | + binutils | | + bison | | + bison-runtime | [] | + bluez-pin | [] [] | + bombono-dvd | | + buzztard | | + cflow | | + clisp | | + coreutils | [] [] | + cpio | | + cppi | | + cpplib | [] | + cryptsetup | | + dfarc | | + dialog | [] [] | + dico | | + diffutils | [] | + dink | | + doodle | | + e2fsprogs | [] | + enscript | [] | + exif | | + fetchmail | [] | + findutils | [] | + flex | [] | + freedink | | + gas | | + gawk | [] [] | + gcal | [] | + gcc | | + gettext-examples | [] [] [] [] | + gettext-runtime | [] [] [] | + gettext-tools | [] [] | + gip | [] | + gjay | | + gliv | [] | + glunarclock | [] [] | + gnubiff | | + gnucash | [] | + gnuedu | | + gnulib | | + gnunet | | + gnunet-gtk | | + gnutls | | + gold | | + gpe-aerial | | + gpe-beam | | + gpe-bluetooth | | + gpe-calendar | | + gpe-clock | [] | + gpe-conf | | + gpe-contacts | | + gpe-edit | | + gpe-filemanager | | + gpe-go | | + gpe-login | | + gpe-ownerinfo | [] | + gpe-package | | + gpe-sketchbook | | + gpe-su | [] | + gpe-taskmanager | [] | + gpe-timesheet | [] | + gpe-today | [] | + gpe-todo | | + gphoto2 | | + gprof | [] | + gpsdrive | | + gramadoir | | + grep | | + grub | [] [] | + gsasl | | + gss | | + gst-plugins-bad | [] | + gst-plugins-base | [] | + gst-plugins-good | [] | + gst-plugins-ugly | [] | + gstreamer | [] [] [] | + gtick | | + gtkam | [] | + gtkorphan | [] | + gtkspell | [] [] [] | + gutenprint | | + hello | [] | + help2man | | + hylafax | | + idutils | | + indent | [] [] | + iso_15924 | | + iso_3166 | [] [] [] [] [] [] [] [] | + iso_3166_2 | | + iso_4217 | | + iso_639 | [] [] [] [] [] | + iso_639_3 | [] | + jwhois | | + kbd | | + keytouch | [] | + keytouch-editor | | + keytouch-keyboa... | [] | + klavaro | [] | + latrine | | + ld | [] | + leafpad | [] [] | + libc | [] [] | + libexif | () | + libextractor | | + libgnutls | | + libgpewidget | | + libgpg-error | | + libgphoto2 | | + libgphoto2_port | | + libgsasl | | + libiconv | [] | + libidn | | + lifelines | | + liferea | [] [] | + lilypond | | + linkdr | [] | + lordsawar | | + lprng | | + lynx | [] | + m4 | | + mailfromd | | + mailutils | | + make | | + man-db | | + man-db-manpages | | + minicom | | + mkisofs | | + myserver | | + nano | [] [] | + opcodes | | + parted | | + pies | | + popt | | + psmisc | | + pspp | [] | + pwdutils | | + radius | [] | + recode | [] [] | + rosegarden | | + rpm | | + rush | | + sarg | | + screem | | + scrollkeeper | [] [] [] | + sed | [] [] | + sharutils | [] [] | + shishi | | + skencil | | + solfege | | + solfege-manual | | + soundtracker | | + sp | | + sysstat | | + tar | [] | + texinfo | | + tin | | + unicode-han-tra... | | + unicode-transla... | | + util-linux-ng | [] | + vice | | + vmm | | + vorbis-tools | | + wastesedge | | + wdiff | | + wget | [] [] | + wyslij-po | | + xchat | [] [] [] [] | + xdg-user-dirs | [] [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] | + +---------------------------------------------------+ + af am ar as ast az be be@latin bg bn_IN bs ca crh + 6 0 2 3 19 1 11 3 28 3 1 38 5 + + cs da de el en en_GB en_ZA eo es et eu fa fi + +-------------------------------------------------+ + a2ps | [] [] [] [] [] [] [] [] | + aegis | [] [] [] | + ant-phone | [] () | + anubis | [] [] [] | + aspell | [] [] [] [] [] | + bash | [] [] [] [] | + bfd | [] [] | + bibshelf | [] [] [] [] | + binutils | [] [] | + bison | [] [] [] | + bison-runtime | [] [] [] [] [] | + bluez-pin | [] [] [] [] [] [] [] | + bombono-dvd | [] [] | + buzztard | [] [] [] | + cflow | [] [] [] | + clisp | [] [] [] [] | + coreutils | [] [] [] [] | + cpio | [] | + cppi | [] | + cpplib | [] [] [] | + cryptsetup | [] | + dfarc | [] [] [] [] | + dialog | [] [] [] [] [] | + dico | | + diffutils | [] [] [] [] [] [] [] | + dink | [] [] [] | + doodle | [] | + e2fsprogs | [] [] [] | + enscript | [] [] [] | + exif | () [] [] [] | + fetchmail | [] [] () [] [] [] | + findutils | [] [] [] [] | + flex | [] [] [] | + freedink | [] [] [] [] | + gas | [] | + gawk | [] [] [] | + gcal | [] | + gcc | [] [] | + gettext-examples | [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] | + gettext-tools | [] [] [] | + gip | [] [] [] [] [] | + gjay | [] [] | + gliv | [] [] [] [] | + glunarclock | [] [] [] | + gnubiff | () | + gnucash | [] () () () () () | + gnuedu | [] [] | + gnulib | [] [] [] | + gnunet | | + gnunet-gtk | [] | + gnutls | [] [] | + gold | [] [] | + gpe-aerial | [] [] [] [] [] | + gpe-beam | [] [] [] [] [] | + gpe-bluetooth | [] [] [] | + gpe-calendar | [] [] | + gpe-clock | [] [] [] [] [] | + gpe-conf | [] [] [] [] | + gpe-contacts | [] [] [] [] | + gpe-edit | [] [] [] | + gpe-filemanager | [] [] [] [] | + gpe-go | [] [] [] [] [] | + gpe-login | [] [] [] | + gpe-ownerinfo | [] [] [] [] [] | + gpe-package | [] [] [] [] | + gpe-sketchbook | [] [] [] [] [] | + gpe-su | [] [] [] [] [] | + gpe-taskmanager | [] [] [] [] [] | + gpe-timesheet | [] [] [] [] [] | + gpe-today | [] [] [] [] [] | + gpe-todo | [] [] [] [] | + gphoto2 | [] [] () [] [] [] [] | + gprof | [] [] [] [] | + gpsdrive | [] [] [] | + gramadoir | [] [] [] | + grep | [] [] | + grub | [] [] [] | + gsasl | [] [] | + gss | [] | + gst-plugins-bad | [] [] [] [] [] [] | + gst-plugins-base | [] [] [] [] [] [] | + gst-plugins-good | [] [] [] [] [] [] [] | + gst-plugins-ugly | [] [] [] [] [] [] [] | + gstreamer | [] [] [] [] [] [] | + gtick | [] () [] [] | + gtkam | [] [] () [] [] | + gtkorphan | [] [] [] [] | + gtkspell | [] [] [] [] [] [] [] [] | + gutenprint | [] [] [] [] | + hello | [] [] [] [] [] | + help2man | [] [] | + hylafax | [] [] | + idutils | [] [] [] | + indent | [] [] [] [] [] [] [] [] | + iso_15924 | [] () [] [] [] | + iso_3166 | [] [] [] () [] [] [] () [] | + iso_3166_2 | () | + iso_4217 | [] [] [] () [] [] [] | + iso_639 | [] [] [] () [] [] [] | + iso_639_3 | | + jwhois | [] [] | + kbd | [] [] [] [] [] | + keytouch | [] [] [] | + keytouch-editor | [] [] [] | + keytouch-keyboa... | [] [] | + klavaro | [] [] [] [] | + latrine | [] () [] | + ld | [] [] [] | + leafpad | [] [] [] [] [] [] [] | + libc | [] [] [] [] [] | + libexif | [] [] () | + libextractor | | + libgnutls | [] | + libgpewidget | [] [] [] | + libgpg-error | [] [] | + libgphoto2 | [] () | + libgphoto2_port | [] () [] | + libgsasl | [] | + libiconv | [] [] [] [] [] [] | + libidn | [] [] [] [] | + lifelines | [] () | + liferea | [] [] [] [] [] | + lilypond | [] [] [] [] | + linkdr | [] [] [] [] | + lordsawar | [] | + lprng | | + lynx | [] [] [] [] | + m4 | [] [] [] [] [] | + mailfromd | | + mailutils | [] | + make | [] [] [] [] | + man-db | | + man-db-manpages | | + minicom | [] [] [] [] [] | + mkisofs | [] | + myserver | | + nano | [] [] [] [] | + opcodes | [] [] [] | + parted | [] [] | + pies | | + popt | [] [] [] [] [] [] | + psmisc | [] [] [] [] | + pspp | [] | + pwdutils | [] | + radius | [] | + recode | [] [] [] [] [] [] [] | + rosegarden | () () () () | + rpm | [] [] [] | + rush | | + sarg | | + screem | | + scrollkeeper | [] [] [] [] [] [] | + sed | [] [] [] [] [] [] [] | + sharutils | [] [] [] [] [] | + shishi | | + skencil | [] () [] | + solfege | [] [] [] [] | + solfege-manual | [] [] | + soundtracker | [] [] [] | + sp | [] | + sysstat | [] [] [] [] | + tar | [] [] [] [] [] | + texinfo | [] [] [] | + tin | [] [] | + unicode-han-tra... | | + unicode-transla... | | + util-linux-ng | [] [] [] [] [] | + vice | () () | + vmm | [] | + vorbis-tools | [] [] | + wastesedge | [] | + wdiff | [] [] [] | + wget | [] [] [] [] | + wyslij-po | [] | + xchat | [] [] [] [] [] [] | + xdg-user-dirs | [] [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] [] [] [] | + +-------------------------------------------------+ + cs da de el en en_GB en_ZA eo es et eu fa fi + 64 105 117 18 1 8 0 28 89 18 19 0 104 + + fr ga gl gu he hi hr hu hy id is it ja ka kn + +------------------------------------------------+ + a2ps | [] [] [] | + aegis | [] [] | + ant-phone | [] [] | + anubis | [] [] [] | + aspell | [] [] [] [] | + bash | [] [] [] | + bfd | [] [] | + bibshelf | [] [] [] [] | + binutils | [] [] | + bison | [] [] [] | + bison-runtime | [] [] [] [] [] | + bluez-pin | [] [] [] [] [] [] [] | + bombono-dvd | | + buzztard | [] | + cflow | [] [] | + clisp | [] | + coreutils | [] [] [] [] [] | + cpio | [] [] [] | + cppi | [] | + cpplib | [] [] | + cryptsetup | [] [] [] | + dfarc | [] [] | + dialog | [] [] [] [] [] [] [] | + dico | | + diffutils | [] [] [] [] [] [] [] [] | + dink | [] | + doodle | [] [] | + e2fsprogs | [] [] | + enscript | [] [] [] [] | + exif | [] [] [] [] [] | + fetchmail | [] [] [] [] | + findutils | [] [] [] [] [] | + flex | [] [] | + freedink | [] [] | + gas | [] [] | + gawk | [] [] [] [] () [] | + gcal | [] | + gcc | [] | + gettext-examples | [] [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] | + gettext-tools | [] [] [] [] | + gip | [] [] [] [] [] | + gjay | | + gliv | () | + glunarclock | [] [] [] | + gnubiff | () [] () | + gnucash | () () () () [] | + gnuedu | [] [] | + gnulib | [] [] [] [] [] | + gnunet | | + gnunet-gtk | [] | + gnutls | [] [] | + gold | [] | + gpe-aerial | [] [] | + gpe-beam | [] [] [] | + gpe-bluetooth | [] [] [] | + gpe-calendar | [] | + gpe-clock | [] [] [] [] | + gpe-conf | [] [] [] | + gpe-contacts | [] [] [] | + gpe-edit | [] [] | + gpe-filemanager | [] [] [] | + gpe-go | [] [] [] [] | + gpe-login | [] [] | + gpe-ownerinfo | [] [] [] [] | + gpe-package | [] [] | + gpe-sketchbook | [] [] [] | + gpe-su | [] [] [] [] [] | + gpe-taskmanager | [] [] [] [] | + gpe-timesheet | [] [] [] [] | + gpe-today | [] [] [] [] [] [] | + gpe-todo | [] [] | + gphoto2 | [] [] [] [] [] | + gprof | [] [] [] | + gpsdrive | [] [] [] | + gramadoir | [] [] [] | + grep | [] | + grub | [] [] [] | + gsasl | [] [] [] [] | + gss | [] [] [] [] | + gst-plugins-bad | [] [] [] [] | + gst-plugins-base | [] [] [] [] [] | + gst-plugins-good | [] [] [] [] [] | + gst-plugins-ugly | [] [] [] [] [] | + gstreamer | [] [] [] [] | + gtick | [] [] [] [] | + gtkam | [] [] [] [] [] | + gtkorphan | [] [] [] | + gtkspell | [] [] [] [] [] [] [] [] | + gutenprint | [] [] [] | + hello | [] [] | + help2man | [] | + hylafax | [] | + idutils | [] [] [] [] [] | + indent | [] [] [] [] [] [] [] | + iso_15924 | () [] [] | + iso_3166 | () [] [] [] [] [] [] [] [] [] [] | + iso_3166_2 | () [] [] [] | + iso_4217 | () [] [] [] [] | + iso_639 | () [] [] [] [] [] [] [] | + iso_639_3 | () [] [] | + jwhois | [] [] [] [] | + kbd | [] [] | + keytouch | [] [] [] [] [] | + keytouch-editor | [] [] [] [] | + keytouch-keyboa... | [] [] [] [] | + klavaro | [] [] | + latrine | [] [] | + ld | [] [] [] | + leafpad | [] [] [] [] [] [] () | + libc | [] [] [] [] | + libexif | | + libextractor | | + libgnutls | [] [] | + libgpewidget | [] [] [] | + libgpg-error | [] [] | + libgphoto2 | [] [] [] | + libgphoto2_port | [] [] [] | + libgsasl | [] [] [] [] | + libiconv | [] [] [] [] [] | + libidn | [] [] [] | + lifelines | () | + liferea | [] [] [] [] | + lilypond | [] | + linkdr | [] [] [] [] | + lordsawar | | + lprng | [] | + lynx | [] [] [] [] [] | + m4 | [] [] [] [] [] | + mailfromd | | + mailutils | [] [] | + make | [] [] [] [] [] [] [] [] | + man-db | [] [] | + man-db-manpages | [] | + minicom | [] [] [] [] | + mkisofs | [] [] [] | + myserver | | + nano | [] [] [] [] [] | + opcodes | [] [] [] | + parted | [] [] [] [] | + pies | | + popt | [] [] [] [] [] [] [] [] | + psmisc | [] [] | + pspp | | + pwdutils | [] [] | + radius | [] [] | + recode | [] [] [] [] [] [] [] | + rosegarden | () () () () | + rpm | [] [] | + rush | | + sarg | [] | + screem | [] [] | + scrollkeeper | [] [] [] | + sed | [] [] [] [] [] [] [] | + sharutils | [] [] [] [] [] [] | + shishi | [] | + skencil | [] | + solfege | [] [] [] | + solfege-manual | [] [] | + soundtracker | [] [] | + sp | [] () | + sysstat | [] [] [] [] | + tar | [] [] [] [] [] [] | + texinfo | [] [] [] [] | + tin | [] | + unicode-han-tra... | | + unicode-transla... | [] [] | + util-linux-ng | [] [] [] [] [] | + vice | () () () | + vmm | [] | + vorbis-tools | [] | + wastesedge | () () | + wdiff | | + wget | [] [] [] [] [] [] [] | + wyslij-po | [] [] | + xchat | [] [] [] [] [] [] [] [] | + xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] [] | + +------------------------------------------------+ + fr ga gl gu he hi hr hu hy id is it ja ka kn + 121 53 20 4 8 2 5 53 2 120 5 83 66 0 4 + + ko ku ky lg lt lv mk ml mn mr ms mt nb nds ne + +-----------------------------------------------+ + a2ps | [] | + aegis | | + ant-phone | | + anubis | [] [] | + aspell | [] | + bash | | + bfd | | + bibshelf | [] [] | + binutils | | + bison | [] | + bison-runtime | [] [] [] [] [] | + bluez-pin | [] [] [] [] [] | + bombono-dvd | | + buzztard | | + cflow | | + clisp | | + coreutils | [] | + cpio | | + cppi | | + cpplib | | + cryptsetup | | + dfarc | [] | + dialog | [] [] [] [] [] | + dico | | + diffutils | [] [] | + dink | | + doodle | | + e2fsprogs | | + enscript | | + exif | [] | + fetchmail | | + findutils | | + flex | | + freedink | [] | + gas | | + gawk | | + gcal | | + gcc | | + gettext-examples | [] [] [] [] | + gettext-runtime | [] | + gettext-tools | [] | + gip | [] [] | + gjay | | + gliv | | + glunarclock | [] | + gnubiff | | + gnucash | () () () () | + gnuedu | | + gnulib | | + gnunet | | + gnunet-gtk | | + gnutls | [] | + gold | | + gpe-aerial | [] | + gpe-beam | [] | + gpe-bluetooth | [] [] | + gpe-calendar | [] | + gpe-clock | [] [] [] [] [] | + gpe-conf | [] [] | + gpe-contacts | [] [] | + gpe-edit | [] | + gpe-filemanager | [] [] | + gpe-go | [] [] [] | + gpe-login | [] | + gpe-ownerinfo | [] [] | + gpe-package | [] [] | + gpe-sketchbook | [] [] | + gpe-su | [] [] [] [] [] [] | + gpe-taskmanager | [] [] [] [] [] [] | + gpe-timesheet | [] [] | + gpe-today | [] [] [] [] | + gpe-todo | [] [] | + gphoto2 | | + gprof | [] | + gpsdrive | | + gramadoir | | + grep | | + grub | | + gsasl | | + gss | | + gst-plugins-bad | [] [] | + gst-plugins-base | [] [] | + gst-plugins-good | [] [] | + gst-plugins-ugly | [] [] [] [] [] | + gstreamer | | + gtick | | + gtkam | [] | + gtkorphan | [] [] | + gtkspell | [] [] [] [] [] [] [] | + gutenprint | | + hello | [] [] [] | + help2man | | + hylafax | | + idutils | | + indent | | + iso_15924 | [] [] | + iso_3166 | [] [] () [] [] [] [] [] | + iso_3166_2 | | + iso_4217 | [] [] | + iso_639 | [] [] | + iso_639_3 | [] | + jwhois | [] | + kbd | | + keytouch | [] | + keytouch-editor | [] | + keytouch-keyboa... | [] | + klavaro | [] | + latrine | [] | + ld | | + leafpad | [] [] [] | + libc | [] | + libexif | | + libextractor | | + libgnutls | [] | + libgpewidget | [] [] | + libgpg-error | | + libgphoto2 | | + libgphoto2_port | | + libgsasl | | + libiconv | | + libidn | | + lifelines | | + liferea | | + lilypond | | + linkdr | | + lordsawar | | + lprng | | + lynx | | + m4 | | + mailfromd | | + mailutils | | + make | [] | + man-db | | + man-db-manpages | | + minicom | [] | + mkisofs | | + myserver | | + nano | [] [] | + opcodes | | + parted | | + pies | | + popt | [] [] [] | + psmisc | | + pspp | | + pwdutils | | + radius | | + recode | | + rosegarden | | + rpm | | + rush | | + sarg | | + screem | | + scrollkeeper | [] [] | + sed | | + sharutils | | + shishi | | + skencil | | + solfege | [] | + solfege-manual | | + soundtracker | | + sp | | + sysstat | [] | + tar | [] | + texinfo | [] | + tin | | + unicode-han-tra... | | + unicode-transla... | | + util-linux-ng | | + vice | | + vmm | | + vorbis-tools | | + wastesedge | | + wdiff | | + wget | [] | + wyslij-po | | + xchat | [] [] [] | + xdg-user-dirs | [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] | + +-----------------------------------------------+ + ko ku ky lg lt lv mk ml mn mr ms mt nb nds ne + 20 5 10 1 12 48 4 2 2 4 24 10 19 3 1 + + nl nn or pa pl ps pt pt_BR ro ru rw sk sl sq sr + +---------------------------------------------------+ + a2ps | [] [] [] [] [] [] [] [] | + aegis | [] [] [] | + ant-phone | [] [] | + anubis | [] [] [] | + aspell | [] [] [] [] [] | + bash | [] [] | + bfd | [] | + bibshelf | [] [] | + binutils | [] [] | + bison | [] [] [] | + bison-runtime | [] [] [] [] [] [] [] | + bluez-pin | [] [] [] [] [] [] [] [] | + bombono-dvd | [] () | + buzztard | [] [] | + cflow | [] | + clisp | [] [] | + coreutils | [] [] [] [] [] [] | + cpio | [] [] [] | + cppi | [] | + cpplib | [] | + cryptsetup | [] | + dfarc | [] | + dialog | [] [] [] [] | + dico | [] | + diffutils | [] [] [] [] [] [] | + dink | () | + doodle | [] [] | + e2fsprogs | [] [] | + enscript | [] [] [] [] [] | + exif | [] [] [] () [] | + fetchmail | [] [] [] [] | + findutils | [] [] [] [] [] | + flex | [] [] [] [] [] | + freedink | [] [] | + gas | | + gawk | [] [] [] [] | + gcal | | + gcc | [] | + gettext-examples | [] [] [] [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] [] [] [] [] | + gettext-tools | [] [] [] [] [] [] | + gip | [] [] [] [] [] | + gjay | | + gliv | [] [] [] [] [] [] | + glunarclock | [] [] [] [] [] | + gnubiff | [] () | + gnucash | [] () () () | + gnuedu | [] | + gnulib | [] [] [] [] | + gnunet | | + gnunet-gtk | | + gnutls | [] [] | + gold | | + gpe-aerial | [] [] [] [] [] [] [] | + gpe-beam | [] [] [] [] [] [] [] | + gpe-bluetooth | [] [] | + gpe-calendar | [] [] [] [] | + gpe-clock | [] [] [] [] [] [] [] [] | + gpe-conf | [] [] [] [] [] [] [] | + gpe-contacts | [] [] [] [] [] | + gpe-edit | [] [] [] | + gpe-filemanager | [] [] [] | + gpe-go | [] [] [] [] [] [] [] [] | + gpe-login | [] [] | + gpe-ownerinfo | [] [] [] [] [] [] [] [] | + gpe-package | [] [] | + gpe-sketchbook | [] [] [] [] [] [] [] | + gpe-su | [] [] [] [] [] [] [] [] | + gpe-taskmanager | [] [] [] [] [] [] [] [] | + gpe-timesheet | [] [] [] [] [] [] [] [] | + gpe-today | [] [] [] [] [] [] [] [] | + gpe-todo | [] [] [] [] [] | + gphoto2 | [] [] [] [] [] [] [] [] | + gprof | [] [] [] | + gpsdrive | [] [] | + gramadoir | [] [] | + grep | [] [] [] [] | + grub | [] [] [] | + gsasl | [] [] [] [] | + gss | [] [] [] | + gst-plugins-bad | [] [] [] [] [] | + gst-plugins-base | [] [] [] [] [] | + gst-plugins-good | [] [] [] [] [] | + gst-plugins-ugly | [] [] [] [] [] [] | + gstreamer | [] [] [] [] [] | + gtick | [] [] [] | + gtkam | [] [] [] [] [] [] | + gtkorphan | [] | + gtkspell | [] [] [] [] [] [] [] [] [] [] | + gutenprint | [] [] | + hello | [] [] [] [] | + help2man | [] [] | + hylafax | [] | + idutils | [] [] [] [] [] | + indent | [] [] [] [] [] [] [] | + iso_15924 | [] [] [] [] | + iso_3166 | [] [] [] [] [] () [] [] [] [] [] [] [] [] | + iso_3166_2 | [] [] [] | + iso_4217 | [] [] [] [] [] [] [] [] | + iso_639 | [] [] [] [] [] [] [] [] [] | + iso_639_3 | [] [] | + jwhois | [] [] [] [] | + kbd | [] [] [] | + keytouch | [] [] [] | + keytouch-editor | [] [] [] | + keytouch-keyboa... | [] [] [] | + klavaro | [] [] | + latrine | [] [] | + ld | | + leafpad | [] [] [] [] [] [] [] [] [] | + libc | [] [] [] [] | + libexif | [] [] () [] | + libextractor | | + libgnutls | [] [] | + libgpewidget | [] [] [] | + libgpg-error | [] [] | + libgphoto2 | [] [] | + libgphoto2_port | [] [] [] [] | + libgsasl | [] [] [] [] [] | + libiconv | [] [] [] [] [] | + libidn | [] [] | + lifelines | [] [] | + liferea | [] [] [] [] [] () () [] | + lilypond | [] | + linkdr | [] [] [] | + lordsawar | | + lprng | [] | + lynx | [] [] [] | + m4 | [] [] [] [] [] | + mailfromd | [] | + mailutils | [] | + make | [] [] [] [] | + man-db | [] [] [] | + man-db-manpages | [] [] [] | + minicom | [] [] [] [] | + mkisofs | [] [] [] | + myserver | | + nano | [] [] [] [] | + opcodes | [] [] | + parted | [] [] [] [] | + pies | [] | + popt | [] [] [] [] | + psmisc | [] [] [] | + pspp | [] [] | + pwdutils | [] | + radius | [] [] [] | + recode | [] [] [] [] [] [] [] [] | + rosegarden | () () | + rpm | [] [] [] | + rush | [] [] | + sarg | | + screem | | + scrollkeeper | [] [] [] [] [] [] [] [] | + sed | [] [] [] [] [] [] [] [] [] | + sharutils | [] [] [] [] | + shishi | [] | + skencil | [] [] | + solfege | [] [] [] [] | + solfege-manual | [] [] [] | + soundtracker | [] | + sp | | + sysstat | [] [] [] [] | + tar | [] [] [] [] | + texinfo | [] [] [] [] | + tin | [] | + unicode-han-tra... | | + unicode-transla... | | + util-linux-ng | [] [] [] [] [] | + vice | [] | + vmm | [] | + vorbis-tools | [] [] | + wastesedge | [] | + wdiff | [] [] | + wget | [] [] [] [] [] [] [] | + wyslij-po | [] [] [] | + xchat | [] [] [] [] [] [] [] [] [] | + xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] [] [] | + +---------------------------------------------------+ + nl nn or pa pl ps pt pt_BR ro ru rw sk sl sq sr + 135 10 4 7 105 1 29 61 47 91 3 55 47 8 37 + + sv sw ta te tg th tr uk vi wa zh_CN zh_HK zh_TW + +---------------------------------------------------+ + a2ps | [] [] [] [] [] | 27 + aegis | [] | 9 + ant-phone | [] [] [] [] | 9 + anubis | [] [] [] [] | 15 + aspell | [] [] [] | 20 + bash | [] [] | 11 + bfd | [] | 6 + bibshelf | [] [] [] | 16 + binutils | [] [] | 8 + bison | [] [] | 12 + bison-runtime | [] [] [] [] [] [] | 29 + bluez-pin | [] [] [] [] [] [] [] [] | 37 + bombono-dvd | [] | 4 + buzztard | [] | 7 + cflow | [] [] [] | 9 + clisp | | 10 + coreutils | [] [] [] [] | 22 + cpio | [] [] [] [] [] [] | 13 + cppi | [] [] | 5 + cpplib | [] [] [] [] [] [] | 13 + cryptsetup | [] [] | 7 + dfarc | [] | 9 + dialog | [] [] [] [] [] [] [] | 30 + dico | [] | 2 + diffutils | [] [] [] [] [] [] | 30 + dink | | 4 + doodle | [] [] | 7 + e2fsprogs | [] [] [] | 11 + enscript | [] [] [] [] | 17 + exif | [] [] [] | 16 + fetchmail | [] [] [] | 17 + findutils | [] [] [] [] [] | 20 + flex | [] [] [] [] | 15 + freedink | [] | 10 + gas | [] | 4 + gawk | [] [] [] [] | 18 + gcal | [] [] | 5 + gcc | [] [] [] | 7 + gettext-examples | [] [] [] [] [] [] [] | 34 + gettext-runtime | [] [] [] [] [] [] [] | 30 + gettext-tools | [] [] [] [] [] [] | 22 + gip | [] [] [] [] | 22 + gjay | [] | 3 + gliv | [] [] [] | 14 + glunarclock | [] [] [] [] [] | 19 + gnubiff | [] [] | 4 + gnucash | () [] () () | 9 + gnuedu | [] [] | 7 + gnulib | [] [] [] [] | 16 + gnunet | [] | 1 + gnunet-gtk | [] [] [] | 5 + gnutls | [] [] [] | 10 + gold | [] | 4 + gpe-aerial | [] [] [] | 18 + gpe-beam | [] [] [] | 19 + gpe-bluetooth | [] [] [] | 13 + gpe-calendar | [] [] [] [] | 12 + gpe-clock | [] [] [] [] [] | 28 + gpe-conf | [] [] [] [] | 20 + gpe-contacts | [] [] [] | 17 + gpe-edit | [] [] [] | 12 + gpe-filemanager | [] [] [] [] | 16 + gpe-go | [] [] [] [] [] | 25 + gpe-login | [] [] [] | 11 + gpe-ownerinfo | [] [] [] [] [] | 25 + gpe-package | [] [] [] | 13 + gpe-sketchbook | [] [] [] | 20 + gpe-su | [] [] [] [] [] | 30 + gpe-taskmanager | [] [] [] [] [] | 29 + gpe-timesheet | [] [] [] [] [] | 25 + gpe-today | [] [] [] [] [] [] | 30 + gpe-todo | [] [] [] [] | 17 + gphoto2 | [] [] [] [] [] | 24 + gprof | [] [] [] | 15 + gpsdrive | [] [] [] | 11 + gramadoir | [] [] [] | 11 + grep | [] [] [] | 10 + grub | [] [] [] | 14 + gsasl | [] [] [] [] | 14 + gss | [] [] [] | 11 + gst-plugins-bad | [] [] [] [] | 22 + gst-plugins-base | [] [] [] [] [] | 24 + gst-plugins-good | [] [] [] [] [] | 25 + gst-plugins-ugly | [] [] [] [] [] | 29 + gstreamer | [] [] [] [] | 22 + gtick | [] [] [] | 13 + gtkam | [] [] [] | 20 + gtkorphan | [] [] [] | 14 + gtkspell | [] [] [] [] [] [] [] [] [] | 45 + gutenprint | [] | 10 + hello | [] [] [] [] [] [] | 21 + help2man | [] [] | 7 + hylafax | [] | 5 + idutils | [] [] [] [] | 17 + indent | [] [] [] [] [] [] | 30 + iso_15924 | () [] () [] [] | 16 + iso_3166 | [] [] () [] [] () [] [] [] () | 53 + iso_3166_2 | () [] () [] | 9 + iso_4217 | [] () [] [] () [] [] | 26 + iso_639 | [] [] [] () [] () [] [] [] [] | 38 + iso_639_3 | [] () | 8 + jwhois | [] [] [] [] [] | 16 + kbd | [] [] [] [] [] | 15 + keytouch | [] [] [] | 16 + keytouch-editor | [] [] [] | 14 + keytouch-keyboa... | [] [] [] | 14 + klavaro | [] | 11 + latrine | [] [] [] | 10 + ld | [] [] [] [] | 11 + leafpad | [] [] [] [] [] [] | 33 + libc | [] [] [] [] [] | 21 + libexif | [] () | 6 + libextractor | [] | 1 + libgnutls | [] [] [] | 9 + libgpewidget | [] [] [] | 14 + libgpg-error | [] [] [] | 9 + libgphoto2 | [] [] | 8 + libgphoto2_port | [] [] [] [] | 13 + libgsasl | [] [] [] | 13 + libiconv | [] [] [] [] | 21 + libidn | () [] [] | 11 + lifelines | [] | 4 + liferea | [] [] [] | 21 + lilypond | [] | 7 + linkdr | [] [] [] [] [] | 17 + lordsawar | | 1 + lprng | [] | 3 + lynx | [] [] [] [] | 17 + m4 | [] [] [] [] | 19 + mailfromd | [] [] | 3 + mailutils | [] | 5 + make | [] [] [] [] | 21 + man-db | [] [] [] | 8 + man-db-manpages | | 4 + minicom | [] [] | 16 + mkisofs | [] [] | 9 + myserver | | 0 + nano | [] [] [] [] | 21 + opcodes | [] [] [] | 11 + parted | [] [] [] [] [] | 15 + pies | [] [] | 3 + popt | [] [] [] [] [] [] | 27 + psmisc | [] [] | 11 + pspp | | 4 + pwdutils | [] [] | 6 + radius | [] [] | 9 + recode | [] [] [] [] | 28 + rosegarden | () | 0 + rpm | [] [] [] | 11 + rush | [] [] | 4 + sarg | | 1 + screem | [] | 3 + scrollkeeper | [] [] [] [] [] | 27 + sed | [] [] [] [] [] | 30 + sharutils | [] [] [] [] [] | 22 + shishi | [] | 3 + skencil | [] [] | 7 + solfege | [] [] [] [] | 16 + solfege-manual | [] | 8 + soundtracker | [] [] [] | 9 + sp | [] | 3 + sysstat | [] [] | 15 + tar | [] [] [] [] [] [] | 23 + texinfo | [] [] [] [] | 16 + tin | | 4 + unicode-han-tra... | | 0 + unicode-transla... | | 2 + util-linux-ng | [] [] [] [] | 20 + vice | () () | 1 + vmm | [] | 4 + vorbis-tools | [] | 6 + wastesedge | | 2 + wdiff | [] [] | 7 + wget | [] [] [] [] [] | 26 + wyslij-po | [] [] | 8 + xchat | [] [] [] [] [] [] | 36 + xdg-user-dirs | [] [] [] [] [] [] [] [] [] | 60 + xkeyboard-config | [] [] [] [] | 25 + +---------------------------------------------------+ + 84 teams sv sw ta te tg th tr uk vi wa zh_CN zh_HK zh_TW + 178 domains 119 1 3 2 0 10 66 50 155 17 97 7 41 2610 + + Some counters in the preceding matrix are higher than the number of +visible blocks let us expect. This is because a few extra PO files are +used for implementing regional variants of languages, or language +dialects. + + For a PO file in the matrix above to be effective, the package to +which it applies should also have been internationalized and +distributed as such by its maintainer. There might be an observable +lag between the mere existence a PO file and its wide availability in a +distribution. + + If May 2010 seems to be old, you may fetch a more recent copy of +this `ABOUT-NLS' file on most GNU archive sites. The most up-to-date +matrix with full percentage details can be found at +`http://translationproject.org/extra/matrix.html'. + +1.6 Using `gettext' in new packages +=================================== + +If you are writing a freely available program and want to +internationalize it you are welcome to use GNU `gettext' in your +package. Of course you have to respect the GNU Library General Public +License which covers the use of the GNU `gettext' library. This means +in particular that even non-free programs can use `libintl' as a shared +library, whereas only free software can use `libintl' as a static +library or use modified versions of `libintl'. + + Once the sources are changed appropriately and the setup can handle +the use of `gettext' the only thing missing are the translations. The +Free Translation Project is also available for packages which are not +developed inside the GNU project. Therefore the information given above +applies also for every other Free Software Project. Contact +`[email protected]' to make the `.pot' files available +to the translation teams. + @@ -1,2 +1 @@ Michal Ratajsky <[email protected]> - diff --git a/Makefile.am b/Makefile.am index 38ca12f..168460b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,9 +5,10 @@ SUBDIRS = \ backends \ data \ docs \ - examples + examples \ + po -EXTRA_DIST = autogen.sh +EXTRA_DIST = build-aux/config.rpath autogen.sh MAINTAINERCLEANFILES = \ $(srcdir)/aclocal.m4 \ @@ -0,0 +1,5 @@ +============= +Version 1.9.0 +============= + +- Initial release @@ -18,6 +18,7 @@ which mate-autogen || { } REQUIRED_AUTOMAKE_VERSION=1.9 +REQUIRED_INTLTOOL_VERSION=0.35 REQUIRED_GTK_DOC_VERSION=1.9 USE_COMMON_DOC_BUILD=yes diff --git a/backends/alsa/Makefile.am b/backends/alsa/Makefile.am index 220bb3b..48dcaba 100644 --- a/backends/alsa/Makefile.am +++ b/backends/alsa/Makefile.am @@ -3,6 +3,7 @@ backenddir = $(libdir)/libmatemixer backend_LTLIBRARIES = libmatemixer-alsa.la AM_CPPFLAGS = \ + -Wno-unknown-pragmas \ -I$(top_srcdir) \ -DG_LOG_DOMAIN=\"libmatemixer-alsa\" diff --git a/backends/alsa/alsa-backend.c b/backends/alsa/alsa-backend.c index 7a17b85..6bac691 100644 --- a/backends/alsa/alsa-backend.c +++ b/backends/alsa/alsa-backend.c @@ -27,12 +27,22 @@ #include "alsa-stream.h" #define BACKEND_NAME "ALSA" -#define BACKEND_PRIORITY 9 +#define BACKEND_PRIORITY 20 + +#define ALSA_DEVICE_GET_ID(d) \ + (g_object_get_data (G_OBJECT (d), "__matemixer_alsa_device_id")) + +#define ALSA_DEVICE_SET_ID(d,id) \ + (g_object_set_data_full (G_OBJECT (d), \ + "__matemixer_alsa_device_id", \ + g_strdup (id), \ + g_free)) struct _AlsaBackendPrivate { GSource *timeout_source; - GHashTable *devices; + GList *streams; + GList *devices; GHashTable *devices_ids; }; @@ -47,26 +57,38 @@ static void alsa_backend_finalize (GObject *object); G_DEFINE_DYNAMIC_TYPE (AlsaBackend, alsa_backend, MATE_MIXER_TYPE_BACKEND) #pragma clang diagnostic pop -static gboolean alsa_backend_open (MateMixerBackend *backend); -static void alsa_backend_close (MateMixerBackend *backend); -static GList * alsa_backend_list_devices (MateMixerBackend *backend); -static GList * alsa_backend_list_streams (MateMixerBackend *backend); +static gboolean alsa_backend_open (MateMixerBackend *backend); +static void alsa_backend_close (MateMixerBackend *backend); +static const GList *alsa_backend_list_devices (MateMixerBackend *backend); +static const GList *alsa_backend_list_streams (MateMixerBackend *backend); + +static gboolean read_devices (AlsaBackend *alsa); + +static gboolean read_device (AlsaBackend *alsa, + const gchar *card); + +static void add_device (AlsaBackend *alsa, + AlsaDevice *device); -static gboolean read_devices (AlsaBackend *alsa); +static void remove_device (AlsaBackend *alsa, + AlsaDevice *device); +static void remove_device_by_name (AlsaBackend *alsa, + const gchar *name); +static void remove_device_by_list_item (AlsaBackend *alsa, + GList *item); -static gboolean read_device (AlsaBackend *alsa, - const gchar *card); +static void remove_stream (AlsaBackend *alsa, + const gchar *name); -static void add_device (AlsaBackend *alsa, - AlsaDevice *device); +static void select_default_input_stream (AlsaBackend *alsa); +static void select_default_output_stream (AlsaBackend *alsa); -static void remove_device (AlsaBackend *alsa, - AlsaDevice *device); -static void remove_stream (AlsaBackend *alsa, - const gchar *name); +static void free_stream_list (AlsaBackend *alsa); -static void select_default_input_stream (AlsaBackend *alsa); -static void select_default_output_stream (AlsaBackend *alsa); +static gint compare_devices (gconstpointer a, + gconstpointer b); +static gint compare_device_name (gconstpointer a, + gconstpointer b); static MateMixerBackendInfo info; @@ -118,11 +140,6 @@ alsa_backend_init (AlsaBackend *alsa) ALSA_TYPE_BACKEND, AlsaBackendPrivate); - alsa->priv->devices = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); - alsa->priv->devices_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, @@ -151,7 +168,6 @@ alsa_backend_finalize (GObject *object) alsa = ALSA_BACKEND (object); - g_hash_table_unref (alsa->priv->devices); g_hash_table_unref (alsa->priv->devices_ids); G_OBJECT_CLASS (alsa_backend_parent_class)->finalize (object); @@ -166,9 +182,8 @@ alsa_backend_open (MateMixerBackend *backend) alsa = ALSA_BACKEND (backend); - /* Poll ALSA for changes every 500 milliseconds, this actually only - * discovers added or changed sound cards, sound card related events - * are handled by AlsaDevices */ + /* Poll ALSA for changes every second, this only discovers added or removed + * sound cards, sound card related events are handled by AlsaDevices */ alsa->priv->timeout_source = g_timeout_source_new_seconds (1); g_source_set_callback (alsa->priv->timeout_source, (GSourceFunc) read_devices, @@ -197,61 +212,60 @@ alsa_backend_close (MateMixerBackend *backend) g_source_destroy (alsa->priv->timeout_source); - g_hash_table_remove_all (alsa->priv->devices); + if (alsa->priv->devices != NULL) { + g_list_free_full (alsa->priv->devices, g_object_unref); + alsa->priv->devices = NULL; + } + + free_stream_list (alsa); + g_hash_table_remove_all (alsa->priv->devices_ids); _mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_IDLE); } -static GList * +static const GList * alsa_backend_list_devices (MateMixerBackend *backend) { - GList *list; - g_return_val_if_fail (ALSA_IS_BACKEND (backend), NULL); - /* Convert the hash table to a linked list, this list is expected to be - * cached in the main library */ - list = g_hash_table_get_values (ALSA_BACKEND (backend)->priv->devices); - if (list != NULL) - g_list_foreach (list, (GFunc) g_object_ref, NULL); - - return list; + return ALSA_BACKEND (backend)->priv->devices; } -static GList * +static const GList * alsa_backend_list_streams (MateMixerBackend *backend) { - AlsaBackend *alsa; - GHashTableIter iter; - gpointer value; - GList *list = NULL; + AlsaBackend *alsa; g_return_val_if_fail (ALSA_IS_BACKEND (backend), NULL); alsa = ALSA_BACKEND (backend); - /* We don't keep a list or hash table of all streams here, instead walk - * through the list of devices and create the list manually, each device - * has at most one input and one output stream */ - g_hash_table_iter_init (&iter, alsa->priv->devices); - - while (g_hash_table_iter_next (&iter, NULL, &value)) { - AlsaDevice *device = ALSA_DEVICE (value); - AlsaStream *stream; - - stream = alsa_device_get_output_stream (device); - if (stream != NULL) - list = g_list_prepend (list, stream); - stream = alsa_device_get_input_stream (device); - if (stream != NULL) - list = g_list_prepend (list, stream); + if (alsa->priv->streams == NULL) { + GList *list; + + /* Walk through the list of devices and create the stream list, each + * device has at most one input and one output stream */ + list = alsa->priv->devices; + + while (list != NULL) { + AlsaDevice *device = ALSA_DEVICE (list->data); + AlsaStream *stream; + + stream = alsa_device_get_input_stream (device); + if (stream != NULL) { + alsa->priv->streams = + g_list_append (alsa->priv->streams, g_object_ref (stream)); + } + stream = alsa_device_get_output_stream (device); + if (stream != NULL) { + alsa->priv->streams = + g_list_append (alsa->priv->streams, g_object_ref (stream)); + } + list = list->next; + } } - - if (list != NULL) - g_list_foreach (list, (GFunc) g_object_ref, NULL); - - return list; + return alsa->priv->streams; } static gboolean @@ -260,12 +274,12 @@ read_devices (AlsaBackend *alsa) gint num; gint ret; gchar card[16]; - gboolean changed = FALSE; + gboolean added = FALSE; /* Read the default device first, it will be either one of the hardware cards * that will be queried later, or a software mixer */ if (read_device (alsa, "default") == TRUE) - changed = TRUE; + added = TRUE; for (num = -1;;) { /* Read number of the next sound card */ @@ -277,12 +291,12 @@ read_devices (AlsaBackend *alsa) g_snprintf (card, sizeof (card), "hw:%d", num); if (read_device (alsa, card) == TRUE) - changed = TRUE; + added = TRUE; } /* If any card has been added, make sure we have the most suitable default * input and output streams */ - if (changed == TRUE) { + if (added == TRUE) { select_default_input_stream (alsa); select_default_output_stream (alsa); } @@ -300,15 +314,13 @@ read_device (AlsaBackend *alsa, const gchar *card) /* The device may be already known, remove it if it's known and fails * to be read, this happens for example when PulseAudio is killed */ - device = g_hash_table_lookup (alsa->priv->devices, card); - ret = snd_ctl_open (&ctl, card, 0); if (ret < 0) { g_warning ("Failed to open ALSA control for %s: %s", card, snd_strerror (ret)); - if (device != NULL) - remove_device (alsa, device); + + remove_device_by_name (alsa, card); return FALSE; } @@ -317,9 +329,8 @@ read_device (AlsaBackend *alsa, const gchar *card) ret = snd_ctl_card_info (ctl, info); if (ret < 0) { g_warning ("Failed to read card info: %s", snd_strerror (ret)); - if (device != NULL) - remove_device (alsa, device); + remove_device_by_name (alsa, card); snd_ctl_close (ctl); return FALSE; } @@ -342,11 +353,7 @@ read_device (AlsaBackend *alsa, const gchar *card) return FALSE; } - g_object_set_data_full (G_OBJECT (device), - "__matemixer_alsa_device_id", - g_strdup (id), - g_free); - + ALSA_DEVICE_SET_ID (device, id); add_device (alsa, device); snd_ctl_close (ctl); @@ -356,19 +363,13 @@ read_device (AlsaBackend *alsa, const gchar *card) static void add_device (AlsaBackend *alsa, AlsaDevice *device) { - const gchar *name; - - name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); + alsa->priv->devices = g_list_insert_sorted_with_data (alsa->priv->devices, + device, + (GCompareDataFunc) compare_devices, + NULL); - g_hash_table_insert (alsa->priv->devices, - g_strdup (name), - g_object_ref (device)); - - /* Remember the device identifier, use a single string copy as we only free - * the hash table key */ - g_hash_table_add (alsa->priv->devices_ids, - g_strdup (g_object_get_data (G_OBJECT (device), - "__matemixer_alsa_device_id"))); + /* Keep track of device identifiers */ + g_hash_table_add (alsa->priv->devices_ids, g_strdup (ALSA_DEVICE_GET_ID (device))); g_signal_connect_swapped (G_OBJECT (device), "closed", @@ -379,7 +380,22 @@ add_device (AlsaBackend *alsa, AlsaDevice *device) G_CALLBACK (remove_stream), alsa); - g_signal_emit_by_name (G_OBJECT (alsa), "device-added", name); + g_signal_connect_swapped (G_OBJECT (device), + "closed", + G_CALLBACK (free_stream_list), + alsa); + g_signal_connect_swapped (G_OBJECT (device), + "stream-added", + G_CALLBACK (free_stream_list), + alsa); + g_signal_connect_swapped (G_OBJECT (device), + "stream-removed", + G_CALLBACK (free_stream_list), + alsa); + + g_signal_emit_by_name (G_OBJECT (alsa), + "device-added", + mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); /* Load the device elements after emitting device-added, because the load * function will most likely emit stream-added on the device and backend */ @@ -389,27 +405,48 @@ add_device (AlsaBackend *alsa, AlsaDevice *device) static void remove_device (AlsaBackend *alsa, AlsaDevice *device) { - const gchar *name; + GList *item; + + item = g_list_find (alsa->priv->devices, device); + if (item != NULL) + remove_device_by_list_item (alsa, item); +} + +static void +remove_device_by_name (AlsaBackend *alsa, const gchar *name) +{ + GList *item; + + item = g_list_find_custom (alsa->priv->devices, name, compare_device_name); + if (item != NULL) + remove_device_by_list_item (alsa, item); +} + +static void +remove_device_by_list_item (AlsaBackend *alsa, GList *item) +{ + AlsaDevice *device; + + device = ALSA_DEVICE (item->data); - name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); + g_signal_handlers_disconnect_by_data (G_OBJECT (device), alsa); - g_signal_handlers_disconnect_by_func (G_OBJECT (device), - G_CALLBACK (remove_device), - alsa); - g_signal_handlers_disconnect_by_func (G_OBJECT (device), - G_CALLBACK (remove_stream), - alsa); + if (alsa_device_is_open (device) == TRUE) + alsa_device_close (device); + + alsa->priv->devices = g_list_delete_link (alsa->priv->devices, item); - /* Remove the device */ g_hash_table_remove (alsa->priv->devices_ids, - g_object_get_data (G_OBJECT (device), - "__matemixer_alsa_device_id")); + ALSA_DEVICE_GET_ID (device)); + + /* The list may and may not have been invalidate by device signals */ + free_stream_list (alsa); - // XXX close the device and make it remove streams - g_hash_table_remove (alsa->priv->devices, name); g_signal_emit_by_name (G_OBJECT (alsa), "device-removed", - name); + mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); + + g_object_unref (device); } static void @@ -419,7 +456,6 @@ remove_stream (AlsaBackend *alsa, const gchar *name) stream = mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (alsa)); - // XXX see if the change happens after stream is removed or before if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0) select_default_input_stream (alsa); @@ -432,36 +468,19 @@ remove_stream (AlsaBackend *alsa, const gchar *name) static void select_default_input_stream (AlsaBackend *alsa) { - AlsaDevice *device; - AlsaStream *stream; - gchar card[16]; - gint num; - - /* Always prefer stream in the "default" device */ - device = g_hash_table_lookup (alsa->priv->devices, "default"); - if (device != NULL) { - stream = alsa_device_get_input_stream (device); - if (stream != NULL) { - _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (alsa), - MATE_MIXER_STREAM (stream)); - return; - } - } + GList *list; - /* If there is no input stream in the default device, search the cards in - * the correct order */ - for (num = 0;; num++) { - g_snprintf (card, sizeof (card), "hw:%d", num); + list = alsa->priv->devices; + while (list != NULL) { + AlsaDevice *device = ALSA_DEVICE (list->data); + AlsaStream *stream = alsa_device_get_input_stream (device); - device = g_hash_table_lookup (alsa->priv->devices, card); - if (device == NULL) - break; - stream = alsa_device_get_input_stream (device); if (stream != NULL) { _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (alsa), MATE_MIXER_STREAM (stream)); return; } + list = list->next; } /* In the worst case unset the default stream */ @@ -471,38 +490,50 @@ select_default_input_stream (AlsaBackend *alsa) static void select_default_output_stream (AlsaBackend *alsa) { - AlsaDevice *device; - AlsaStream *stream; - gchar card[16]; - gint num; - - /* Always prefer stream in the "default" device */ - device = g_hash_table_lookup (alsa->priv->devices, "default"); - if (device != NULL) { - stream = alsa_device_get_output_stream (device); - if (stream != NULL) { - _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa), - MATE_MIXER_STREAM (stream)); - return; - } - } + GList *list; - /* If there is no input stream in the default device, search the cards in - * the correct order */ - for (num = 0;; num++) { - g_snprintf (card, sizeof (card), "hw:%d", num); + list = alsa->priv->devices; + while (list != NULL) { + AlsaDevice *device = ALSA_DEVICE (list->data); + AlsaStream *stream = alsa_device_get_output_stream (device); - device = g_hash_table_lookup (alsa->priv->devices, card); - if (device == NULL) - break; - stream = alsa_device_get_output_stream (device); if (stream != NULL) { _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa), MATE_MIXER_STREAM (stream)); return; } + list = list->next; } /* In the worst case unset the default stream */ _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa), NULL); } + +static void +free_stream_list (AlsaBackend *alsa) +{ + if (alsa->priv->streams == NULL) + return; + + g_list_free_full (alsa->priv->streams, g_object_unref); + + alsa->priv->streams = NULL; +} + +static gint +compare_devices (gconstpointer a, gconstpointer b) +{ + MateMixerDevice *d1 = MATE_MIXER_DEVICE (a); + MateMixerDevice *d2 = MATE_MIXER_DEVICE (b); + + return strcmp (mate_mixer_device_get_name (d1), mate_mixer_device_get_name (d2)); +} + +static gint +compare_device_name (gconstpointer a, gconstpointer b) +{ + MateMixerDevice *device = MATE_MIXER_DEVICE (a); + const gchar *name = (const gchar *) b; + + return strcmp (mate_mixer_device_get_name (device), name); +} diff --git a/backends/alsa/alsa-constants.c b/backends/alsa/alsa-constants.c index 2124a2e..7b7021f 100644 --- a/backends/alsa/alsa-constants.c +++ b/backends/alsa/alsa-constants.c @@ -22,15 +22,190 @@ #include "alsa-constants.h" -// XXX add more and probably move them somewhere else +/* + * These lists of ALSA mixer elements are based on PulseAudio's mixer paths and own + * observations. The intention is to provide translatable and in some cases better + * readable labels and role assignments. The controls list is also used for selecting + * the default controls and the selection machanism relies on the order of elements, + * so more useful elements should be placed on the top. The last two boolean values + * indicate whether we prefer the element to be used as a default input or output + * control. + * + * Of course the lists are very incomplete and it would be great if users validated and + * refreshed them from time to time. + */ const AlsaControlInfo alsa_controls[] = { - { "Master", N_("Master"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER }, - { "Speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER }, - { "Capture", N_("Capture"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER }, - { "PCM", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM }, - { "Line", N_("Line"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT }, - { "Mic", N_("Mic"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT }, + /* Output controls */ + { "Master", N_("Master"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, FALSE, TRUE }, + { "Hardware Master", N_("Hardware Master"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, FALSE, TRUE }, + { "PCM", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, FALSE, TRUE }, + { "Desktop Speaker", N_("Desktop Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Front", N_("Front Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Front Speaker", N_("Front Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Speaker Front", N_("Front Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Headphone", N_("Headphone"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Headphone2", N_("Headphone 2"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Headset", N_("Headset"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Master Surround", N_("Surround Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Surround", N_("Surround Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Surround Speaker", N_("Surround Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Speaker Surround", N_("Surround Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Center", N_("Center Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Center Speaker", N_("Center Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "CLFE", N_("CLFE Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Speaker CLFE", N_("CLFE Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Master Mono", N_("Master Mono"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, FALSE, TRUE }, + { "Master Digital", N_("Master Digital"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, FALSE, TRUE }, + { "Digital/SPDIF", N_("Digital"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, FALSE, TRUE }, + { "Speaker Side", N_("Side Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Side", N_("Side Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Rear", N_("Rear Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE }, + { "Wave", N_("Wave"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, TRUE }, + { "Phone", N_("Phone"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, TRUE }, + { "CD", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, FALSE, TRUE }, + { "Music", N_("Music"), MATE_MIXER_STREAM_CONTROL_ROLE_MUSIC, FALSE, TRUE }, + { "AC97", N_("AC97"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, TRUE }, + { "LFE", N_("LFE Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, FALSE }, + { "LFE Speaker", N_("LFE Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, FALSE }, + { "Bass Speaker", N_("Bass Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, FALSE }, + { "PC Speaker", N_("PC Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, FALSE }, + { "Synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, FALSE }, + { "MIDI", N_("MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, FALSE }, + { "Synth/MIDI", N_("Synth/MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, FALSE }, + { "Bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, FALSE, FALSE }, + { "Treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, FALSE, FALSE }, + + /* Input controls */ + { "Capture", N_("Capture"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, TRUE, FALSE }, + { "Mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE }, + { "Mic/Line", N_("Microphone/Line In"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, TRUE, FALSE }, + { "Internal Mic", N_("Internal Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE }, + { "Int Mic", N_("Internal Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE }, + { "Front Mic", N_("Front Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE }, + { "Rear Mic", N_("Rear Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE }, + { "Dock Mic", N_("Dock Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE }, + { "Headphone Mic", N_("Headphone Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE }, + { "Headset Mic", N_("Headset Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE }, + { "Inverted Internal Mic", N_("Inverted Internal Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE }, + { "Line", N_("Line In"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, TRUE, FALSE }, + { "Aux", N_("Auxiliary"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, TRUE, FALSE }, + { "Video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_VIDEO, TRUE, FALSE }, + { "TV Tuner", N_("TV Tuner"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, TRUE, FALSE }, + { "FM", N_("FM"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, TRUE, FALSE }, + { "Mic Boost", N_("Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE }, + { "Mic Boost (+20dB)", N_("Microphone Boost (+20dB)"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE }, + { "Int Mic Boost", N_("Internal Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE }, + { "Internal Mic Boost", N_("Internal Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE }, + { "Front Mic Boost", N_("Front Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE }, + { "Rear Mic Boost", N_("Rear Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE }, + { "Dock Mic Boost", N_("Dock Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE }, + { "Headphone Mic Boost", N_("Headphone Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE }, + { "Headset Mic Boost", N_("Headset Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE }, + { "Line Boost", N_("Line In Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE }, + { NULL } +}; + +/* Switches and toggles */ +const AlsaSwitchInfo alsa_switches[] = +{ + { "Analog Output", N_("Analog Output"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Analog Source", N_("Analog Source"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Capture Source", N_("Capture Source"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Input Source", N_("Input Source"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Input Source Select", N_("Input Source Select"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Digital Input Source", N_("Digital Input Source"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "PCM Capture Source", N_("PCM Capture Source"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "IEC958 Playback Source", N_("Digital Playback Source"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Mono Output Select", N_("Mono Output Select"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Shared Mic/Line in", N_("Shared Mic/Line In"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Mic Select", N_("Microphone Select"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Mic Jack Mode", N_("Microphone Jack Mode"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Surround Jack Mode", N_("Surround Jack Mode"), MATE_MIXER_SWITCH_ROLE_UNKNOWN }, + { "Auto-Mute Mode", N_("Auto-Mute Mode"), MATE_MIXER_SWITCH_ROLE_UNKNOWN }, + + /* (Probably) toggles */ + { "External Amplifier", N_("External Amplifier"), MATE_MIXER_SWITCH_ROLE_UNKNOWN }, + { "Bass Boost", N_("Bass Boost"), MATE_MIXER_SWITCH_ROLE_BOOST }, + { "Capture Boost", N_("Capture Boost"), MATE_MIXER_SWITCH_ROLE_BOOST }, + { "IEC958", N_("Digital"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "IEC958 In", N_("Digital In"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "IEC958 Optical Raw", N_("Optical"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Auto Gain Control", N_("Auto Gain Control"), MATE_MIXER_SWITCH_ROLE_UNKNOWN }, + { "Mix", N_("Mix"), MATE_MIXER_SWITCH_ROLE_UNKNOWN }, + { "Mix Mono", N_("Mix Mono"), MATE_MIXER_SWITCH_ROLE_UNKNOWN }, + { "Mic Capture", N_("Mic Capture"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Input 1", N_("Input 1"), MATE_MIXER_SWITCH_ROLE_PORT }, + { "Input 2", N_("Input 2"), MATE_MIXER_SWITCH_ROLE_PORT }, + { NULL } +}; + +const AlsaSwitchOptionInfo alsa_switch_options[] = +{ + /* Output options */ + { "Speakers", N_("Speakers"), NULL }, + { "Headphones", N_("Headphones"), NULL }, + { "FP Headphones", N_("Front Panel Headphones"), NULL }, + + /* Microphone options */ + { "Mic", N_("Microphone"), "audio-input-microphone" }, + { "Microphone", N_("Microphone"), "audio-input-microphone" }, + { "Mic1", N_("Microphone 1"), "audio-input-microphone" }, + { "Mic2", N_("Microphone 2"), "audio-input-microphone" }, + { "Mic in", N_("Microphone In"), "audio-input-microphone" }, + { "Mic In", N_("Microphone In"), "audio-input-microphone" }, + { "Front Mic", N_("Front Microphone"), "audio-input-microphone" }, + { "Front Microphone", N_("Front Microphone"), "audio-input-microphone" }, + { "Headphone Mic", N_("Headphone Microphone"), "audio-input-microphone" }, + { "Headset Mic", N_("Headset Microphone"), "audio-input-microphone" }, + { "Dock Mic", N_("Dock Microphone"), "audio-input-microphone" }, + { "Internal Mic", N_("Internal Microphone"), "audio-input-microphone" }, + { "Int Mic", N_("Internal Microphone"), "audio-input-microphone" }, + { "Internal Mic 1", N_("Internal Microphone 1"), "audio-input-microphone" }, + { "iMic", N_("Internal Microphone"), "audio-input-microphone" }, + { "i-Mic", N_("Internal Microphone"), "audio-input-microphone" }, + { "IntMic", N_("Internal Microphone"), "audio-input-microphone" }, + { "Int DMic", N_("Internal Digital Microphone"), "audio-input-microphone" }, + { "Digital Mic", N_("Digital Microphone"), "audio-input-microphone" }, + { "Digital Mic 1", N_("Digital Microphone 1"), "audio-input-microphone" }, + { "Digital Mic 2", N_("Digital Microphone 2"), "audio-input-microphone" }, + { "D-Mic", N_("Digital Microphone"), "audio-input-microphone" }, + { "ExtMic", N_("External Microphone"), "audio-input-microphone" }, + { "Ext Mic", N_("External Microphone"), "audio-input-microphone" }, + { "E-Mic", N_("External Microphone"), "audio-input-microphone" }, + { "e-Mic", N_("External Microphone"), "audio-input-microphone" }, + { "Rear Mic", N_("Rear Microphone"), "audio-input-microphone" }, + { "Cam Mic", N_("Camera Microphone"), "audio-input-microphone" }, + + /* Other options */ + { "Analog", N_("Analog"), NULL }, + { "Analog In", N_("Analog In"), NULL }, + { "Analog Inputs", N_("Analog Inputs"), NULL }, + { "Line in", N_("Line In"), NULL }, + { "Line In", N_("Line In"), NULL }, + { "Line-In", N_("Line In"), NULL }, + { "Mic/Line", N_("Microphone/Line In"), NULL }, + { "Line/Mic", N_("Line In/Microphone"), NULL }, + { "LineIn", N_("Line In"), NULL }, + { "Line", N_("Line In"), NULL }, + { "Input1", N_("Input 1"), NULL }, + { "Input2", N_("Input 2"), NULL }, + { "IEC958 In", N_("Digital In"), NULL }, + { "TV Tuner", N_("TV Tuner"), NULL }, + { "FM", N_("FM"), NULL }, + { "AUX", N_("Auxiliary"), NULL }, + { "AUX IN", N_("Auxiliary In"), NULL }, + { "Aux In", N_("Auxiliary In"), NULL }, + { "Aux", N_("Auxiliary"), NULL }, + { "Aux0", N_("Auxiliary 0"), NULL }, + { "Aux1", N_("Auxiliary 1"), NULL }, + { "Aux2", N_("Auxiliary 2"), NULL }, + { "Aux3", N_("Auxiliary 3"), NULL }, + { "Docking-Station", N_("Docking Station"), NULL }, + { "Mixer", N_("Mixer"), NULL }, + { "Unknown1", N_("Unknown 1"), NULL }, + { "Unknown2", N_("Unknown 2"), NULL }, { NULL } }; diff --git a/backends/alsa/alsa-constants.h b/backends/alsa/alsa-constants.h index 81257c7..8137289 100644 --- a/backends/alsa/alsa-constants.h +++ b/backends/alsa/alsa-constants.h @@ -22,14 +22,35 @@ #include <alsa/asoundlib.h> #include <libmatemixer/matemixer.h> +G_BEGIN_DECLS + typedef struct { gchar *name; gchar *label; MateMixerStreamControlRole role; + gboolean use_default_input; + gboolean use_default_output; } AlsaControlInfo; +typedef struct { + gchar *name; + gchar *label; + MateMixerSwitchRole role; +} AlsaSwitchInfo; + +typedef struct { + gchar *name; + gchar *label; + gchar *icon; +} AlsaSwitchOptionInfo; + extern const AlsaControlInfo alsa_controls[]; +extern const AlsaSwitchInfo alsa_switches[]; +extern const AlsaSwitchOptionInfo alsa_switch_options[]; + extern const MateMixerChannelPosition alsa_channel_map_from[]; extern const snd_mixer_selem_channel_id_t alsa_channel_map_to[]; +G_END_DECLS + #endif /* ALSA_CONSTANTS_H */ diff --git a/backends/alsa/alsa-device.c b/backends/alsa/alsa-device.c index 5acc6f5..f7f705e 100644 --- a/backends/alsa/alsa-device.c +++ b/backends/alsa/alsa-device.c @@ -15,7 +15,9 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#include <strings.h> +#include <string.h> +#include <libintl.h> + #include <glib.h> #include <glib/gi18n.h> #include <glib-object.h> @@ -35,6 +37,18 @@ #define ALSA_DEVICE_ICON "audio-card" +#define ALSA_STREAM_CONTROL_GET_SCORE(c) \ + (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (c), \ + "__matemixer_alsa_control_score"))) + +#define ALSA_STREAM_CONTROL_SET_SCORE(c,score) \ + (g_object_set_data (G_OBJECT (c), \ + "__matemixer_alsa_control_score", \ + GINT_TO_POINTER (score))) + +#define ALSA_STREAM_DEFAULT_CONTROL_GET_SCORE(s) \ + (ALSA_STREAM_CONTROL_GET_SCORE (alsa_stream_get_default_control (ALSA_STREAM (s)))) + struct _AlsaDevicePrivate { snd_mixer_t *handle; @@ -43,7 +57,8 @@ struct _AlsaDevicePrivate GCond cond; AlsaStream *input; AlsaStream *output; - GHashTable *switches; + GList *streams; + GList *switches; gboolean events_pending; }; @@ -61,64 +76,83 @@ static void alsa_device_finalize (GObject *object); G_DEFINE_TYPE (AlsaDevice, alsa_device, MATE_MIXER_TYPE_DEVICE) -static MateMixerSwitch *alsa_device_get_switch (MateMixerDevice *mmd, - const gchar *name); +static const GList * alsa_device_list_streams (MateMixerDevice *mmd); +static const GList * alsa_device_list_switches (MateMixerDevice *mmd); + +static gboolean add_stream_input_control (AlsaDevice *device, + snd_mixer_elem_t *el); +static gboolean add_stream_output_control (AlsaDevice *device, + snd_mixer_elem_t *el); + +static gboolean add_switch (AlsaDevice *device, + AlsaStream *stream, + snd_mixer_elem_t *el); -static GList * alsa_device_list_streams (MateMixerDevice *mmd); -static GList * alsa_device_list_switches (MateMixerDevice *mmd); +static gboolean add_device_switch (AlsaDevice *device, + snd_mixer_elem_t *el); -static gboolean add_stream_input_control (AlsaDevice *device, - snd_mixer_elem_t *el); -static gboolean add_stream_output_control (AlsaDevice *device, - snd_mixer_elem_t *el); +static gboolean add_stream_input_switch (AlsaDevice *device, + snd_mixer_elem_t *el); +static gboolean add_stream_output_switch (AlsaDevice *device, + snd_mixer_elem_t *el); -static gboolean add_switch (AlsaDevice *device, - AlsaStream *stream, - snd_mixer_elem_t *el); +static gboolean add_stream_input_toggle (AlsaDevice *device, + snd_mixer_elem_t *el); +static gboolean add_stream_output_toggle (AlsaDevice *device, + snd_mixer_elem_t *el); -static gboolean add_device_switch (AlsaDevice *device, - snd_mixer_elem_t *el); +static void load_element (AlsaDevice *device, + snd_mixer_elem_t *el); -static gboolean add_stream_input_switch (AlsaDevice *device, - snd_mixer_elem_t *el); -static gboolean add_stream_output_switch (AlsaDevice *device, - snd_mixer_elem_t *el); +static void load_elements_by_name (AlsaDevice *device, + const gchar *name); -static gboolean add_stream_input_toggle (AlsaDevice *device, - snd_mixer_elem_t *el); -static gboolean add_stream_output_toggle (AlsaDevice *device, - snd_mixer_elem_t *el); +static void remove_elements_by_name (AlsaDevice *device, + const gchar *name); -static void load_element (AlsaDevice *device, - snd_mixer_elem_t *el); +static void handle_poll (AlsaDevice *device); -static void load_elements_by_name (AlsaDevice *device, - const gchar *name); +static gboolean handle_process_events (AlsaDevice *device); -static void remove_elements_by_name (AlsaDevice *device, - const gchar *name); +static int handle_callback (snd_mixer_t *handle, + guint mask, + snd_mixer_elem_t *el); +static int handle_element_callback (snd_mixer_elem_t *el, + guint mask); -static void handle_poll (AlsaDevice *device); +static void validate_default_controls (AlsaDevice *device); -static gboolean handle_process_events (AlsaDevice *device); +static AlsaStreamControl *get_best_stream_control (AlsaStream *stream); -static int handle_callback (snd_mixer_t *handle, - guint mask, - snd_mixer_elem_t *el); -static int handle_element_callback (snd_mixer_elem_t *el, - guint mask); +static gchar * get_element_name (snd_mixer_elem_t *el); -static void close_device (AlsaDevice *device); +static void get_control_info (snd_mixer_elem_t *el, + gchar **name, + gchar **label, + MateMixerStreamControlRole *role, + gint *score); +static void get_input_control_info (snd_mixer_elem_t *el, + gchar **name, + gchar **label, + MateMixerStreamControlRole *role, + gint *score); +static void get_output_control_info (snd_mixer_elem_t *el, + gchar **name, + gchar **label, + MateMixerStreamControlRole *role, + gint *score); -static gchar * get_element_name (snd_mixer_elem_t *el); -static void get_control_info (snd_mixer_elem_t *el, - gchar **name, - gchar **label, - MateMixerStreamControlRole *role); +static void get_switch_info (snd_mixer_elem_t *el, + gchar **name, + gchar **label, + MateMixerSwitchRole *role); -static void get_switch_info (snd_mixer_elem_t *el, - gchar **name, - gchar **label); +static void close_mixer (AlsaDevice *device); + +static void free_stream_list (AlsaDevice *device); + +static gint compare_switch_name (gconstpointer a, + gconstpointer b); static void alsa_device_class_init (AlsaDeviceClass *klass) @@ -131,14 +165,13 @@ alsa_device_class_init (AlsaDeviceClass *klass) object_class->finalize = alsa_device_finalize; device_class = MATE_MIXER_DEVICE_CLASS (klass); - device_class->get_switch = alsa_device_get_switch; device_class->list_streams = alsa_device_list_streams; device_class->list_switches = alsa_device_list_switches; signals[CLOSED] = g_signal_new ("closed", G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, + G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (AlsaDeviceClass, closed), NULL, NULL, @@ -157,11 +190,6 @@ alsa_device_init (AlsaDevice *device) ALSA_TYPE_DEVICE, AlsaDevicePrivate); - device->priv->switches = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); - device->priv->context = g_main_context_ref_thread_default (); g_mutex_init (&device->priv->mutex); @@ -178,7 +206,12 @@ alsa_device_dispose (GObject *object) g_clear_object (&device->priv->input); g_clear_object (&device->priv->output); - g_hash_table_remove_all (device->priv->switches); + if (device->priv->switches != NULL) { + g_list_free_full (device->priv->switches, g_object_unref); + device->priv->switches = NULL; + } + + free_stream_list (device); G_OBJECT_CLASS (alsa_device_parent_class)->dispose (object); } @@ -193,11 +226,9 @@ alsa_device_finalize (GObject *object) g_mutex_clear (&device->priv->mutex); g_cond_clear (&device->priv->cond); - g_hash_table_unref (device->priv->switches); g_main_context_unref (device->priv->context); - if (device->priv->handle != NULL) - snd_mixer_close (device->priv->handle); + close_mixer (device); G_OBJECT_CLASS (alsa_device_parent_class)->dispose (object); } @@ -208,7 +239,7 @@ alsa_device_new (const gchar *name, const gchar *label) AlsaDevice *device; gchar *stream_name; - g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (label != NULL, NULL); device = g_object_new (ALSA_TYPE_DEVICE, @@ -223,13 +254,13 @@ alsa_device_new (const gchar *name, const gchar *label) stream_name = g_strdup_printf ("alsa-input-%s", name); device->priv->input = alsa_stream_new (stream_name, MATE_MIXER_DEVICE (device), - MATE_MIXER_STREAM_INPUT); + MATE_MIXER_DIRECTION_INPUT); g_free (stream_name); stream_name = g_strdup_printf ("alsa-output-%s", name); device->priv->output = alsa_stream_new (stream_name, MATE_MIXER_DEVICE (device), - MATE_MIXER_STREAM_OUTPUT); + MATE_MIXER_DIRECTION_OUTPUT); g_free (stream_name); return device; @@ -289,6 +320,72 @@ alsa_device_open (AlsaDevice *device) return TRUE; } +gboolean +alsa_device_is_open (AlsaDevice *device) +{ + g_return_val_if_fail (ALSA_IS_DEVICE (device), FALSE); + + if (device->priv->handle != NULL) + return TRUE; + + return FALSE; +} + +void +alsa_device_close (AlsaDevice *device) +{ + GList *list; + + g_return_if_fail (ALSA_IS_DEVICE (device)); + + if (device->priv->handle == NULL) + return; + + /* Make each stream remove its controls and switches */ + if (alsa_stream_has_controls_or_switches (device->priv->input) == TRUE) { + const gchar *name = + mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->input)); + + alsa_stream_remove_all (device->priv->input); + free_stream_list (device); + + g_signal_emit_by_name (G_OBJECT (device), + "stream-removed", + name); + } + + if (alsa_stream_has_controls_or_switches (device->priv->output) == TRUE) { + const gchar *name = + mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->output)); + + alsa_stream_remove_all (device->priv->output); + free_stream_list (device); + + g_signal_emit_by_name (G_OBJECT (device), + "stream-removed", + name); + } + + /* Remove device switches */ + list = device->priv->switches; + while (list != NULL) { + MateMixerSwitch *swtch = MATE_MIXER_SWITCH (list->data); + GList *next = list->next; + + device->priv->switches = g_list_delete_link (device->priv->switches, list); + g_signal_emit_by_name (G_OBJECT (device), + "switch-removed", + mate_mixer_switch_get_name (swtch)); + g_object_unref (swtch); + + list = next; + } + + close_mixer (device); + + g_signal_emit (G_OBJECT (device), signals[CLOSED], 0); +} + void alsa_device_load (AlsaDevice *device) { @@ -307,6 +404,9 @@ alsa_device_load (AlsaDevice *device) el = snd_mixer_elem_next (el); } + /* Assign proper default controls */ + validate_default_controls (device); + /* Set callback for ALSA events */ snd_mixer_set_callback (device->priv->handle, handle_callback); snd_mixer_set_callback_private (device->priv->handle, device); @@ -332,7 +432,7 @@ alsa_device_get_input_stream (AlsaDevice *device) /* Normally controlless streams should not exist, here we simulate the * behaviour for the owning instance */ - if (alsa_stream_is_empty (device->priv->input) == FALSE) + if (alsa_stream_has_controls_or_switches (device->priv->input) == TRUE) return device->priv->input; return NULL; @@ -345,7 +445,7 @@ alsa_device_get_output_stream (AlsaDevice *device) /* Normally controlless streams should not exist, here we simulate the * behaviour for the owning instance */ - if (alsa_stream_is_empty (device->priv->output) == FALSE) + if (alsa_stream_has_controls_or_switches (device->priv->output) == TRUE) return device->priv->output; return NULL; @@ -360,44 +460,39 @@ add_element (AlsaDevice *device, AlsaStream *stream, AlsaElement *element) return FALSE; if (stream != NULL) { - gboolean empty = FALSE; - - if (alsa_stream_is_empty (stream) == TRUE) { + if (alsa_stream_has_controls_or_switches (stream) == FALSE) { const gchar *name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); + free_stream_list (device); + /* Pretend the stream has just been created now that we are adding * the first control */ g_signal_emit_by_name (G_OBJECT (device), "stream-added", name); - empty = TRUE; } if (ALSA_IS_STREAM_CONTROL (element)) { + /* Stream control */ alsa_stream_add_control (stream, ALSA_STREAM_CONTROL (element)); - - /* If this is the first control, set it as the default one. - * The controls often seem to come in the order of importance, but this is - * driver specific, so we may later see if there is another control which - * better matches the default. */ - if (empty == TRUE) - alsa_stream_set_default_control (stream, ALSA_STREAM_CONTROL (element)); - added = TRUE; } else if (ALSA_IS_SWITCH (element)) { /* Switch belonging to a stream */ alsa_stream_add_switch (stream, ALSA_SWITCH (element)); added = TRUE; + } else if (ALSA_IS_TOGGLE (element)) { + /* Toggle belonging to a stream */ + alsa_stream_add_toggle (stream, ALSA_TOGGLE (element)); + added = TRUE; } - } else if (ALSA_IS_SWITCH (element)) { + } else if (ALSA_IS_SWITCH (element) || ALSA_IS_TOGGLE (element)) { /* Switch belonging to the device */ const gchar *name = mate_mixer_switch_get_name (MATE_MIXER_SWITCH (element)); - g_hash_table_insert (device->priv->switches, - g_strdup (name), - g_object_ref (element)); + device->priv->switches = + g_list_append (device->priv->switches, g_object_ref (element)); g_signal_emit_by_name (G_OBJECT (device), "switch-added", @@ -414,47 +509,32 @@ add_element (AlsaDevice *device, AlsaStream *stream, AlsaElement *element) return added; } -static MateMixerSwitch * -alsa_device_get_switch (MateMixerDevice *mmd, const gchar *name) -{ - g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL); - g_return_val_if_fail (name != NULL, NULL); - - return g_hash_table_lookup (ALSA_DEVICE (mmd)->priv->switches, name); -} - -static GList * +static const GList * alsa_device_list_streams (MateMixerDevice *mmd) { AlsaDevice *device; - GList *list = NULL; g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL); device = ALSA_DEVICE (mmd); - if (device->priv->output != NULL) - list = g_list_prepend (list, g_object_ref (device->priv->output)); - if (device->priv->input != NULL) - list = g_list_prepend (list, g_object_ref (device->priv->input)); - - return list; + if (device->priv->streams == NULL) { + if (device->priv->output != NULL) + device->priv->streams = g_list_prepend (device->priv->streams, + g_object_ref (device->priv->output)); + if (device->priv->input != NULL) + device->priv->streams = g_list_prepend (device->priv->streams, + g_object_ref (device->priv->input)); + } + return device->priv->streams; } -static GList * +static const GList * alsa_device_list_switches (MateMixerDevice *mmd) { - GList *list; - g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL); - /* Convert the hash table to a linked list, this list is expected to be - * cached in the main library */ - list = g_hash_table_get_values (ALSA_DEVICE (mmd)->priv->switches); - if (list != NULL) - g_list_foreach (list, (GFunc) g_object_ref, NULL); - - return list; + return ALSA_DEVICE (mmd)->priv->switches; } static gboolean @@ -463,25 +543,28 @@ add_stream_input_control (AlsaDevice *device, snd_mixer_elem_t *el) AlsaStreamControl *control; gchar *name; gchar *label; + gboolean ret; + gint score; MateMixerStreamControlRole role; - get_control_info (el, &name, &label, &role); + get_input_control_info (el, &name, &label, &role, &score); - g_debug ("Found device %s input control %s", + g_debug ("Reading device %s input control %s", mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)), - label); + name); - control = alsa_stream_input_control_new (name, label, role); + control = alsa_stream_input_control_new (name, label, role, device->priv->input); g_free (name); g_free (label); + ALSA_STREAM_CONTROL_SET_SCORE (control, score); + alsa_element_set_snd_element (ALSA_ELEMENT (control), el); - if (add_element (device, device->priv->input, ALSA_ELEMENT (control)) == FALSE) { - g_object_unref (control); - return FALSE; - } - return TRUE; + ret = add_element (device, device->priv->input, ALSA_ELEMENT (control)); + + g_object_unref (control); + return ret; } static gboolean @@ -490,49 +573,51 @@ add_stream_output_control (AlsaDevice *device, snd_mixer_elem_t *el) AlsaStreamControl *control; gchar *label; gchar *name; + gboolean ret; + gint score; MateMixerStreamControlRole role; - get_control_info (el, &name, &label, &role); + get_output_control_info (el, &name, &label, &role, &score); - g_debug ("Found device %s output control %s", + g_debug ("Reading device %s output control %s", mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)), - label); + name); - control = alsa_stream_output_control_new (name, label, role); + control = alsa_stream_output_control_new (name, label, role, device->priv->output); g_free (name); g_free (label); + ALSA_STREAM_CONTROL_SET_SCORE (control, score); + alsa_element_set_snd_element (ALSA_ELEMENT (control), el); - if (add_element (device, device->priv->output, ALSA_ELEMENT (control)) == FALSE) { - g_object_unref (control); - return FALSE; - } - return TRUE; + ret = add_element (device, device->priv->output, ALSA_ELEMENT (control)); + + g_object_unref (control); + return ret; } static AlsaToggle * create_toggle (AlsaDevice *device, snd_mixer_elem_t *el, AlsaToggleType type) { - AlsaToggle *toggle; - AlsaSwitchOption *on; - AlsaSwitchOption *off; - gchar *name; - gchar *label; + AlsaToggle *toggle; + AlsaSwitchOption *on; + AlsaSwitchOption *off; + gchar *name; + gchar *label; + MateMixerSwitchRole role; on = alsa_switch_option_new ("On", _("On"), NULL, 1); off = alsa_switch_option_new ("Off", _("Off"), NULL, 0); - get_switch_info (el, &name, &label); - toggle = alsa_toggle_new (name, - label, - type, - on, off); - g_free (name); - g_free (label); + get_switch_info (el, &name, &label, &role); + + toggle = alsa_toggle_new (name, label, role, type, on, off); alsa_element_set_snd_element (ALSA_ELEMENT (toggle), el); + g_free (name); + g_free (label); g_object_unref (on); g_object_unref (off); @@ -542,14 +627,15 @@ create_toggle (AlsaDevice *device, snd_mixer_elem_t *el, AlsaToggleType type) static gboolean add_switch (AlsaDevice *device, AlsaStream *stream, snd_mixer_elem_t *el) { - AlsaElement *element = NULL; - GList *options = NULL; - gchar *name; - gchar *label; - gchar item[128]; - guint i; - gint count; - gint ret; + AlsaElement *element = NULL; + GList *options = NULL; + gchar *name; + gchar *label; + gchar item[128]; + guint i; + gint count; + gboolean ret; + MateMixerSwitchRole role; count = snd_mixer_selem_get_enum_items (el); if G_UNLIKELY (count <= 0) { @@ -560,30 +646,44 @@ add_switch (AlsaDevice *device, AlsaStream *stream, snd_mixer_elem_t *el) } for (i = 0; i < count; i++) { - ret = snd_mixer_selem_get_enum_item_name (el, i, - sizeof (item), - item); - if G_LIKELY (ret == 0) - options = g_list_prepend (options, - alsa_switch_option_new (item, item, NULL, i)); - else + gint ret = snd_mixer_selem_get_enum_item_name (el, i, sizeof (item), item); + + if G_LIKELY (ret == 0) { + gint j; + AlsaSwitchOption *option = NULL; + + for (j = 0; alsa_switch_options[j].name != NULL; j++) + if (strcmp (item, alsa_switch_options[j].name) == 0) { + option = alsa_switch_option_new (item, + gettext (alsa_switch_options[j].label), + alsa_switch_options[j].icon, + i); + break; + } + + if (option == NULL) + option = alsa_switch_option_new (item, item, NULL, i); + + options = g_list_prepend (options, option); + } else g_warning ("Failed to read switch item name: %s", snd_strerror (ret)); } - get_switch_info (el, &name, &label); + get_switch_info (el, &name, &label, &role); /* Takes ownership of options */ - element = ALSA_ELEMENT (alsa_switch_new (name, label, g_list_reverse (options))); + element = ALSA_ELEMENT (alsa_switch_new (name, label, + role, + g_list_reverse (options))); g_free (name); g_free (label); alsa_element_set_snd_element (element, el); - if (add_element (device, stream, element) == FALSE) { - g_object_unref (element); - return FALSE; - } - return TRUE; + ret = add_element (device, stream, element); + + g_object_unref (element); + return ret; } static gboolean @@ -623,6 +723,7 @@ static gboolean add_stream_input_toggle (AlsaDevice *device, snd_mixer_elem_t *el) { AlsaToggle *toggle; + gboolean ret; g_debug ("Reading device %s input toggle %s", mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)), @@ -630,17 +731,17 @@ add_stream_input_toggle (AlsaDevice *device, snd_mixer_elem_t *el) toggle = create_toggle (device, el, ALSA_TOGGLE_CAPTURE); - if (add_element (device, device->priv->input, ALSA_ELEMENT (toggle)) == FALSE) { - g_object_unref (toggle); - return FALSE; - } - return TRUE; + ret = add_element (device, device->priv->input, ALSA_ELEMENT (toggle)); + + g_object_unref (toggle); + return ret; } static gboolean add_stream_output_toggle (AlsaDevice *device, snd_mixer_elem_t *el) { AlsaToggle *toggle; + gboolean ret; g_debug ("Reading device %s output toggle %s", mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)), @@ -648,11 +749,10 @@ add_stream_output_toggle (AlsaDevice *device, snd_mixer_elem_t *el) toggle = create_toggle (device, el, ALSA_TOGGLE_PLAYBACK); - if (add_element (device, device->priv->output, ALSA_ELEMENT (toggle)) == FALSE) { - g_object_unref (toggle); - return FALSE; - } - return TRUE; + ret = add_element (device, device->priv->output, ALSA_ELEMENT (toggle)); + + g_object_unref (toggle); + return ret; } static void @@ -671,8 +771,7 @@ load_element (AlsaDevice *device, snd_mixer_elem_t *el) penum = TRUE; /* Enumerated controls which are not marked as capture or playback - * are considered to be a part of the whole device, although sometimes - * this is incorrectly reported by the driver */ + * are considered to be a part of the whole device */ if (cenum == FALSE && penum == FALSE) { add_device_switch (device, el); } @@ -707,25 +806,28 @@ load_element (AlsaDevice *device, snd_mixer_elem_t *el) static void load_elements_by_name (AlsaDevice *device, const gchar *name) { - AlsaElement *element; + GList *item; alsa_stream_load_elements (device->priv->input, name); alsa_stream_load_elements (device->priv->output, name); - element = g_hash_table_lookup (device->priv->switches, name); - if (element != NULL) - alsa_element_load (element); + item = g_list_find_custom (device->priv->switches, name, compare_switch_name); + if (item != NULL) + alsa_element_load (ALSA_ELEMENT (item->data)); } static void remove_elements_by_name (AlsaDevice *device, const gchar *name) { + GList *item; + if (alsa_stream_remove_elements (device->priv->input, name) == TRUE) { /* Removing last stream element "removes" the stream */ - if (alsa_stream_is_empty (device->priv->input) == TRUE) { + if (alsa_stream_has_controls_or_switches (device->priv->input) == FALSE) { const gchar *stream_name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->input)); + free_stream_list (device); g_signal_emit_by_name (G_OBJECT (device), "stream-removed", stream_name); @@ -734,20 +836,28 @@ remove_elements_by_name (AlsaDevice *device, const gchar *name) if (alsa_stream_remove_elements (device->priv->output, name) == TRUE) { /* Removing last stream element "removes" the stream */ - if (alsa_stream_is_empty (device->priv->output) == TRUE) { + if (alsa_stream_has_controls_or_switches (device->priv->output) == FALSE) { const gchar *stream_name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->output)); + free_stream_list (device); g_signal_emit_by_name (G_OBJECT (device), "stream-removed", stream_name); } } - if (g_hash_table_remove (device->priv->switches, name) == TRUE) + item = g_list_find_custom (device->priv->switches, name, compare_switch_name); + if (item != NULL) { + MateMixerSwitch *swtch = MATE_MIXER_SWITCH (item->data); + + device->priv->switches = g_list_delete_link (device->priv->switches, item); g_signal_emit_by_name (G_OBJECT (device), "switch-removed", - name); + mate_mixer_switch_get_name (swtch)); + + g_object_unref (swtch); + } } static void @@ -806,7 +916,7 @@ handle_process_events (AlsaDevice *device) if (device->priv->handle != NULL) { gint ret = snd_mixer_handle_events (device->priv->handle); if (ret < 0) - close_device (device); + alsa_device_close (device); } device->priv->events_pending = FALSE; @@ -826,7 +936,15 @@ handle_callback (snd_mixer_t *handle, guint mask, snd_mixer_elem_t *el) if (mask & SND_CTL_EVENT_MASK_ADD) { AlsaDevice *device = snd_mixer_get_callback_private (handle); + if (device->priv->handle == NULL) { + /* The mixer is already closed */ + return 0; + } + load_element (device, el); + + /* Revalidate default controls assignment */ + validate_default_controls (device); } return 0; } @@ -838,6 +956,11 @@ handle_element_callback (snd_mixer_elem_t *el, guint mask) gchar *name; device = snd_mixer_elem_get_callback_private (el); + if (device->priv->handle == NULL) { + /* The mixer is already closed */ + return 0; + } + name = get_element_name (el); if (mask == SND_CTL_EVENT_MASK_REMOVE) { @@ -846,10 +969,16 @@ handle_element_callback (snd_mixer_elem_t *el, guint mask) snd_mixer_elem_set_callback (el, NULL); remove_elements_by_name (device, name); + + /* Revalidate default controls assignment */ + validate_default_controls (device); } else { if (mask & SND_CTL_EVENT_MASK_INFO) { remove_elements_by_name (device, name); load_element (device, el); + + /* Revalidate default controls assignment */ + validate_default_controls (device); } if (mask & SND_CTL_EVENT_MASK_VALUE) load_elements_by_name (device, name); @@ -860,16 +989,86 @@ handle_element_callback (snd_mixer_elem_t *el, guint mask) } static void -close_device (AlsaDevice *device) +validate_default_controls (AlsaDevice *device) { - if (device->priv->handle != NULL) { - snd_mixer_close (device->priv->handle); - device->priv->handle = NULL; + AlsaStreamControl *best; + gint best_score; + gint current_score; + + /* + * Select the most suitable default control. Don't try too hard here because + * our list of known elements is incomplete and most drivers seem to provide + * the list in a reasonable order with the best element at the start. Each + * element in our list has a value (or score) which is simply its position + * in the list. Better elements are on the top, so smaller value represents + * a better element. + * + * Two cases are handled here: + * 1) The current default control is in our list, but the list also includes + * a better element. + * 2) The current default control is not in our list, but the list includes + * an element which is reasonably good. + * + * In other cases just keep the first control as the default. + */ + if (alsa_stream_has_controls (device->priv->input) == TRUE) { + best = get_best_stream_control (device->priv->input); + + best_score = ALSA_STREAM_CONTROL_GET_SCORE (best); + current_score = ALSA_STREAM_DEFAULT_CONTROL_GET_SCORE (device->priv->input); + + /* See if the best element would make a good default one */ + if (best_score > -1) { + g_debug ("Found usable default input element %s (score %d)", + mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (best)), + best_score); + + if (current_score == -1 || best_score < current_score) + alsa_stream_set_default_control (device->priv->input, best); + } } - /* This signal tells the owner that the device has been closed voluntarily - * from within the instance */ - g_signal_emit (G_OBJECT (device), signals[CLOSED], 0); + if (alsa_stream_has_controls (device->priv->output) == TRUE) { + best = get_best_stream_control (device->priv->output); + + best_score = ALSA_STREAM_CONTROL_GET_SCORE (best); + current_score = ALSA_STREAM_DEFAULT_CONTROL_GET_SCORE (device->priv->output); + + /* See if the best element would make a good default one */ + if (best_score > -1) { + g_debug ("Found usable default output element %s (score %d)", + mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (best)), + best_score); + + if (current_score == -1 || best_score < current_score) + alsa_stream_set_default_control (device->priv->output, best); + } + } +} + +static AlsaStreamControl * +get_best_stream_control (AlsaStream *stream) +{ + const GList *list; + AlsaStreamControl *best = NULL; + guint best_score = -1; + + list = mate_mixer_stream_list_controls (MATE_MIXER_STREAM (stream)); + while (list != NULL) { + AlsaStreamControl *current; + guint current_score; + + current = ALSA_STREAM_CONTROL (list->data); + current_score = ALSA_STREAM_CONTROL_GET_SCORE (current); + + if (best == NULL || (current_score != -1 && + (best_score == -1 || current_score < best_score))) { + best = current; + best_score = current_score; + } + list = list->next; + } + return best; } static gchar * @@ -884,7 +1083,8 @@ static void get_control_info (snd_mixer_elem_t *el, gchar **name, gchar **label, - MateMixerStreamControlRole *role) + MateMixerStreamControlRole *role, + gint *score) { MateMixerStreamControlRole r = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN; const gchar *n; @@ -893,49 +1093,119 @@ get_control_info (snd_mixer_elem_t *el, n = snd_mixer_selem_get_name (el); - for (i = 0; alsa_controls[i].name != NULL; i++) - if (strcmp (n, alsa_controls[i].name) == 0) { - l = alsa_controls[i].label; - r = alsa_controls[i].role; - break; - } + for (i = 0; alsa_controls[i].name != NULL; i++) { + if (strcmp (n, alsa_controls[i].name) != 0) + continue; + + l = gettext (alsa_controls[i].label); + r = alsa_controls[i].role; + break; + } *name = get_element_name (el); - if (l != NULL) + if (l != NULL) { *label = g_strdup (l); - else + *score = i; + } else { *label = g_strdup (n); + *score = -1; + } *role = r; } static void -get_switch_info (snd_mixer_elem_t *el, - gchar **name, - gchar **label) +get_input_control_info (snd_mixer_elem_t *el, + gchar **name, + gchar **label, + MateMixerStreamControlRole *role, + gint *score) { - // MateMixerStreamControlRole r = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN; - const gchar *n; - const gchar *l = NULL; - // gint i; + get_control_info (el, name, label, role, score); + + if (*score > -1 && alsa_controls[*score].use_default_input == FALSE) + *score = -1; +} + +static void +get_output_control_info (snd_mixer_elem_t *el, + gchar **name, + gchar **label, + MateMixerStreamControlRole *role, + gint *score) +{ + get_control_info (el, name, label, role, score); + + if (*score > -1 && alsa_controls[*score].use_default_output == FALSE) + *score = -1; +} + +static void +get_switch_info (snd_mixer_elem_t *el, + gchar **name, + gchar **label, + MateMixerSwitchRole *role) +{ + MateMixerSwitchRole r = MATE_MIXER_SWITCH_ROLE_UNKNOWN; + const gchar *n; + const gchar *l = NULL; + gint i; n = snd_mixer_selem_get_name (el); - // TODO provide translated label and flags + for (i = 0; alsa_switches[i].name != NULL; i++) { + if (strcmp (n, alsa_switches[i].name) != 0) + continue; + + l = gettext (alsa_switches[i].label); + r = alsa_switches[i].role; + break; + } -/* - for (i = 0; alsa_controls[i].name != NULL; i++) - if (strcmp (n, alsa_controls[i].name) == 0) { - l = alsa_controls[i].label; - r = alsa_controls[i].role; - break; - } -*/ *name = get_element_name (el); if (l != NULL) *label = g_strdup (l); else *label = g_strdup (n); - // *role = r; + *role = r; +} + +static void +close_mixer (AlsaDevice *device) +{ + snd_mixer_t *handle; + + if (device->priv->handle == NULL) + return; + + /* Closing the mixer may fire up remove callbacks, prevent this by unsetting + * the handle before closing it and checking it in the callback. + * Ideally, we should unset callbacks from all the elements, but this seems + * to do the job. */ + handle = device->priv->handle; + + device->priv->handle = NULL; + snd_mixer_close (handle); +} + +static void +free_stream_list (AlsaDevice *device) +{ + /* This function is called each time the stream list changes */ + if (device->priv->streams == NULL) + return; + + g_list_free_full (device->priv->streams, g_object_unref); + + device->priv->streams = NULL; +} + +static gint +compare_switch_name (gconstpointer a, gconstpointer b) +{ + MateMixerSwitch *swtch = MATE_MIXER_SWITCH (a); + const gchar *name = (const gchar *) b; + + return strcmp (mate_mixer_switch_get_name (swtch), name); } diff --git a/backends/alsa/alsa-device.h b/backends/alsa/alsa-device.h index 3b3c970..9e908cf 100644 --- a/backends/alsa/alsa-device.h +++ b/backends/alsa/alsa-device.h @@ -20,6 +20,7 @@ #include <glib.h> #include <glib-object.h> +#include <libmatemixer/matemixer.h> #include "alsa-stream.h" @@ -64,6 +65,9 @@ AlsaDevice *alsa_device_new (const gchar *name, const gchar *label); gboolean alsa_device_open (AlsaDevice *device); +gboolean alsa_device_is_open (AlsaDevice *device); +void alsa_device_close (AlsaDevice *device); + void alsa_device_load (AlsaDevice *device); AlsaStream *alsa_device_get_input_stream (AlsaDevice *device); diff --git a/backends/alsa/alsa-element.c b/backends/alsa/alsa-element.c index f925064..d837965 100644 --- a/backends/alsa/alsa-element.c +++ b/backends/alsa/alsa-element.c @@ -51,3 +51,19 @@ alsa_element_load (AlsaElement *element) return ALSA_ELEMENT_GET_INTERFACE (element)->load (element); } + +void +alsa_element_close (AlsaElement *element) +{ + AlsaElementInterface *iface; + + g_return_if_fail (ALSA_IS_ELEMENT (element)); + + /* Close the element by unsetting the ALSA element and optionally calling + * a closing function */ + alsa_element_set_snd_element (element, NULL); + + iface = ALSA_ELEMENT_GET_INTERFACE (element); + if (iface->close != NULL) + iface->close (element); +} diff --git a/backends/alsa/alsa-element.h b/backends/alsa/alsa-element.h index 01d30f1..1c30f68 100644 --- a/backends/alsa/alsa-element.h +++ b/backends/alsa/alsa-element.h @@ -46,6 +46,7 @@ struct _AlsaElementInterface snd_mixer_elem_t *el); gboolean (*load) (AlsaElement *element); + void (*close) (AlsaElement *element); }; GType alsa_element_get_type (void) G_GNUC_CONST; @@ -56,6 +57,8 @@ void alsa_element_set_snd_element (AlsaElement *element, gboolean alsa_element_load (AlsaElement *element); +void alsa_element_close (AlsaElement *element); + G_END_DECLS #endif /* ALSA_ELEMENT_H */ diff --git a/backends/alsa/alsa-stream-control.c b/backends/alsa/alsa-stream-control.c index bc7a937..97a0f8b 100644 --- a/backends/alsa/alsa-stream-control.c +++ b/backends/alsa/alsa-stream-control.c @@ -18,6 +18,7 @@ #include <glib.h> #include <glib-object.h> #include <alsa/asoundlib.h> + #include <libmatemixer/matemixer.h> #include <libmatemixer/matemixer-private.h> @@ -159,64 +160,67 @@ alsa_stream_control_set_data (AlsaStreamControl *control, AlsaControlData *data) { MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_NO_FLAGS; MateMixerStreamControl *mmsc; + gboolean mute = FALSE; g_return_if_fail (ALSA_IS_STREAM_CONTROL (control)); g_return_if_fail (data != NULL); mmsc = MATE_MIXER_STREAM_CONTROL (control); + control->priv->data = *data; + g_object_freeze_notify (G_OBJECT (control)); if (data->channels > 0) { if (data->switch_usable == TRUE) { - flags |= MATE_MIXER_STREAM_CONTROL_HAS_MUTE; + /* If the mute switch is joined, all the channels get the same value, + * otherwise the element has per-channel mute, which we don't support. + * In that case, treat the control as unmuted if any channel is + * unmuted. */ + if (data->channels == 1 || data->switch_joined == TRUE) { + mute = data->m[0]; + } else { + gint i; + mute = TRUE; + for (i = 0; i < data->channels; i++) + if (data->m[i] == FALSE) { + mute = FALSE; + break; + } + } + + flags |= MATE_MIXER_STREAM_CONTROL_MUTE_READABLE; if (data->active == TRUE) - flags |= MATE_MIXER_STREAM_CONTROL_CAN_SET_MUTE; + flags |= MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE; } - flags |= MATE_MIXER_STREAM_CONTROL_HAS_VOLUME; + + flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE; if (data->active == TRUE) - flags |= MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME; - } - if (data->max_decibel > -MATE_MIXER_INFINITY) - flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; + flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE; - control->priv->data = *data; - control->priv->channel_mask = _mate_mixer_create_channel_mask (data->c, data->channels); + if (data->max_decibel > -MATE_MIXER_INFINITY) + flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; - if (data->volume_joined == FALSE) { - if (MATE_MIXER_CHANNEL_MASK_HAS_LEFT (control->priv->channel_mask) && - MATE_MIXER_CHANNEL_MASK_HAS_RIGHT (control->priv->channel_mask)) - flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; + control->priv->channel_mask = _mate_mixer_create_channel_mask (data->c, data->channels); + + if (data->volume_joined == FALSE) { + if (MATE_MIXER_CHANNEL_MASK_HAS_LEFT (control->priv->channel_mask) && + MATE_MIXER_CHANNEL_MASK_HAS_RIGHT (control->priv->channel_mask)) + flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; + + if (MATE_MIXER_CHANNEL_MASK_HAS_FRONT (control->priv->channel_mask) && + MATE_MIXER_CHANNEL_MASK_HAS_BACK (control->priv->channel_mask)) + flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE; + } - if (MATE_MIXER_CHANNEL_MASK_HAS_FRONT (control->priv->channel_mask) && - MATE_MIXER_CHANNEL_MASK_HAS_BACK (control->priv->channel_mask)) - flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE; + g_object_notify (G_OBJECT (control), "volume"); + } else { + control->priv->channel_mask = 0; } + _mate_mixer_stream_control_set_mute (mmsc, mute); _mate_mixer_stream_control_set_flags (mmsc, flags); - if (data->switch_usable == TRUE) { - gboolean mute; - - /* If the mute switch is joined, all the channels get the same value, - * otherwise the element has per-channel mute, which we don't support. - * In that case, treat the control as unmuted if any channel is - * unmuted. */ - if (data->channels == 1 || data->switch_joined == TRUE) { - mute = data->m[0]; - } else { - gint i; - mute = TRUE; - for (i = 0; i < data->channels; i++) - if (data->m[i] == FALSE) { - mute = FALSE; - break; - } - } - _mate_mixer_stream_control_set_mute (mmsc, mute); - } else - _mate_mixer_stream_control_set_mute (mmsc, FALSE); - if (flags & MATE_MIXER_STREAM_CONTROL_CAN_BALANCE) _mate_mixer_stream_control_set_balance (mmsc, control_data_get_balance (data)); if (flags & MATE_MIXER_STREAM_CONTROL_CAN_FADE) @@ -237,7 +241,6 @@ static void alsa_stream_control_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el) { g_return_if_fail (ALSA_IS_STREAM_CONTROL (element)); - g_return_if_fail (el != NULL); ALSA_STREAM_CONTROL (element)->priv->element = el; } @@ -288,6 +291,8 @@ alsa_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute) for (i = 0; i < control->priv->data.channels; i++) control->priv->data.m[i] = mute; + + _mate_mixer_stream_control_set_mute (mmsc, mute); } return TRUE; } @@ -344,6 +349,8 @@ alsa_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume) for (i = 0; i < control->priv->data.channels; i++) control->priv->data.v[i] = volume; + control->priv->data.volume = volume; + g_object_notify (G_OBJECT (control), "volume"); } return TRUE; @@ -364,7 +371,7 @@ alsa_stream_control_get_decibel (MateMixerStreamControl *mmsc) volume = alsa_stream_control_get_volume (mmsc); if (klass->get_decibel_from_volume (control, volume, &decibel) == FALSE) - return FALSE; + return -MATE_MIXER_INFINITY; return decibel; } @@ -428,7 +435,7 @@ alsa_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint chan control = ALSA_STREAM_CONTROL (mmsc); if (channel >= control->priv->data.channels) - return FALSE; + return 0; return control->priv->data.v[channel]; } @@ -463,6 +470,7 @@ alsa_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, guint chan if (klass->set_channel_volume (control, c, volume) == FALSE) return FALSE; + // XXX recalc total volume control->priv->data.v[channel] = volume; g_object_notify (G_OBJECT (control), "volume"); @@ -483,13 +491,13 @@ alsa_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc, guint cha control = ALSA_STREAM_CONTROL (mmsc); if (channel >= control->priv->data.channels) - return FALSE; + return -MATE_MIXER_INFINITY; klass = ALSA_STREAM_CONTROL_GET_CLASS (control); volume = control->priv->data.v[channel]; if (klass->get_decibel_from_volume (control, volume, &decibel) == FALSE) - return FALSE; + return -MATE_MIXER_INFINITY; return decibel; } @@ -637,27 +645,27 @@ alsa_stream_control_set_fade (MateMixerStreamControl *mmsc, gfloat fade) } static guint -alsa_stream_control_get_min_volume (MateMixerStreamControl *msc) +alsa_stream_control_get_min_volume (MateMixerStreamControl *mmsc) { - return ALSA_STREAM_CONTROL (msc)->priv->data.min; + return ALSA_STREAM_CONTROL (mmsc)->priv->data.min; } static guint -alsa_stream_control_get_max_volume (MateMixerStreamControl *msc) +alsa_stream_control_get_max_volume (MateMixerStreamControl *mmsc) { - return ALSA_STREAM_CONTROL (msc)->priv->data.max; + return ALSA_STREAM_CONTROL (mmsc)->priv->data.max; } static guint -alsa_stream_control_get_normal_volume (MateMixerStreamControl *msc) +alsa_stream_control_get_normal_volume (MateMixerStreamControl *mmsc) { - return ALSA_STREAM_CONTROL (msc)->priv->data.max; + return ALSA_STREAM_CONTROL (mmsc)->priv->data.max; } static guint -alsa_stream_control_get_base_volume (MateMixerStreamControl *msc) +alsa_stream_control_get_base_volume (MateMixerStreamControl *mmsc) { - return ALSA_STREAM_CONTROL (msc)->priv->data.max; + return ALSA_STREAM_CONTROL (mmsc)->priv->data.max; } static void diff --git a/backends/alsa/alsa-stream-control.h b/backends/alsa/alsa-stream-control.h index f9ac6b6..acd02bd 100644 --- a/backends/alsa/alsa-stream-control.h +++ b/backends/alsa/alsa-stream-control.h @@ -41,9 +41,6 @@ typedef struct { guint channels; } AlsaControlData; -extern const MateMixerChannelPosition alsa_channel_map_from[SND_MIXER_SCHN_LAST]; -extern const snd_mixer_selem_channel_id_t alsa_channel_map_to[MATE_MIXER_CHANNEL_MAX]; - #define ALSA_TYPE_STREAM_CONTROL \ (alsa_stream_control_get_type ()) #define ALSA_STREAM_CONTROL(o) \ @@ -103,9 +100,6 @@ AlsaControlData * alsa_stream_control_get_data (AlsaStreamControl void alsa_stream_control_set_data (AlsaStreamControl *control, AlsaControlData *data); -gboolean alsa_stream_control_set_role (AlsaStreamControl *control, - MateMixerStreamControlRole role); - G_END_DECLS #endif /* ALSA_STREAM_CONTROL_H */ diff --git a/backends/alsa/alsa-stream-input-control.c b/backends/alsa/alsa-stream-input-control.c index 2ef0c42..2e3f46d 100644 --- a/backends/alsa/alsa-stream-input-control.c +++ b/backends/alsa/alsa-stream-input-control.c @@ -22,6 +22,7 @@ #include <libmatemixer/matemixer.h> #include <libmatemixer/matemixer-private.h> +#include "alsa-constants.h" #include "alsa-element.h" #include "alsa-stream-control.h" #include "alsa-stream-input-control.h" @@ -51,8 +52,7 @@ static gboolean alsa_stream_input_control_get_decibel_from_volume (AlsaStreamCon guint volume, gdouble *decibel); -static void read_volume_data (snd_mixer_elem_t *el, - AlsaControlData *data); +static void read_volume_data (snd_mixer_elem_t *el, AlsaControlData *data); static void alsa_stream_input_control_class_init (AlsaStreamInputControlClass *klass) @@ -77,12 +77,14 @@ alsa_stream_input_control_init (AlsaStreamInputControl *control) AlsaStreamControl * alsa_stream_input_control_new (const gchar *name, const gchar *label, - MateMixerStreamControlRole role) + MateMixerStreamControlRole role, + AlsaStream *stream) { return g_object_new (ALSA_TYPE_STREAM_INPUT_CONTROL, "name", name, "label", label, "role", role, + "stream", stream, NULL); } @@ -98,7 +100,6 @@ alsa_stream_input_control_load (AlsaStreamControl *control) if G_UNLIKELY (el == NULL) return FALSE; - /* Expect that the element has a volume control */ if G_UNLIKELY (snd_mixer_selem_has_capture_volume (el) == 0 && snd_mixer_selem_has_common_volume (el) == 0) { g_warn_if_reached (); @@ -180,8 +181,7 @@ alsa_stream_input_control_set_channel_volume (AlsaStreamControl *contr if G_UNLIKELY (el == NULL) return FALSE; - /* Set the volume for a single channels, the volume may still be "joined" and - * set all the channels by itself */ + /* Set the volume for a single channel */ ret = snd_mixer_selem_set_capture_volume (el, channel, volume); if (ret < 0) { g_warning ("Failed to set channel volume: %s", snd_strerror (ret)); diff --git a/backends/alsa/alsa-stream-input-control.h b/backends/alsa/alsa-stream-input-control.h index c427e3c..0ce885a 100644 --- a/backends/alsa/alsa-stream-input-control.h +++ b/backends/alsa/alsa-stream-input-control.h @@ -22,6 +22,7 @@ #include <glib-object.h> #include <libmatemixer/matemixer.h> +#include "alsa-stream.h" #include "alsa-stream-control.h" G_BEGIN_DECLS @@ -57,7 +58,8 @@ GType alsa_stream_input_control_get_type (void) G_GNUC_CONST; AlsaStreamControl *alsa_stream_input_control_new (const gchar *name, const gchar *label, - MateMixerStreamControlRole role); + MateMixerStreamControlRole role, + AlsaStream *stream); G_END_DECLS diff --git a/backends/alsa/alsa-stream-output-control.c b/backends/alsa/alsa-stream-output-control.c index 5a3e6b3..1f4faf8 100644 --- a/backends/alsa/alsa-stream-output-control.c +++ b/backends/alsa/alsa-stream-output-control.c @@ -22,6 +22,7 @@ #include <libmatemixer/matemixer.h> #include <libmatemixer/matemixer-private.h> +#include "alsa-constants.h" #include "alsa-element.h" #include "alsa-stream-control.h" #include "alsa-stream-output-control.h" @@ -51,8 +52,7 @@ static gboolean alsa_stream_output_control_get_decibel_from_volume (AlsaStreamCo guint volume, gdouble *decibel); -static void read_volume_data (snd_mixer_elem_t *el, - AlsaControlData *data); +static void read_volume_data (snd_mixer_elem_t *el, AlsaControlData *data); static void alsa_stream_output_control_class_init (AlsaStreamOutputControlClass *klass) @@ -77,12 +77,14 @@ alsa_stream_output_control_init (AlsaStreamOutputControl *control) AlsaStreamControl * alsa_stream_output_control_new (const gchar *name, const gchar *label, - MateMixerStreamControlRole role) + MateMixerStreamControlRole role, + AlsaStream *stream) { return g_object_new (ALSA_TYPE_STREAM_OUTPUT_CONTROL, "name", name, "label", label, "role", role, + "stream", stream, NULL); } @@ -98,7 +100,6 @@ alsa_stream_output_control_load (AlsaStreamControl *control) if G_UNLIKELY (el == NULL) return FALSE; - /* Expect that the element has a volume control */ if G_UNLIKELY (snd_mixer_selem_has_playback_volume (el) == 0 && snd_mixer_selem_has_common_volume (el) == 0) { g_warn_if_reached (); @@ -180,8 +181,7 @@ alsa_stream_output_control_set_channel_volume (AlsaStreamControl *cont if G_UNLIKELY (el == NULL) return FALSE; - /* Set the volume for a single channels, the volume may still be "joined" and - * set all the channels by itself */ + /* Set the volume for a single channel */ ret = snd_mixer_selem_set_playback_volume (el, channel, volume); if (ret < 0) { g_warning ("Failed to set channel volume: %s", snd_strerror (ret)); diff --git a/backends/alsa/alsa-stream-output-control.h b/backends/alsa/alsa-stream-output-control.h index 845eaae..9a5b708 100644 --- a/backends/alsa/alsa-stream-output-control.h +++ b/backends/alsa/alsa-stream-output-control.h @@ -22,6 +22,7 @@ #include <glib-object.h> #include <libmatemixer/matemixer.h> +#include "alsa-stream.h" #include "alsa-stream-control.h" G_BEGIN_DECLS @@ -57,7 +58,8 @@ GType alsa_stream_output_control_get_type (void) G_GNUC_CONST; AlsaStreamControl *alsa_stream_output_control_new (const gchar *name, const gchar *label, - MateMixerStreamControlRole role); + MateMixerStreamControlRole role, + AlsaStream *stream); G_END_DECLS diff --git a/backends/alsa/alsa-stream.c b/backends/alsa/alsa-stream.c index d2f68d4..bc9c1b5 100644 --- a/backends/alsa/alsa-stream.c +++ b/backends/alsa/alsa-stream.c @@ -18,6 +18,7 @@ #include <glib.h> #include <glib-object.h> #include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include "alsa-element.h" #include "alsa-stream.h" @@ -26,27 +27,23 @@ struct _AlsaStreamPrivate { - GHashTable *switches; - GHashTable *controls; - MateMixerStreamControl *control; + GList *switches; + GList *controls; }; static void alsa_stream_class_init (AlsaStreamClass *klass); static void alsa_stream_init (AlsaStream *stream); static void alsa_stream_dispose (GObject *object); -static void alsa_stream_finalize (GObject *object); G_DEFINE_TYPE (AlsaStream, alsa_stream, MATE_MIXER_TYPE_STREAM) -static MateMixerStreamControl *alsa_stream_get_control (MateMixerStream *mms, - const gchar *name); -static MateMixerStreamControl *alsa_stream_get_default_control (MateMixerStream *mms); - -static MateMixerSwitch * alsa_stream_get_switch (MateMixerStream *mms, - const gchar *name); +static const GList *alsa_stream_list_controls (MateMixerStream *mms); +static const GList *alsa_stream_list_switches (MateMixerStream *mms); -static GList * alsa_stream_list_controls (MateMixerStream *mms); -static GList * alsa_stream_list_switches (MateMixerStream *mms); +static gint compare_control_name (gconstpointer a, + gconstpointer b); +static gint compare_switch_name (gconstpointer a, + gconstpointer b); static void alsa_stream_class_init (AlsaStreamClass *klass) @@ -55,15 +52,11 @@ alsa_stream_class_init (AlsaStreamClass *klass) MateMixerStreamClass *stream_class; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = alsa_stream_dispose; - object_class->finalize = alsa_stream_finalize; + object_class->dispose = alsa_stream_dispose; stream_class = MATE_MIXER_STREAM_CLASS (klass); - stream_class->get_control = alsa_stream_get_control; - stream_class->get_default_control = alsa_stream_get_default_control; - stream_class->get_switch = alsa_stream_get_switch; - stream_class->list_controls = alsa_stream_list_controls; - stream_class->list_switches = alsa_stream_list_switches; + stream_class->list_controls = alsa_stream_list_controls; + stream_class->list_switches = alsa_stream_list_switches; g_type_class_add_private (object_class, sizeof (AlsaStreamPrivate)); } @@ -74,16 +67,6 @@ alsa_stream_init (AlsaStream *stream) stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, ALSA_TYPE_STREAM, AlsaStreamPrivate); - - stream->priv->controls = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); - - stream->priv->switches = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); } static void @@ -93,36 +76,30 @@ alsa_stream_dispose (GObject *object) stream = ALSA_STREAM (object); - g_hash_table_remove_all (stream->priv->controls); - g_hash_table_remove_all (stream->priv->switches); - - g_clear_object (&stream->priv->control); + if (stream->priv->controls != NULL) { + g_list_free_full (stream->priv->controls, g_object_unref); + stream->priv->controls = NULL; + } + if (stream->priv->switches != NULL) { + g_list_free_full (stream->priv->switches, g_object_unref); + stream->priv->switches = NULL; + } G_OBJECT_CLASS (alsa_stream_parent_class)->dispose (object); } -static void -alsa_stream_finalize (GObject *object) -{ - AlsaStream *stream; - - stream = ALSA_STREAM (object); - - g_hash_table_destroy (stream->priv->controls); - g_hash_table_destroy (stream->priv->switches); - - G_OBJECT_CLASS (alsa_stream_parent_class)->finalize (object); -} - AlsaStream * -alsa_stream_new (const gchar *name, - MateMixerDevice *device, - MateMixerStreamFlags flags) +alsa_stream_new (const gchar *name, + MateMixerDevice *device, + MateMixerDirection direction) { + const gchar *label = mate_mixer_device_get_label (device); + return g_object_new (ALSA_TYPE_STREAM, "name", name, + "label", label, "device", device, - "flags", flags, + "direction", direction, NULL); } @@ -135,9 +112,16 @@ alsa_stream_add_control (AlsaStream *stream, AlsaStreamControl *control) g_return_if_fail (ALSA_IS_STREAM_CONTROL (control)); name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control)); - g_hash_table_insert (stream->priv->controls, - g_strdup (name), - g_object_ref (control)); + + stream->priv->controls = + g_list_append (stream->priv->controls, g_object_ref (control)); + + g_signal_emit_by_name (G_OBJECT (stream), + "control-added", + name); + + if (alsa_stream_has_default_control (stream) == FALSE) + alsa_stream_set_default_control (stream, control); } void @@ -149,125 +133,250 @@ alsa_stream_add_switch (AlsaStream *stream, AlsaSwitch *swtch) g_return_if_fail (ALSA_IS_SWITCH (swtch)); name = mate_mixer_switch_get_name (MATE_MIXER_SWITCH (swtch)); - g_hash_table_insert (stream->priv->switches, - g_strdup (name), - g_object_ref (swtch)); + + stream->priv->switches = + g_list_append (stream->priv->switches, g_object_ref (swtch)); + + g_signal_emit_by_name (G_OBJECT (stream), + "switch-added", + name); +} + +void +alsa_stream_add_toggle (AlsaStream *stream, AlsaToggle *toggle) +{ + const gchar *name; + + g_return_if_fail (ALSA_IS_STREAM (stream)); + g_return_if_fail (ALSA_IS_TOGGLE (toggle)); + + name = mate_mixer_switch_get_name (MATE_MIXER_SWITCH (toggle)); + + /* Toggle is MateMixerSwitch, but not AlsaSwitch */ + stream->priv->switches = + g_list_append (stream->priv->switches, g_object_ref (toggle)); + + g_signal_emit_by_name (G_OBJECT (stream), + "switch-added", + name); +} + +gboolean +alsa_stream_has_controls (AlsaStream *stream) +{ + g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE); + + if (stream->priv->controls != NULL) + return TRUE; + + return FALSE; } gboolean -alsa_stream_is_empty (AlsaStream *stream) +alsa_stream_has_switches (AlsaStream *stream) { g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE); - if (g_hash_table_size (stream->priv->controls) > 0 || - g_hash_table_size (stream->priv->switches) > 0) - return FALSE; + if (stream->priv->switches != NULL) + return TRUE; - return TRUE; + return FALSE; +} + +gboolean +alsa_stream_has_controls_or_switches (AlsaStream *stream) +{ + g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE); + + if (stream->priv->controls != NULL || + stream->priv->switches != NULL) + return TRUE; + + return FALSE; +} + +gboolean +alsa_stream_has_default_control (AlsaStream *stream) +{ + g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE); + + if (mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream)) != NULL) + return TRUE; + + return FALSE; +} + +AlsaStreamControl * +alsa_stream_get_default_control (AlsaStream *stream) +{ + MateMixerStreamControl *control; + + g_return_val_if_fail (ALSA_IS_STREAM (stream), NULL); + + control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream)); + if (control != NULL) + return ALSA_STREAM_CONTROL (control); + + return NULL; } void alsa_stream_set_default_control (AlsaStream *stream, AlsaStreamControl *control) { g_return_if_fail (ALSA_IS_STREAM (stream)); - g_return_if_fail (ALSA_IS_STREAM_CONTROL (control)); - - /* This function is only used internally so avoid validating that the control - * belongs to this stream */ - if (stream->priv->control != NULL) - g_object_unref (stream->priv->control); + g_return_if_fail (control == NULL || ALSA_IS_STREAM_CONTROL (control)); - if (control != NULL) - stream->priv->control = MATE_MIXER_STREAM_CONTROL (g_object_ref (control)); + if (control == NULL) + _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (stream), NULL); else - stream->priv->control = NULL; + _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (stream), + MATE_MIXER_STREAM_CONTROL (control)); } void alsa_stream_load_elements (AlsaStream *stream, const gchar *name) { - AlsaElement *element; + GList *item; g_return_if_fail (ALSA_IS_STREAM (stream)); g_return_if_fail (name != NULL); - element = g_hash_table_lookup (stream->priv->controls, name); - if (element != NULL) - alsa_element_load (element); + item = g_list_find_custom (stream->priv->controls, name, compare_control_name); + if (item != NULL) + alsa_element_load (ALSA_ELEMENT (item->data)); - element = g_hash_table_lookup (stream->priv->switches, name); - if (element != NULL) - alsa_element_load (element); + item = g_list_find_custom (stream->priv->switches, name, compare_switch_name); + if (item != NULL) + alsa_element_load (ALSA_ELEMENT (item->data)); } gboolean alsa_stream_remove_elements (AlsaStream *stream, const gchar *name) { + GList *item; gboolean removed = FALSE; g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE); g_return_val_if_fail (name != NULL, FALSE); - if (g_hash_table_remove (stream->priv->controls, name) == TRUE) + item = g_list_find_custom (stream->priv->controls, name, compare_control_name); + if (item != NULL) { + MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (item->data); + + alsa_element_close (ALSA_ELEMENT (control)); + stream->priv->controls = g_list_delete_link (stream->priv->controls, item); + + /* Change the default control if we have just removed it */ + if (control == mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream))) { + AlsaStreamControl *first = NULL; + + if (stream->priv->controls != NULL) + first = ALSA_STREAM_CONTROL (stream->priv->controls->data); + + alsa_stream_set_default_control (stream, first); + } + + g_signal_emit_by_name (G_OBJECT (stream), + "control-removed", + mate_mixer_stream_control_get_name (control)); + + g_object_unref (control); removed = TRUE; - if (g_hash_table_remove (stream->priv->switches, name) == TRUE) + } + + item = g_list_find_custom (stream->priv->switches, name, compare_switch_name); + if (item != NULL) { + MateMixerSwitch *swtch = MATE_MIXER_SWITCH (item->data); + + alsa_element_close (ALSA_ELEMENT (swtch)); + + stream->priv->switches = g_list_delete_link (stream->priv->switches, item); + g_signal_emit_by_name (G_OBJECT (stream), + "switch-removed", + mate_mixer_switch_get_name (swtch)); + + g_object_unref (swtch); removed = TRUE; + } return removed; } -static MateMixerStreamControl * -alsa_stream_get_control (MateMixerStream *mms, const gchar *name) +void +alsa_stream_remove_all (AlsaStream *stream) { - g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL); + GList *list; - return g_hash_table_lookup (ALSA_STREAM (mms)->priv->controls, name); -} + g_return_if_fail (ALSA_IS_STREAM (stream)); -static MateMixerStreamControl * -alsa_stream_get_default_control (MateMixerStream *mms) -{ - g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL); + /* Remove all stream controls */ + list = stream->priv->controls; + while (list != NULL) { + MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (list->data); + GList *next = list->next; + + alsa_element_close (ALSA_ELEMENT (control)); + + stream->priv->controls = g_list_delete_link (stream->priv->controls, list); + g_signal_emit_by_name (G_OBJECT (stream), + "control-removed", + mate_mixer_stream_control_get_name (control)); + + g_object_unref (control); + list = next; + } + + /* Unset the default stream control */ + alsa_stream_set_default_control (stream, NULL); + + /* Remove all stream switches */ + list = stream->priv->switches; + while (list != NULL) { + MateMixerSwitch *swtch = MATE_MIXER_SWITCH (list->data); + GList *next = list->next; + + alsa_element_close (ALSA_ELEMENT (swtch)); - return ALSA_STREAM (mms)->priv->control; + stream->priv->switches = g_list_delete_link (stream->priv->switches, list); + g_signal_emit_by_name (G_OBJECT (stream), + "switch-removed", + mate_mixer_switch_get_name (swtch)); + + g_object_unref (swtch); + list = next; + } } -static MateMixerSwitch * -alsa_stream_get_switch (MateMixerStream *mms, const gchar *name) +static const GList * +alsa_stream_list_controls (MateMixerStream *mms) { g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL); - return g_hash_table_lookup (ALSA_STREAM (mms)->priv->switches, name); + return ALSA_STREAM (mms)->priv->controls; } -static GList * -alsa_stream_list_controls (MateMixerStream *mms) +static const GList * +alsa_stream_list_switches (MateMixerStream *mms) { - GList *list; - g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL); - /* Convert the hash table to a linked list, this list is expected to be - * cached in the main library */ - list = g_hash_table_get_values (ALSA_STREAM (mms)->priv->controls); - if (list != NULL) - g_list_foreach (list, (GFunc) g_object_ref, NULL); - - return list; + return ALSA_STREAM (mms)->priv->switches; } -static GList * -alsa_stream_list_switches (MateMixerStream *mms) +static gint +compare_control_name (gconstpointer a, gconstpointer b) { - GList *list; + MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (a); + const gchar *name = (const gchar *) b; - g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL); + return strcmp (mate_mixer_stream_control_get_name (control), name); +} - /* Convert the hash table to a linked list, this list is expected to be - * cached in the main library */ - list = g_hash_table_get_values (ALSA_STREAM (mms)->priv->switches); - if (list != NULL) - g_list_foreach (list, (GFunc) g_object_ref, NULL); +static gint +compare_switch_name (gconstpointer a, gconstpointer b) +{ + MateMixerSwitch *swtch = MATE_MIXER_SWITCH (a); + const gchar *name = (const gchar *) b; - return list; + return strcmp (mate_mixer_switch_get_name (swtch), name); } diff --git a/backends/alsa/alsa-stream.h b/backends/alsa/alsa-stream.h index f26a643..5aa3095 100644 --- a/backends/alsa/alsa-stream.h +++ b/backends/alsa/alsa-stream.h @@ -22,9 +22,9 @@ #include <glib-object.h> #include <libmatemixer/matemixer.h> -#include "alsa-element.h" #include "alsa-stream-control.h" #include "alsa-switch.h" +#include "alsa-toggle.h" G_BEGIN_DECLS @@ -58,30 +58,35 @@ struct _AlsaStreamClass MateMixerStreamClass parent_class; }; -GType alsa_stream_get_type (void) G_GNUC_CONST; +GType alsa_stream_get_type (void) G_GNUC_CONST; -AlsaStream *alsa_stream_new (const gchar *name, - MateMixerDevice *device, - MateMixerStreamFlags flags); +AlsaStream * alsa_stream_new (const gchar *name, + MateMixerDevice *device, + MateMixerDirection direction); -void alsa_stream_add_control (AlsaStream *stream, - AlsaStreamControl *control); +void alsa_stream_add_control (AlsaStream *stream, + AlsaStreamControl *control); +void alsa_stream_add_switch (AlsaStream *stream, + AlsaSwitch *swtch); +void alsa_stream_add_toggle (AlsaStream *stream, + AlsaToggle *toggle); -void alsa_stream_add_switch (AlsaStream *stream, - AlsaSwitch *swtch); +gboolean alsa_stream_has_controls (AlsaStream *stream); +gboolean alsa_stream_has_switches (AlsaStream *stream); +gboolean alsa_stream_has_controls_or_switches (AlsaStream *stream); +gboolean alsa_stream_has_default_control (AlsaStream *stream); -gboolean alsa_stream_is_empty (AlsaStream *stream); +AlsaStreamControl *alsa_stream_get_default_control (AlsaStream *stream); +void alsa_stream_set_default_control (AlsaStream *stream, + AlsaStreamControl *control); -void alsa_stream_set_default_control (AlsaStream *stream, - AlsaStreamControl *control); +void alsa_stream_load_elements (AlsaStream *stream, + const gchar *name); -void alsa_stream_load_elements (AlsaStream *stream, - const gchar *name); +gboolean alsa_stream_remove_elements (AlsaStream *stream, + const gchar *name); -gboolean alsa_stream_remove_elements (AlsaStream *stream, - const gchar *name); - -void alsa_stream_remove_all (AlsaStream *stream); +void alsa_stream_remove_all (AlsaStream *stream); G_END_DECLS diff --git a/backends/alsa/alsa-switch-option.c b/backends/alsa/alsa-switch-option.c index 2173113..1800df2 100644 --- a/backends/alsa/alsa-switch-option.c +++ b/backends/alsa/alsa-switch-option.c @@ -18,9 +18,7 @@ #include <glib.h> #include <glib-object.h> #include <alsa/asoundlib.h> - #include <libmatemixer/matemixer.h> -#include <libmatemixer/matemixer-private.h> #include "alsa-switch-option.h" @@ -59,6 +57,7 @@ alsa_switch_option_new (const gchar *name, option = g_object_new (ALSA_TYPE_SWITCH_OPTION, "name", name, "label", label, + "icon", icon, NULL); option->priv->id = id; diff --git a/backends/alsa/alsa-switch-option.h b/backends/alsa/alsa-switch-option.h index c2dda87..98d4f57 100644 --- a/backends/alsa/alsa-switch-option.h +++ b/backends/alsa/alsa-switch-option.h @@ -24,17 +24,17 @@ G_BEGIN_DECLS -#define ALSA_TYPE_SWITCH_OPTION \ +#define ALSA_TYPE_SWITCH_OPTION \ (alsa_switch_option_get_type ()) -#define ALSA_SWITCH_OPTION(o) \ +#define ALSA_SWITCH_OPTION(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOption)) -#define ALSA_IS_SWITCH_OPTION(o) \ +#define ALSA_IS_SWITCH_OPTION(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_SWITCH_OPTION)) -#define ALSA_SWITCH_OPTION_CLASS(k) \ +#define ALSA_SWITCH_OPTION_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOptionClass)) -#define ALSA_IS_SWITCH_OPTION_CLASS(k) \ +#define ALSA_IS_SWITCH_OPTION_CLASS(k) \ (G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_SWITCH_OPTION)) -#define ALSA_SWITCH_OPTION_GET_CLASS(o) \ +#define ALSA_SWITCH_OPTION_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOptionClass)) typedef struct _AlsaSwitchOption AlsaSwitchOption; diff --git a/backends/alsa/alsa-switch.c b/backends/alsa/alsa-switch.c index 15151ae..6a0f1f4 100644 --- a/backends/alsa/alsa-switch.c +++ b/backends/alsa/alsa-switch.c @@ -37,6 +37,7 @@ static void alsa_element_interface_init (AlsaElementInterface *iface); static void alsa_switch_class_init (AlsaSwitchClass *klass); static void alsa_switch_init (AlsaSwitch *swtch); +static void alsa_switch_dispose (GObject *object); G_DEFINE_TYPE_WITH_CODE (AlsaSwitch, alsa_switch, MATE_MIXER_TYPE_SWITCH, @@ -46,7 +47,7 @@ G_DEFINE_TYPE_WITH_CODE (AlsaSwitch, alsa_switch, static gboolean alsa_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso); -static GList * alsa_switch_list_options (MateMixerSwitch *mms); +static const GList * alsa_switch_list_options (MateMixerSwitch *mms); static snd_mixer_elem_t * alsa_switch_get_snd_element (AlsaElement *element); static void alsa_switch_set_snd_element (AlsaElement *element, @@ -64,8 +65,12 @@ alsa_element_interface_init (AlsaElementInterface *iface) static void alsa_switch_class_init (AlsaSwitchClass *klass) { + GObjectClass *object_class; MateMixerSwitchClass *switch_class; + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = alsa_switch_dispose; + switch_class = MATE_MIXER_SWITCH_CLASS (klass); switch_class->set_active_option = alsa_switch_set_active_option; switch_class->list_options = alsa_switch_list_options; @@ -74,6 +79,21 @@ alsa_switch_class_init (AlsaSwitchClass *klass) } static void +alsa_switch_dispose (GObject *object) +{ + AlsaSwitch *swtch; + + swtch = ALSA_SWITCH (object); + + if (swtch->priv->options != NULL) { + g_list_free_full (swtch->priv->options, g_object_unref); + swtch->priv->options = NULL; + } + + G_OBJECT_CLASS (alsa_switch_parent_class)->dispose (object); +} + +static void alsa_switch_init (AlsaSwitch *swtch) { swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch, @@ -82,13 +102,17 @@ alsa_switch_init (AlsaSwitch *swtch) } AlsaSwitch * -alsa_switch_new (const gchar *name, const gchar *label, GList *options) +alsa_switch_new (const gchar *name, + const gchar *label, + MateMixerSwitchRole role, + GList *options) { AlsaSwitch *swtch; swtch = g_object_new (ALSA_TYPE_SWITCH, "name", name, "label", label, + "role", role, NULL); /* Takes ownership of options */ @@ -109,6 +133,9 @@ alsa_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso swtch = ALSA_SWITCH (mms); + if G_UNLIKELY (swtch->priv->element == NULL) + return FALSE; + /* The channel mask is created when reading the active option the first * time, so a successful load must be done before changing the option */ if G_UNLIKELY (swtch->priv->channel_mask == 0) { @@ -136,12 +163,12 @@ alsa_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso return set_item; } -static GList * -alsa_switch_list_options (MateMixerSwitch *swtch) +static const GList * +alsa_switch_list_options (MateMixerSwitch *mms) { - g_return_val_if_fail (ALSA_IS_SWITCH (swtch), NULL); + g_return_val_if_fail (ALSA_IS_SWITCH (mms), NULL); - return ALSA_SWITCH (swtch)->priv->options; + return ALSA_SWITCH (mms)->priv->options; } static snd_mixer_elem_t * @@ -156,7 +183,6 @@ static void alsa_switch_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el) { g_return_if_fail (ALSA_IS_SWITCH (element)); - g_return_if_fail (el != NULL); ALSA_SWITCH (element)->priv->element = el; } @@ -170,8 +196,13 @@ alsa_switch_load (AlsaElement *element) gint ret; snd_mixer_selem_channel_id_t c; + g_return_val_if_fail (ALSA_IS_SWITCH (element), FALSE); + swtch = ALSA_SWITCH (element); + if G_UNLIKELY (swtch->priv->element == NULL) + return FALSE; + /* When reading the first time we try all the channels, otherwise only the * ones which returned success before */ if (swtch->priv->channel_mask == 0) { @@ -220,7 +251,7 @@ alsa_switch_load (AlsaElement *element) } g_warning ("Unknown active option of switch %s: %d", - snd_mixer_selem_get_name (swtch->priv->element), + mate_mixer_switch_get_name (MATE_MIXER_SWITCH (swtch)), item); return FALSE; diff --git a/backends/alsa/alsa-switch.h b/backends/alsa/alsa-switch.h index fdcfb87..b7f5931 100644 --- a/backends/alsa/alsa-switch.h +++ b/backends/alsa/alsa-switch.h @@ -56,9 +56,10 @@ struct _AlsaSwitchClass GType alsa_switch_get_type (void) G_GNUC_CONST; -AlsaSwitch *alsa_switch_new (const gchar *name, - const gchar *label, - GList *options); +AlsaSwitch *alsa_switch_new (const gchar *name, + const gchar *label, + MateMixerSwitchRole role, + GList *options); G_END_DECLS diff --git a/backends/alsa/alsa-toggle.c b/backends/alsa/alsa-toggle.c index efa3460..a7958c9 100644 --- a/backends/alsa/alsa-toggle.c +++ b/backends/alsa/alsa-toggle.c @@ -42,13 +42,13 @@ G_DEFINE_TYPE_WITH_CODE (AlsaToggle, alsa_toggle, MATE_MIXER_TYPE_TOGGLE, G_IMPLEMENT_INTERFACE (ALSA_TYPE_ELEMENT, alsa_element_interface_init)) -static gboolean alsa_toggle_set_active_option (MateMixerSwitch *mms, - MateMixerSwitchOption *mmso); +static gboolean alsa_toggle_set_active_option (MateMixerSwitch *mms, + MateMixerSwitchOption *mmso); -static snd_mixer_elem_t * alsa_toggle_get_snd_element (AlsaElement *element); -static void alsa_toggle_set_snd_element (AlsaElement *element, - snd_mixer_elem_t *el); -static gboolean alsa_toggle_load (AlsaElement *element); +static snd_mixer_elem_t *alsa_toggle_get_snd_element (AlsaElement *element); +static void alsa_toggle_set_snd_element (AlsaElement *element, + snd_mixer_elem_t *el); +static gboolean alsa_toggle_load (AlsaElement *element); static void alsa_element_interface_init (AlsaElementInterface *iface) @@ -78,17 +78,20 @@ alsa_toggle_init (AlsaToggle *toggle) } AlsaToggle * -alsa_toggle_new (const gchar *name, - const gchar *label, - AlsaToggleType type, - AlsaSwitchOption *on, - AlsaSwitchOption *off) +alsa_toggle_new (const gchar *name, + const gchar *label, + MateMixerSwitchRole role, + AlsaToggleType type, + AlsaSwitchOption *on, + AlsaSwitchOption *off) { AlsaToggle *toggle; toggle = g_object_new (ALSA_TYPE_TOGGLE, "name", name, "label", label, + "flags", MATE_MIXER_SWITCH_TOGGLE, + "role", role, "state-option-on", on, "state-option-off", off, NULL); @@ -109,7 +112,12 @@ alsa_toggle_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso toggle = ALSA_TOGGLE (mms); - /* For toggles the 0/1 value is stored as the switch option id */ + if G_UNLIKELY (toggle->priv->element == NULL) + return FALSE; + + /* For toggles the 0/1 value is stored as the switch option id, there is not really + * a need to validate that the option belong to the switch, just make sure it + * contains the value 0 or 1 */ value = alsa_switch_option_get_id (ALSA_SWITCH_OPTION (mmso)); if G_UNLIKELY (value != 0 && value != 1) { g_warn_if_reached (); @@ -143,7 +151,6 @@ static void alsa_toggle_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el) { g_return_if_fail (ALSA_IS_TOGGLE (element)); - g_return_if_fail (el != NULL); ALSA_TOGGLE (element)->priv->element = el; } @@ -158,6 +165,9 @@ alsa_toggle_load (AlsaElement *element) toggle = ALSA_TOGGLE (element); + if G_UNLIKELY (toggle->priv->element == NULL) + return FALSE; + /* When reading the first time we try all the channels, otherwise only the * ones which returned success before */ if (toggle->priv->channel_mask == 0) { @@ -207,7 +217,6 @@ alsa_toggle_load (AlsaElement *element) active = mate_mixer_toggle_get_state_option (MATE_MIXER_TOGGLE (toggle), FALSE); _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (toggle), active); - return TRUE; } diff --git a/backends/alsa/alsa-toggle.h b/backends/alsa/alsa-toggle.h index d9c083b..1e1993c 100644 --- a/backends/alsa/alsa-toggle.h +++ b/backends/alsa/alsa-toggle.h @@ -63,11 +63,12 @@ struct _AlsaToggleClass GType alsa_toggle_get_type (void) G_GNUC_CONST; -AlsaToggle *alsa_toggle_new (const gchar *name, - const gchar *label, - AlsaToggleType type, - AlsaSwitchOption *on, - AlsaSwitchOption *off); +AlsaToggle *alsa_toggle_new (const gchar *name, + const gchar *label, + MateMixerSwitchRole role, + AlsaToggleType type, + AlsaSwitchOption *on, + AlsaSwitchOption *off); G_END_DECLS diff --git a/backends/null/Makefile.am b/backends/null/Makefile.am index 8ce28d1..08005e4 100644 --- a/backends/null/Makefile.am +++ b/backends/null/Makefile.am @@ -3,6 +3,7 @@ backenddir = $(libdir)/libmatemixer backend_LTLIBRARIES = libmatemixer-null.la AM_CPPFLAGS = \ + -Wno-unknown-pragmas \ -I$(top_srcdir) \ -DG_LOG_DOMAIN=\"libmatemixer-null\" diff --git a/backends/null/null-backend.c b/backends/null/null-backend.c index 49038b1..ee0ad2e 100644 --- a/backends/null/null-backend.c +++ b/backends/null/null-backend.c @@ -29,7 +29,10 @@ static void null_backend_class_init (NullBackendClass *klass); static void null_backend_class_finalize (NullBackendClass *klass); static void null_backend_init (NullBackend *null); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" G_DEFINE_DYNAMIC_TYPE (NullBackend, null_backend, MATE_MIXER_TYPE_BACKEND) +#pragma clang diagnostic pop static gboolean null_backend_open (MateMixerBackend *backend); diff --git a/backends/oss/Makefile.am b/backends/oss/Makefile.am index 44caeb8..f535a37 100644 --- a/backends/oss/Makefile.am +++ b/backends/oss/Makefile.am @@ -3,6 +3,7 @@ backenddir = $(libdir)/libmatemixer backend_LTLIBRARIES = libmatemixer-oss.la AM_CPPFLAGS = \ + -Wno-unknown-pragmas \ -I$(top_srcdir) \ -DG_LOG_DOMAIN=\"libmatemixer-oss\" @@ -19,7 +20,12 @@ libmatemixer_oss_la_SOURCES = \ oss-stream.c \ oss-stream.h \ oss-stream-control.c \ - oss-stream-control.h + oss-stream-control.h \ + oss-switch.c \ + oss-switch.h \ + oss-switch-option.c \ + oss-switch-option.h \ + oss-types.h libmatemixer_oss_la_LIBADD = \ $(GLIB_LIBS) \ diff --git a/backends/oss/oss-backend.c b/backends/oss/oss-backend.c index 2b5eca7..23d265b 100644 --- a/backends/oss/oss-backend.c +++ b/backends/oss/oss-backend.c @@ -32,7 +32,7 @@ #include "oss-stream.h" #define BACKEND_NAME "OSS" -#define BACKEND_PRIORITY 9 +#define BACKEND_PRIORITY 10 #if !defined(__linux__) && !defined(__NetBSD__) && !defined(__OpenBSD__) /* At least on systems based on FreeBSD we will need to read device names @@ -47,7 +47,9 @@ struct _OssBackendPrivate { gchar *default_device; GSource *timeout_source; - GHashTable *devices; + GList *streams; + GList *devices; + GHashTable *devices_paths; }; static void oss_backend_class_init (OssBackendClass *klass); @@ -61,36 +63,49 @@ static void oss_backend_finalize (GObject *object); G_DEFINE_DYNAMIC_TYPE (OssBackend, oss_backend, MATE_MIXER_TYPE_BACKEND) #pragma clang diagnostic pop -static gboolean oss_backend_open (MateMixerBackend *backend); -static void oss_backend_close (MateMixerBackend *backend); -static GList * oss_backend_list_devices (MateMixerBackend *backend); -static GList * oss_backend_list_streams (MateMixerBackend *backend); +static gboolean oss_backend_open (MateMixerBackend *backend); +static void oss_backend_close (MateMixerBackend *backend); +static const GList *oss_backend_list_devices (MateMixerBackend *backend); +static const GList *oss_backend_list_streams (MateMixerBackend *backend); -static gboolean read_devices (OssBackend *oss); +static gboolean read_devices (OssBackend *oss); -static gboolean read_device (OssBackend *oss, - const gchar *path, - gboolean *added); +static gboolean read_device (OssBackend *oss, + const gchar *path, + gboolean *added); -static gchar * read_device_label (OssBackend *oss, - const gchar *path, - gint fd); +static gchar * read_device_label (OssBackend *oss, + const gchar *path, + gint fd); -static gchar * read_device_label_sndstat (OssBackend *oss, - const gchar *sndstat, - const gchar *path, - guint index) G_GNUC_UNUSED; +static gchar * read_device_label_sndstat (OssBackend *oss, + const gchar *sndstat, + const gchar *path, + guint index) G_GNUC_UNUSED; -static void add_device (OssBackend *oss, - OssDevice *device); -static void remove_device (OssBackend *oss, - OssDevice *device); +static void add_device (OssBackend *oss, + OssDevice *device); +static void remove_device (OssBackend *oss, + OssDevice *device); -static void remove_stream (OssBackend *oss, - const gchar *name); +static void remove_device_by_path (OssBackend *oss, + const gchar *path); -static void select_default_input_stream (OssBackend *oss); -static void select_default_output_stream (OssBackend *oss); +static void remove_device_by_list_item (OssBackend *oss, + GList *item); + +static void remove_stream (OssBackend *oss, + const gchar *name); + +static void select_default_input_stream (OssBackend *oss); +static void select_default_output_stream (OssBackend *oss); + +static void free_stream_list (OssBackend *oss); + +static gint compare_devices (gconstpointer a, + gconstpointer b); +static gint compare_device_path (gconstpointer a, + gconstpointer b); static MateMixerBackendInfo info; @@ -142,10 +157,10 @@ oss_backend_init (OssBackend *oss) OSS_TYPE_BACKEND, OssBackendPrivate); - oss->priv->devices = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); + oss->priv->devices_paths = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); } static void @@ -170,7 +185,7 @@ oss_backend_finalize (GObject *object) oss = OSS_BACKEND (object); - g_hash_table_destroy (oss->priv->devices); + g_hash_table_unref (oss->priv->devices_paths); G_OBJECT_CLASS (oss_backend_parent_class)->finalize (object); } @@ -212,63 +227,65 @@ oss_backend_close (MateMixerBackend *backend) oss = OSS_BACKEND (backend); g_source_destroy (oss->priv->timeout_source); - g_hash_table_remove_all (oss->priv->devices); - g_free (oss->priv->default_device); - oss->priv->default_device = NULL; + if (oss->priv->devices != NULL) { + g_list_free_full (oss->priv->devices, g_object_unref); + oss->priv->devices = NULL; + } + if (oss->priv->default_device != NULL) { + g_free (oss->priv->default_device); + oss->priv->default_device = NULL; + } + + free_stream_list (oss); + + g_hash_table_remove_all (oss->priv->devices_paths); _mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_IDLE); } -static GList * +static const GList * oss_backend_list_devices (MateMixerBackend *backend) { - GList *list; - g_return_val_if_fail (OSS_IS_BACKEND (backend), NULL); - /* Convert the hash table to a linked list, this list is expected to be - * cached in the main library */ - list = g_hash_table_get_values (OSS_BACKEND (backend)->priv->devices); - if (list != NULL) - g_list_foreach (list, (GFunc) g_object_ref, NULL); - - return list; + return OSS_BACKEND (backend)->priv->devices; } -static GList * +static const GList * oss_backend_list_streams (MateMixerBackend *backend) { - OssBackend *oss; - GHashTableIter iter; - gpointer value; - GList *list = NULL; + OssBackend *oss; g_return_val_if_fail (OSS_IS_BACKEND (backend), NULL); oss = OSS_BACKEND (backend); - /* We don't keep a list or hash table of all streams here, instead walk - * through the list of devices and create the list manually, each device - * has at most one input and one output stream */ - g_hash_table_iter_init (&iter, oss->priv->devices); + if (oss->priv->streams == NULL) { + GList *list; - while (g_hash_table_iter_next (&iter, NULL, &value)) { - OssDevice *device = OSS_DEVICE (value); - OssStream *stream; + /* Walk through the list of devices and create the stream list, each + * device has at most one input and one output stream */ + list = oss->priv->devices; - stream = oss_device_get_output_stream (device); - if (stream != NULL) - list = g_list_prepend (list, stream); - stream = oss_device_get_input_stream (device); - if (stream != NULL) - list = g_list_prepend (list, stream); - } + while (list != NULL) { + OssDevice *device = OSS_DEVICE (list->data); + OssStream *stream; - if (list != NULL) - g_list_foreach (list, (GFunc) g_object_ref, NULL); - - return list; + stream = oss_device_get_input_stream (device); + if (stream != NULL) { + oss->priv->streams = + g_list_append (oss->priv->streams, g_object_ref (stream)); + } + stream = oss_device_get_output_stream (device); + if (stream != NULL) { + oss->priv->streams = + g_list_append (oss->priv->streams, g_object_ref (stream)); + } + list = list->next; + } + } + return oss->priv->streams; } static gboolean @@ -278,8 +295,10 @@ read_devices (OssBackend *oss) gboolean added = FALSE; for (i = 0; i < OSS_MAX_DEVICES; i++) { - gboolean added_current; - gchar *path = g_strdup_printf ("/dev/mixer%i", i); + gchar *path; + gboolean added_current = FALSE; + + path = g_strdup_printf ("/dev/mixer%i", i); /* On recent FreeBSD both /dev/mixer and /dev/mixer0 point to the same * mixer device, on NetBSD and OpenBSD /dev/mixer is a symlink to one @@ -289,7 +308,7 @@ read_devices (OssBackend *oss) if (read_device (oss, path, &added_current) == FALSE && i == 0) read_device (oss, "/dev/mixer", &added_current); - if (added_current) + if (added_current == TRUE) added = TRUE; g_free (path); @@ -312,16 +331,12 @@ read_device (OssBackend *oss, const gchar *path, gboolean *added) gchar *bname; gchar *label; - device = g_hash_table_lookup (oss->priv->devices, path); - *added = FALSE; - fd = g_open (path, O_RDWR, 0); if (fd == -1) { if (errno != ENOENT && errno != ENXIO) g_debug ("%s: %s", path, g_strerror (errno)); - if (device != NULL) - remove_device (oss, device); + remove_device_by_path (oss, path); return FALSE; } @@ -329,7 +344,7 @@ read_device (OssBackend *oss, const gchar *path, gboolean *added) * still tested to be absolutely sure that the device is removed it case * it has disappeared, but normally the device's polling facility should * handle this by itself */ - if (device != NULL) { + if (g_hash_table_contains (oss->priv->devices_paths, path) == TRUE) { close (fd); return TRUE; } @@ -345,8 +360,9 @@ read_device (OssBackend *oss, const gchar *path, gboolean *added) if ((*added = oss_device_open (device)) == TRUE) add_device (oss, device); + else + g_object_unref (device); - g_object_unref (device); return *added; } @@ -445,44 +461,104 @@ read_device_label_sndstat (OssBackend *oss, static void add_device (OssBackend *oss, OssDevice *device) { - const gchar *name; + oss->priv->devices = + g_list_insert_sorted_with_data (oss->priv->devices, + device, + (GCompareDataFunc) compare_devices, + NULL); - name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); + /* Keep track of added device paths */ + g_hash_table_add (oss->priv->devices_paths, + g_strdup (oss_device_get_path (device))); - g_hash_table_insert (oss->priv->devices, - g_strdup (oss_device_get_path (device)), - g_object_ref (device)); - - // XXX make device emit it when closed + g_signal_connect_swapped (G_OBJECT (device), + "closed", + G_CALLBACK (remove_device), + oss); g_signal_connect_swapped (G_OBJECT (device), "stream-removed", G_CALLBACK (remove_stream), oss); - g_signal_emit_by_name (G_OBJECT (oss), "device-added", name); + g_signal_connect_swapped (G_OBJECT (device), + "closed", + G_CALLBACK (free_stream_list), + oss); + g_signal_connect_swapped (G_OBJECT (device), + "stream-added", + G_CALLBACK (free_stream_list), + oss); + g_signal_connect_swapped (G_OBJECT (device), + "stream-removed", + G_CALLBACK (free_stream_list), + oss); + g_signal_emit_by_name (G_OBJECT (oss), + "device-added", + mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); + + /* Load the device elements after emitting device-added, because the load + * function will most likely emit stream-added on the device and backend */ oss_device_load (device); } static void remove_device (OssBackend *oss, OssDevice *device) { - const gchar *name; + GList *item; + + item = g_list_find (oss->priv->devices, device); + if (item != NULL) + remove_device_by_list_item (oss, item); +} + +static void +remove_device_by_path (OssBackend *oss, const gchar *path) +{ + GList *item; + + item = g_list_find_custom (oss->priv->devices, path, compare_device_path); + if (item != NULL) + remove_device_by_list_item (oss, item); +} + +static void +remove_device_by_list_item (OssBackend *oss, GList *item) +{ + OssDevice *device; const gchar *path; - path = oss_device_get_path (device); - name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); + device = OSS_DEVICE (item->data); + + g_signal_handlers_disconnect_by_func (G_OBJECT (device), + G_CALLBACK (remove_device), + oss); + + if (oss_device_is_open (device) == TRUE) + oss_device_close (device); g_signal_handlers_disconnect_by_func (G_OBJECT (device), G_CALLBACK (remove_stream), oss); - // XXX close the device and make it remove streams - g_hash_table_remove (oss->priv->devices, path); + oss->priv->devices = g_list_delete_link (oss->priv->devices, item); + + path = oss_device_get_path (device); + g_hash_table_remove (oss->priv->devices_paths, path); + + if (g_strcmp0 (oss->priv->default_device, path) == 0) { + g_free (oss->priv->default_device); + oss->priv->default_device = NULL; + } + + /* The list may and may not have been invalidate by device signals */ + free_stream_list (oss); g_signal_emit_by_name (G_OBJECT (oss), "device-removed", - name); + mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); + + g_object_unref (device); } static void @@ -492,7 +568,6 @@ remove_stream (OssBackend *oss, const gchar *name) stream = mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (oss)); - // XXX see if the change happens after stream is removed or before if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0) select_default_input_stream (oss); @@ -502,16 +577,31 @@ remove_stream (OssBackend *oss, const gchar *name) select_default_output_stream (oss); } +static OssDevice * +get_default_device (OssBackend *oss) +{ + GList *item; + + if (oss->priv->default_device == NULL) + return NULL; + + item = g_list_find_custom (oss->priv->devices, + oss->priv->default_device, + compare_device_path); + if G_LIKELY (item != NULL) + return OSS_DEVICE (item->data); + + return NULL; +} + static void select_default_input_stream (OssBackend *oss) { - OssDevice *device = NULL; + OssDevice *device; OssStream *stream; - gint i; + GList *list; - /* Always prefer stream in the "default" device */ - if (oss->priv->default_device != NULL) - device = g_hash_table_lookup (oss->priv->devices, oss->priv->default_device); + device = get_default_device (oss); if (device != NULL) { stream = oss_device_get_input_stream (device); if (stream != NULL) { @@ -521,23 +611,17 @@ select_default_input_stream (OssBackend *oss) } } - for (i = 0; i < OSS_MAX_DEVICES; i++) { - gchar *path = g_strdup_printf ("/dev/mixer%i", i); - - device = g_hash_table_lookup (oss->priv->devices, path); - if (device == NULL && i == 0) - device = g_hash_table_lookup (oss->priv->devices, "/dev/mixer"); + list = oss->priv->devices; + while (list != NULL) { + device = OSS_DEVICE (list->data); + stream = oss_device_get_input_stream (device); - if (device != NULL) { - stream = oss_device_get_input_stream (device); - if (stream != NULL) { - _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss), - MATE_MIXER_STREAM (stream)); - g_free (path); - return; - } + if (stream != NULL) { + _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss), + MATE_MIXER_STREAM (stream)); + return; } - g_free (path); + list = list->next; } /* In the worst case unset the default stream */ @@ -547,13 +631,11 @@ select_default_input_stream (OssBackend *oss) static void select_default_output_stream (OssBackend *oss) { - OssDevice *device = NULL; + OssDevice *device; OssStream *stream; - gint i; + GList *list; - /* Always prefer stream in the "default" device */ - if (oss->priv->default_device != NULL) - device = g_hash_table_lookup (oss->priv->devices, oss->priv->default_device); + device = get_default_device (oss); if (device != NULL) { stream = oss_device_get_output_stream (device); if (stream != NULL) { @@ -563,25 +645,48 @@ select_default_output_stream (OssBackend *oss) } } - for (i = 0; i < OSS_MAX_DEVICES; i++) { - gchar *path = g_strdup_printf ("/dev/mixer%i", i); - - device = g_hash_table_lookup (oss->priv->devices, path); - if (device == NULL && i == 0) - device = g_hash_table_lookup (oss->priv->devices, "/dev/mixer"); + list = oss->priv->devices; + while (list != NULL) { + device = OSS_DEVICE (list->data); + stream = oss_device_get_output_stream (device); - if (device != NULL) { - stream = oss_device_get_output_stream (device); - if (stream != NULL) { - _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), - MATE_MIXER_STREAM (stream)); - g_free (path); - return; - } + if (stream != NULL) { + _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), + MATE_MIXER_STREAM (stream)); + return; } - g_free (path); + list = list->next; } /* In the worst case unset the default stream */ _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), NULL); } + +static void +free_stream_list (OssBackend *oss) +{ + if (oss->priv->streams == NULL) + return; + + g_list_free_full (oss->priv->streams, g_object_unref); + + oss->priv->streams = NULL; +} + +static gint +compare_devices (gconstpointer a, gconstpointer b) +{ + MateMixerDevice *d1 = MATE_MIXER_DEVICE (a); + MateMixerDevice *d2 = MATE_MIXER_DEVICE (b); + + return strcmp (mate_mixer_device_get_name (d1), mate_mixer_device_get_name (d2)); +} + +static gint +compare_device_path (gconstpointer a, gconstpointer b) +{ + OssDevice *device = OSS_DEVICE (a); + const gchar *path = (const gchar *) b; + + return strcmp (oss_device_get_path (device), path); +} diff --git a/backends/oss/oss-backend.h b/backends/oss/oss-backend.h index 325b61c..9617e79 100644 --- a/backends/oss/oss-backend.h +++ b/backends/oss/oss-backend.h @@ -20,9 +20,10 @@ #include <glib.h> #include <glib-object.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> -#include <libmatemixer/matemixer-backend.h> -#include <libmatemixer/matemixer-backend-module.h> +#include "oss-types.h" #define OSS_TYPE_BACKEND \ (oss_backend_get_type ()) @@ -37,7 +38,6 @@ #define OSS_BACKEND_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_BACKEND, OssBackendClass)) -typedef struct _OssBackend OssBackend; typedef struct _OssBackendClass OssBackendClass; typedef struct _OssBackendPrivate OssBackendPrivate; @@ -54,7 +54,7 @@ struct _OssBackendClass MateMixerBackendClass parent_class; }; -GType oss_backend_get_type (void) G_GNUC_CONST; +GType oss_backend_get_type (void) G_GNUC_CONST; /* Support function for dynamic loading of the backend module */ void backend_module_init (GTypeModule *module); diff --git a/backends/oss/oss-device.c b/backends/oss/oss-device.c index cf51705..44ed18f 100644 --- a/backends/oss/oss-device.c +++ b/backends/oss/oss-device.c @@ -16,7 +16,6 @@ */ #include <errno.h> -#include <unistd.h> #include <glib.h> #include <glib/gi18n.h> #include <glib/gstdio.h> @@ -29,87 +28,184 @@ #include "oss-device.h" #include "oss-stream.h" #include "oss-stream-control.h" +#include "oss-switch-option.h" + +/* + * NOTES: + * + * OSS has a predefined list of channels (or "devices"), which historically used + * to be mapped to individual sound card pins. At this time, the channels are + * chosen somehow arbitrarily by drivers. + * + * Each of the channels may have a record switch, which toggles between playback + * and capture direction. OSS doesn't have mute switches and we can't really use + * the record switch as one. For this reason all channels are modelled as + * muteless stream controls and the record switch is modelled as a port switch. + * + * Also, we avoid modelling capturable channels as both input and output channels, + * because the ones which allow capture are normally capture-only channels + * (OSS just doesn't have the ability to make the distinction), so each channel in + * the list contains a flag of whether it can be used as a capture source, given + * that it's reported as capturable. Capturable channels are therefore modelled + * as input controls and this approach avoids for example putting PCM in an input + * stream (which makes no sense). + * + * OSS also has an indicator of whether the record switch is exclusive (only + * allows one capture source at a time), to simplify the lives of applications + * we always create a port switch and therefore assume the exclusivity is always + * true. Ideally, we should probably model a bunch of toggles, one for each channel + * with capture capability if they are known not to be exclusive. + */ #define OSS_DEVICE_ICON "audio-card" -typedef enum -{ +#define OSS_POLL_TIMEOUT_NORMAL 500 +#define OSS_POLL_TIMEOUT_RAPID 50 +#define OSS_POLL_TIMEOUT_RESTORE 3000 + +typedef enum { + OSS_POLL_NORMAL, + OSS_POLL_RAPID +} OssPollMode; + +typedef enum { OSS_DEV_ANY, OSS_DEV_INPUT, OSS_DEV_OUTPUT -} OssDevType; +} OssDevChannelType; -typedef struct -{ +typedef struct { gchar *name; gchar *label; MateMixerStreamControlRole role; - OssDevType type; -} OssDev; - -static const OssDev oss_devices[] = { - { "vol", N_("Volume"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, OSS_DEV_OUTPUT }, - { "bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, OSS_DEV_OUTPUT }, - { "treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, OSS_DEV_OUTPUT }, - { "synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT }, - { "pcm", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, OSS_DEV_OUTPUT }, + OssDevChannelType type; + gchar *icon; +} OssDevChannel; + +/* Index of a channel in the array corresponds to the channel number passed to ioctl()s, + * device names are takes from soundcard.h */ +static const OssDevChannel oss_devices[] = { + { "vol", N_("Volume"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, OSS_DEV_OUTPUT, NULL }, + { "bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, OSS_DEV_OUTPUT, NULL }, + { "treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, OSS_DEV_OUTPUT, NULL }, + { "synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT, NULL }, + { "pcm", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, OSS_DEV_OUTPUT, NULL }, /* OSS manual says this should be the beeper, but Linux OSS seems to assign it to * regular volume control */ - { "speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, OSS_DEV_OUTPUT }, - { "line", N_("Line-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, - { "mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, - { "cd", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, OSS_DEV_INPUT }, + { "speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, OSS_DEV_OUTPUT, NULL }, + { "line", N_("Line In"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT, NULL }, + { "mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, OSS_DEV_INPUT, "audio-input-microphone" }, + { "cd", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, OSS_DEV_INPUT, NULL }, /* Recording monitor */ - { "mix", N_("Mixer"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, - { "pcm2", N_("PCM-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, OSS_DEV_OUTPUT }, + { "mix", N_("Mixer"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT, NULL }, + { "pcm2", N_("PCM 2"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, OSS_DEV_OUTPUT, NULL }, /* Recording level (master input) */ - { "rec", N_("Record"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, OSS_DEV_INPUT }, - { "igain", N_("In-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT }, - { "ogain", N_("Out-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, - { "line1", N_("Line-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, - { "line2", N_("Line-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, - { "line3", N_("Line-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, - { "dig1", N_("Digital-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY }, - { "dig2", N_("Digital-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY }, - { "dig3", N_("Digital-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY }, - { "phin", N_("Phone-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, - { "phout", N_("Phone-out"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_OUTPUT }, - { "video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, - { "radio", N_("Radio"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT }, - { "monitor", N_("Monitor"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, - { "depth", N_("3D-depth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, - { "center", N_("3D-center"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT }, - { "midi", N_("MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT } + { "rec", N_("Record"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, OSS_DEV_INPUT, NULL }, + { "igain", N_("Input Gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT, NULL }, + { "ogain", N_("Output Gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT, NULL }, + { "line1", N_("Line In 1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT, NULL }, + { "line2", N_("Line In 2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT, NULL }, + { "line3", N_("Line In 3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT, NULL }, + /* These 3 can be attached to either digital input or output */ + { "dig1", N_("Digital 1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY, NULL }, + { "dig2", N_("Digital 2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY, NULL }, + { "dig3", N_("Digital 3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY, NULL }, + { "phin", N_("Phone In"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT, NULL }, + { "phout", N_("Phone Out"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_OUTPUT, NULL }, + { "video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_VIDEO, OSS_DEV_INPUT, NULL }, + { "radio", N_("Radio"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT, NULL }, + + /* soundcard.h on some systems include more channels, but different files provide + * different meanings for the remaining ones and the value is doubtful */ }; #define OSS_N_DEVICES MIN (G_N_ELEMENTS (oss_devices), SOUND_MIXER_NRDEVICES) +/* Priorities for selecting default controls */ +static const gint oss_input_priority[] = { + 11, /* rec */ + 12, /* igain */ + 7, /* mic */ + 6, /* line */ + 14, /* line1 */ + 15, /* line2 */ + 16, /* line3 */ + 17, /* dig1 */ + 18, /* dig2 */ + 19, /* dig3 */ + 20, /* phin */ + 8, /* cd */ + 22, /* video */ + 23, /* radio */ + 3, /* synth */ + 27 /* midi */ +}; + +static const gint oss_output_priority[] = { + 0, /* vol */ + 4, /* pcm */ + 10, /* pcm2 */ + 5, /* speaker */ + 17, /* dig1 */ + 18, /* dig2 */ + 19, /* dig3 */ + 25, /* depth */ + 26, /* center */ + 21, /* phone out */ + 13, /* ogain */ + 9, /* mix */ + 24, /* monitor */ + 1, /* bass */ + 2 /* treble */ +}; + struct _OssDevicePrivate { - gint fd; - gchar *path; - gint devmask; - gint stereodevs; - gint recmask; - gint recsrc; - OssStream *input; - OssStream *output; + gint fd; + gchar *path; + gint devmask; + gint stereodevs; + gint recmask; + guint poll_tag; + guint poll_tag_restore; + guint poll_counter; + gboolean poll_use_counter; + OssPollMode poll_mode; + GList *streams; + OssStream *input; + OssStream *output; +}; + +enum { + CLOSED, + N_SIGNALS }; -static void oss_device_class_init (OssDeviceClass *klass); -static void oss_device_init (OssDevice *device); -static void oss_device_dispose (GObject *object); -static void oss_device_finalize (GObject *object); +static guint signals[N_SIGNALS] = { 0, }; + +static void oss_device_class_init (OssDeviceClass *klass); +static void oss_device_init (OssDevice *device); +static void oss_device_dispose (GObject *object); +static void oss_device_finalize (GObject *object); G_DEFINE_TYPE (OssDevice, oss_device, MATE_MIXER_TYPE_DEVICE) -static GList * oss_device_list_streams (MateMixerDevice *device); +static const GList *oss_device_list_streams (MateMixerDevice *mmd); + +static gboolean poll_mixer (OssDevice *device); +static gboolean poll_mixer_restore (OssDevice *device); + +static void read_mixer_devices (OssDevice *device); +static void read_mixer_switch (OssDevice *device); -static gboolean read_mixer_devices (OssDevice *device); +static guint create_poll_source (OssDevice *device, + OssPollMode mode); +static guint create_poll_restore_source (OssDevice *device); -static gboolean set_stream_default_control (OssStream *stream, - OssStreamControl *control, - gboolean force); +static void free_stream_list (OssDevice *device); + +static gint compare_stream_control_devnum (gconstpointer a, + gconstpointer b); static void oss_device_class_init (OssDeviceClass *klass) @@ -124,6 +220,18 @@ oss_device_class_init (OssDeviceClass *klass) device_class = MATE_MIXER_DEVICE_CLASS (klass); device_class->list_streams = oss_device_list_streams; + signals[CLOSED] = + g_signal_new ("closed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (OssDeviceClass, closed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0, + G_TYPE_NONE); + g_type_class_add_private (object_class, sizeof (OssDevicePrivate)); } @@ -145,6 +253,8 @@ oss_device_dispose (GObject *object) g_clear_object (&device->priv->input); g_clear_object (&device->priv->output); + free_stream_list (device); + G_OBJECT_CLASS (oss_device_parent_class)->dispose (object); } @@ -153,13 +263,19 @@ oss_device_finalize (GObject *object) { OssDevice *device = OSS_DEVICE (object); - close (device->priv->fd); + if (device->priv->fd != -1) + close (device->priv->fd); + + g_free (device->priv->path); G_OBJECT_CLASS (oss_device_parent_class)->finalize (object); } OssDevice * -oss_device_new (const gchar *name, const gchar *label, const gchar *path, gint fd) +oss_device_new (const gchar *name, + const gchar *label, + const gchar *path, + gint fd) { OssDevice *device; gchar *stream_name; @@ -180,13 +296,13 @@ oss_device_new (const gchar *name, const gchar *label, const gchar *path, gint f stream_name = g_strdup_printf ("oss-input-%s", name); device->priv->input = oss_stream_new (stream_name, MATE_MIXER_DEVICE (device), - MATE_MIXER_STREAM_INPUT); + MATE_MIXER_DIRECTION_INPUT); g_free (stream_name); stream_name = g_strdup_printf ("oss-output-%s", name); device->priv->output = oss_stream_new (stream_name, MATE_MIXER_DEVICE (device), - MATE_MIXER_STREAM_OUTPUT); + MATE_MIXER_DIRECTION_OUTPUT); g_free (stream_name); return device; @@ -205,30 +321,19 @@ oss_device_open (OssDevice *device) /* Read the essential information about the device, these values are not * expected to change and will not be queried */ - ret = ioctl (device->priv->fd, - MIXER_READ (SOUND_MIXER_DEVMASK), + ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_DEVMASK), &device->priv->devmask); - if (ret != 0) + if (ret == -1) goto fail; - ret = ioctl (device->priv->fd, - MIXER_READ (SOUND_MIXER_STEREODEVS), + ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_STEREODEVS), &device->priv->stereodevs); - if (ret < 0) + if (ret == -1) goto fail; - ret = ioctl (device->priv->fd, - MIXER_READ (SOUND_MIXER_RECMASK), + ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_RECMASK), &device->priv->recmask); - if (ret < 0) - goto fail; - - /* The recording source mask may change at any time, here we just read - * the initial value */ - ret = ioctl (device->priv->fd, - MIXER_READ (SOUND_MIXER_RECSRC), - &device->priv->recsrc); - if (ret < 0) + if (ret == -1) goto fail; /* NOTE: Linux also supports SOUND_MIXER_OUTSRC and SOUND_MIXER_OUTMASK which @@ -246,41 +351,135 @@ fail: } gboolean -oss_device_load (OssDevice *device) +oss_device_is_open (OssDevice *device) { - MateMixerStreamControl *control; - g_return_val_if_fail (OSS_IS_DEVICE (device), FALSE); - read_mixer_devices (device); + if (device->priv->fd != -1) + return TRUE; + + return FALSE; +} + +void +oss_device_close (OssDevice *device) +{ + g_return_if_fail (OSS_IS_DEVICE (device)); - control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (device->priv->input)); - if (control == NULL) { - // XXX pick something + if (device->priv->fd == -1) + return; + + /* Make each stream remove its controls and switch */ + if (oss_stream_has_controls (device->priv->input) == TRUE) { + const gchar *name = + mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->input)); + + oss_stream_remove_all (device->priv->input); + free_stream_list (device); + + g_signal_emit_by_name (G_OBJECT (device), + "stream-removed", + name); } - if (control != NULL) - g_debug ("Default input stream control is %s", - mate_mixer_stream_control_get_label (control)); + if (oss_stream_has_controls (device->priv->output) == TRUE) { + const gchar *name = + mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->output)); + + oss_stream_remove_all (device->priv->output); + free_stream_list (device); - control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (device->priv->output)); - if (control == NULL) { - // XXX pick something + g_signal_emit_by_name (G_OBJECT (device), + "stream-removed", + name); } - if (control != NULL) - g_debug ("Default output stream control is %s", - mate_mixer_stream_control_get_label (control)); + if (device->priv->poll_tag != 0) + g_source_remove (device->priv->poll_tag); - return TRUE; + if (device->priv->poll_tag_restore != 0) + g_source_remove (device->priv->poll_tag_restore); + + close (device->priv->fd); + device->priv->fd = -1; + + g_signal_emit (G_OBJECT (device), signals[CLOSED], 0); } -gint -oss_device_get_fd (OssDevice *device) +void +oss_device_load (OssDevice *device) { - g_return_val_if_fail (OSS_IS_DEVICE (device), -1); + const GList *controls; + guint i; + + g_return_if_fail (OSS_IS_DEVICE (device)); + + read_mixer_devices (device); - return device->priv->fd; + /* Set default input control */ + if (oss_stream_has_controls (device->priv->input) == TRUE) { + controls = mate_mixer_stream_list_controls (MATE_MIXER_STREAM (device->priv->input)); + + for (i = 0; i < G_N_ELEMENTS (oss_input_priority); i++) { + GList *item = g_list_find_custom ((GList *) controls, + GINT_TO_POINTER (oss_input_priority[i]), + compare_stream_control_devnum); + if (item == NULL) + continue; + + oss_stream_set_default_control (device->priv->input, + OSS_STREAM_CONTROL (item->data)); + break; + } + } + + /* Set default output control */ + if (oss_stream_has_controls (device->priv->output) == TRUE) { + controls = mate_mixer_stream_list_controls (MATE_MIXER_STREAM (device->priv->output)); + + for (i = 0; i < G_N_ELEMENTS (oss_output_priority); i++) { + GList *item = g_list_find_custom ((GList *) controls, + GINT_TO_POINTER (oss_output_priority[i]), + compare_stream_control_devnum); + if (item == NULL) + continue; + + oss_stream_set_default_control (device->priv->output, + OSS_STREAM_CONTROL (item->data)); + break; + } + } + + if (device->priv->recmask != 0) + read_mixer_switch (device); + + /* See if we can use the modify_counter field to optimize polling */ +#ifdef SOUND_MIXER_INFO + do { + struct mixer_info info; + gint ret; + + ret = ioctl (device->priv->fd, SOUND_MIXER_INFO, &info); + if (ret == 0) { + device->priv->poll_counter = info.modify_counter; + device->priv->poll_use_counter = TRUE; + } + } while (0); +#endif + + /* + * Use a polling strategy inspired by KMix: + * + * Poll for changes with the OSS_POLL_TIMEOUT_NORMAL interval, when we + * encounter a change in modify_counter, decrease the interval to + * OSS_POLL_TIMEOUT_RAPID for a few seconds to allow for smoother + * adjustments, for example when user drags a slider. + * + * This way is not used on systems which don't support the modify_counter + * field, because there is no way to find out whether anything has + * changed and therefore when to start the rapid polling. + */ + device->priv->poll_tag = create_poll_source (device, OSS_POLL_NORMAL); } const gchar * @@ -307,98 +506,254 @@ oss_device_get_output_stream (OssDevice *device) return device->priv->output; } -static GList * +static const GList * oss_device_list_streams (MateMixerDevice *mmd) { OssDevice *device; - GList *list = NULL; g_return_val_if_fail (OSS_IS_DEVICE (mmd), NULL); device = OSS_DEVICE (mmd); - if (device->priv->output != NULL) - list = g_list_prepend (list, g_object_ref (device->priv->output)); - if (device->priv->input != NULL) - list = g_list_prepend (list, g_object_ref (device->priv->input)); - - return list; + if (device->priv->streams == NULL) { + if (device->priv->output != NULL) + device->priv->streams = g_list_prepend (device->priv->streams, + g_object_ref (device->priv->output)); + if (device->priv->input != NULL) + device->priv->streams = g_list_prepend (device->priv->streams, + g_object_ref (device->priv->input)); + } + return device->priv->streams; } #define OSS_MASK_HAS_DEVICE(mask,i) ((gboolean) (((mask) & (1 << (i))) > 0)) -static gboolean +static void read_mixer_devices (OssDevice *device) { - gint i; + OssStreamControl *control; + const gchar *name; + guint i; + + name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); for (i = 0; i < OSS_N_DEVICES; i++) { - OssStreamControl *control; - gboolean input = FALSE; + OssStream *stream; + gboolean stereo; /* Skip unavailable controls */ if (OSS_MASK_HAS_DEVICE (device->priv->devmask, i) == FALSE) continue; - if (oss_devices[i].type == OSS_DEV_ANY) { - input = OSS_MASK_HAS_DEVICE (device->priv->recmask, i); - } - else if (oss_devices[i].type == OSS_DEV_INPUT) { - input = TRUE; - } + if (OSS_MASK_HAS_DEVICE (device->priv->recmask, i) == TRUE) + stream = device->priv->input; + else + stream = device->priv->output; - control = oss_stream_control_new (oss_devices[i].name, - oss_devices[i].label, + stereo = OSS_MASK_HAS_DEVICE (device->priv->stereodevs, i); + control = oss_stream_control_new (oss_devices[i].name, gettext (oss_devices[i].label), oss_devices[i].role, + stream, device->priv->fd, i, - OSS_MASK_HAS_DEVICE (device->priv->stereodevs, i)); - - if (input == TRUE) { - oss_stream_add_control (OSS_STREAM (device->priv->input), control); - - if (i == SOUND_MIXER_RECLEV || i == SOUND_MIXER_IGAIN) { - if (i == SOUND_MIXER_RECLEV) - set_stream_default_control (OSS_STREAM (device->priv->input), - control, - TRUE); - else - set_stream_default_control (OSS_STREAM (device->priv->input), - control, - FALSE); - } + stereo); + + if (oss_stream_has_controls (stream) == FALSE) { + const gchar *name = + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); + + free_stream_list (device); + + /* Pretend the stream has just been created now that we are adding + * the first control */ + g_signal_emit_by_name (G_OBJECT (device), + "stream-added", + name); + } + + g_debug ("Adding device %s control %s", + name, + mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control))); + + oss_stream_add_control (stream, control); + oss_stream_control_load (control); + + g_object_unref (control); + } +} + +static void +read_mixer_switch (OssDevice *device) +{ + GList *options = NULL; + guint i; + + for (i = 0; i < OSS_N_DEVICES; i++) { + OssSwitchOption *option; + + if (OSS_MASK_HAS_DEVICE (device->priv->recmask, i) == FALSE) + continue; + + option = oss_switch_option_new (oss_devices[i].name, + gettext (oss_devices[i].label), + oss_devices[i].icon, + i); + options = g_list_prepend (options, option); + } + + if G_LIKELY (options != NULL) + oss_stream_set_switch_data (device->priv->input, + device->priv->fd, + options); +} + +static gboolean +poll_mixer (OssDevice *device) +{ + gboolean load = TRUE; + + if G_UNLIKELY (device->priv->fd == -1) + return G_SOURCE_REMOVE; + +#ifdef SOUND_MIXER_INFO + if (device->priv->poll_use_counter == TRUE) { + gint ret; + struct mixer_info info; + + /* + * The modify_counter field increases each time a change happens on + * the device. + * + * If this ioctl() works, we use the field to only poll the controls + * if a change actually occured and we can also adjust the poll interval. + * + * This works well at least on Linux, NetBSD and OpenBSD. This call is + * not supported on FreeBSD as of version 10. + * + * The call is also used to detect unplugged devices early, when not + * supported, the unplug is still caught in the backend. + */ + ret = ioctl (device->priv->fd, SOUND_MIXER_INFO, &info); + if (ret == -1) { + if (errno == EINTR) + return G_SOURCE_CONTINUE; + + oss_device_close (device); + return G_SOURCE_REMOVE; + } + + if (device->priv->poll_counter < info.modify_counter) { + device->priv->poll_counter = info.modify_counter; } else { - oss_stream_add_control (OSS_STREAM (device->priv->output), control); - - if (i == SOUND_MIXER_VOLUME || i == SOUND_MIXER_PCM) { - if (i == SOUND_MIXER_VOLUME) - set_stream_default_control (OSS_STREAM (device->priv->output), - control, - TRUE); - else - set_stream_default_control (OSS_STREAM (device->priv->output), - control, - FALSE); - } + load = FALSE; } + } +#endif + + if (load == TRUE) { + oss_stream_load (device->priv->input); + oss_stream_load (device->priv->output); + + if (device->priv->poll_use_counter == TRUE && + device->priv->poll_mode == OSS_POLL_NORMAL) { + /* Create a new rapid source */ + device->priv->poll_tag = create_poll_source (device, OSS_POLL_RAPID); - g_debug ("Added control %s", - mate_mixer_stream_control_get_label (MATE_MIXER_STREAM_CONTROL (control))); + /* Also create another source to restore the poll interval to the + * original state */ + device->priv->poll_tag_restore = create_poll_restore_source (device); - oss_stream_control_update (control); + device->priv->poll_mode = OSS_POLL_RAPID; + return G_SOURCE_REMOVE; + } } - return TRUE; + return G_SOURCE_CONTINUE; } static gboolean -set_stream_default_control (OssStream *stream, OssStreamControl *control, gboolean force) +poll_mixer_restore (OssDevice *device) { - MateMixerStreamControl *current; + if G_LIKELY (device->priv->poll_mode == OSS_POLL_RAPID) { + /* Remove the current rapid source */ + g_source_remove (device->priv->poll_tag); - current = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream)); - if (current == NULL || force == TRUE) { - oss_stream_set_default_control (stream, OSS_STREAM_CONTROL (control)); - return TRUE; + device->priv->poll_tag = create_poll_source (device, OSS_POLL_NORMAL); + device->priv->poll_mode = OSS_POLL_NORMAL; } - return FALSE; + + /* Remove the tag for this function as it is only called once, the tag + * is only kept so we can remove it in the case the device is closed */ + device->priv->poll_tag_restore = 0; + + return G_SOURCE_REMOVE; +} + +static guint +create_poll_source (OssDevice *device, OssPollMode mode) +{ + GSource *source; + guint timeout; + guint tag; + + switch (mode) { + case OSS_POLL_NORMAL: + timeout = OSS_POLL_TIMEOUT_NORMAL; + break; + case OSS_POLL_RAPID: + timeout = OSS_POLL_TIMEOUT_RAPID; + break; + default: + g_warn_if_reached (); + return 0; + } + + source = g_timeout_source_new (timeout); + g_source_set_callback (source, + (GSourceFunc) poll_mixer, + device, + NULL); + + tag = g_source_attach (source, g_main_context_get_thread_default ()); + g_source_unref (source); + + return tag; +} + +static guint +create_poll_restore_source (OssDevice *device) +{ + GSource *source; + guint tag; + + source = g_timeout_source_new (OSS_POLL_TIMEOUT_RESTORE); + g_source_set_callback (source, + (GSourceFunc) poll_mixer_restore, + device, + NULL); + + tag = g_source_attach (source, g_main_context_get_thread_default ()); + g_source_unref (source); + + return tag; +} + +static void +free_stream_list (OssDevice *device) +{ + /* This function is called each time the stream list changes */ + if (device->priv->streams == NULL) + return; + + g_list_free_full (device->priv->streams, g_object_unref); + + device->priv->streams = NULL; +} + +static gint +compare_stream_control_devnum (gconstpointer a, gconstpointer b) +{ + OssStreamControl *control = OSS_STREAM_CONTROL (a); + guint devnum = GPOINTER_TO_INT (b); + + return !(oss_stream_control_get_devnum (control) == devnum); } diff --git a/backends/oss/oss-device.h b/backends/oss/oss-device.h index 261a884..a723f41 100644 --- a/backends/oss/oss-device.h +++ b/backends/oss/oss-device.h @@ -22,7 +22,7 @@ #include <glib-object.h> #include <libmatemixer/matemixer.h> -#include "oss-stream.h" +#include "oss-types.h" G_BEGIN_DECLS @@ -39,7 +39,6 @@ G_BEGIN_DECLS #define OSS_DEVICE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_DEVICE, OssDeviceClass)) -typedef struct _OssDevice OssDevice; typedef struct _OssDeviceClass OssDeviceClass; typedef struct _OssDevicePrivate OssDevicePrivate; @@ -54,6 +53,9 @@ struct _OssDevice struct _OssDeviceClass { MateMixerDeviceClass parent; + + /*< private >*/ + void (*closed) (OssDevice *device); }; GType oss_device_get_type (void) G_GNUC_CONST; @@ -64,9 +66,11 @@ OssDevice * oss_device_new (const gchar *name, gint fd); gboolean oss_device_open (OssDevice *device); -gboolean oss_device_load (OssDevice *device); +gboolean oss_device_is_open (OssDevice *device); +void oss_device_close (OssDevice *device); + +void oss_device_load (OssDevice *device); -gint oss_device_get_fd (OssDevice *device); const gchar *oss_device_get_path (OssDevice *device); OssStream * oss_device_get_input_stream (OssDevice *device); diff --git a/backends/oss/oss-stream-control.c b/backends/oss/oss-stream-control.c index 5161528..0307fc7 100644 --- a/backends/oss/oss-stream-control.c +++ b/backends/oss/oss-stream-control.c @@ -26,27 +26,28 @@ #include "oss-common.h" #include "oss-stream-control.h" +#define OSS_VOLUME_JOIN(left,right) (((left) & 0xFF) | (((right) & 0xFF) << 8)) + +#define OSS_VOLUME_JOIN_SAME(volume) (OSS_VOLUME_JOIN ((volume), (volume))) +#define OSS_VOLUME_JOIN_ARRAY(volume) (OSS_VOLUME_JOIN ((volume[0]), (volume[1]))) + +#define OSS_VOLUME_TAKE_LEFT(volume) ((volume) & 0xFF) +#define OSS_VOLUME_TAKE_RIGHT(volume) (((volume) >> 8) & 0xFF) + struct _OssStreamControlPrivate { - gint fd; - gint devnum; - guint volume[2]; - gfloat balance; - gboolean stereo; - MateMixerStreamControlRole role; - MateMixerStreamControlFlags flags; + gint fd; + gint devnum; + guint8 volume[2]; + gboolean stereo; }; static void oss_stream_control_class_init (OssStreamControlClass *klass); - static void oss_stream_control_init (OssStreamControl *control); static void oss_stream_control_finalize (GObject *object); G_DEFINE_TYPE (OssStreamControl, oss_stream_control, MATE_MIXER_TYPE_STREAM_CONTROL) -static gboolean oss_stream_control_set_mute (MateMixerStreamControl *mmsc, - gboolean mute); - static guint oss_stream_control_get_num_channels (MateMixerStreamControl *mmsc); static guint oss_stream_control_get_volume (MateMixerStreamControl *mmsc); @@ -73,7 +74,9 @@ static guint oss_stream_control_get_max_volume (MateMix static guint oss_stream_control_get_normal_volume (MateMixerStreamControl *mmsc); static guint oss_stream_control_get_base_volume (MateMixerStreamControl *mmsc); -static gboolean write_volume (OssStreamControl *control, +static void read_balance (OssStreamControl *control); + +static gboolean write_and_set_volume (OssStreamControl *control, gint volume); static void @@ -86,14 +89,13 @@ oss_stream_control_class_init (OssStreamControlClass *klass) object_class->finalize = oss_stream_control_finalize; control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass); - control_class->set_mute = oss_stream_control_set_mute; control_class->get_num_channels = oss_stream_control_get_num_channels; control_class->get_volume = oss_stream_control_get_volume; control_class->set_volume = oss_stream_control_set_volume; control_class->get_channel_volume = oss_stream_control_get_channel_volume; control_class->set_channel_volume = oss_stream_control_set_channel_volume; - control_class->get_channel_position = oss_stream_control_get_channel_position; control_class->has_channel_position = oss_stream_control_has_channel_position; + control_class->get_channel_position = oss_stream_control_get_channel_position; control_class->set_balance = oss_stream_control_set_balance; control_class->get_min_volume = oss_stream_control_get_min_volume; control_class->get_max_volume = oss_stream_control_get_max_volume; @@ -118,7 +120,8 @@ oss_stream_control_finalize (GObject *object) control = OSS_STREAM_CONTROL (object); - close (control->priv->fd); + if (control->priv->fd != -1) + close (control->priv->fd); G_OBJECT_CLASS (oss_stream_control_parent_class)->finalize (object); } @@ -127,6 +130,7 @@ OssStreamControl * oss_stream_control_new (const gchar *name, const gchar *label, MateMixerStreamControlRole role, + OssStream *stream, gint fd, gint devnum, gboolean stereo) @@ -134,8 +138,11 @@ oss_stream_control_new (const gchar *name, OssStreamControl *control; MateMixerStreamControlFlags flags; - flags = MATE_MIXER_STREAM_CONTROL_HAS_VOLUME | - MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME; + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (label != NULL, NULL); + + flags = MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE; if (stereo == TRUE) flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; @@ -143,6 +150,8 @@ oss_stream_control_new (const gchar *name, "name", name, "label", label, "flags", flags, + "role", role, + "stream", stream, NULL); control->priv->fd = fd; @@ -151,52 +160,50 @@ oss_stream_control_new (const gchar *name, return control; } -gboolean -oss_stream_control_update (OssStreamControl *control) +gint +oss_stream_control_get_devnum (OssStreamControl *control) { - gint v; - gint ret; + g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), 0); - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE); + return control->priv->devnum; +} - ret = ioctl (control->priv->fd, MIXER_READ (control->priv->devnum), &v); - if (ret < 0) { - g_warning ("Failed to read volume: %s", g_strerror (errno)); - return FALSE; - } +void +oss_stream_control_load (OssStreamControl *control) +{ + gint v, ret; - control->priv->volume[0] = v & 0xFF; + g_return_if_fail (OSS_IS_STREAM_CONTROL (control)); - if (control->priv->stereo == TRUE) { - gfloat balance; - guint left; - guint right; - - control->priv->volume[1] = (v >> 8) & 0xFF; - - /* Calculate balance */ - left = control->priv->volume[0]; - right = control->priv->volume[1]; - if (left == right) - balance = 0.0f; - else if (left > right) - balance = -1.0f + ((gfloat) right / (gfloat) left); - else - balance = +1.0f - ((gfloat) left / (gfloat) right); + if G_UNLIKELY (control->priv->fd == -1) + return; + + ret = ioctl (control->priv->fd, MIXER_READ (control->priv->devnum), &v); + if (ret == -1) + return; + + if (v != OSS_VOLUME_JOIN_ARRAY (control->priv->volume)) { + control->priv->volume[0] = OSS_VOLUME_TAKE_LEFT (v); + if (control->priv->stereo == TRUE) + control->priv->volume[1] = OSS_VOLUME_TAKE_RIGHT (v); - _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (control), - balance); + g_object_notify (G_OBJECT (control), "volume"); } - return TRUE; + + if (control->priv->stereo == TRUE) + read_balance (control); } -static gboolean -oss_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute) +void +oss_stream_control_close (OssStreamControl *control) { - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); + g_return_if_fail (OSS_IS_STREAM_CONTROL (control)); - // TODO - return TRUE; + if (control->priv->fd == -1) + return; + + close (control->priv->fd); + control->priv->fd = -1; } static guint @@ -226,17 +233,15 @@ static gboolean oss_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume) { OssStreamControl *control; - gint v; g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); control = OSS_STREAM_CONTROL (mmsc); - v = CLAMP (volume, 0, 100); - if (control->priv->stereo == TRUE) - v |= (volume & 0xFF) << 8; + if G_UNLIKELY (control->priv->fd == -1) + return FALSE; - return write_volume (control, v); + return write_and_set_volume (control, OSS_VOLUME_JOIN_SAME (CLAMP (volume, 0, 100))); } static guint @@ -269,27 +274,26 @@ oss_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); control = OSS_STREAM_CONTROL (mmsc); - volume = CLAMP (volume, 0, 100); - if (control->priv->stereo == TRUE) { - if (channel > 1) - return FALSE; + if G_UNLIKELY (control->priv->fd == -1) + return FALSE; + if (channel > 1 || (control->priv->stereo == FALSE && channel > 0)) + return FALSE; - /* Stereo channel volume - left channel is in the lowest 8 bits and + volume = CLAMP (volume, 0, 100); + + if (control->priv->stereo == TRUE) { + /* Stereo channel volume - left channel is in the lower 8 bits and * right channel is in the higher 8 bits */ if (channel == 0) - v = volume | (control->priv->volume[1] << 8); + v = OSS_VOLUME_JOIN (volume, control->priv->volume[1]); else - v = control->priv->volume[0] | (volume << 8); + v = OSS_VOLUME_JOIN (control->priv->volume[0], volume); } else { - if (channel > 0) - return FALSE; - - /* Single channel volume - only lowest 8 bits are used */ + /* Single channel volume - only lower 8 bits are used */ v = volume; } - - return write_volume (control, v); + return write_and_set_volume (control, v); } static MateMixerChannelPosition @@ -334,24 +338,24 @@ oss_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance) { OssStreamControl *control; guint max; - gint v; + gint volume[2]; g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE); control = OSS_STREAM_CONTROL (mmsc); + if G_UNLIKELY (control->priv->fd == -1) + return FALSE; + max = MAX (control->priv->volume[0], control->priv->volume[1]); if (balance <= 0) { - control->priv->volume[1] = (balance + 1.0f) * max; - control->priv->volume[0] = max; + volume[1] = (balance + 1.0f) * max; + volume[0] = max; } else { - control->priv->volume[0] = (1.0f - balance) * max; - control->priv->volume[1] = max; + volume[0] = (1.0f - balance) * max; + volume[1] = max; } - - v = control->priv->volume[0] | (control->priv->volume[1] << 8); - - return write_volume (control, v); + return write_and_set_volume (control, OSS_VOLUME_JOIN_ARRAY (volume)); } static guint @@ -378,15 +382,45 @@ oss_stream_control_get_base_volume (MateMixerStreamControl *mmsc) return 100; } +static void +read_balance (OssStreamControl *control) +{ + gfloat balance; + guint left = control->priv->volume[0]; + guint right = control->priv->volume[1]; + + if (left == right) + balance = 0.0f; + else if (left > right) + balance = -1.0f + ((gfloat) right / (gfloat) left); + else + balance = +1.0f - ((gfloat) left / (gfloat) right); + + _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (control), + balance); +} + static gboolean -write_volume (OssStreamControl *control, gint volume) +write_and_set_volume (OssStreamControl *control, gint volume) { gint ret; + /* Nothing to do? */ + if (volume == OSS_VOLUME_JOIN_ARRAY (control->priv->volume)) + return TRUE; + ret = ioctl (control->priv->fd, MIXER_WRITE (control->priv->devnum), &volume); - if (ret < 0) { - g_warning ("Failed to set volume: %s", g_strerror (errno)); + if (ret == -1) return FALSE; - } + + oss_stream_control_load (control); + return TRUE; + + /* The ioctl may make adjustments to the passed volume, so make sure we have + * the correct value saved */ + control->priv->volume[0] = OSS_VOLUME_TAKE_LEFT (volume); + control->priv->volume[1] = OSS_VOLUME_TAKE_RIGHT (volume); + + g_object_notify (G_OBJECT (control), "volume"); return TRUE; } diff --git a/backends/oss/oss-stream-control.h b/backends/oss/oss-stream-control.h index c839faf..1957088 100644 --- a/backends/oss/oss-stream-control.h +++ b/backends/oss/oss-stream-control.h @@ -22,6 +22,8 @@ #include <glib-object.h> #include <libmatemixer/matemixer.h> +#include "oss-types.h" + G_BEGIN_DECLS #define OSS_TYPE_STREAM_CONTROL \ @@ -37,7 +39,6 @@ G_BEGIN_DECLS #define OSS_STREAM_CONTROL_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_STREAM_CONTROL, OssStreamControlClass)) -typedef struct _OssStreamControl OssStreamControl; typedef struct _OssStreamControlClass OssStreamControlClass; typedef struct _OssStreamControlPrivate OssStreamControlPrivate; @@ -54,16 +55,20 @@ struct _OssStreamControlClass MateMixerStreamControlClass parent; }; -GType oss_stream_control_get_type (void) G_GNUC_CONST; +GType oss_stream_control_get_type (void) G_GNUC_CONST; + +OssStreamControl *oss_stream_control_new (const gchar *name, + const gchar *label, + MateMixerStreamControlRole role, + OssStream *stream, + gint fd, + gint devnum, + gboolean stereo); -OssStreamControl *oss_stream_control_new (const gchar *name, - const gchar *label, - MateMixerStreamControlRole role, - gint fd, - gint devnum, - gboolean stereo); +gint oss_stream_control_get_devnum (OssStreamControl *control); -gboolean oss_stream_control_update (OssStreamControl *control); +void oss_stream_control_load (OssStreamControl *control); +void oss_stream_control_close (OssStreamControl *control); G_END_DECLS diff --git a/backends/oss/oss-stream.c b/backends/oss/oss-stream.c index 5f0c629..227c88b 100644 --- a/backends/oss/oss-stream.c +++ b/backends/oss/oss-stream.c @@ -16,32 +16,32 @@ */ #include <glib.h> +#include <glib/gi18n.h> #include <glib-object.h> #include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> -#include "oss-device.h" #include "oss-stream.h" #include "oss-stream-control.h" +#include "oss-switch.h" + +#define OSS_STREAM_SWITCH_NAME "port" struct _OssStreamPrivate { - GHashTable *controls; - OssStreamControl *control; + OssSwitch *swtch; + GList *switches; + GList *controls; }; -static void oss_stream_class_init (OssStreamClass *klass); - -static void oss_stream_init (OssStream *ostream); -static void oss_stream_dispose (GObject *object); -static void oss_stream_finalize (GObject *object); +static void oss_stream_class_init (OssStreamClass *klass); +static void oss_stream_init (OssStream *stream); +static void oss_stream_dispose (GObject *object); G_DEFINE_TYPE (OssStream, oss_stream, MATE_MIXER_TYPE_STREAM) -static MateMixerStreamControl *oss_stream_get_control (MateMixerStream *stream, - const gchar *name); -static MateMixerStreamControl *oss_stream_get_default_control (MateMixerStream *stream); - -static GList * oss_stream_list_controls (MateMixerStream *stream); +static const GList *oss_stream_list_controls (MateMixerStream *mms); +static const GList *oss_stream_list_switches (MateMixerStream *mms); static void oss_stream_class_init (OssStreamClass *klass) @@ -50,13 +50,11 @@ oss_stream_class_init (OssStreamClass *klass) MateMixerStreamClass *stream_class; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = oss_stream_dispose; - object_class->finalize = oss_stream_finalize; + object_class->dispose = oss_stream_dispose; stream_class = MATE_MIXER_STREAM_CLASS (klass); - stream_class->get_control = oss_stream_get_control; - stream_class->get_default_control = oss_stream_get_default_control; - stream_class->list_controls = oss_stream_list_controls; + stream_class->list_controls = oss_stream_list_controls; + stream_class->list_switches = oss_stream_list_switches; g_type_class_add_private (object_class, sizeof (OssStreamPrivate)); } @@ -67,11 +65,6 @@ oss_stream_init (OssStream *stream) stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, OSS_TYPE_STREAM, OssStreamPrivate); - - stream->priv->controls = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); } static void @@ -81,103 +74,201 @@ oss_stream_dispose (GObject *object) stream = OSS_STREAM (object); - g_clear_object (&stream->priv->control); - g_hash_table_remove_all (stream->priv->controls); + if (stream->priv->controls != NULL) { + g_list_free_full (stream->priv->controls, g_object_unref); + stream->priv->controls = NULL; + } + if (stream->priv->switches != NULL) { + g_list_free_full (stream->priv->switches, g_object_unref); + stream->priv->switches = NULL; + } + + g_clear_object (&stream->priv->swtch); G_OBJECT_CLASS (oss_stream_parent_class)->dispose (object); } -static void -oss_stream_finalize (GObject *object) +OssStream * +oss_stream_new (const gchar *name, + MateMixerDevice *device, + MateMixerDirection direction) { - OssStream *stream; + const gchar *label = mate_mixer_device_get_label (device); + + return g_object_new (OSS_TYPE_STREAM, + "name", name, + "label", label, + "device", device, + "direction", direction, + NULL); +} - stream = OSS_STREAM (object); +void +oss_stream_add_control (OssStream *stream, OssStreamControl *control) +{ + const gchar *name; + + g_return_if_fail (OSS_IS_STREAM (stream)); + g_return_if_fail (OSS_IS_STREAM_CONTROL (control)); - g_hash_table_destroy (stream->priv->controls); + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control)); + + stream->priv->controls = + g_list_append (stream->priv->controls, g_object_ref (control)); - G_OBJECT_CLASS (oss_stream_parent_class)->finalize (object); + g_signal_emit_by_name (G_OBJECT (stream), + "control-added", + name); } -OssStream * -oss_stream_new (const gchar *name, - MateMixerDevice *device, - MateMixerStreamFlags flags) +void +oss_stream_load (OssStream *stream) { - OssStream *stream; + GList *list; + + g_return_if_fail (OSS_IS_STREAM (stream)); + + list = stream->priv->controls; + while (list != NULL) { + OssStreamControl *control = OSS_STREAM_CONTROL (list->data); - stream = g_object_new (OSS_TYPE_STREAM, - "name", name, - "device", device, - "flags", flags, - NULL); - return stream; + oss_stream_control_load (control); + list = list->next; + } + + if (stream->priv->swtch != NULL) + oss_switch_load (stream->priv->swtch); } gboolean -oss_stream_add_control (OssStream *stream, OssStreamControl *control) +oss_stream_has_controls (OssStream *stream) { - const gchar *name; - g_return_val_if_fail (OSS_IS_STREAM (stream), FALSE); - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE); - name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control)); + if (stream->priv->controls != NULL) + return TRUE; - g_hash_table_insert (stream->priv->controls, - g_strdup (name), - control); - return TRUE; + return FALSE; } gboolean -oss_stream_set_default_control (OssStream *stream, OssStreamControl *control) +oss_stream_has_default_control (OssStream *stream) { g_return_val_if_fail (OSS_IS_STREAM (stream), FALSE); - g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE); - - /* This function is only used internally so avoid validating that the control - * belongs to this stream */ - if (stream->priv->control != NULL) - g_object_unref (stream->priv->control); - if G_LIKELY (control != NULL) - stream->priv->control = g_object_ref (control); - else - stream->priv->control = NULL; + if (mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream)) != NULL) + return TRUE; - return TRUE; + return FALSE; } -static MateMixerStreamControl * -oss_stream_get_control (MateMixerStream *mms, const gchar *name) +OssStreamControl * +oss_stream_get_default_control (OssStream *stream) { - g_return_val_if_fail (OSS_IS_STREAM (mms), NULL); - g_return_val_if_fail (name != NULL, NULL); + MateMixerStreamControl *control; + + g_return_val_if_fail (OSS_IS_STREAM (stream), NULL); + + control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream)); + if (control != NULL) + return OSS_STREAM_CONTROL (control); - return g_hash_table_lookup (OSS_STREAM (mms)->priv->controls, name); + return NULL; } -static MateMixerStreamControl * -oss_stream_get_default_control (MateMixerStream *mms) +void +oss_stream_set_default_control (OssStream *stream, OssStreamControl *control) { - g_return_val_if_fail (OSS_IS_STREAM (mms), NULL); + g_return_if_fail (OSS_IS_STREAM (stream)); + g_return_if_fail (control == NULL || OSS_IS_STREAM_CONTROL (control)); + + if (control == NULL) + _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (stream), NULL); + else + _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (stream), + MATE_MIXER_STREAM_CONTROL (control)); +} - return MATE_MIXER_STREAM_CONTROL (OSS_STREAM (mms)->priv->control); +void +oss_stream_set_switch_data (OssStream *stream, gint fd, GList *options) +{ + g_return_if_fail (OSS_IS_STREAM (stream)); + g_return_if_fail (fd != -1); + g_return_if_fail (options != NULL); + + /* Function may only be called once */ + if G_UNLIKELY (stream->priv->swtch != NULL) { + g_warn_if_reached (); + return; + } + + /* Takes ownership of options */ + stream->priv->swtch = oss_switch_new (OSS_STREAM_SWITCH_NAME, + _("Capture Source"), + fd, + options); + + /* Read the active selection */ + oss_switch_load (stream->priv->swtch); + + stream->priv->switches = g_list_prepend (NULL, g_object_ref (stream->priv->swtch)); + g_signal_emit_by_name (G_OBJECT (stream), + "switch-added", + OSS_STREAM_SWITCH_NAME); } -static GList * -oss_stream_list_controls (MateMixerStream *mms) +void +oss_stream_remove_all (OssStream *stream) { GList *list; + g_return_if_fail (OSS_IS_STREAM (stream)); + + list = stream->priv->controls; + while (list != NULL) { + MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (list->data); + GList *next = list->next; + + oss_stream_control_close (OSS_STREAM_CONTROL (control)); + + stream->priv->controls = g_list_delete_link (stream->priv->controls, list); + g_signal_emit_by_name (G_OBJECT (stream), + "control-removed", + mate_mixer_stream_control_get_name (control)); + + g_object_unref (control); + list = next; + } + + /* Unset the default stream control */ + oss_stream_set_default_control (stream, NULL); + + if (stream->priv->swtch != NULL) { + oss_switch_close (stream->priv->swtch); + + g_list_free_full (stream->priv->switches, g_object_unref); + stream->priv->switches = NULL; + + g_signal_emit_by_name (G_OBJECT (stream), + "switch-removed", + OSS_STREAM_SWITCH_NAME); + + g_clear_object (&stream->priv->swtch); + } +} + +static const GList * +oss_stream_list_controls (MateMixerStream *mms) +{ g_return_val_if_fail (OSS_IS_STREAM (mms), NULL); - /* Convert the hash table to a linked list, this list is expected to be - * cached in the main library */ - list = g_hash_table_get_values (OSS_STREAM (mms)->priv->controls); - if (list != NULL) - g_list_foreach (list, (GFunc) g_object_ref, NULL); + return OSS_STREAM (mms)->priv->controls; +} + +static const GList * +oss_stream_list_switches (MateMixerStream *mms) +{ + g_return_val_if_fail (OSS_IS_STREAM (mms), NULL); - return list; + return OSS_STREAM (mms)->priv->switches; } diff --git a/backends/oss/oss-stream.h b/backends/oss/oss-stream.h index 0470eb7..2ade0d8 100644 --- a/backends/oss/oss-stream.h +++ b/backends/oss/oss-stream.h @@ -22,8 +22,7 @@ #include <glib-object.h> #include <libmatemixer/matemixer.h> -#include "oss-device.h" -#include "oss-stream-control.h" +#include "oss-types.h" G_BEGIN_DECLS @@ -40,7 +39,6 @@ G_BEGIN_DECLS #define OSS_STREAM_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_STREAM, OssStreamClass)) -typedef struct _OssStream OssStream; typedef struct _OssStreamClass OssStreamClass; typedef struct _OssStreamPrivate OssStreamPrivate; @@ -57,17 +55,29 @@ struct _OssStreamClass MateMixerStreamClass parent; }; -GType oss_stream_get_type (void) G_GNUC_CONST; +GType oss_stream_get_type (void) G_GNUC_CONST; -OssStream *oss_stream_new (const gchar *name, - MateMixerDevice *device, - MateMixerStreamFlags flags); +OssStream * oss_stream_new (const gchar *name, + MateMixerDevice *device, + MateMixerDirection direction); -gboolean oss_stream_add_control (OssStream *stream, - OssStreamControl *control); +void oss_stream_add_control (OssStream *stream, + OssStreamControl *control); -gboolean oss_stream_set_default_control (OssStream *stream, - OssStreamControl *control); +void oss_stream_load (OssStream *stream); + +gboolean oss_stream_has_controls (OssStream *stream); +gboolean oss_stream_has_default_control (OssStream *stream); + +OssStreamControl *oss_stream_get_default_control (OssStream *stream); +void oss_stream_set_default_control (OssStream *stream, + OssStreamControl *control); + +void oss_stream_set_switch_data (OssStream *stream, + gint fd, + GList *options); + +void oss_stream_remove_all (OssStream *stream); G_END_DECLS diff --git a/backends/oss/oss-switch-option.c b/backends/oss/oss-switch-option.c new file mode 100644 index 0000000..862133d --- /dev/null +++ b/backends/oss/oss-switch-option.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "oss-switch-option.h" + +struct _OssSwitchOptionPrivate +{ + guint devnum; +}; + +static void oss_switch_option_class_init (OssSwitchOptionClass *klass); +static void oss_switch_option_init (OssSwitchOption *option); + +G_DEFINE_TYPE (OssSwitchOption, oss_switch_option, MATE_MIXER_TYPE_SWITCH_OPTION) + +static void +oss_switch_option_class_init (OssSwitchOptionClass *klass) +{ + g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (OssSwitchOptionPrivate)); +} + +static void +oss_switch_option_init (OssSwitchOption *option) +{ + option->priv = G_TYPE_INSTANCE_GET_PRIVATE (option, + OSS_TYPE_SWITCH_OPTION, + OssSwitchOptionPrivate); +} + +OssSwitchOption * +oss_switch_option_new (const gchar *name, + const gchar *label, + const gchar *icon, + guint devnum) +{ + OssSwitchOption *option; + + option = g_object_new (OSS_TYPE_SWITCH_OPTION, + "name", name, + "label", label, + "icon", icon, + NULL); + + option->priv->devnum = devnum; + return option; +} + +guint +oss_switch_option_get_devnum (OssSwitchOption *option) +{ + g_return_val_if_fail (OSS_IS_SWITCH_OPTION (option), 0); + + return option->priv->devnum; +} diff --git a/backends/oss/oss-switch-option.h b/backends/oss/oss-switch-option.h new file mode 100644 index 0000000..91c21e3 --- /dev/null +++ b/backends/oss/oss-switch-option.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef OSS_SWITCH_OPTION_H +#define OSS_SWITCH_OPTION_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "oss-types.h" + +G_BEGIN_DECLS + +#define OSS_TYPE_SWITCH_OPTION \ + (oss_switch_option_get_type ()) +#define OSS_SWITCH_OPTION(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), OSS_TYPE_SWITCH_OPTION, OssSwitchOption)) +#define OSS_IS_SWITCH_OPTION(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSS_TYPE_SWITCH_OPTION)) +#define OSS_SWITCH_OPTION_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), OSS_TYPE_SWITCH_OPTION, OssSwitchOptionClass)) +#define OSS_IS_SWITCH_OPTION_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), OSS_TYPE_SWITCH_OPTION)) +#define OSS_SWITCH_OPTION_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_SWITCH_OPTION, OssSwitchOptionClass)) + +typedef struct _OssSwitchOptionClass OssSwitchOptionClass; +typedef struct _OssSwitchOptionPrivate OssSwitchOptionPrivate; + +struct _OssSwitchOption +{ + MateMixerSwitchOption parent; + + /*< private >*/ + OssSwitchOptionPrivate *priv; +}; + +struct _OssSwitchOptionClass +{ + MateMixerSwitchOptionClass parent_class; +}; + +GType oss_switch_option_get_type (void) G_GNUC_CONST; + +OssSwitchOption *oss_switch_option_new (const gchar *name, + const gchar *label, + const gchar *icon, + guint devnum); + +guint oss_switch_option_get_devnum (OssSwitchOption *option); + +G_END_DECLS + +#endif /* OSS_SWITCH_OPTION_H */ diff --git a/backends/oss/oss-switch.c b/backends/oss/oss-switch.c new file mode 100644 index 0000000..b138051 --- /dev/null +++ b/backends/oss/oss-switch.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "oss-common.h" +#include "oss-switch.h" +#include "oss-switch-option.h" + +struct _OssSwitchPrivate +{ + gint fd; + GList *options; +}; + +static void oss_switch_class_init (OssSwitchClass *klass); +static void oss_switch_init (OssSwitch *swtch); +static void oss_switch_dispose (GObject *object); +static void oss_switch_finalize (GObject *object); + +G_DEFINE_TYPE (OssSwitch, oss_switch, MATE_MIXER_TYPE_SWITCH) + +static gboolean oss_switch_set_active_option (MateMixerSwitch *mms, + MateMixerSwitchOption *mmso); + +static const GList *oss_switch_list_options (MateMixerSwitch *mms); + +static void +oss_switch_class_init (OssSwitchClass *klass) +{ + GObjectClass *object_class; + MateMixerSwitchClass *switch_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = oss_switch_dispose; + object_class->finalize = oss_switch_finalize; + + switch_class = MATE_MIXER_SWITCH_CLASS (klass); + switch_class->set_active_option = oss_switch_set_active_option; + switch_class->list_options = oss_switch_list_options; + + g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (OssSwitchPrivate)); +} + +static void +oss_switch_init (OssSwitch *swtch) +{ + swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch, + OSS_TYPE_SWITCH, + OssSwitchPrivate); +} + +static void +oss_switch_dispose (GObject *object) +{ + OssSwitch *swtch; + + swtch = OSS_SWITCH (object); + + if (swtch->priv->options != NULL) { + g_list_free_full (swtch->priv->options, g_object_unref); + swtch->priv->options = NULL; + } + + G_OBJECT_CLASS (oss_switch_parent_class)->dispose (object); +} + +static void +oss_switch_finalize (GObject *object) +{ + OssSwitch *swtch; + + swtch = OSS_SWITCH (object); + + if (swtch->priv->fd != -1) + close (swtch->priv->fd); + + G_OBJECT_CLASS (oss_switch_parent_class)->finalize (object); +} + +OssSwitch * +oss_switch_new (const gchar *name, + const gchar *label, + gint fd, + GList *options) +{ + OssSwitch *swtch; + + swtch = g_object_new (OSS_TYPE_SWITCH, + "name", name, + "label", label, + "flags", MATE_MIXER_SWITCH_ALLOWS_NO_ACTIVE_OPTION, + "role", MATE_MIXER_SWITCH_ROLE_PORT, + NULL); + + /* Takes ownership of options */ + swtch->priv->fd = dup (fd); + swtch->priv->options = options; + + return swtch; +} + +void +oss_switch_load (OssSwitch *swtch) +{ + GList *list; + gint recsrc; + gint ret; + + g_return_if_fail (OSS_IS_SWITCH (swtch)); + + if G_UNLIKELY (swtch->priv->fd == -1) + return; + + ret = ioctl (swtch->priv->fd, MIXER_READ (SOUND_MIXER_RECSRC), &recsrc); + if (ret == -1) + return; + if (recsrc == 0) { + _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (swtch), NULL); + return; + } + + list = swtch->priv->options; + while (list != NULL) { + OssSwitchOption *option = OSS_SWITCH_OPTION (list->data); + gint devnum = oss_switch_option_get_devnum (option); + + /* Mark the selected option when we find it */ + if (recsrc & (1 << devnum)) { + _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (swtch), + MATE_MIXER_SWITCH_OPTION (option)); + return; + } + list = list->next; + } + + g_warning ("Unknown active option of switch %s", + mate_mixer_switch_get_name (MATE_MIXER_SWITCH (swtch))); +} + +void +oss_switch_close (OssSwitch *swtch) +{ + g_return_if_fail (OSS_IS_SWITCH (swtch)); + + if (swtch->priv->fd == -1) + return; + + close (swtch->priv->fd); + swtch->priv->fd = -1; +} + +static gboolean +oss_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso) +{ + OssSwitch *swtch; + gint ret; + gint recsrc; + gint devnum; + + g_return_val_if_fail (OSS_IS_SWITCH (mms), FALSE); + g_return_val_if_fail (OSS_IS_SWITCH_OPTION (mmso), FALSE); + + swtch = OSS_SWITCH (mms); + + if G_UNLIKELY (swtch->priv->fd == -1) + return FALSE; + + devnum = oss_switch_option_get_devnum (OSS_SWITCH_OPTION (mmso)); + recsrc = 1 << devnum; + + ret = ioctl (swtch->priv->fd, MIXER_WRITE (SOUND_MIXER_RECSRC), &recsrc); + if (ret == -1) + return FALSE; + + return TRUE; +} + +static const GList * +oss_switch_list_options (MateMixerSwitch *mms) +{ + g_return_val_if_fail (OSS_IS_SWITCH (mms), NULL); + + return OSS_SWITCH (mms)->priv->options; +} diff --git a/backends/oss/oss-switch.h b/backends/oss/oss-switch.h new file mode 100644 index 0000000..dc91758 --- /dev/null +++ b/backends/oss/oss-switch.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef OSS_SWITCH_H +#define OSS_SWITCH_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "oss-types.h" + +G_BEGIN_DECLS + +#define OSS_TYPE_SWITCH \ + (oss_switch_get_type ()) +#define OSS_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), OSS_TYPE_SWITCH, OssSwitch)) +#define OSS_IS_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSS_TYPE_SWITCH)) +#define OSS_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), OSS_TYPE_SWITCH, OssSwitchClass)) +#define OSS_IS_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), OSS_TYPE_SWITCH)) +#define OSS_SWITCH_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_SWITCH, OssSwitchClass)) + +typedef struct _OssSwitchClass OssSwitchClass; +typedef struct _OssSwitchPrivate OssSwitchPrivate; + +struct _OssSwitch +{ + MateMixerSwitch parent; + + /*< private >*/ + OssSwitchPrivate *priv; +}; + +struct _OssSwitchClass +{ + MateMixerSwitchClass parent_class; +}; + +GType oss_switch_get_type (void) G_GNUC_CONST; + +OssSwitch *oss_switch_new (const gchar *name, + const gchar *label, + gint fd, + GList *options); + +void oss_switch_load (OssSwitch *swtch); +void oss_switch_close (OssSwitch *swtch); + +G_END_DECLS + +#endif /* OSS_SWITCH_H */ diff --git a/backends/oss/oss-types.h b/backends/oss/oss-types.h new file mode 100644 index 0000000..afd4f58 --- /dev/null +++ b/backends/oss/oss-types.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef OSS_TYPES_H +#define OSS_TYPES_H + +G_BEGIN_DECLS + +typedef struct _OssBackend OssBackend; +typedef struct _OssDevice OssDevice; +typedef struct _OssStream OssStream; +typedef struct _OssStreamControl OssStreamControl; +typedef struct _OssSwitch OssSwitch; +typedef struct _OssSwitchOption OssSwitchOption; + +G_END_DECLS + +#endif /* OSS_TYPES_H */ diff --git a/backends/pulse/Makefile.am b/backends/pulse/Makefile.am index 0e5a4d6..4c851bf 100644 --- a/backends/pulse/Makefile.am +++ b/backends/pulse/Makefile.am @@ -3,6 +3,7 @@ backenddir = $(libdir)/libmatemixer backend_LTLIBRARIES = libmatemixer-pulse.la AM_CPPFLAGS = \ + -Wno-unknown-pragmas \ -I$(top_srcdir) \ -DG_LOG_DOMAIN=\"libmatemixer-pulse\" @@ -13,12 +14,14 @@ libmatemixer_pulse_la_CFLAGS = \ libmatemixer_pulse_la_SOURCES = \ pulse-backend.c \ pulse-backend.h \ - pulse-client-stream.c \ - pulse-client-stream.h \ pulse-connection.c \ pulse-connection.h \ pulse-device.c \ pulse-device.h \ + pulse-device-profile.c \ + pulse-device-profile.h \ + pulse-device-switch.c \ + pulse-device-switch.h \ pulse-enums.h \ pulse-enum-types.c \ pulse-enum-types.h \ @@ -28,16 +31,31 @@ libmatemixer_pulse_la_SOURCES = \ pulse-helpers.h \ pulse-monitor.c \ pulse-monitor.h \ + pulse-port.c \ + pulse-port.h \ + pulse-port-switch.c \ + pulse-port-switch.h \ pulse-stream.c \ pulse-stream.h \ + pulse-stream-control.c \ + pulse-stream-control.h \ pulse-sink.c \ pulse-sink.h \ + pulse-sink-control.c \ + pulse-sink-control.h \ pulse-sink-input.c \ pulse-sink-input.h \ + pulse-sink-switch.c \ + pulse-sink-switch.h \ pulse-source.c \ pulse-source.h \ + pulse-source-control.c \ + pulse-source-control.h \ pulse-source-output.c \ - pulse-source-output.h + pulse-source-output.h \ + pulse-source-switch.c \ + pulse-source-switch.h \ + pulse-types.h libmatemixer_pulse_la_LIBADD = \ $(GLIB_LIBS) \ diff --git a/backends/pulse/pulse-backend.c b/backends/pulse/pulse-backend.c index 0494545..e1b7ed5 100644 --- a/backends/pulse/pulse-backend.c +++ b/backends/pulse/pulse-backend.c @@ -19,9 +19,8 @@ #include <glib.h> #include <glib-object.h> -#include <libmatemixer/matemixer-backend.h> -#include <libmatemixer/matemixer-backend-module.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include <pulse/ext-stream-restore.h> @@ -38,72 +37,110 @@ #include "pulse-source-output.h" #define BACKEND_NAME "PulseAudio" -#define BACKEND_PRIORITY 10 +#define BACKEND_PRIORITY 100 struct _PulseBackendPrivate { - gchar *app_name; - gchar *app_id; - gchar *app_version; - gchar *app_icon; - gchar *server_address; - gboolean connected_once; - GSource *connect_source; - MateMixerStream *default_sink; - MateMixerStream *default_source; - GHashTable *devices; - GHashTable *streams; - GHashTable *ext_streams; - MateMixerState state; - PulseConnection *connection; -}; - -enum { - PROP_0, - PROP_STATE, - PROP_DEFAULT_INPUT, - PROP_DEFAULT_OUTPUT, - N_PROPERTIES + guint connect_tag; + gboolean connected_once; + GHashTable *devices; + GHashTable *sinks; + GHashTable *sources; + GHashTable *sink_inputs; + GHashTable *source_outputs; + GHashTable *ext_streams; + GList *devices_list; + GList *streams_list; + GList *ext_streams_list; + MateMixerAppInfo *app_info; + gchar *server_address; + PulseConnection *connection; }; -static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface); +#define PULSE_CHANGE_STATE(p, s) \ + (_mate_mixer_backend_set_state (MATE_MIXER_BACKEND (p), (s))) +#define PULSE_GET_DEFAULT_SINK(p) \ + (mate_mixer_backend_get_default_output_stream (MATE_MIXER_BACKEND (p))) +#define PULSE_GET_DEFAULT_SOURCE(p) \ + (mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (p))) +#define PULSE_SET_DEFAULT_SINK(p, s) \ + (_mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (p), MATE_MIXER_STREAM (s))) +#define PULSE_SET_DEFAULT_SOURCE(p, s) \ + (_mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (p), MATE_MIXER_STREAM (s))) + +#define PULSE_GET_PENDING_SINK(p) \ + (g_object_get_data (G_OBJECT (p), \ + "__matemixer_pulse_pending_sink")) \ + +#define PULSE_SET_PENDING_SINK(p,name) \ + (g_object_set_data_full (G_OBJECT (p), \ + "__matemixer_pulse_pending_sink", \ + g_strdup (name), \ + g_free)) + +#define PULSE_SET_PENDING_SINK_NULL(p) \ + (g_object_set_data (G_OBJECT (p), \ + "__matemixer_pulse_pending_sink", \ + NULL)) + +#define PULSE_GET_PENDING_SOURCE(p) \ + (g_object_get_data (G_OBJECT (p), \ + "__matemixer_pulse_pending_source")) \ + +#define PULSE_SET_PENDING_SOURCE(p,name) \ + (g_object_set_data_full (G_OBJECT (p), \ + "__matemixer_pulse_pending_source", \ + g_strdup (name), \ + g_free)) + +#define PULSE_SET_PENDING_SOURCE_NULL(p) \ + (g_object_set_data (G_OBJECT (p), \ + "__matemixer_pulse_pending_source", \ + NULL)) + +#define PULSE_GET_HANGING(o) \ + ((gboolean) g_object_get_data (G_OBJECT (o), \ + "__matemixer_pulse_hanging")) + +#define PULSE_SET_HANGING(o) \ + (g_object_set_data (G_OBJECT (o), \ + "__matemixer_pulse_hanging", \ + GUINT_TO_POINTER (1))) + +#define PULSE_UNSET_HANGING(o) \ + (g_object_steal_data (G_OBJECT (o), \ + "__matemixer_pulse_hanging")) static void pulse_backend_class_init (PulseBackendClass *klass); static void pulse_backend_class_finalize (PulseBackendClass *klass); -static void pulse_backend_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); - static void pulse_backend_init (PulseBackend *pulse); static void pulse_backend_dispose (GObject *object); static void pulse_backend_finalize (GObject *object); -G_DEFINE_DYNAMIC_TYPE_EXTENDED (PulseBackend, pulse_backend, - G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND, - mate_mixer_backend_interface_init)) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_DYNAMIC_TYPE (PulseBackend, pulse_backend, MATE_MIXER_TYPE_BACKEND) +#pragma clang diagnostic pop -static gboolean pulse_backend_open (MateMixerBackend *backend); -static void pulse_backend_close (MateMixerBackend *backend); +static gboolean pulse_backend_open (MateMixerBackend *backend); +static void pulse_backend_close (MateMixerBackend *backend); -static MateMixerState pulse_backend_get_state (MateMixerBackend *backend); +static void pulse_backend_set_app_info (MateMixerBackend *backend, + MateMixerAppInfo *info); -static void pulse_backend_set_data (MateMixerBackend *backend, - const MateMixerBackendData *data); +static void pulse_backend_set_server_address (MateMixerBackend *backend, + const gchar *address); -static GList * pulse_backend_list_devices (MateMixerBackend *backend); -static GList * pulse_backend_list_streams (MateMixerBackend *backend); -static GList * pulse_backend_list_cached_streams (MateMixerBackend *backend); +static const GList * pulse_backend_list_devices (MateMixerBackend *backend); +static const GList * pulse_backend_list_streams (MateMixerBackend *backend); +static const GList * pulse_backend_list_stored_controls (MateMixerBackend *backend); -static MateMixerStream *pulse_backend_get_default_input_stream (MateMixerBackend *backend); -static gboolean pulse_backend_set_default_input_stream (MateMixerBackend *backend, - MateMixerStream *stream); +static gboolean pulse_backend_set_default_input_stream (MateMixerBackend *backend, + MateMixerStream *stream); -static MateMixerStream *pulse_backend_get_default_output_stream (MateMixerBackend *backend); -static gboolean pulse_backend_set_default_output_stream (MateMixerBackend *backend, - MateMixerStream *stream); +static gboolean pulse_backend_set_default_output_stream (MateMixerBackend *backend, + MateMixerStream *stream); static void on_connection_state_notify (PulseConnection *connection, GParamSpec *pspec, @@ -152,32 +189,16 @@ static void on_connection_ext_stream_info (PulseConnection PulseBackend *pulse); static gboolean connect_source_reconnect (PulseBackend *pulse); -static void connect_source_remove (PulseBackend *pulse); static void check_pending_sink (PulseBackend *pulse, PulseStream *stream); static void check_pending_source (PulseBackend *pulse, PulseStream *stream); -static void mark_hanging (PulseBackend *pulse); -static void mark_hanging_hash (GHashTable *hash); - -static void unmark_hanging (PulseBackend *pulse, - GObject *object); - -static void remove_hanging (PulseBackend *pulse); -static void remove_device (PulseBackend *pulse, - PulseDevice *device); -static void remove_stream (PulseBackend *pulse, - PulseStream *stream); - -static void change_state (PulseBackend *backend, - MateMixerState state); +static void free_list_devices (PulseBackend *pulse); +static void free_list_streams (PulseBackend *pulse); +static void free_list_ext_streams (PulseBackend *pulse); -static gint compare_devices (gconstpointer a, - gconstpointer b); -static gint compare_streams (gconstpointer a, - gconstpointer b); static gboolean compare_stream_names (gpointer key, gpointer value, gpointer user_data); @@ -195,78 +216,42 @@ backend_module_init (GTypeModule *module) info.backend_type = MATE_MIXER_BACKEND_PULSEAUDIO; } -const MateMixerBackendInfo * -backend_module_get_info (void) +const MateMixerBackendInfo *backend_module_get_info (void) { return &info; } static void -mate_mixer_backend_interface_init (MateMixerBackendInterface *iface) -{ - iface->open = pulse_backend_open; - iface->close = pulse_backend_close; - iface->get_state = pulse_backend_get_state; - iface->set_data = pulse_backend_set_data; - iface->list_devices = pulse_backend_list_devices; - iface->list_streams = pulse_backend_list_streams; - iface->list_cached_streams = pulse_backend_list_cached_streams; - iface->get_default_input_stream = pulse_backend_get_default_input_stream; - iface->set_default_input_stream = pulse_backend_set_default_input_stream; - iface->get_default_output_stream = pulse_backend_get_default_output_stream; - iface->set_default_output_stream = pulse_backend_set_default_output_stream; -} - -static void pulse_backend_class_init (PulseBackendClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerBackendClass *backend_class; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = pulse_backend_dispose; - object_class->finalize = pulse_backend_finalize; - object_class->get_property = pulse_backend_get_property; - - g_object_class_override_property (object_class, PROP_STATE, "state"); - g_object_class_override_property (object_class, PROP_DEFAULT_INPUT, "default-input"); - g_object_class_override_property (object_class, PROP_DEFAULT_OUTPUT, "default-output"); + object_class->dispose = pulse_backend_dispose; + object_class->finalize = pulse_backend_finalize; + + backend_class = MATE_MIXER_BACKEND_CLASS (klass); + backend_class->set_app_info = pulse_backend_set_app_info; + backend_class->set_server_address = pulse_backend_set_server_address; + backend_class->open = pulse_backend_open; + backend_class->close = pulse_backend_close; + backend_class->list_devices = pulse_backend_list_devices; + backend_class->list_streams = pulse_backend_list_streams; + backend_class->list_stored_controls = pulse_backend_list_stored_controls; + backend_class->set_default_input_stream = pulse_backend_set_default_input_stream; + backend_class->set_default_output_stream = pulse_backend_set_default_output_stream; g_type_class_add_private (object_class, sizeof (PulseBackendPrivate)); } -/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */ +/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE() */ static void pulse_backend_class_finalize (PulseBackendClass *klass) { } static void -pulse_backend_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - PulseBackend *pulse; - - pulse = PULSE_BACKEND (object); - - switch (param_id) { - case PROP_STATE: - g_value_set_enum (value, pulse->priv->state); - break; - case PROP_DEFAULT_INPUT: - g_value_set_object (value, pulse->priv->default_source); - break; - case PROP_DEFAULT_OUTPUT: - g_value_set_object (value, pulse->priv->default_sink); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void pulse_backend_init (PulseBackend *pulse) { pulse->priv = G_TYPE_INSTANCE_GET_PRIVATE (pulse, @@ -279,22 +264,46 @@ pulse_backend_init (PulseBackend *pulse) g_direct_equal, NULL, g_object_unref); - pulse->priv->streams = - g_hash_table_new_full (g_int64_hash, - g_int64_equal, - g_free, + pulse->priv->sinks = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, g_object_unref); + pulse->priv->sources = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); + pulse->priv->ext_streams = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + + pulse->priv->sink_inputs = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); + pulse->priv->source_outputs = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); } static void pulse_backend_dispose (GObject *object) { - pulse_backend_close (MATE_MIXER_BACKEND (object)); + MateMixerBackend *backend; + MateMixerState state; + + backend = MATE_MIXER_BACKEND (object); + + state = mate_mixer_backend_get_state (backend); + if (state != MATE_MIXER_STATE_IDLE) + pulse_backend_close (backend); G_OBJECT_CLASS (pulse_backend_parent_class)->dispose (object); } @@ -306,19 +315,24 @@ pulse_backend_finalize (GObject *object) pulse = PULSE_BACKEND (object); - g_free (pulse->priv->app_name); - g_free (pulse->priv->app_id); - g_free (pulse->priv->app_version); - g_free (pulse->priv->app_icon); - g_free (pulse->priv->server_address); + if (pulse->priv->app_info != NULL) + _mate_mixer_app_info_free (pulse->priv->app_info); - g_hash_table_destroy (pulse->priv->devices); - g_hash_table_destroy (pulse->priv->streams); - g_hash_table_destroy (pulse->priv->ext_streams); + g_hash_table_unref (pulse->priv->devices); + g_hash_table_unref (pulse->priv->sinks); + g_hash_table_unref (pulse->priv->sources); + g_hash_table_unref (pulse->priv->ext_streams); + g_hash_table_unref (pulse->priv->sink_inputs); + g_hash_table_unref (pulse->priv->source_outputs); G_OBJECT_CLASS (pulse_backend_parent_class)->finalize (object); } +#define PULSE_APP_NAME(p) (mate_mixer_app_info_get_name (p->priv->app_info)) +#define PULSE_APP_ID(p) (mate_mixer_app_info_get_id (p->priv->app_info)) +#define PULSE_APP_VERSION(p) (mate_mixer_app_info_get_version (p->priv->app_info)) +#define PULSE_APP_ICON(p) (mate_mixer_app_info_get_icon (p->priv->app_info)) + static gboolean pulse_backend_open (MateMixerBackend *backend) { @@ -329,22 +343,22 @@ pulse_backend_open (MateMixerBackend *backend) pulse = PULSE_BACKEND (backend); - if (G_UNLIKELY (pulse->priv->connection != NULL)) { + if G_UNLIKELY (pulse->priv->connection != NULL) { g_warn_if_reached (); return TRUE; } - connection = pulse_connection_new (pulse->priv->app_name, - pulse->priv->app_id, - pulse->priv->app_version, - pulse->priv->app_icon, + connection = pulse_connection_new (PULSE_APP_NAME (pulse), + PULSE_APP_ID (pulse), + PULSE_APP_VERSION (pulse), + PULSE_APP_ICON (pulse), pulse->priv->server_address); /* No connection attempt is made during the construction of the connection, * but it sets up the PulseAudio structures, which might fail in an * unlikely case */ - if (G_UNLIKELY (connection == NULL)) { - change_state (pulse, MATE_MIXER_STATE_FAILED); + if G_UNLIKELY (connection == NULL) { + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_FAILED); return FALSE; } @@ -409,16 +423,21 @@ pulse_backend_open (MateMixerBackend *backend) G_CALLBACK (on_connection_ext_stream_info), pulse); - change_state (pulse, MATE_MIXER_STATE_CONNECTING); + PULSE_CHANGE_STATE (backend, MATE_MIXER_STATE_CONNECTING); /* Connect to the PulseAudio server, this might fail either instantly or * asynchronously, for example when remote connection timeouts */ if (pulse_connection_connect (connection, FALSE) == FALSE) { g_object_unref (connection); - change_state (pulse, MATE_MIXER_STATE_FAILED); + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_FAILED); return FALSE; } + _mate_mixer_backend_set_flags (backend, + MATE_MIXER_BACKEND_HAS_APPLICATION_CONTROLS | + MATE_MIXER_BACKEND_CAN_SET_DEFAULT_INPUT_STREAM | + MATE_MIXER_BACKEND_CAN_SET_DEFAULT_OUTPUT_STREAM); + pulse->priv->connection = connection; return TRUE; } @@ -432,7 +451,10 @@ pulse_backend_close (MateMixerBackend *backend) pulse = PULSE_BACKEND (backend); - connect_source_remove (pulse); + if (pulse->priv->connect_tag != 0) { + g_source_remove (pulse->priv->connect_tag); + pulse->priv->connect_tag = 0; + } if (pulse->priv->connection != NULL) { g_signal_handlers_disconnect_by_data (G_OBJECT (pulse->priv->connection), @@ -441,109 +463,100 @@ pulse_backend_close (MateMixerBackend *backend) g_clear_object (&pulse->priv->connection); } - g_clear_object (&pulse->priv->default_sink); - g_clear_object (&pulse->priv->default_source); - g_hash_table_remove_all (pulse->priv->devices); - g_hash_table_remove_all (pulse->priv->streams); + g_hash_table_remove_all (pulse->priv->sinks); + g_hash_table_remove_all (pulse->priv->sources); g_hash_table_remove_all (pulse->priv->ext_streams); pulse->priv->connected_once = FALSE; - change_state (pulse, MATE_MIXER_STATE_IDLE); + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_IDLE); } -static MateMixerState -pulse_backend_get_state (MateMixerBackend *backend) +static void +pulse_backend_set_app_info (MateMixerBackend *backend, MateMixerAppInfo *info) { - g_return_val_if_fail (PULSE_IS_BACKEND (backend), MATE_MIXER_STATE_UNKNOWN); + PulseBackend *pulse; + + g_return_if_fail (PULSE_IS_BACKEND (backend)); + g_return_if_fail (info != NULL); + + pulse = PULSE_BACKEND (backend); - return PULSE_BACKEND (backend)->priv->state; + if (pulse->priv->app_info != NULL) + _mate_mixer_app_info_free (pulse->priv->app_info); + + pulse->priv->app_info = _mate_mixer_app_info_copy (info); } static void -pulse_backend_set_data (MateMixerBackend *backend, const MateMixerBackendData *data) +pulse_backend_set_server_address (MateMixerBackend *backend, const gchar *address) { - PulseBackend *pulse; - g_return_if_fail (PULSE_IS_BACKEND (backend)); - g_return_if_fail (data != NULL); - pulse = PULSE_BACKEND (backend); + g_free (PULSE_BACKEND (backend)->priv->server_address); - g_free (pulse->priv->app_name); - g_free (pulse->priv->app_id); - g_free (pulse->priv->app_version); - g_free (pulse->priv->app_icon); - g_free (pulse->priv->server_address); - - pulse->priv->app_name = g_strdup (data->app_name); - pulse->priv->app_id = g_strdup (data->app_id); - pulse->priv->app_version = g_strdup (data->app_version); - pulse->priv->app_icon = g_strdup (data->app_icon); - pulse->priv->server_address = g_strdup (data->server_address); + PULSE_BACKEND (backend)->priv->server_address = g_strdup (address); } -static GList * +static const GList * pulse_backend_list_devices (MateMixerBackend *backend) { - GList *list; + PulseBackend *pulse; g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); - /* Convert the hash table to a sorted linked list, this list is expected - * to be cached in the main library */ - list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->devices); - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); + pulse = PULSE_BACKEND (backend); - return g_list_sort (list, compare_devices); + if (pulse->priv->devices_list == NULL) { + pulse->priv->devices_list = g_hash_table_get_values (pulse->priv->devices); + if (pulse->priv->devices_list != NULL) + g_list_foreach (pulse->priv->devices_list, (GFunc) g_object_ref, NULL); } - return NULL; + return pulse->priv->devices_list; } -static GList * +static const GList * pulse_backend_list_streams (MateMixerBackend *backend) { - GList *list; + PulseBackend *pulse; g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); - /* Convert the hash table to a sorted linked list, this list is expected - * to be cached in the main library */ - list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->streams); - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); - - return g_list_sort (list, compare_streams); - } - return NULL; -} + pulse = PULSE_BACKEND (backend); -static GList * -pulse_backend_list_cached_streams (MateMixerBackend *backend) -{ - GList *list; + if (pulse->priv->streams_list == NULL) { + GList *sinks; + GList *sources; - g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); + sinks = g_hash_table_get_values (pulse->priv->sinks); + if (sinks != NULL) + g_list_foreach (sinks, (GFunc) g_object_ref, NULL); - /* Convert the hash table to a sorted linked list, this list is expected - * to be cached in the main library */ - list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->ext_streams); - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); + sources = g_hash_table_get_values (pulse->priv->sources); + if (sources != NULL) + g_list_foreach (sources, (GFunc) g_object_ref, NULL); - return g_list_sort (list, compare_streams); + pulse->priv->streams_list = g_list_concat (sinks, sources); } - return NULL; + return pulse->priv->streams_list; } -static MateMixerStream * -pulse_backend_get_default_input_stream (MateMixerBackend *backend) +static const GList * +pulse_backend_list_stored_controls (MateMixerBackend *backend) { + PulseBackend *pulse; + g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); - return PULSE_BACKEND (backend)->priv->default_source; + pulse = PULSE_BACKEND (backend); + + if (pulse->priv->ext_streams_list == NULL) { + pulse->priv->ext_streams_list = g_hash_table_get_values (pulse->priv->ext_streams); + if (pulse->priv->ext_streams_list != NULL) + g_list_foreach (pulse->priv->ext_streams_list, (GFunc) g_object_ref, NULL); + } + return pulse->priv->ext_streams_list; } static gboolean @@ -562,29 +575,13 @@ pulse_backend_set_default_input_stream (MateMixerBackend *backend, if (pulse_connection_set_default_source (pulse->priv->connection, name) == FALSE) return FALSE; - if (pulse->priv->default_source != NULL) - g_object_unref (pulse->priv->default_source); - - pulse->priv->default_source = g_object_ref (stream); - /* We might be in the process of setting a default source for which the details * are not yet known, make sure the change does not happen */ - g_object_set_data (G_OBJECT (pulse), - "backend-pending-source", - NULL); - - g_object_notify (G_OBJECT (pulse), "default-input"); + PULSE_SET_PENDING_SOURCE_NULL (pulse); + PULSE_SET_DEFAULT_SOURCE (pulse, stream); return TRUE; } -static MateMixerStream * -pulse_backend_get_default_output_stream (MateMixerBackend *backend) -{ - g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL); - - return PULSE_BACKEND (backend)->priv->default_sink; -} - static gboolean pulse_backend_set_default_output_stream (MateMixerBackend *backend, MateMixerStream *stream) @@ -601,18 +598,10 @@ pulse_backend_set_default_output_stream (MateMixerBackend *backend, if (pulse_connection_set_default_sink (pulse->priv->connection, name) == FALSE) return FALSE; - if (pulse->priv->default_sink != NULL) - g_object_unref (pulse->priv->default_sink); - - pulse->priv->default_sink = g_object_ref (stream); - /* We might be in the process of setting a default sink for which the details * are not yet known, make sure the change does not happen */ - g_object_set_data (G_OBJECT (pulse), - "backend-pending-sink", - NULL); - - g_object_notify (G_OBJECT (pulse), "default-output"); + PULSE_SET_PENDING_SINK_NULL (pulse); + PULSE_SET_DEFAULT_SINK (pulse, stream); return TRUE; } @@ -633,41 +622,41 @@ on_connection_state_notify (PulseConnection *connection, * Stream callbacks will unmark available streams and remaining * unavailable streams will be removed when the CONNECTED state * is reached. */ - mark_hanging (pulse); - change_state (pulse, MATE_MIXER_STATE_CONNECTING); + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_CONNECTING); - if (pulse->priv->connect_source == NULL && - pulse_connection_connect (connection, TRUE) == FALSE) { - pulse->priv->connect_source = g_timeout_source_new (200); + if G_UNLIKELY (pulse->priv->connect_tag != 0) + break; - g_source_set_callback (pulse->priv->connect_source, + if (pulse_connection_connect (connection, TRUE) == FALSE) { + GSource *source; + + source = g_timeout_source_new (200); + g_source_set_callback (source, (GSourceFunc) connect_source_reconnect, pulse, - (GDestroyNotify) connect_source_remove); + NULL); + pulse->priv->connect_tag = + g_source_attach (source, g_main_context_get_thread_default ()); - g_source_attach (pulse->priv->connect_source, - g_main_context_get_thread_default ()); + g_source_unref (source); } break; } /* First connection attempt has failed */ - change_state (pulse, MATE_MIXER_STATE_FAILED); + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_FAILED); break; case PULSE_CONNECTION_CONNECTING: case PULSE_CONNECTION_AUTHORIZING: case PULSE_CONNECTION_LOADING: - change_state (pulse, MATE_MIXER_STATE_CONNECTING); + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_CONNECTING); break; case PULSE_CONNECTION_CONNECTED: - if (pulse->priv->connected_once == TRUE) - remove_hanging (pulse); - else - pulse->priv->connected_once = TRUE; + pulse->priv->connected_once = TRUE; - change_state (pulse, MATE_MIXER_STATE_READY); + PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_READY); break; } } @@ -677,18 +666,17 @@ on_connection_server_info (PulseConnection *connection, const pa_server_info *info, PulseBackend *pulse) { - const gchar *name_source = NULL; - const gchar *name_sink = NULL; + MateMixerStream *stream; + const gchar *name_source = NULL; + const gchar *name_sink = NULL; - if (pulse->priv->default_source != NULL) - name_source = mate_mixer_stream_get_name (pulse->priv->default_source); + stream = PULSE_GET_DEFAULT_SOURCE (pulse); + if (stream != NULL) + name_source = mate_mixer_stream_get_name (stream); if (g_strcmp0 (name_source, info->default_source_name) != 0) { - if (pulse->priv->default_source != NULL) - g_clear_object (&pulse->priv->default_source); - if (info->default_source_name != NULL) { - MateMixerStream *stream = g_hash_table_find (pulse->priv->streams, + MateMixerStream *stream = g_hash_table_find (pulse->priv->sources, compare_stream_names, (gpointer) info->default_source_name); @@ -698,20 +686,14 @@ on_connection_server_info (PulseConnection *connection, * When this happens, remember the name of the stream and wait for the * stream info callback. */ if (stream != NULL) { - pulse->priv->default_source = g_object_ref (stream); - g_object_set_data (G_OBJECT (pulse), - "backend-pending-source", - NULL); - - g_debug ("Default input stream changed to %s", info->default_source_name); + PULSE_SET_DEFAULT_SOURCE (pulse, stream); + PULSE_SET_PENDING_SOURCE_NULL (pulse); } else { g_debug ("Default input stream changed to unknown stream %s", info->default_source_name); - g_object_set_data_full (G_OBJECT (pulse), - "backend-pending-source", - g_strdup (info->default_source_name), - g_free); + PULSE_SET_PENDING_SOURCE (pulse, info->default_source_name); + PULSE_SET_DEFAULT_SOURCE (pulse, NULL); /* In most cases (for example changing profile) the stream info * arrives by itself, but do not rely on it and request it explicitely. @@ -721,20 +703,16 @@ on_connection_server_info (PulseConnection *connection, info->default_source_name); } } else - g_debug ("Default input stream unset"); - - g_object_notify (G_OBJECT (pulse), "default-input"); + PULSE_SET_DEFAULT_SOURCE (pulse, NULL); } - if (pulse->priv->default_sink != NULL) - name_sink = mate_mixer_stream_get_name (pulse->priv->default_sink); + stream = PULSE_GET_DEFAULT_SINK (pulse); + if (stream != NULL) + name_sink = mate_mixer_stream_get_name (stream); if (g_strcmp0 (name_sink, info->default_sink_name) != 0) { - if (pulse->priv->default_sink != NULL) - g_clear_object (&pulse->priv->default_sink); - if (info->default_sink_name != NULL) { - MateMixerStream *stream = g_hash_table_find (pulse->priv->streams, + MateMixerStream *stream = g_hash_table_find (pulse->priv->sinks, compare_stream_names, (gpointer) info->default_sink_name); @@ -744,21 +722,14 @@ on_connection_server_info (PulseConnection *connection, * When this happens, remember the name of the stream and wait for the * stream info callback. */ if (stream != NULL) { - pulse->priv->default_sink = g_object_ref (stream); - g_object_set_data (G_OBJECT (pulse), - "backend-pending-sink", - NULL); - - g_debug ("Default output stream changed to %s", info->default_sink_name); - + PULSE_SET_DEFAULT_SINK (pulse, stream); + PULSE_SET_PENDING_SINK_NULL (pulse); } else { g_debug ("Default output stream changed to unknown stream %s", info->default_sink_name); - g_object_set_data_full (G_OBJECT (pulse), - "backend-pending-sink", - g_strdup (info->default_sink_name), - g_free); + PULSE_SET_PENDING_SINK (pulse, info->default_sink_name); + PULSE_SET_DEFAULT_SINK (pulse, NULL); /* In most cases (for example changing profile) the stream info * arrives by itself, but do not rely on it and request it explicitely. @@ -768,12 +739,10 @@ on_connection_server_info (PulseConnection *connection, info->default_sink_name); } } else - g_debug ("Default output stream unset"); - - g_object_notify (G_OBJECT (pulse), "default-output"); + PULSE_SET_DEFAULT_SINK (pulse, NULL); } - if (pulse->priv->state != MATE_MIXER_STATE_READY) + if (mate_mixer_backend_get_state (MATE_MIXER_BACKEND (pulse)) != MATE_MIXER_STATE_READY) g_debug ("Sound server is %s version %s, running on %s", info->server_name, info->server_version, @@ -790,21 +759,17 @@ on_connection_card_info (PulseConnection *connection, device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (info->index)); if (device == NULL) { device = pulse_device_new (connection, info); - if (G_UNLIKELY (device == NULL)) - return; - g_hash_table_insert (pulse->priv->devices, GUINT_TO_POINTER (info->index), device); + g_hash_table_insert (pulse->priv->devices, + GUINT_TO_POINTER (info->index), + device); + free_list_devices (pulse); g_signal_emit_by_name (G_OBJECT (pulse), "device-added", mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); - } else { + } else pulse_device_update (device, info); - - /* The object might be hanging if reconnecting is in progress, remove the - * hanging flag to prevent it from being removed when connected */ - unmark_hanging (pulse, G_OBJECT (device)); - } } static void @@ -813,29 +778,22 @@ on_connection_card_removed (PulseConnection *connection, PulseBackend *pulse) { PulseDevice *device; + gchar *name; device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (index)); - if (G_UNLIKELY (device == NULL)) + if G_UNLIKELY (device == NULL) return; - remove_device (pulse, device); -} + name = g_strdup (mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); -/* PulseAudio uses 32-bit integers as indices for sinks, sink inputs, sources and - * source inputs, these indices are not unique among the different kinds of streams, - * but we want to keep all of them in a single hash table. Allow this by using 64-bit - * hash table keys, use the lower 32 bits for the PulseAudio index and some of the - * higher bits to indicate what kind of stream it is. */ -enum { - HASH_BIT_SINK = (1ULL << 63), - HASH_BIT_SINK_INPUT = (1ULL << 62), - HASH_BIT_SOURCE = (1ULL << 61), - HASH_BIT_SOURCE_OUTPUT = (1ULL << 60) -}; -#define HASH_ID_SINK(idx) (((idx) & 0xffffffff) | HASH_BIT_SINK) -#define HASH_ID_SINK_INPUT(idx) (((idx) & 0xffffffff) | HASH_BIT_SINK_INPUT) -#define HASH_ID_SOURCE(idx) (((idx) & 0xffffffff) | HASH_BIT_SOURCE) -#define HASH_ID_SOURCE_OUTPUT(idx) (((idx) & 0xffffffff) | HASH_BIT_SOURCE_OUTPUT) + g_hash_table_remove (pulse->priv->devices, GUINT_TO_POINTER (index)); + + free_list_devices (pulse); + g_signal_emit_by_name (G_OBJECT (pulse), + "device-removed", + name); + g_free (name); +} static void on_connection_sink_info (PulseConnection *connection, @@ -844,32 +802,36 @@ on_connection_sink_info (PulseConnection *connection, { PulseDevice *device = NULL; PulseStream *stream; - gint64 index = HASH_ID_SINK (info->index); if (info->card != PA_INVALID_INDEX) - device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (info->card)); + device = g_hash_table_lookup (pulse->priv->devices, + GUINT_TO_POINTER (info->card)); - stream = g_hash_table_lookup (pulse->priv->streams, &index); + stream = g_hash_table_lookup (pulse->priv->sinks, GUINT_TO_POINTER (info->index)); if (stream == NULL) { - stream = pulse_sink_new (connection, info, device); - if (G_UNLIKELY (stream == NULL)) - return; + stream = PULSE_STREAM (pulse_sink_new (connection, info, device)); - g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream); + free_list_streams (pulse); + g_hash_table_insert (pulse->priv->sinks, + GUINT_TO_POINTER (info->index), + stream); - g_signal_emit_by_name (G_OBJECT (pulse), - "stream-added", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + if (device != NULL) { + pulse_device_add_stream (device, stream); + } else { + const gchar *name = + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); + /* Only emit when not a part of the device, otherwise emitted by + * the main library */ + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-added", + name); + } /* We might be waiting for this sink to set it as the default */ check_pending_sink (pulse, stream); - } else { - pulse_sink_update (stream, info, device); - - /* The object might be hanging if reconnecting is in progress, remove the - * hanging flag to prevent it from being removed when connected */ - unmark_hanging (pulse, G_OBJECT (stream)); - } + } else + pulse_sink_update (PULSE_SINK (stream), info); } static void @@ -878,13 +840,38 @@ on_connection_sink_removed (PulseConnection *connection, PulseBackend *pulse) { PulseStream *stream; - gint64 index = HASH_ID_SINK (idx); + PulseDevice *device; - stream = g_hash_table_lookup (pulse->priv->streams, &index); - if (G_UNLIKELY (stream == NULL)) + stream = g_hash_table_lookup (pulse->priv->sinks, GUINT_TO_POINTER (idx)); + if G_UNLIKELY (stream == NULL) return; - remove_stream (pulse, stream); + g_object_ref (stream); + + free_list_streams (pulse); + g_hash_table_remove (pulse->priv->sinks, GUINT_TO_POINTER (idx)); + + device = pulse_stream_get_device (stream); + if (device != NULL) { + pulse_device_remove_stream (device, stream); + } else { + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-removed", + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + } + + /* The removed stream might be one of the default streams, this happens + * especially when switching profiles, after which PulseAudio removes the + * old streams and creates new ones with different names */ + if (MATE_MIXER_STREAM (stream) == PULSE_GET_DEFAULT_SINK (pulse)) { + PULSE_SET_DEFAULT_SINK (pulse, NULL); + + /* PulseAudio usually sends a server info update by itself when default + * stream changes, but there is at least one case when it does not - setting + * a card profile to off, so to be sure request an update explicitely */ + pulse_connection_load_server_info (pulse->priv->connection); + } + g_object_unref (stream); } static void @@ -892,40 +879,20 @@ on_connection_sink_input_info (PulseConnection *connection, const pa_sink_input_info *info, PulseBackend *pulse) { - PulseStream *stream; - PulseStream *parent = NULL; - gint64 index; + PulseSink *sink; - if (G_LIKELY (info->sink != PA_INVALID_INDEX)) { - index = HASH_ID_SINK (info->sink); - - parent = g_hash_table_lookup (pulse->priv->streams, &index); - if (G_UNLIKELY (parent == NULL)) - g_debug ("Unknown parent %d of PulseAudio sink input %s", - info->sink, - info->name); - } - - index = HASH_ID_SINK_INPUT (info->index); - - stream = g_hash_table_lookup (pulse->priv->streams, &index); - if (stream == NULL) { - stream = pulse_sink_input_new (connection, info, parent); - if (G_UNLIKELY (stream == NULL)) - return; + if G_UNLIKELY (info->sink == PA_INVALID_INDEX) + return; - g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream); + sink = g_hash_table_lookup (pulse->priv->sinks, GUINT_TO_POINTER (info->sink)); + if G_UNLIKELY (sink == NULL) + return; - g_signal_emit_by_name (G_OBJECT (pulse), - "stream-added", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); - } else { - pulse_sink_input_update (stream, info, parent); + pulse_sink_add_input (sink, info); - /* The object might be hanging if reconnecting is in progress, remove the - * hanging flag to prevent it from being removed when connected */ - unmark_hanging (pulse, G_OBJECT (stream)); - } + g_hash_table_insert (pulse->priv->sink_inputs, + GUINT_TO_POINTER (info->index), + g_object_ref (sink)); } static void @@ -933,14 +900,13 @@ on_connection_sink_input_removed (PulseConnection *connection, guint idx, PulseBackend *pulse) { - PulseStream *stream; - gint64 index = HASH_ID_SINK_INPUT (idx); + PulseSink *sink; - stream = g_hash_table_lookup (pulse->priv->streams, &index); - if (G_UNLIKELY (stream == NULL)) + sink = g_hash_table_lookup (pulse->priv->sink_inputs, GUINT_TO_POINTER (idx)); + if G_UNLIKELY (sink == NULL) return; - remove_stream (pulse, stream); + pulse_sink_remove_input (sink, idx); } static void @@ -950,36 +916,40 @@ on_connection_source_info (PulseConnection *connection, { PulseDevice *device = NULL; PulseStream *stream; - gint64 index = HASH_ID_SOURCE (info->index); /* Skip monitor streams */ if (info->monitor_of_sink != PA_INVALID_INDEX) return; if (info->card != PA_INVALID_INDEX) - device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (info->card)); + device = g_hash_table_lookup (pulse->priv->devices, + GUINT_TO_POINTER (info->card)); - stream = g_hash_table_lookup (pulse->priv->streams, &index); + stream = g_hash_table_lookup (pulse->priv->sources, GUINT_TO_POINTER (info->index)); if (stream == NULL) { - stream = pulse_source_new (connection, info, device); - if (G_UNLIKELY (stream == NULL)) - return; + stream = PULSE_STREAM (pulse_source_new (connection, info, device)); - g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream); + free_list_streams (pulse); + g_hash_table_insert (pulse->priv->sources, + GUINT_TO_POINTER (info->index), + stream); - g_signal_emit_by_name (G_OBJECT (pulse), - "stream-added", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + if (device != NULL) { + pulse_device_add_stream (device, stream); + } else { + const gchar *name = + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); + /* Only emit when not a part of the device, otherwise emitted by + * the main library */ + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-added", + name); + } /* We might be waiting for this source to set it as the default */ check_pending_source (pulse, stream); - } else { - pulse_source_update (stream, info, device); - - /* The object might be hanging if reconnecting is in progress, remove the - * hanging flag to prevent it from being removed when connected */ - unmark_hanging (pulse, G_OBJECT (stream)); - } + } else + pulse_source_update (PULSE_SOURCE (stream), info); } static void @@ -987,14 +957,39 @@ on_connection_source_removed (PulseConnection *connection, guint idx, PulseBackend *pulse) { + PulseDevice *device; PulseStream *stream; - gint64 index = HASH_ID_SOURCE (idx); - stream = g_hash_table_lookup (pulse->priv->streams, &index); - if (G_UNLIKELY (stream == NULL)) + stream = g_hash_table_lookup (pulse->priv->sources, GUINT_TO_POINTER (idx)); + if G_UNLIKELY (stream == NULL) return; - remove_stream (pulse, stream); + g_object_ref (stream); + + free_list_streams (pulse); + g_hash_table_remove (pulse->priv->sources, GUINT_TO_POINTER (idx)); + + device = pulse_stream_get_device (stream); + if (device != NULL) { + pulse_device_remove_stream (device, stream); + } else { + g_signal_emit_by_name (G_OBJECT (pulse), + "stream-removed", + mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + } + + /* The removed stream might be one of the default streams, this happens + * especially when switching profiles, after which PulseAudio removes the + * old streams and creates new ones with different names */ + if (MATE_MIXER_STREAM (stream) == PULSE_GET_DEFAULT_SOURCE (pulse)) { + PULSE_SET_DEFAULT_SOURCE (pulse, NULL); + + /* PulseAudio usually sends a server info update by itself when default + * stream changes, but there is at least one case when it does not - setting + * a card profile to off, so to be sure request an update explicitely */ + pulse_connection_load_server_info (pulse->priv->connection); + } + g_object_unref (stream); } static void @@ -1002,39 +997,20 @@ on_connection_source_output_info (PulseConnection *connection, const pa_source_output_info *info, PulseBackend *pulse) { - PulseStream *stream; - PulseStream *parent = NULL; - gint64 index; - - if (G_LIKELY (info->source != PA_INVALID_INDEX)) { - index = HASH_ID_SOURCE (info->source); - - /* Most likely a monitor source that we have skipped */ - parent = g_hash_table_lookup (pulse->priv->streams, &index); - if (parent == NULL) - return; - } + PulseSource *source; - index = HASH_ID_SOURCE_OUTPUT (info->index); - - stream = g_hash_table_lookup (pulse->priv->streams, &index); - if (stream == NULL) { - stream = pulse_source_output_new (connection, info, parent); - if (G_UNLIKELY (stream == NULL)) - return; + if G_UNLIKELY (info->source == PA_INVALID_INDEX) + return; - g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream); + source = g_hash_table_lookup (pulse->priv->sources, GUINT_TO_POINTER (info->source)); + if G_UNLIKELY (source == NULL) + return; - g_signal_emit_by_name (G_OBJECT (pulse), - "stream-added", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); - } else { - pulse_source_output_update (stream, info, parent); + pulse_source_add_output (source, info); - /* The object might be hanging if reconnecting is in progress, remove the - * hanging flag to prevent it from being removed when connected */ - unmark_hanging (pulse, G_OBJECT (stream)); - } + g_hash_table_insert (pulse->priv->source_outputs, + GUINT_TO_POINTER (info->index), + g_object_ref (source)); } static void @@ -1042,14 +1018,13 @@ on_connection_source_output_removed (PulseConnection *connection, guint idx, PulseBackend *pulse) { - PulseStream *stream; - gint64 index = HASH_ID_SOURCE_OUTPUT (idx); + PulseSource *source; - stream = g_hash_table_lookup (pulse->priv->streams, &index); - if (G_UNLIKELY (stream == NULL)) + source = g_hash_table_lookup (pulse->priv->source_outputs, GUINT_TO_POINTER (idx)); + if G_UNLIKELY (source == NULL) return; - remove_stream (pulse, stream); + pulse_source_remove_output (source, idx); } static void @@ -1057,63 +1032,75 @@ on_connection_ext_stream_info (PulseConnection *connection, const pa_ext_stream_restore_info *info, PulseBackend *pulse) { - PulseStream *stream; - PulseStream *parent = NULL; + PulseExtStream *ext; + PulseStream *parent = NULL; - if (G_LIKELY (info->device != NULL)) - parent = g_hash_table_find (pulse->priv->streams, compare_stream_names, + if (info->device != NULL) { + parent = g_hash_table_find (pulse->priv->sinks, compare_stream_names, (gpointer) info->device); - stream = g_hash_table_lookup (pulse->priv->ext_streams, info->name); - if (stream == NULL) { - stream = pulse_ext_stream_new (connection, info, parent); - if (G_UNLIKELY (stream == NULL)) - return; + if (parent == NULL) + parent = g_hash_table_find (pulse->priv->sources, compare_stream_names, + (gpointer) info->device); + } + + ext = g_hash_table_lookup (pulse->priv->ext_streams, info->name); + if (ext == NULL) { + ext = pulse_ext_stream_new (connection, info, parent); - g_hash_table_insert (pulse->priv->ext_streams, g_strdup (info->name), stream); + free_list_ext_streams (pulse); + g_hash_table_insert (pulse->priv->ext_streams, + g_strdup (info->name), + ext); g_signal_emit_by_name (G_OBJECT (pulse), - "cached-stream-added", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); + "stored-control-added", + mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (ext))); } else { - pulse_ext_stream_update (stream, info, parent); + pulse_ext_stream_update (ext, info, parent); /* The object might be hanging if ext-streams are being loaded, remove * the hanging flag to prevent it from being removed */ - unmark_hanging (pulse, G_OBJECT (stream)); + PULSE_UNSET_HANGING (ext); } } static void on_connection_ext_stream_loading (PulseConnection *connection, PulseBackend *pulse) { - mark_hanging_hash (pulse->priv->ext_streams); + GHashTableIter iter; + gpointer ext; + + g_hash_table_iter_init (&iter, pulse->priv->ext_streams); + + while (g_hash_table_iter_next (&iter, NULL, &ext) == TRUE) + PULSE_SET_HANGING (ext); } static void on_connection_ext_stream_loaded (PulseConnection *connection, PulseBackend *pulse) { GHashTableIter iter; - gpointer value; + gpointer name; + gpointer ext; g_hash_table_iter_init (&iter, pulse->priv->ext_streams); - while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) { - guint hanging = - GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value), "backend-hanging")); + while (g_hash_table_iter_next (&iter, &name, &ext) == TRUE) { + if (PULSE_GET_HANGING (ext) == FALSE) + continue; - if (hanging == 1) { - gchar *name = g_strdup ((const gchar *) value); + free_list_ext_streams (pulse); + g_hash_table_remove (pulse->priv->ext_streams, (gconstpointer) name); - g_hash_table_remove (pulse->priv->ext_streams, (gconstpointer) name); - g_signal_emit_by_name (G_OBJECT (pulse), - "cached-stream-removed", - name); - g_free (name); - } + g_signal_emit_by_name (G_OBJECT (pulse), + "stored-control-removed", + name); } + g_debug ("Ext-streams refreshed"); } +// XXX rename static gboolean connect_source_reconnect (PulseBackend *pulse) { @@ -1121,16 +1108,10 @@ connect_source_reconnect (PulseBackend *pulse) * and wait for the connection state notifications, otherwise this function * will be called again */ if (pulse_connection_connect (pulse->priv->connection, TRUE) == TRUE) { - connect_source_remove (pulse); - return FALSE; + pulse->priv->connect_tag = 0; + return G_SOURCE_REMOVE; } - return TRUE; -} - -static void -connect_source_remove (PulseBackend *pulse) -{ - g_clear_pointer (&pulse->priv->connect_source, g_source_unref); + return G_SOURCE_CONTINUE; } static void @@ -1141,7 +1122,7 @@ check_pending_sink (PulseBackend *pulse, PulseStream *stream) /* See if the currently added sream matches the default input stream * we are waiting for */ - pending = g_object_get_data (G_OBJECT (pulse), "backend-pending-sink"); + pending = PULSE_GET_PENDING_SINK (pulse); if (pending == NULL) return; @@ -1149,14 +1130,10 @@ check_pending_sink (PulseBackend *pulse, PulseStream *stream) if (g_strcmp0 (pending, name) != 0) return; - pulse->priv->default_sink = g_object_ref (stream); - g_object_set_data (G_OBJECT (pulse), - "backend-pending-sink", - NULL); + g_debug ("Setting default output stream to pending stream %s", name); - g_debug ("Default output stream changed to pending stream %s", name); - - g_object_notify (G_OBJECT (pulse), "default-output"); + PULSE_SET_PENDING_SINK_NULL (pulse); + PULSE_SET_DEFAULT_SINK (pulse, stream); } static void @@ -1167,7 +1144,7 @@ check_pending_source (PulseBackend *pulse, PulseStream *stream) /* See if the currently added sream matches the default input stream * we are waiting for */ - pending = g_object_get_data (G_OBJECT (pulse), "backend-pending-source"); + pending = PULSE_GET_PENDING_SOURCE (pulse); if (pending == NULL) return; @@ -1175,160 +1152,43 @@ check_pending_source (PulseBackend *pulse, PulseStream *stream) if (g_strcmp0 (pending, name) != 0) return; - pulse->priv->default_source = g_object_ref (stream); - g_object_set_data (G_OBJECT (pulse), - "backend-pending-source", - NULL); - - g_debug ("Default input stream changed to pending stream %s", name); + g_debug ("Setting default input stream to pending stream %s", name); - g_object_notify (G_OBJECT (pulse), "default-input"); + PULSE_SET_PENDING_SOURCE_NULL (pulse); + PULSE_SET_DEFAULT_SOURCE (pulse, stream); } static void -mark_hanging (PulseBackend *pulse) +free_list_devices (PulseBackend *pulse) { - /* Mark devices and streams as hanging, ext-streams are handled separately */ - mark_hanging_hash (pulse->priv->devices); - mark_hanging_hash (pulse->priv->streams); -} - -static void -mark_hanging_hash (GHashTable *hash) -{ - GHashTableIter iter; - gpointer value; - - g_hash_table_iter_init (&iter, hash); - - while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) - g_object_set_data (G_OBJECT (value), "backend-hanging", GUINT_TO_POINTER (1)); -} - -static void -unmark_hanging (PulseBackend *pulse, GObject *object) -{ - if (pulse->priv->connected_once == FALSE) - return; - if (pulse->priv->state == MATE_MIXER_STATE_READY) + if (pulse->priv->devices_list == NULL) return; - g_object_steal_data (object, "backend-hanging"); -} - -static void -remove_hanging (PulseBackend *pulse) -{ - GHashTableIter iter; - gpointer value; - - g_hash_table_iter_init (&iter, pulse->priv->devices); - - while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) { - guint hanging = - GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value), "backend-hanging")); - - if (hanging == 1) - remove_device (pulse, PULSE_DEVICE (value)); - } - - g_hash_table_iter_init (&iter, pulse->priv->streams); - - while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) { - guint hanging = - GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value), "backend-hanging")); + g_list_free_full (pulse->priv->devices_list, g_object_unref); - if (hanging == 1) - remove_stream (pulse, PULSE_STREAM (value)); - } -} - -static void -remove_device (PulseBackend *pulse, PulseDevice *device) -{ - gchar *name; - - name = g_strdup (mate_mixer_device_get_name (MATE_MIXER_DEVICE (device))); - - g_hash_table_remove (pulse->priv->devices, - GUINT_TO_POINTER (pulse_device_get_index (device))); - - g_signal_emit_by_name (G_OBJECT (pulse), "device-removed", name); - g_free (name); + pulse->priv->devices_list = NULL; } static void -remove_stream (PulseBackend *pulse, PulseStream *stream) +free_list_streams (PulseBackend *pulse) { - gchar *name; - guint32 idx; - gint64 index; - gboolean reload = FALSE; - - /* The removed stream might be one of the default streams, this happens - * especially when switching profiles, after which PulseAudio removes the - * old streams and creates new ones with different names */ - if (MATE_MIXER_STREAM (stream) == pulse->priv->default_sink) { - g_clear_object (&pulse->priv->default_sink); - - g_object_notify (G_OBJECT (pulse), "default-output"); - reload = TRUE; - } - else if (MATE_MIXER_STREAM (stream) == pulse->priv->default_source) { - g_clear_object (&pulse->priv->default_source); - - g_object_notify (G_OBJECT (pulse), "default-input"); - reload = TRUE; - } - - idx = pulse_stream_get_index (stream); - - if (PULSE_IS_SINK (stream)) - index = HASH_ID_SINK (idx); - else if (PULSE_IS_SINK_INPUT (stream)) - index = HASH_ID_SINK_INPUT (idx); - else if (PULSE_IS_SOURCE (stream)) - index = HASH_ID_SOURCE (idx); - else - index = HASH_ID_SOURCE_OUTPUT (idx); - - name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream))); - - g_hash_table_remove (pulse->priv->streams, &index); + if (pulse->priv->streams_list == NULL) + return; - /* PulseAudio usually sends a server info update by itself when default - * stream changes, but there is at least one case when it does not - setting - * a card profile to off, so to be sure request an update explicitely */ - if (reload == TRUE) - pulse_connection_load_server_info (pulse->priv->connection); + g_list_free_full (pulse->priv->streams_list, g_object_unref); - g_signal_emit_by_name (G_OBJECT (pulse), "stream-removed", name); - g_free (name); + pulse->priv->streams_list = NULL; } static void -change_state (PulseBackend *backend, MateMixerState state) +free_list_ext_streams (PulseBackend *pulse) { - if (backend->priv->state == state) + if (pulse->priv->ext_streams_list == NULL) return; - backend->priv->state = state; - - g_object_notify (G_OBJECT (backend), "state"); -} + g_list_free_full (pulse->priv->ext_streams_list, g_object_unref); -static gint -compare_devices (gconstpointer a, gconstpointer b) -{ - return strcmp (mate_mixer_device_get_name (MATE_MIXER_DEVICE (a)), - mate_mixer_device_get_name (MATE_MIXER_DEVICE (b))); -} - -static gint -compare_streams (gconstpointer a, gconstpointer b) -{ - return strcmp (mate_mixer_stream_get_name (MATE_MIXER_STREAM (a)), - mate_mixer_stream_get_name (MATE_MIXER_STREAM (b))); + pulse->priv->ext_streams_list = NULL; } static gboolean @@ -1336,5 +1196,5 @@ compare_stream_names (gpointer key, gpointer value, gpointer user_data) { MateMixerStream *stream = MATE_MIXER_STREAM (value); - return !strcmp (mate_mixer_stream_get_name (stream), (const gchar *) user_data); + return strcmp (mate_mixer_stream_get_name (stream), (const gchar *) user_data) == 0; } diff --git a/backends/pulse/pulse-backend.h b/backends/pulse/pulse-backend.h index bd1face..2c4b8a8 100644 --- a/backends/pulse/pulse-backend.h +++ b/backends/pulse/pulse-backend.h @@ -20,8 +20,10 @@ #include <glib.h> #include <glib-object.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> -#include <libmatemixer/matemixer-backend-module.h> +#include "pulse-types.h" #define PULSE_TYPE_BACKEND \ (pulse_backend_get_type ()) @@ -36,13 +38,12 @@ #define PULSE_BACKEND_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_BACKEND, PulseBackendClass)) -typedef struct _PulseBackend PulseBackend; typedef struct _PulseBackendClass PulseBackendClass; typedef struct _PulseBackendPrivate PulseBackendPrivate; struct _PulseBackend { - GObject parent; + MateMixerBackend parent; /*< private >*/ PulseBackendPrivate *priv; @@ -50,7 +51,7 @@ struct _PulseBackend struct _PulseBackendClass { - GObjectClass parent_class; + MateMixerBackendClass parent_class; }; GType pulse_backend_get_type (void) G_GNUC_CONST; diff --git a/backends/pulse/pulse-client-stream.c b/backends/pulse/pulse-client-stream.c deleted file mode 100644 index b99c498..0000000 --- a/backends/pulse/pulse-client-stream.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include <string.h> -#include <glib.h> -#include <glib-object.h> - -#include <libmatemixer/matemixer-client-stream.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> - -#include <pulse/pulseaudio.h> - -#include "pulse-client-stream.h" -#include "pulse-sink.h" -#include "pulse-source.h" -#include "pulse-stream.h" - -struct _PulseClientStreamPrivate -{ - gchar *app_name; - gchar *app_id; - gchar *app_version; - gchar *app_icon; - MateMixerStream *parent; - MateMixerClientStreamFlags flags; - MateMixerClientStreamRole role; -}; - -enum { - PROP_0, - PROP_CLIENT_FLAGS, - PROP_ROLE, - PROP_PARENT, - PROP_APP_NAME, - PROP_APP_ID, - PROP_APP_VERSION, - PROP_APP_ICON -}; - -enum { - REMOVED, - N_SIGNALS -}; - -static guint signals[N_SIGNALS] = { 0, }; - -static void mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface); - -static void pulse_client_stream_class_init (PulseClientStreamClass *klass); - -static void pulse_client_stream_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); - -static void pulse_client_stream_init (PulseClientStream *client); -static void pulse_client_stream_dispose (GObject *object); -static void pulse_client_stream_finalize (GObject *object); - -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseClientStream, pulse_client_stream, PULSE_TYPE_STREAM, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_CLIENT_STREAM, - mate_mixer_client_stream_interface_init)) - -static MateMixerClientStreamFlags pulse_client_stream_get_flags (MateMixerClientStream *client); -static MateMixerClientStreamRole pulse_client_stream_get_role (MateMixerClientStream *client); - -static MateMixerStream * pulse_client_stream_get_parent (MateMixerClientStream *client); -static gboolean pulse_client_stream_set_parent (MateMixerClientStream *client, - MateMixerStream *parent); - -static gboolean pulse_client_stream_remove (MateMixerClientStream *client); - -static const gchar * pulse_client_stream_get_app_name (MateMixerClientStream *client); -static const gchar * pulse_client_stream_get_app_id (MateMixerClientStream *client); -static const gchar * pulse_client_stream_get_app_version (MateMixerClientStream *client); -static const gchar * pulse_client_stream_get_app_icon (MateMixerClientStream *client); - -static void -mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface) -{ - iface->get_flags = pulse_client_stream_get_flags; - iface->get_role = pulse_client_stream_get_role; - iface->get_parent = pulse_client_stream_get_parent; - iface->set_parent = pulse_client_stream_set_parent; - iface->remove = pulse_client_stream_remove; - iface->get_app_name = pulse_client_stream_get_app_name; - iface->get_app_id = pulse_client_stream_get_app_id; - iface->get_app_version = pulse_client_stream_get_app_version; - iface->get_app_icon = pulse_client_stream_get_app_icon; -} - -static void -pulse_client_stream_class_init (PulseClientStreamClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = pulse_client_stream_dispose; - object_class->finalize = pulse_client_stream_finalize; - object_class->get_property = pulse_client_stream_get_property; - - signals[REMOVED] = - g_signal_new ("removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (PulseClientStreamClass, removed), - NULL, - NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0, - G_TYPE_NONE); - - g_object_class_override_property (object_class, PROP_CLIENT_FLAGS, "client-flags"); - g_object_class_override_property (object_class, PROP_ROLE, "role"); - g_object_class_override_property (object_class, PROP_PARENT, "parent"); - g_object_class_override_property (object_class, PROP_APP_NAME, "app-name"); - g_object_class_override_property (object_class, PROP_APP_ID, "app-id"); - g_object_class_override_property (object_class, PROP_APP_VERSION, "app-version"); - g_object_class_override_property (object_class, PROP_APP_ICON, "app-icon"); - - g_type_class_add_private (object_class, sizeof (PulseClientStreamPrivate)); -} - -static void -pulse_client_stream_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - PulseClientStream *client; - - client = PULSE_CLIENT_STREAM (object); - - switch (param_id) { - case PROP_CLIENT_FLAGS: - g_value_set_flags (value, client->priv->flags); - break; - case PROP_ROLE: - g_value_set_enum (value, client->priv->role); - break; - case PROP_PARENT: - g_value_set_object (value, client->priv->parent); - break; - case PROP_APP_NAME: - g_value_set_string (value, client->priv->app_name); - break; - case PROP_APP_ID: - g_value_set_string (value, client->priv->app_id); - break; - case PROP_APP_VERSION: - g_value_set_string (value, client->priv->app_version); - break; - case PROP_APP_ICON: - g_value_set_string (value, client->priv->app_icon); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -pulse_client_stream_init (PulseClientStream *client) -{ - client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, - PULSE_TYPE_CLIENT_STREAM, - PulseClientStreamPrivate); -} - -static void -pulse_client_stream_dispose (GObject *object) -{ - PulseClientStream *client; - - client = PULSE_CLIENT_STREAM (object); - - g_clear_object (&client->priv->parent); - - G_OBJECT_CLASS (pulse_client_stream_parent_class)->dispose (object); -} - -static void -pulse_client_stream_finalize (GObject *object) -{ - PulseClientStream *client; - - client = PULSE_CLIENT_STREAM (object); - - g_free (client->priv->app_name); - g_free (client->priv->app_id); - g_free (client->priv->app_version); - g_free (client->priv->app_icon); - - G_OBJECT_CLASS (pulse_client_stream_parent_class)->finalize (object); -} - -gboolean -pulse_client_stream_update_flags (PulseClientStream *pclient, - MateMixerClientStreamFlags flags) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (pclient->priv->flags != flags) { - pclient->priv->flags = flags; - - g_object_notify (G_OBJECT (pclient), "client-flags"); - } - return TRUE; -} - -gboolean -pulse_client_stream_update_parent (PulseClientStream *pclient, MateMixerStream *parent) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (pclient->priv->parent != parent) { - g_clear_object (&pclient->priv->parent); - - if (G_LIKELY (parent != NULL)) - pclient->priv->parent = g_object_ref (parent); - - g_object_notify (G_OBJECT (pclient), "parent"); - } - return TRUE; -} - -gboolean -pulse_client_stream_update_role (PulseClientStream *pclient, - MateMixerClientStreamRole role) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (pclient->priv->role != role) { - pclient->priv->role = role; - - g_object_notify (G_OBJECT (pclient), "role"); - } - return TRUE; -} - -gboolean -pulse_client_stream_update_app_name (PulseClientStream *pclient, const gchar *app_name) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (g_strcmp0 (pclient->priv->app_name, app_name) != 0) { - g_free (pclient->priv->app_name); - pclient->priv->app_name = g_strdup (app_name); - - g_object_notify (G_OBJECT (pclient), "app-name"); - } - return TRUE; -} - -gboolean -pulse_client_stream_update_app_id (PulseClientStream *pclient, const gchar *app_id) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (g_strcmp0 (pclient->priv->app_id, app_id) != 0) { - g_free (pclient->priv->app_id); - pclient->priv->app_id = g_strdup (app_id); - - g_object_notify (G_OBJECT (pclient), "app-id"); - } - return TRUE; -} - -gboolean -pulse_client_stream_update_app_version (PulseClientStream *pclient, const gchar *app_version) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (g_strcmp0 (pclient->priv->app_version, app_version) != 0) { - g_free (pclient->priv->app_version); - pclient->priv->app_version = g_strdup (app_version); - - g_object_notify (G_OBJECT (pclient), "app-version"); - } - return TRUE; -} - -gboolean -pulse_client_stream_update_app_icon (PulseClientStream *pclient, const gchar *app_icon) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE); - - if (g_strcmp0 (pclient->priv->app_icon, app_icon) != 0) { - g_free (pclient->priv->app_icon); - pclient->priv->app_icon = g_strdup (app_icon); - - g_object_notify (G_OBJECT (pclient), "app-icon"); - } - return TRUE; -} - -static MateMixerClientStreamFlags -pulse_client_stream_get_flags (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_NO_FLAGS); - - return PULSE_CLIENT_STREAM (client)->priv->flags; -} - -static MateMixerClientStreamRole -pulse_client_stream_get_role (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_ROLE_NONE); - - return PULSE_CLIENT_STREAM (client)->priv->role; -} - -static MateMixerStream * -pulse_client_stream_get_parent (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); - - return PULSE_CLIENT_STREAM (client)->priv->parent; -} - -static gboolean -pulse_client_stream_set_parent (MateMixerClientStream *client, MateMixerStream *parent) -{ - MateMixerStreamFlags flags; - PulseClientStream *pclient; - PulseClientStreamClass *klass; - - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE); - g_return_val_if_fail (PULSE_IS_STREAM (parent), FALSE); - - pclient = PULSE_CLIENT_STREAM (client); - klass = PULSE_CLIENT_STREAM_GET_CLASS (pclient); - - if (pclient->priv->parent == parent) - return TRUE; - - flags = mate_mixer_stream_get_flags (MATE_MIXER_STREAM (pclient)); - - /* Validate the parent stream */ - if (flags & MATE_MIXER_STREAM_INPUT && !PULSE_IS_SOURCE (parent)) { - g_warning ("Could not change stream parent to %s: not a parent input stream", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent))); - return FALSE; - } else if (flags & MATE_MIXER_STREAM_OUTPUT && !PULSE_IS_SINK (parent)) { - g_warning ("Could not change stream parent to %s: not a parent output stream", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent))); - return FALSE; - } - - /* Set the parent */ - if (klass->set_parent (pclient, PULSE_STREAM (parent)) == FALSE) - return FALSE; - - if (pclient->priv->parent != NULL) - g_object_unref (pclient->priv->parent); - - /* It is allowed for the parent to be NULL when the instance is created, but - * changing the parent requires a valid parent stream */ - pclient->priv->parent = g_object_ref (parent); - - g_object_notify (G_OBJECT (client), "parent"); - return TRUE; -} - -static gboolean -pulse_client_stream_remove (MateMixerClientStream *client) -{ - PulseClientStream *pclient; - PulseClientStreamClass *klass; - - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE); - - pclient = PULSE_CLIENT_STREAM (client); - klass = PULSE_CLIENT_STREAM_GET_CLASS (pclient); - - if (klass->remove (pclient) == FALSE) - return FALSE; - - // XXX handle this in the backend - g_signal_emit (G_OBJECT (client), - signals[REMOVED], - 0); - - return TRUE; -} - -static const gchar * -pulse_client_stream_get_app_name (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); - - return PULSE_CLIENT_STREAM (client)->priv->app_name; -} - -static const gchar * -pulse_client_stream_get_app_id (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); - - return PULSE_CLIENT_STREAM (client)->priv->app_id; -} - -static const gchar * -pulse_client_stream_get_app_version (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); - - return PULSE_CLIENT_STREAM (client)->priv->app_version; -} - -static const gchar * -pulse_client_stream_get_app_icon (MateMixerClientStream *client) -{ - g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL); - - return PULSE_CLIENT_STREAM (client)->priv->app_icon; -} diff --git a/backends/pulse/pulse-client-stream.h b/backends/pulse/pulse-client-stream.h deleted file mode 100644 index fe24dc3..0000000 --- a/backends/pulse/pulse-client-stream.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef PULSE_CLIENT_STREAM_H -#define PULSE_CLIENT_STREAM_H - -#include <glib.h> -#include <glib-object.h> - -#include <libmatemixer/matemixer-client-stream.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> - -#include "pulse-stream.h" - -G_BEGIN_DECLS - -#define PULSE_TYPE_CLIENT_STREAM \ - (pulse_client_stream_get_type ()) -#define PULSE_CLIENT_STREAM(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_CLIENT_STREAM, PulseClientStream)) -#define PULSE_IS_CLIENT_STREAM(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_CLIENT_STREAM)) -#define PULSE_CLIENT_STREAM_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass)) -#define PULSE_IS_CLIENT_STREAM_CLASS(k) \ - (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CLIENT_STREAM)) -#define PULSE_CLIENT_STREAM_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass)) - -typedef struct _PulseClientStream PulseClientStream; -typedef struct _PulseClientStreamClass PulseClientStreamClass; -typedef struct _PulseClientStreamPrivate PulseClientStreamPrivate; - -struct _PulseClientStream -{ - PulseStream parent; - - /*< private >*/ - PulseClientStreamPrivate *priv; -}; - -struct _PulseClientStreamClass -{ - PulseStreamClass parent_class; - - /*< private >*/ - /* Virtual table */ - gboolean (*set_parent) (PulseClientStream *pclient, - PulseStream *pstream); - - gboolean (*remove) (PulseClientStream *pclient); - - /* Signals */ - void (*removed) (PulseClientStream *pclient); -}; - -GType pulse_client_stream_get_type (void) G_GNUC_CONST; - -gboolean pulse_client_stream_update_flags (PulseClientStream *pclient, - MateMixerClientStreamFlags flags); - -gboolean pulse_client_stream_update_role (PulseClientStream *pclient, - MateMixerClientStreamRole role); - -gboolean pulse_client_stream_update_parent (PulseClientStream *pclient, - MateMixerStream *parent); - -gboolean pulse_client_stream_update_app_name (PulseClientStream *pclient, - const gchar *app_name); -gboolean pulse_client_stream_update_app_id (PulseClientStream *pclient, - const gchar *app_id); -gboolean pulse_client_stream_update_app_version (PulseClientStream *pclient, - const gchar *app_version); -gboolean pulse_client_stream_update_app_icon (PulseClientStream *pclient, - const gchar *app_icon); - -G_END_DECLS - -#endif /* PULSE_CLIENT_STREAM_H */ diff --git a/backends/pulse/pulse-connection.c b/backends/pulse/pulse-connection.c index cc39caf..7344d2e 100644 --- a/backends/pulse/pulse-connection.c +++ b/backends/pulse/pulse-connection.c @@ -31,14 +31,14 @@ struct _PulseConnectionPrivate { - gchar *server; - guint outstanding; - pa_context *context; - pa_proplist *proplist; - pa_glib_mainloop *mainloop; - gboolean ext_streams_loading; - gboolean ext_streams_dirty; - PulseConnectionState state; + gchar *server; + guint outstanding; + pa_context *context; + pa_proplist *proplist; + pa_glib_mainloop *mainloop; + gboolean ext_streams_loading; + gboolean ext_streams_dirty; + PulseConnectionState state; }; enum { @@ -417,7 +417,7 @@ pulse_connection_new (const gchar *app_name, PulseConnection *connection; mainloop = pa_glib_mainloop_new (g_main_context_get_thread_default ()); - if (G_UNLIKELY (mainloop == NULL)) { + if G_UNLIKELY (mainloop == NULL) { g_warning ("Failed to create PulseAudio main loop"); return NULL; } @@ -468,7 +468,7 @@ pulse_connection_connect (PulseConnection *connection, gboolean wait_for_daemon) context = pa_context_new_with_proplist (mainloop, NULL, connection->priv->proplist); - if (G_UNLIKELY (context == NULL)) { + if G_UNLIKELY (context == NULL) { g_warning ("Failed to create PulseAudio context"); return FALSE; } @@ -774,7 +774,6 @@ pulse_connection_create_monitor (PulseConnection *connection, return pulse_monitor_new (connection->priv->context, connection->priv->proplist, - NULL, index_source, index_sink_input); } @@ -1227,7 +1226,7 @@ load_lists (PulseConnection *connection) GSList *ops = NULL; pa_operation *op; - if (G_UNLIKELY (connection->priv->outstanding > 0)) { + if G_UNLIKELY (connection->priv->outstanding > 0) { g_warn_if_reached (); return FALSE; } @@ -1235,7 +1234,7 @@ load_lists (PulseConnection *connection) op = pa_context_get_card_info_list (connection->priv->context, pulse_card_info_cb, connection); - if (G_UNLIKELY (op == NULL)) + if G_UNLIKELY (op == NULL) goto error; ops = g_slist_prepend (ops, op); @@ -1243,7 +1242,7 @@ load_lists (PulseConnection *connection) op = pa_context_get_sink_info_list (connection->priv->context, pulse_sink_info_cb, connection); - if (G_UNLIKELY (op == NULL)) + if G_UNLIKELY (op == NULL) goto error; ops = g_slist_prepend (ops, op); @@ -1251,7 +1250,7 @@ load_lists (PulseConnection *connection) op = pa_context_get_sink_input_info_list (connection->priv->context, pulse_sink_input_info_cb, connection); - if (G_UNLIKELY (op == NULL)) + if G_UNLIKELY (op == NULL) goto error; ops = g_slist_prepend (ops, op); @@ -1259,7 +1258,7 @@ load_lists (PulseConnection *connection) op = pa_context_get_source_info_list (connection->priv->context, pulse_source_info_cb, connection); - if (G_UNLIKELY (op == NULL)) + if G_UNLIKELY (op == NULL) goto error; ops = g_slist_prepend (ops, op); @@ -1267,7 +1266,7 @@ load_lists (PulseConnection *connection) op = pa_context_get_source_output_info_list (connection->priv->context, pulse_source_output_info_cb, connection); - if (G_UNLIKELY (op == NULL)) + if G_UNLIKELY (op == NULL) goto error; ops = g_slist_prepend (ops, op); @@ -1303,7 +1302,7 @@ load_list_finished (PulseConnection *connection) * as the final step in the connection process */ connection->priv->outstanding--; - if (G_UNLIKELY (connection->priv->outstanding < 0)) { + if G_UNLIKELY (connection->priv->outstanding < 0) { g_warn_if_reached (); connection->priv->outstanding = 0; } @@ -1311,7 +1310,7 @@ load_list_finished (PulseConnection *connection) if (connection->priv->outstanding == 0) { gboolean ret = pulse_connection_load_server_info (connection); - if (G_UNLIKELY (ret == FALSE)) { + if G_UNLIKELY (ret == FALSE) { pulse_connection_disconnect (connection); return FALSE; } @@ -1640,7 +1639,7 @@ change_state (PulseConnection *connection, PulseConnectionState state) static gboolean process_pulse_operation (PulseConnection *connection, pa_operation *op) { - if (G_UNLIKELY (op == NULL)) { + if G_UNLIKELY (op == NULL) { g_warning ("PulseAudio operation failed: %s", pa_strerror (pa_context_errno (connection->priv->context))); return FALSE; diff --git a/backends/pulse/pulse-connection.h b/backends/pulse/pulse-connection.h index b9119fd..7dc0bfb 100644 --- a/backends/pulse/pulse-connection.h +++ b/backends/pulse/pulse-connection.h @@ -25,7 +25,7 @@ #include <pulse/ext-stream-restore.h> #include "pulse-enums.h" -#include "pulse-monitor.h" +#include "pulse-types.h" G_BEGIN_DECLS @@ -42,7 +42,6 @@ G_BEGIN_DECLS #define PULSE_CONNECTION_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CONNECTION, PulseConnectionClass)) -typedef struct _PulseConnection PulseConnection; typedef struct _PulseConnectionClass PulseConnectionClass; typedef struct _PulseConnectionPrivate PulseConnectionPrivate; @@ -59,7 +58,6 @@ struct _PulseConnectionClass GObjectClass parent_class; /*< private >*/ - /* Signals */ void (*server_info) (PulseConnection *connection, const pa_server_info *info); @@ -201,7 +199,6 @@ gboolean pulse_connection_kill_source_output (PulseConnection gboolean pulse_connection_write_ext_stream (PulseConnection *connection, const pa_ext_stream_restore_info *info); - gboolean pulse_connection_delete_ext_stream (PulseConnection *connection, const gchar *name); diff --git a/backends/pulse/pulse-device-profile.c b/backends/pulse/pulse-device-profile.c new file mode 100644 index 0000000..5487841 --- /dev/null +++ b/backends/pulse/pulse-device-profile.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-device.h" +#include "pulse-device-profile.h" + +struct _PulseDeviceProfilePrivate +{ + guint priority; +}; + +static void pulse_device_profile_class_init (PulseDeviceProfileClass *klass); +static void pulse_device_profile_init (PulseDeviceProfile *profile); + +G_DEFINE_TYPE (PulseDeviceProfile, pulse_device_profile, MATE_MIXER_TYPE_SWITCH_OPTION) + +static void +pulse_device_profile_class_init (PulseDeviceProfileClass *klass) +{ + g_type_class_add_private (klass, sizeof (PulseDeviceProfilePrivate)); +} + +static void +pulse_device_profile_init (PulseDeviceProfile *profile) +{ + profile->priv = G_TYPE_INSTANCE_GET_PRIVATE (profile, + PULSE_TYPE_DEVICE_PROFILE, + PulseDeviceProfilePrivate); +} + +PulseDeviceProfile * +pulse_device_profile_new (const gchar *name, + const gchar *label, + guint priority) +{ + PulseDeviceProfile *profile; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (label != NULL, NULL); + + profile = g_object_new (PULSE_TYPE_DEVICE_PROFILE, + "name", name, + "label", label, + NULL); + + profile->priv->priority = priority; + return profile; +} + +const gchar * +pulse_device_profile_get_name (PulseDeviceProfile *profile) +{ + g_return_val_if_fail (PULSE_IS_DEVICE_PROFILE (profile), NULL); + + return mate_mixer_switch_option_get_name (MATE_MIXER_SWITCH_OPTION (profile)); +} + +guint +pulse_device_profile_get_priority (PulseDeviceProfile *profile) +{ + g_return_val_if_fail (PULSE_IS_DEVICE_PROFILE (profile), 0); + + return profile->priv->priority; +} diff --git a/backends/pulse/pulse-device-profile.h b/backends/pulse/pulse-device-profile.h new file mode 100644 index 0000000..0a9c3f4 --- /dev/null +++ b/backends/pulse/pulse-device-profile.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_DEVICE_PROFILE_H +#define PULSE_DEVICE_PROFILE_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_DEVICE_PROFILE \ + (pulse_device_profile_get_type ()) +#define PULSE_DEVICE_PROFILE(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_DEVICE_PROFILE, PulseDeviceProfile)) +#define PULSE_IS_DEVICE_PROFILE(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_DEVICE_PROFILE)) +#define PULSE_DEVICE_PROFILE_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_DEVICE_PROFILE, PulseDeviceProfileClass)) +#define PULSE_IS_DEVICE_PROFILE_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE_PROFILE)) +#define PULSE_DEVICE_PROFILE_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_DEVICE_PROFILE, PulseDeviceProfileClass)) + +typedef struct _PulseDeviceProfileClass PulseDeviceProfileClass; +typedef struct _PulseDeviceProfilePrivate PulseDeviceProfilePrivate; + +struct _PulseDeviceProfile +{ + MateMixerSwitchOption parent; + + /*< private >*/ + PulseDeviceProfilePrivate *priv; +}; + +struct _PulseDeviceProfileClass +{ + MateMixerSwitchOptionClass parent; +}; + +GType pulse_device_profile_get_type (void) G_GNUC_CONST; + +PulseDeviceProfile *pulse_device_profile_new (const gchar *name, + const gchar *label, + guint priority); + +const gchar * pulse_device_profile_get_name (PulseDeviceProfile *profile); +guint pulse_device_profile_get_priority (PulseDeviceProfile *profile); + +G_END_DECLS + +#endif /* PULSE_DEVICE_PROFILE_H */ diff --git a/backends/pulse/pulse-device-switch.c b/backends/pulse/pulse-device-switch.c new file mode 100644 index 0000000..7a43d0a --- /dev/null +++ b/backends/pulse/pulse-device-switch.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "pulse-connection.h" +#include "pulse-device.h" +#include "pulse-device-profile.h" +#include "pulse-device-switch.h" + +struct _PulseDeviceSwitchPrivate +{ + GList *profiles; + PulseDevice *device; +}; + +enum { + PROP_0, + PROP_DEVICE, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void pulse_device_switch_class_init (PulseDeviceSwitchClass *klass); + +static void pulse_device_switch_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void pulse_device_switch_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void pulse_device_switch_init (PulseDeviceSwitch *swtch); +static void pulse_device_switch_dispose (GObject *object); + +G_DEFINE_TYPE (PulseDeviceSwitch, pulse_device_switch, MATE_MIXER_TYPE_SWITCH) + +static gboolean pulse_device_switch_set_active_option (MateMixerSwitch *mms, + MateMixerSwitchOption *mmso); + +static const GList *pulse_device_switch_list_options (MateMixerSwitch *mms); + +static gint compare_profiles (gconstpointer a, + gconstpointer b); +static gint compare_profile_name (gconstpointer a, + gconstpointer b); + +static void +pulse_device_switch_class_init (PulseDeviceSwitchClass *klass) +{ + GObjectClass *object_class; + MateMixerSwitchClass *switch_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_device_switch_dispose; + object_class->get_property = pulse_device_switch_get_property; + object_class->set_property = pulse_device_switch_set_property; + + switch_class = MATE_MIXER_SWITCH_CLASS (klass); + switch_class->set_active_option = pulse_device_switch_set_active_option; + switch_class->list_options = pulse_device_switch_list_options; + + properties[PROP_DEVICE] = + g_param_spec_object ("device", + "Device", + "PulseAudio device", + PULSE_TYPE_DEVICE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (PulseDeviceSwitchPrivate)); +} + +static void +pulse_device_switch_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + PulseDeviceSwitch *swtch; + + swtch = PULSE_DEVICE_SWITCH (object); + + switch (param_id) { + case PROP_DEVICE: + g_value_set_object (value, swtch->priv->device); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_device_switch_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + PulseDeviceSwitch *swtch; + + swtch = PULSE_DEVICE_SWITCH (object); + + switch (param_id) { + case PROP_DEVICE: + /* Construct-only object */ + swtch->priv->device = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_device_switch_init (PulseDeviceSwitch *swtch) +{ + swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch, + PULSE_TYPE_DEVICE_SWITCH, + PulseDeviceSwitchPrivate); +} + +static void +pulse_device_switch_dispose (GObject *object) +{ + PulseDeviceSwitch *swtch; + + swtch = PULSE_DEVICE_SWITCH (object); + + g_clear_object (&swtch->priv->device); + + G_OBJECT_CLASS (pulse_device_switch_parent_class)->dispose (object); +} + +PulseDeviceSwitch * +pulse_device_switch_new (const gchar *name, const gchar *label, PulseDevice *device) +{ + return g_object_new (PULSE_TYPE_DEVICE_SWITCH, + "name", name, + "label", label, + "role", MATE_MIXER_SWITCH_ROLE_DEVICE_PROFILE, + "device", device, + NULL); +} + +PulseDevice * +pulse_device_switch_get_device (PulseDeviceSwitch *swtch) +{ + g_return_val_if_fail (PULSE_IS_DEVICE_SWITCH (swtch), NULL); + + return swtch->priv->device; +} + +void +pulse_device_switch_add_profile (PulseDeviceSwitch *swtch, PulseDeviceProfile *profile) +{ + g_return_if_fail (PULSE_IS_DEVICE_SWITCH (swtch)); + g_return_if_fail (PULSE_IS_DEVICE_PROFILE (profile)); + + swtch->priv->profiles = g_list_insert_sorted (swtch->priv->profiles, + profile, + compare_profiles); +} + +void +pulse_device_switch_set_active_profile (PulseDeviceSwitch *swtch, + PulseDeviceProfile *profile) +{ + g_return_if_fail (PULSE_IS_DEVICE_SWITCH (swtch)); + g_return_if_fail (PULSE_IS_DEVICE_PROFILE (profile)); + + _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (swtch), + MATE_MIXER_SWITCH_OPTION (profile)); +} + +void +pulse_device_switch_set_active_profile_by_name (PulseDeviceSwitch *swtch, const gchar *name) +{ + GList *item; + + g_return_if_fail (PULSE_IS_DEVICE_SWITCH (swtch)); + g_return_if_fail (name != NULL); + + item = g_list_find_custom (swtch->priv->profiles, name, compare_profile_name); + if G_UNLIKELY (item == NULL) { + g_debug ("Invalid device switch profile name %s", name); + return; + } + pulse_device_switch_set_active_profile (swtch, PULSE_DEVICE_PROFILE (item->data)); +} + +static gboolean +pulse_device_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso) +{ + PulseDevice *device; + const gchar *device_name; + const gchar *profile_name; + + g_return_val_if_fail (PULSE_IS_DEVICE_SWITCH (mms), FALSE); + g_return_val_if_fail (PULSE_IS_DEVICE_PROFILE (mmso), FALSE); + + device = pulse_device_switch_get_device (PULSE_DEVICE_SWITCH (mms)); + if G_UNLIKELY (device == NULL) + return FALSE; + + device_name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)); + profile_name = mate_mixer_switch_option_get_name (mmso); + + return pulse_connection_set_card_profile (pulse_device_get_connection (device), + device_name, + profile_name); +} + +static const GList * +pulse_device_switch_list_options (MateMixerSwitch *swtch) +{ + g_return_val_if_fail (PULSE_IS_DEVICE_SWITCH (swtch), NULL); + + return PULSE_DEVICE_SWITCH (swtch)->priv->profiles; +} + +static gint +compare_profiles (gconstpointer a, gconstpointer b) +{ + return pulse_device_profile_get_priority (PULSE_DEVICE_PROFILE (b)) - + pulse_device_profile_get_priority (PULSE_DEVICE_PROFILE (a)); +} + +static gint +compare_profile_name (gconstpointer a, gconstpointer b) +{ + PulseDeviceProfile *profile = PULSE_DEVICE_PROFILE (a); + const gchar *name = (const gchar *) b; + + return strcmp (pulse_device_profile_get_name (profile), name); +} diff --git a/backends/pulse/pulse-device-switch.h b/backends/pulse/pulse-device-switch.h new file mode 100644 index 0000000..50f4a68 --- /dev/null +++ b/backends/pulse/pulse-device-switch.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_DEVICE_SWITCH_H +#define PULSE_DEVICE_SWITCH_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_DEVICE_SWITCH \ + (pulse_device_switch_get_type ()) +#define PULSE_DEVICE_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_DEVICE_SWITCH, PulseDeviceSwitch)) +#define PULSE_IS_DEVICE_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_DEVICE_SWITCH)) +#define PULSE_DEVICE_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_DEVICE_SWITCH, PulseDeviceSwitchClass)) +#define PULSE_IS_DEVICE_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE_SWITCH)) +#define PULSE_DEVICE_SWITCH_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_DEVICE_SWITCH, PulseDeviceSwitchClass)) + +typedef struct _PulseDeviceSwitchClass PulseDeviceSwitchClass; +typedef struct _PulseDeviceSwitchPrivate PulseDeviceSwitchPrivate; + +struct _PulseDeviceSwitch +{ + MateMixerSwitch parent; + + /*< private >*/ + PulseDeviceSwitchPrivate *priv; +}; + +struct _PulseDeviceSwitchClass +{ + MateMixerSwitchClass parent_class; +}; + +GType pulse_device_switch_get_type (void) G_GNUC_CONST; + +PulseDeviceSwitch *pulse_device_switch_new (const gchar *name, + const gchar *label, + PulseDevice *device); + +PulseDevice * pulse_device_switch_get_device (PulseDeviceSwitch *swtch); + +void pulse_device_switch_add_profile (PulseDeviceSwitch *swtch, + PulseDeviceProfile *profile); + +void pulse_device_switch_set_active_profile (PulseDeviceSwitch *swtch, + PulseDeviceProfile *profile); + +void pulse_device_switch_set_active_profile_by_name (PulseDeviceSwitch *swtch, + const gchar *name); + +G_END_DECLS + +#endif /* PULSE_DEVICE_SWITCH_H */ diff --git a/backends/pulse/pulse-device.c b/backends/pulse/pulse-device.c index 96e06c8..5403712 100644 --- a/backends/pulse/pulse-device.c +++ b/backends/pulse/pulse-device.c @@ -17,45 +17,40 @@ #include <string.h> #include <glib.h> +#include <glib/gi18n.h> #include <glib-object.h> -#include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-device-profile.h> -#include <libmatemixer/matemixer-device-profile-private.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-port.h> -#include <libmatemixer/matemixer-port-private.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" #include "pulse-device.h" +#include "pulse-device-profile.h" +#include "pulse-device-switch.h" +#include "pulse-port.h" +#include "pulse-stream.h" struct _PulseDevicePrivate { - guint32 index; - gchar *name; - gchar *description; - gchar *icon; - GHashTable *ports; - GList *ports_list; - GHashTable *profiles; - GList *profiles_list; - PulseConnection *connection; - MateMixerDeviceProfile *profile; + guint32 index; + GHashTable *ports; + GHashTable *streams; + GList *streams_list; + GList *switches_list; + PulseConnection *connection; + PulseDeviceSwitch *pswitch; }; enum { PROP_0, - PROP_NAME, - PROP_DESCRIPTION, - PROP_ICON, - PROP_ACTIVE_PROFILE, PROP_INDEX, - PROP_CONNECTION + PROP_CONNECTION, + N_PROPERTIES }; -static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface); +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; static void pulse_device_class_init (PulseDeviceClass *klass); @@ -72,60 +67,25 @@ static void pulse_device_init (PulseDevice *device); static void pulse_device_dispose (GObject *object); static void pulse_device_finalize (GObject *object); -G_DEFINE_TYPE_WITH_CODE (PulseDevice, pulse_device, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE, - mate_mixer_device_interface_init)) +G_DEFINE_TYPE (PulseDevice, pulse_device, MATE_MIXER_TYPE_DEVICE) -#if PA_CHECK_VERSION (5, 0, 0) -typedef pa_card_profile_info2 _pa_card_profile_info; -#else -typedef pa_card_profile_info _pa_card_profile_info; -#endif - -static const gchar * pulse_device_get_name (MateMixerDevice *device); -static const gchar * pulse_device_get_description (MateMixerDevice *device); -static const gchar * pulse_device_get_icon (MateMixerDevice *device); +static MateMixerStream *pulse_device_get_stream (MateMixerDevice *mmd, + const gchar *name); -static MateMixerPort * pulse_device_get_port (MateMixerDevice *device, - const gchar *name); -static MateMixerDeviceProfile *pulse_device_get_profile (MateMixerDevice *device, - const gchar *name); +static const GList * pulse_device_list_streams (MateMixerDevice *mmd); +static const GList * pulse_device_list_switches (MateMixerDevice *mmd); -static const GList * pulse_device_list_ports (MateMixerDevice *device); -static const GList * pulse_device_list_profiles (MateMixerDevice *device); +static void pulse_device_load (PulseDevice *device, + const pa_card_info *info); -static MateMixerDeviceProfile *pulse_device_get_active_profile (MateMixerDevice *device); -static gboolean pulse_device_set_active_profile (MateMixerDevice *device, - MateMixerDeviceProfile *profile); - -static void update_port (PulseDevice *device, - pa_card_port_info *p_info); -static void update_profile (PulseDevice *device, - _pa_card_profile_info *p_info); - -static gint compare_ports (gconstpointer a, - gconstpointer b); -static gint compare_profiles (gconstpointer a, - gconstpointer b); - -static void -mate_mixer_device_interface_init (MateMixerDeviceInterface *iface) -{ - iface->get_name = pulse_device_get_name; - iface->get_description = pulse_device_get_description; - iface->get_icon = pulse_device_get_icon; - iface->get_port = pulse_device_get_port; - iface->get_profile = pulse_device_get_profile; - iface->list_ports = pulse_device_list_ports; - iface->list_profiles = pulse_device_list_profiles; - iface->get_active_profile = pulse_device_get_active_profile; - iface->set_active_profile = pulse_device_set_active_profile; -} +static void free_list_streams (PulseDevice *device); +static void free_list_switches (PulseDevice *device); static void pulse_device_class_init (PulseDeviceClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerDeviceClass *device_class; object_class = G_OBJECT_CLASS (klass); object_class->dispose = pulse_device_dispose; @@ -133,31 +93,32 @@ pulse_device_class_init (PulseDeviceClass *klass) object_class->get_property = pulse_device_get_property; object_class->set_property = pulse_device_set_property; - g_object_class_install_property (object_class, - PROP_INDEX, - g_param_spec_uint ("index", - "Index", - "Device index", - 0, - G_MAXUINT, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, - PROP_CONNECTION, - g_param_spec_object ("connection", - "Connection", - "PulseAudio connection", - PULSE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_override_property (object_class, PROP_NAME, "name"); - g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); - g_object_class_override_property (object_class, PROP_ICON, "icon"); - g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile"); + device_class = MATE_MIXER_DEVICE_CLASS (klass); + device_class->get_stream = pulse_device_get_stream; + device_class->list_streams = pulse_device_list_streams; + device_class->list_switches = pulse_device_list_switches; + + properties[PROP_INDEX] = + g_param_spec_uint ("index", + "Index", + "Index of the device", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_CONNECTION] = + g_param_spec_object ("connection", + "Connection", + "PulseAudio connection", + PULSE_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); g_type_class_add_private (object_class, sizeof (PulseDevicePrivate)); } @@ -173,18 +134,6 @@ pulse_device_get_property (GObject *object, device = PULSE_DEVICE (object); switch (param_id) { - case PROP_NAME: - g_value_set_string (value, device->priv->name); - break; - case PROP_DESCRIPTION: - g_value_set_string (value, device->priv->description); - break; - case PROP_ICON: - g_value_set_string (value, device->priv->icon); - break; - case PROP_ACTIVE_PROFILE: - g_value_set_object (value, device->priv->profile); - break; case PROP_INDEX: g_value_set_uint (value, device->priv->index); break; @@ -212,7 +161,6 @@ pulse_device_set_property (GObject *object, device->priv->index = g_value_get_uint (value); break; case PROP_CONNECTION: - /* Construct-only object */ device->priv->connection = g_value_dup_object (value); break; default: @@ -233,10 +181,10 @@ pulse_device_init (PulseDevice *device) g_free, g_object_unref); - device->priv->profiles = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); + device->priv->streams = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); } static void @@ -246,20 +194,14 @@ pulse_device_dispose (GObject *object) device = PULSE_DEVICE (object); - if (device->priv->ports_list != NULL) { - g_list_free_full (device->priv->ports_list, g_object_unref); - device->priv->ports_list = NULL; - } g_hash_table_remove_all (device->priv->ports); + g_hash_table_remove_all (device->priv->streams); - if (device->priv->profiles_list != NULL) { - g_list_free_full (device->priv->profiles_list, g_object_unref); - device->priv->profiles_list = NULL; - } - g_hash_table_remove_all (device->priv->profiles); - - g_clear_object (&device->priv->profile); g_clear_object (&device->priv->connection); + g_clear_object (&device->priv->pswitch); + + free_list_streams (device); + free_list_switches (device); G_OBJECT_CLASS (pulse_device_parent_class)->dispose (object); } @@ -271,12 +213,8 @@ pulse_device_finalize (GObject *object) device = PULSE_DEVICE (object); - g_free (device->priv->name); - g_free (device->priv->description); - g_free (device->priv->icon); - - g_hash_table_destroy (device->priv->ports); - g_hash_table_destroy (device->priv->profiles); + g_hash_table_unref (device->priv->ports); + g_hash_table_unref (device->priv->streams); G_OBJECT_CLASS (pulse_device_parent_class)->finalize (object); } @@ -285,123 +223,86 @@ PulseDevice * pulse_device_new (PulseConnection *connection, const pa_card_info *info) { PulseDevice *device; + const gchar *label; + const gchar *icon; g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); g_return_val_if_fail (info != NULL, NULL); + label = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_DESCRIPTION); + if G_UNLIKELY (label == NULL) + label = info->name; + + icon = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_ICON_NAME); + if G_UNLIKELY (icon == NULL) + icon = "audio-card"; + /* Consider the device index as unchanging parameter */ device = g_object_new (PULSE_TYPE_DEVICE, - "connection", connection, "index", info->index, + "connection", connection, + "name", info->name, + "label", label, + "icon", icon, NULL); - /* Other data may change at any time, so let's make a use of our update function */ + pulse_device_load (device, info); pulse_device_update (device, info); return device; } -gboolean +void pulse_device_update (PulseDevice *device, const pa_card_info *info) { - MateMixerDeviceProfile *profile = NULL; - const gchar *prop; - guint32 i; - - g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE); - g_return_val_if_fail (info != NULL, FALSE); - - /* Let all the information update before emitting notify signals */ - g_object_freeze_notify (G_OBJECT (device)); - - /* Name */ - if (g_strcmp0 (device->priv->name, info->name) != 0) { - g_free (device->priv->name); - device->priv->name = g_strdup (info->name); - - g_object_notify (G_OBJECT (device), "name"); - } - - /* Description */ - prop = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_DESCRIPTION); - - if (G_UNLIKELY (prop == NULL)) - prop = info->name; - - if (g_strcmp0 (device->priv->description, prop) != 0) { - g_free (device->priv->description); - device->priv->description = g_strdup (prop); - - g_object_notify (G_OBJECT (device), "description"); - } - - /* Icon */ - prop = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_ICON_NAME); - - if (G_UNLIKELY (prop == NULL)) - prop = "audio-card"; - - if (g_strcmp0 (device->priv->icon, prop) != 0) { - g_free (device->priv->icon); - device->priv->icon = g_strdup (prop); - - g_object_notify (G_OBJECT (device), "icon"); - } - -#if PA_CHECK_VERSION (2, 0, 0) - /* List of ports */ - for (i = 0; i < info->n_ports; i++) { - update_port (device, info->ports[i]); - } -#endif + g_return_if_fail (PULSE_IS_DEVICE (device)); + g_return_if_fail (info != NULL); - /* List of profiles */ - for (i = 0; i < info->n_profiles; i++) { #if PA_CHECK_VERSION (5, 0, 0) - pa_card_profile_info2 *p_info = info->profiles2[i]; - - /* PulseAudio 5.0 includes a new pa_card_profile_info2 which only - * differs in the new available flag, we use it not to include profiles - * which are unavailable */ - if (p_info->available == 0) - continue; + if G_LIKELY (info->active_profile2 != NULL) + pulse_device_switch_set_active_profile_by_name (device->priv->pswitch, + info->active_profile2->name); #else - /* The old profile list is an array of structs, not pointers */ - pa_card_profile_info *p_info = &info->profiles[i]; + if G_LIKELY (info->active_profile != NULL) + pulse_device_switch_set_active_profile_by_name (device->priv->pswitch, + info->active_profile->name); #endif - update_profile (device, p_info); - } - - /* Figure out whether the currently active profile has changed */ - profile = NULL; +} -#if PA_CHECK_VERSION (5, 0, 0) - if (G_LIKELY (info->active_profile2 != NULL)) - profile = g_hash_table_lookup (device->priv->profiles, info->active_profile2->name); -#else - if (G_LIKELY (info->active_profile != NULL)) - profile = g_hash_table_lookup (device->priv->profiles, info->active_profile->name); -#endif +void +pulse_device_add_stream (PulseDevice *device, PulseStream *stream) +{ + const gchar *name; - if (profile != device->priv->profile) { - g_clear_object (&device->priv->profile); + g_return_if_fail (PULSE_IS_DEVICE (device)); + g_return_if_fail (PULSE_IS_STREAM (stream)); - if (G_LIKELY (profile != NULL)) - device->priv->profile = g_object_ref (profile); + name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); - g_object_notify (G_OBJECT (device), "active-profile"); - } + free_list_streams (device); - g_object_thaw_notify (G_OBJECT (device)); - return TRUE; + g_hash_table_insert (device->priv->streams, g_strdup (name), stream); + g_signal_emit_by_name (G_OBJECT (device), + "stream-added", + name); } -PulseConnection * -pulse_device_get_connection (PulseDevice *device) +void +pulse_device_remove_stream (PulseDevice *device, PulseStream *stream) { - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); + const gchar *name; - return device->priv->connection; + g_return_if_fail (PULSE_IS_DEVICE (device)); + g_return_if_fail (PULSE_IS_STREAM (stream)); + + name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)); + + free_list_streams (device); + + g_hash_table_remove (device->priv->streams, name); + g_signal_emit_by_name (G_OBJECT (device), + "stream-removed", + name); } guint32 @@ -412,222 +313,130 @@ pulse_device_get_index (PulseDevice *device) return device->priv->index; } -static const gchar * -pulse_device_get_name (MateMixerDevice *device) -{ - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - - return PULSE_DEVICE (device)->priv->name; -} - -static const gchar * -pulse_device_get_description (MateMixerDevice *device) -{ - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - - return PULSE_DEVICE (device)->priv->description; -} - -static const gchar * -pulse_device_get_icon (MateMixerDevice *device) +PulseConnection * +pulse_device_get_connection (PulseDevice *device) { g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - return PULSE_DEVICE (device)->priv->icon; + return device->priv->connection; } -static MateMixerPort * -pulse_device_get_port (MateMixerDevice *device, const gchar *name) +PulsePort * +pulse_device_get_port (PulseDevice *device, const gchar *name) { g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - g_return_val_if_fail (name != NULL, NULL); - return g_hash_table_lookup (PULSE_DEVICE (device)->priv->ports, name); + return g_hash_table_lookup (device->priv->ports, name); } -static MateMixerDeviceProfile * -pulse_device_get_profile (MateMixerDevice *device, const gchar *name) +static MateMixerStream * +pulse_device_get_stream (MateMixerDevice *mmd, const gchar *name) { - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); + g_return_val_if_fail (PULSE_IS_DEVICE (mmd), NULL); g_return_val_if_fail (name != NULL, NULL); - return g_hash_table_lookup (PULSE_DEVICE (device)->priv->profiles, name); + return g_hash_table_lookup (PULSE_DEVICE (mmd)->priv->streams, name); } static const GList * -pulse_device_list_ports (MateMixerDevice *device) +pulse_device_list_streams (MateMixerDevice *mmd) { - PulseDevice *pulse; - - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - - pulse = PULSE_DEVICE (device); + PulseDevice *device; - if (pulse->priv->ports_list == NULL) { - GList *list = g_hash_table_get_values (pulse->priv->ports); + g_return_val_if_fail (PULSE_IS_DEVICE (mmd), NULL); - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); + device = PULSE_DEVICE (mmd); - pulse->priv->ports_list = g_list_sort (list, compare_ports); - } + if (device->priv->streams_list == NULL) { + device->priv->streams_list = g_hash_table_get_values (device->priv->streams); + if (device->priv->streams_list != NULL) + g_list_foreach (device->priv->streams_list, (GFunc) g_object_ref, NULL); } - - return (const GList *) pulse->priv->ports_list; + return device->priv->streams_list; } static const GList * -pulse_device_list_profiles (MateMixerDevice *device) +pulse_device_list_switches (MateMixerDevice *mmd) { - PulseDevice *pulse; - - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); - - pulse = PULSE_DEVICE (device); - - if (pulse->priv->profiles_list == NULL) { - GList *list = g_hash_table_get_values (pulse->priv->profiles); - - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); - - pulse->priv->profiles_list = g_list_sort (list, compare_profiles); - } - } + g_return_val_if_fail (PULSE_IS_DEVICE (mmd), NULL); - return (const GList *) pulse->priv->profiles_list; + return PULSE_DEVICE (mmd)->priv->switches_list; } -static MateMixerDeviceProfile * -pulse_device_get_active_profile (MateMixerDevice *device) +static void +pulse_device_load (PulseDevice *device, const pa_card_info *info) { - g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL); + guint i; - return PULSE_DEVICE (device)->priv->profile; -} +#if PA_CHECK_VERSION (2, 0, 0) + for (i = 0; i < info->n_ports; i++) { + PulsePort *port; + const gchar *name; + const gchar *icon; -static gboolean -pulse_device_set_active_profile (MateMixerDevice *device, MateMixerDeviceProfile *profile) -{ - PulseDevice *pulse; - const gchar *name; - gboolean ret; + name = info->ports[i]->name; + icon = pa_proplist_gets (info->ports[i]->proplist, "device.icon_name"); - g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); + port = pulse_port_new (name, + info->ports[i]->description, + icon, + info->ports[i]->priority); - pulse = PULSE_DEVICE (device); + g_hash_table_insert (device->priv->ports, g_strdup (name), port); + } +#endif - name = mate_mixer_device_profile_get_name (profile); + /* Create the device profile switch */ + if (info->n_profiles > 0) { + device->priv->pswitch = pulse_device_switch_new ("profile", + _("Profile"), + device); - /* Make sure the profile belongs to the device */ - if (g_hash_table_lookup (pulse->priv->profiles, name) == NULL) { - g_warning ("Profile %s does not belong to device %s", name, pulse->priv->name); - return FALSE; + device->priv->switches_list = g_list_prepend (NULL, g_object_ref (device->priv->pswitch)); } - ret = pulse_connection_set_card_profile (pulse->priv->connection, - pulse->priv->name, - name); - if (ret == TRUE) { - if (pulse->priv->profile != NULL) - g_object_unref (pulse->priv->profile); + for (i = 0; i < info->n_profiles; i++) { + PulseDeviceProfile *profile; + +#if PA_CHECK_VERSION (5, 0, 0) + pa_card_profile_info2 *p_info = info->profiles2[i]; + + /* PulseAudio 5.0 includes a new pa_card_profile_info2 which only + * differs in the new available flag, we use it not to include profiles + * which are unavailable */ + if (p_info->available == 0) + continue; +#else + /* The old profile list is an array of structs, not pointers */ + pa_card_profile_info *p_info = &info->profiles[i]; +#endif - pulse->priv->profile = g_object_ref (profile); + profile = pulse_device_profile_new (p_info->name, + p_info->description, + p_info->priority); - g_object_notify (G_OBJECT (device), "active-profile"); + pulse_device_switch_add_profile (device->priv->pswitch, profile); } - return ret; } static void -update_port (PulseDevice *device, pa_card_port_info *p_info) +free_list_streams (PulseDevice *device) { - MateMixerPort *port; - MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS; - const gchar *icon; - - icon = pa_proplist_gets (p_info->proplist, "device.icon_name"); - - if (p_info->available == PA_PORT_AVAILABLE_YES) - flags |= MATE_MIXER_PORT_AVAILABLE; - - if (p_info->direction & PA_DIRECTION_INPUT) - flags |= MATE_MIXER_PORT_INPUT; - if (p_info->direction & PA_DIRECTION_OUTPUT) - flags |= MATE_MIXER_PORT_OUTPUT; - - port = g_hash_table_lookup (device->priv->ports, p_info->name); - - if (port != NULL) { - /* Update existing port */ - _mate_mixer_port_update_description (port, p_info->description); - _mate_mixer_port_update_icon (port, icon); - _mate_mixer_port_update_priority (port, p_info->priority); - _mate_mixer_port_update_flags (port, flags); - } else { - /* Add previously unknown port to the hash table */ - port = _mate_mixer_port_new (p_info->name, - p_info->description, - icon, - p_info->priority, - flags); - - g_hash_table_insert (device->priv->ports, g_strdup (p_info->name), port); - } + if (device->priv->streams_list == NULL) + return; + + g_list_free_full (device->priv->streams_list, g_object_unref); + + device->priv->streams_list = NULL; } static void -update_profile (PulseDevice *device, _pa_card_profile_info *p_info) +free_list_switches (PulseDevice *device) { - MateMixerDeviceProfile *profile; - - profile = g_hash_table_lookup (device->priv->profiles, p_info->name); - - if (profile != NULL) { - /* Update existing profile */ - _mate_mixer_device_profile_update_description (profile, p_info->description); - _mate_mixer_device_profile_update_priority (profile, p_info->priority); - _mate_mixer_device_profile_update_num_input_streams (profile, p_info->n_sources); - _mate_mixer_device_profile_update_num_output_streams (profile, p_info->n_sinks); - } else { - /* Add previously unknown profile to the hash table */ - profile = _mate_mixer_device_profile_new (p_info->name, - p_info->description, - p_info->priority, - p_info->n_sources, - p_info->n_sinks); - - g_hash_table_insert (device->priv->profiles, g_strdup (p_info->name), profile); - } -} + if (device->priv->switches_list == NULL) + return; -static gint -compare_ports (gconstpointer a, gconstpointer b) -{ - MateMixerPort *p1 = MATE_MIXER_PORT (a); - MateMixerPort *p2 = MATE_MIXER_PORT (b); - - gint ret = (gint) (mate_mixer_port_get_priority (p2) - - mate_mixer_port_get_priority (p1)); - if (ret != 0) - return ret; - else - return strcmp (mate_mixer_port_get_name (p1), - mate_mixer_port_get_name (p2)); -} + g_list_free_full (device->priv->switches_list, g_object_unref); -static gint -compare_profiles (gconstpointer a, gconstpointer b) -{ - MateMixerDeviceProfile *p1 = MATE_MIXER_DEVICE_PROFILE (a); - MateMixerDeviceProfile *p2 = MATE_MIXER_DEVICE_PROFILE (b); - - gint ret = (gint) (mate_mixer_device_profile_get_priority (p2) - - mate_mixer_device_profile_get_priority (p1)); - if (ret != 0) - return ret; - else - return strcmp (mate_mixer_device_profile_get_name (p1), - mate_mixer_device_profile_get_name (p2)); + device->priv->switches_list = NULL; } diff --git a/backends/pulse/pulse-device.h b/backends/pulse/pulse-device.h index 94c331f..863330f 100644 --- a/backends/pulse/pulse-device.h +++ b/backends/pulse/pulse-device.h @@ -20,33 +20,33 @@ #include <glib.h> #include <glib-object.h> +#include <libmatemixer/matemixer.h> #include <pulse/pulseaudio.h> -#include "pulse-connection.h" +#include "pulse-types.h" G_BEGIN_DECLS -#define PULSE_TYPE_DEVICE \ +#define PULSE_TYPE_DEVICE \ (pulse_device_get_type ()) -#define PULSE_DEVICE(o) \ +#define PULSE_DEVICE(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_DEVICE, PulseDevice)) -#define PULSE_IS_DEVICE(o) \ +#define PULSE_IS_DEVICE(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_DEVICE)) -#define PULSE_DEVICE_CLASS(k) \ +#define PULSE_DEVICE_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_DEVICE, PulseDeviceClass)) -#define PULSE_IS_DEVICE_CLASS(k) \ +#define PULSE_IS_DEVICE_CLASS(k) \ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE)) -#define PULSE_DEVICE_GET_CLASS(o) \ +#define PULSE_DEVICE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_DEVICE, PulseDeviceClass)) -typedef struct _PulseDevice PulseDevice; typedef struct _PulseDeviceClass PulseDeviceClass; typedef struct _PulseDevicePrivate PulseDevicePrivate; struct _PulseDevice { - GObject parent; + MateMixerDevice parent; /*< private >*/ PulseDevicePrivate *priv; @@ -54,20 +54,29 @@ struct _PulseDevice struct _PulseDeviceClass { - GObjectClass parent; + MateMixerDeviceClass parent; }; GType pulse_device_get_type (void) G_GNUC_CONST; -PulseDevice *pulse_device_new (PulseConnection *connection, +PulseDevice * pulse_device_new (PulseConnection *connection, const pa_card_info *info); -gboolean pulse_device_update (PulseDevice *device, +void pulse_device_update (PulseDevice *device, const pa_card_info *info); +void pulse_device_add_stream (PulseDevice *device, + PulseStream *stream); + +void pulse_device_remove_stream (PulseDevice *device, + PulseStream *stream); + guint32 pulse_device_get_index (PulseDevice *device); PulseConnection *pulse_device_get_connection (PulseDevice *device); +PulsePort * pulse_device_get_port (PulseDevice *device, + const gchar *name); + G_END_DECLS #endif /* PULSE_DEVICE_H */ diff --git a/backends/pulse/pulse-ext-stream.c b/backends/pulse/pulse-ext-stream.c index b00e967..3e7490a 100644 --- a/backends/pulse/pulse-ext-stream.c +++ b/backends/pulse/pulse-ext-stream.c @@ -19,252 +19,326 @@ #include <glib.h> #include <glib-object.h> -#include <libmatemixer/matemixer-client-stream.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include <pulse/ext-stream-restore.h> #include "pulse-connection.h" -#include "pulse-client-stream.h" #include "pulse-ext-stream.h" #include "pulse-helpers.h" #include "pulse-stream.h" +#include "pulse-stream-control.h" -static void pulse_ext_stream_class_init (PulseExtStreamClass *klass); -static void pulse_ext_stream_init (PulseExtStream *ext); +struct _PulseExtStreamPrivate +{ + MateMixerAppInfo *app_info; + MateMixerDirection direction; +}; -G_DEFINE_TYPE (PulseExtStream, pulse_ext_stream, PULSE_TYPE_CLIENT_STREAM); +enum { + PROP_0, + PROP_DIRECTION +}; -static void pulse_ext_stream_reload (PulseStream *pstream); +static void mate_mixer_stored_control_interface_init (MateMixerStoredControlInterface *iface); -static gboolean pulse_ext_stream_set_mute (PulseStream *pstream, - gboolean mute); -static gboolean pulse_ext_stream_set_volume (PulseStream *pstream, - pa_cvolume *cvolume); -static gboolean pulse_ext_stream_set_parent (PulseClientStream *pclient, - PulseStream *parent); -static gboolean pulse_ext_stream_remove (PulseClientStream *pclient); +static void pulse_ext_stream_class_init (PulseExtStreamClass *klass); -static void -pulse_ext_stream_class_init (PulseExtStreamClass *klass) -{ - PulseStreamClass *stream_class; - PulseClientStreamClass *client_class; +static void pulse_ext_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void pulse_ext_stream_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); - stream_class = PULSE_STREAM_CLASS (klass); +static void pulse_ext_stream_init (PulseExtStream *ext); - stream_class->reload = pulse_ext_stream_reload; - stream_class->set_mute = pulse_ext_stream_set_mute; - stream_class->set_volume = pulse_ext_stream_set_volume; +G_DEFINE_TYPE_WITH_CODE (PulseExtStream, pulse_ext_stream, PULSE_TYPE_STREAM_CONTROL, + G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STORED_CONTROL, + mate_mixer_stored_control_interface_init)) - client_class = PULSE_CLIENT_STREAM_CLASS (klass); +static MateMixerDirection pulse_ext_stream_get_direction (MateMixerStoredControl *mmsc); - client_class->set_parent = pulse_ext_stream_set_parent; - client_class->remove = pulse_ext_stream_remove; -} +static MateMixerAppInfo * pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc); + +static gboolean pulse_ext_stream_set_stream (MateMixerStreamControl *mmsc, + MateMixerStream *mms); + +static gboolean pulse_ext_stream_set_mute (PulseStreamControl *control, + gboolean mute); +static gboolean pulse_ext_stream_set_volume (PulseStreamControl *control, + pa_cvolume *cvolume); + +static void fill_ext_stream_restore_info (PulseStreamControl *control, + pa_ext_stream_restore_info *info); static void -pulse_ext_stream_init (PulseExtStream *ext) +mate_mixer_stored_control_interface_init (MateMixerStoredControlInterface *iface) { + iface->get_direction = pulse_ext_stream_get_direction; } -PulseStream * -pulse_ext_stream_new (PulseConnection *connection, - const pa_ext_stream_restore_info *info, - PulseStream *parent) +static void +pulse_ext_stream_class_init (PulseExtStreamClass *klass) { - PulseStream *ext; + GObjectClass *object_class; + MateMixerStreamControlClass *control_class; + PulseStreamControlClass *pulse_class; - g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (info != NULL, NULL); + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = pulse_ext_stream_get_property; + object_class->set_property = pulse_ext_stream_set_property; - ext = g_object_new (PULSE_TYPE_EXT_STREAM, - "connection", connection, - NULL); + control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass); + control_class->get_app_info = pulse_ext_stream_get_app_info; + control_class->set_stream = pulse_ext_stream_set_stream; - /* Consider the stream name as unchanging parameter */ - pulse_stream_update_name (ext, info->name); + pulse_class = PULSE_STREAM_CONTROL_CLASS (klass); + pulse_class->set_mute = pulse_ext_stream_set_mute; + pulse_class->set_volume = pulse_ext_stream_set_volume; - /* Other data may change at any time, so let's make a use of our update function */ - pulse_ext_stream_update (ext, info, parent); + g_object_class_override_property (object_class, PROP_DIRECTION, "direction"); - return ext; + g_type_class_add_private (object_class, sizeof (PulseExtStreamPrivate)); } -gboolean -pulse_ext_stream_update (PulseStream *pstream, - const pa_ext_stream_restore_info *info, - PulseStream *parent) +static void +pulse_ext_stream_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) { - MateMixerClientStreamRole role = MATE_MIXER_CLIENT_STREAM_ROLE_NONE; - MateMixerStreamFlags flags = MATE_MIXER_STREAM_CLIENT | - MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_HAS_MUTE | - MATE_MIXER_STREAM_CAN_SET_VOLUME; - MateMixerClientStreamFlags client_flags = - MATE_MIXER_CLIENT_STREAM_CACHED; + PulseExtStream *ext; - PulseClientStream *pclient; - gchar *suffix; + ext = PULSE_EXT_STREAM (object); - g_return_val_if_fail (PULSE_IS_EXT_STREAM (pstream), FALSE); - g_return_val_if_fail (info != NULL, FALSE); + switch (param_id) { + case PROP_DIRECTION: + g_value_set_enum (value, ext->priv->direction); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} - pclient = PULSE_CLIENT_STREAM (pstream); +static void +pulse_ext_stream_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + PulseExtStream *ext; - suffix = strchr (info->name, ':'); - if (suffix != NULL) - suffix++; + ext = PULSE_EXT_STREAM (object); - /* Let all the information update before emitting notify signals */ - g_object_freeze_notify (G_OBJECT (pstream)); + switch (param_id) { + case PROP_DIRECTION: + ext->priv->direction = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_ext_stream_init (PulseExtStream *ext) +{ + ext->priv = G_TYPE_INSTANCE_GET_PRIVATE (ext, + PULSE_TYPE_EXT_STREAM, + PulseExtStreamPrivate); +} + +PulseExtStream * +pulse_ext_stream_new (PulseConnection *connection, + const pa_ext_stream_restore_info *info, + PulseStream *parent) +{ + PulseExtStream *ext; + gchar *suffix; + MateMixerDirection direction; + MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE | + MATE_MIXER_STREAM_CONTROL_MUTE_READABLE | + MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE; + MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN; + MateMixerAppInfo *app_info; + + MateMixerStreamControlMediaRole media_role = MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN; + + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (info != NULL, NULL); if (g_str_has_prefix (info->name, "sink-input")) - flags |= MATE_MIXER_STREAM_OUTPUT; + direction = MATE_MIXER_DIRECTION_OUTPUT; else if (g_str_has_prefix (info->name, "source-output")) - flags |= MATE_MIXER_STREAM_INPUT; + direction = MATE_MIXER_DIRECTION_INPUT; else - g_debug ("Unknown ext-stream %s", info->name); + direction = MATE_MIXER_DIRECTION_UNKNOWN; + + app_info = _mate_mixer_app_info_new (); + + suffix = strchr (info->name, ':'); + if (suffix != NULL) + suffix++; if (strstr (info->name, "-by-media-role:")) { if (G_LIKELY (suffix != NULL)) - role = pulse_convert_media_role_name (suffix); + media_role = pulse_convert_media_role_name (suffix); } else if (strstr (info->name, "-by-application-name:")) { - client_flags |= MATE_MIXER_CLIENT_STREAM_APPLICATION; + role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION; if (G_LIKELY (suffix != NULL)) - pulse_client_stream_update_app_name (pclient, suffix); + _mate_mixer_app_info_set_name (app_info, suffix); } else if (strstr (info->name, "-by-application-id:")) { - client_flags |= MATE_MIXER_CLIENT_STREAM_APPLICATION; + role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION; if (G_LIKELY (suffix != NULL)) - pulse_client_stream_update_app_id (pclient, suffix); + _mate_mixer_app_info_set_id (app_info, suffix); } - /* Flags needed before volume */ - pulse_stream_update_flags (pstream, flags); + ext = g_object_new (PULSE_TYPE_EXT_STREAM, + "flags", flags, + "role", role, + "media-role", media_role, + "name", info->name, + "connection", connection, + "direction", direction, + "stream", parent, + NULL); - pulse_stream_update_channel_map (pstream, &info->channel_map); - pulse_stream_update_volume (pstream, &info->volume, 0); + if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) + ext->priv->app_info = app_info; + else + _mate_mixer_app_info_free (app_info); - pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE); + pulse_ext_stream_update (ext, info, parent); + return ext; +} - pulse_client_stream_update_flags (pclient, client_flags); - pulse_client_stream_update_role (pclient, role); +void +pulse_ext_stream_update (PulseExtStream *ext, + const pa_ext_stream_restore_info *info, + PulseStream *parent) +{ + g_return_if_fail (PULSE_IS_EXT_STREAM (ext)); + g_return_if_fail (info != NULL); - if (parent != NULL) - pulse_client_stream_update_parent (pclient, MATE_MIXER_STREAM (parent)); - else - pulse_client_stream_update_parent (pclient, NULL); + /* Let all the information update before emitting notify signals */ + g_object_freeze_notify (G_OBJECT (ext)); + + _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (ext), + info->mute ? TRUE : FALSE); - g_object_thaw_notify (G_OBJECT (pstream)); - return TRUE; + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (ext), + &info->channel_map); + + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (ext), + &info->volume, + 0); + + _mate_mixer_stream_control_set_stream (MATE_MIXER_STREAM_CONTROL (ext), + MATE_MIXER_STREAM (parent)); + + g_object_thaw_notify (G_OBJECT (ext)); } -static void -pulse_ext_stream_reload (PulseStream *pstream) +static MateMixerDirection +pulse_ext_stream_get_direction (MateMixerStoredControl *mmsc) { - g_return_if_fail (PULSE_IS_EXT_STREAM (pstream)); + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), MATE_MIXER_DIRECTION_UNKNOWN); - pulse_connection_load_ext_stream_info (pulse_stream_get_connection (pstream)); + return PULSE_EXT_STREAM (mmsc)->priv->direction; } -static gboolean -pulse_ext_stream_set_mute (PulseStream *pstream, gboolean mute) +static MateMixerAppInfo * +pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc) { - MateMixerStream *parent; - const pa_channel_map *map; - const pa_cvolume *cvolume; - pa_ext_stream_restore_info info; + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), NULL); - g_return_val_if_fail (PULSE_IS_EXT_STREAM (pstream), FALSE); + return PULSE_EXT_STREAM (mmsc)->priv->app_info; +} - info.name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)); - info.mute = mute; +static gboolean +pulse_ext_stream_set_stream (MateMixerStreamControl *mmsc, MateMixerStream *mms) +{ + pa_ext_stream_restore_info info; - map = pulse_stream_get_channel_map (pstream); - info.channel_map = *map; + g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE); + g_return_val_if_fail (mms == NULL || PULSE_IS_STREAM (mms), FALSE); - cvolume = pulse_stream_get_cvolume (pstream); - info.volume = *cvolume; + fill_ext_stream_restore_info (PULSE_STREAM_CONTROL (mms), &info); - parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream)); - if (parent != NULL) - info.device = mate_mixer_stream_get_name (parent); + if (mms != NULL) + info.device = mate_mixer_stream_get_name (mms); else info.device = NULL; - return pulse_connection_write_ext_stream (pulse_stream_get_connection (pstream), &info); + return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (PULSE_STREAM_CONTROL (mmsc)), + &info); } static gboolean -pulse_ext_stream_set_volume (PulseStream *pstream, pa_cvolume *cvolume) +pulse_ext_stream_set_mute (PulseStreamControl *control, gboolean mute) { - MateMixerStream *parent; - const pa_channel_map *map; pa_ext_stream_restore_info info; - g_return_val_if_fail (PULSE_IS_EXT_STREAM (pstream), FALSE); - g_return_val_if_fail (cvolume != NULL, FALSE); - - info.name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)); - info.mute = mate_mixer_stream_get_mute (MATE_MIXER_STREAM (pstream)); + g_return_val_if_fail (PULSE_IS_EXT_STREAM (control), FALSE); - map = pulse_stream_get_channel_map (pstream); - info.channel_map = *map; - - parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream)); - if (parent != NULL) - info.device = mate_mixer_stream_get_name (parent); - else - info.device = NULL; + fill_ext_stream_restore_info (control, &info); - info.volume = *cvolume; + info.mute = mute; - return pulse_connection_write_ext_stream (pulse_stream_get_connection (pstream), &info); + return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (control), + &info); } static gboolean -pulse_ext_stream_set_parent (PulseClientStream *pclient, PulseStream *parent) +pulse_ext_stream_set_volume (PulseStreamControl *control, pa_cvolume *cvolume) { - PulseStream *pstream; - const pa_channel_map *map; - const pa_cvolume *cvolume; pa_ext_stream_restore_info info; - g_return_val_if_fail (PULSE_IS_EXT_STREAM (pclient), FALSE); - g_return_val_if_fail (PULSE_IS_STREAM (parent), FALSE); - - pstream = PULSE_STREAM (pclient); - - info.name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)); - info.mute = mate_mixer_stream_get_mute (MATE_MIXER_STREAM (pstream)); + g_return_val_if_fail (PULSE_IS_EXT_STREAM (control), FALSE); + g_return_val_if_fail (cvolume != NULL, FALSE); - map = pulse_stream_get_channel_map (pstream); - info.channel_map = *map; + fill_ext_stream_restore_info (control, &info); - cvolume = pulse_stream_get_cvolume (pstream); info.volume = *cvolume; - info.device = mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent)); - - return pulse_connection_write_ext_stream (pulse_stream_get_connection (pstream), &info); + return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (control), + &info); } -static gboolean -pulse_ext_stream_remove (PulseClientStream *pclient) +static void +fill_ext_stream_restore_info (PulseStreamControl *control, + pa_ext_stream_restore_info *info) { - PulseStream *pstream; - const gchar *name; + MateMixerStream *stream; + MateMixerStreamControl *mmsc; + const pa_channel_map *map; + const pa_cvolume *cvolume; + + mmsc = MATE_MIXER_STREAM_CONTROL (control); - g_return_val_if_fail (PULSE_IS_EXT_STREAM (pclient), FALSE); + info->name = mate_mixer_stream_control_get_name (mmsc); + info->mute = mate_mixer_stream_control_get_mute (mmsc); - pstream = PULSE_STREAM (pclient); - name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)); + map = pulse_stream_control_get_channel_map (control); + info->channel_map = *map; - return pulse_connection_delete_ext_stream (pulse_stream_get_connection (pstream), name); + cvolume = pulse_stream_control_get_cvolume (control); + info->volume = *cvolume; + + stream = mate_mixer_stream_control_get_stream (mmsc); + if (stream != NULL) + info->device = mate_mixer_stream_get_name (stream); + else + info->device = NULL; } diff --git a/backends/pulse/pulse-ext-stream.h b/backends/pulse/pulse-ext-stream.h index e8dabb6..b667dc7 100644 --- a/backends/pulse/pulse-ext-stream.h +++ b/backends/pulse/pulse-ext-stream.h @@ -24,9 +24,8 @@ #include <pulse/pulseaudio.h> #include <pulse/ext-stream-restore.h> -#include "pulse-client-stream.h" -#include "pulse-connection.h" -#include "pulse-stream.h" +#include "pulse-stream-control.h" +#include "pulse-types.h" G_BEGIN_DECLS @@ -43,28 +42,31 @@ G_BEGIN_DECLS #define PULSE_EXT_STREAM_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_EXT_STREAM, PulseExtStreamClass)) -typedef struct _PulseExtStream PulseExtStream; -typedef struct _PulseExtStreamClass PulseExtStreamClass; +typedef struct _PulseExtStreamClass PulseExtStreamClass; +typedef struct _PulseExtStreamPrivate PulseExtStreamPrivate; struct _PulseExtStream { - PulseClientStream parent; + PulseStreamControl parent; + + /*< private >*/ + PulseExtStreamPrivate *priv; }; struct _PulseExtStreamClass { - PulseClientStreamClass parent_class; + PulseStreamControlClass parent_class; }; -GType pulse_ext_stream_get_type (void) G_GNUC_CONST; +GType pulse_ext_stream_get_type (void) G_GNUC_CONST; -PulseStream *pulse_ext_stream_new (PulseConnection *connection, - const pa_ext_stream_restore_info *info, - PulseStream *parent); +PulseExtStream *pulse_ext_stream_new (PulseConnection *connection, + const pa_ext_stream_restore_info *info, + PulseStream *parent); -gboolean pulse_ext_stream_update (PulseStream *pstream, - const pa_ext_stream_restore_info *info, - PulseStream *parent); +void pulse_ext_stream_update (PulseExtStream *ext, + const pa_ext_stream_restore_info *info, + PulseStream *parent); G_END_DECLS diff --git a/backends/pulse/pulse-helpers.c b/backends/pulse/pulse-helpers.c index 577f2c6..73f8cdb 100644 --- a/backends/pulse/pulse-helpers.c +++ b/backends/pulse/pulse-helpers.c @@ -29,6 +29,7 @@ typedef struct { pa_channel_position_t pa_position; } PositionMap; +// XXX optimize static PositionMap const position_map[] = { { MATE_MIXER_CHANNEL_UNKNOWN, PA_CHANNEL_POSITION_INVALID }, { MATE_MIXER_CHANNEL_MONO, PA_CHANNEL_POSITION_MONO }, @@ -76,42 +77,42 @@ pulse_convert_position_to_pulse (MateMixerChannelPosition position) return PA_CHANNEL_POSITION_INVALID; } -MateMixerClientStreamRole +MateMixerStreamControlMediaRole pulse_convert_media_role_name (const gchar *name) { if (!strcmp (name, "video")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_VIDEO; } else if (!strcmp (name, "music")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_MUSIC; } else if (!strcmp (name, "game")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_GAME; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_GAME; } else if (!strcmp (name, "event")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_EVENT; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT; } else if (!strcmp (name, "phone")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_PHONE; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PHONE; } else if (!strcmp (name, "animation")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ANIMATION; } else if (!strcmp (name, "production")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PRODUCTION; } else if (!strcmp (name, "a11y")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_A11Y; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_A11Y; } else if (!strcmp (name, "test")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_TEST; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_TEST; } else if (!strcmp (name, "abstract")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ABSTRACT; } else if (!strcmp (name, "filter")) { - return MATE_MIXER_CLIENT_STREAM_ROLE_FILTER; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_FILTER; } - return MATE_MIXER_CLIENT_STREAM_ROLE_NONE; + return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN; } diff --git a/backends/pulse/pulse-helpers.h b/backends/pulse/pulse-helpers.h index 7ccd753..667fc3c 100644 --- a/backends/pulse/pulse-helpers.h +++ b/backends/pulse/pulse-helpers.h @@ -19,17 +19,16 @@ #define PULSE_HELPERS_H #include <glib.h> - -#include <libmatemixer/matemixer-enums.h> +#include <libmatemixer/matemixer.h> #include <pulse/pulseaudio.h> G_BEGIN_DECLS -MateMixerChannelPosition pulse_convert_position_from_pulse (pa_channel_position_t position); -pa_channel_position_t pulse_convert_position_to_pulse (MateMixerChannelPosition position); +MateMixerChannelPosition pulse_convert_position_from_pulse (pa_channel_position_t position); +pa_channel_position_t pulse_convert_position_to_pulse (MateMixerChannelPosition position); -MateMixerClientStreamRole pulse_convert_media_role_name (const gchar *name); +MateMixerStreamControlMediaRole pulse_convert_media_role_name (const gchar *name); G_END_DECLS diff --git a/backends/pulse/pulse-monitor.c b/backends/pulse/pulse-monitor.c index 3d5b4a8..915b71b 100644 --- a/backends/pulse/pulse-monitor.c +++ b/backends/pulse/pulse-monitor.c @@ -16,6 +16,7 @@ */ #include <glib.h> +#include <glib/gi18n.h> #include <glib-object.h> #include <pulse/pulseaudio.h> @@ -27,7 +28,6 @@ struct _PulseMonitorPrivate pa_context *context; pa_proplist *proplist; pa_stream *stream; - gchar *name; guint32 index_source; guint32 index_sink_input; gboolean enabled; @@ -36,7 +36,6 @@ struct _PulseMonitorPrivate enum { PROP_0, PROP_ENABLED, - PROP_NAME, PROP_INDEX_SOURCE, PROP_INDEX_SINK_INPUT, N_PROPERTIES @@ -91,15 +90,6 @@ pulse_monitor_class_init (PulseMonitorClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - properties[PROP_NAME] = - g_param_spec_string ("name", - "Name", - "Name of the monitor", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS); - properties[PROP_INDEX_SOURCE] = g_param_spec_uint ("index-source", "Index of source", @@ -153,9 +143,6 @@ pulse_monitor_get_property (GObject *object, case PROP_ENABLED: g_value_set_boolean (value, monitor->priv->enabled); break; - case PROP_NAME: - g_value_set_string (value, monitor->priv->name); - break; case PROP_INDEX_SOURCE: g_value_set_uint (value, monitor->priv->index_source); break; @@ -179,9 +166,6 @@ pulse_monitor_set_property (GObject *object, monitor = PULSE_MONITOR (object); switch (param_id) { - case PROP_NAME: - pulse_monitor_set_name (monitor, g_value_get_string (value)); - break; case PROP_INDEX_SOURCE: monitor->priv->index_source = g_value_get_uint (value); break; @@ -218,15 +202,12 @@ pulse_monitor_finalize (GObject *object) pa_context_unref (monitor->priv->context); pa_proplist_free (monitor->priv->proplist); - g_free (monitor->priv->name); - G_OBJECT_CLASS (pulse_monitor_parent_class)->finalize (object); } PulseMonitor * pulse_monitor_new (pa_context *context, pa_proplist *proplist, - const gchar *name, guint32 index_source, guint32 index_sink_input) { @@ -236,7 +217,6 @@ pulse_monitor_new (pa_context *context, g_return_val_if_fail (proplist != NULL, NULL); monitor = g_object_new (PULSE_TYPE_MONITOR, - "name", name, "index-source", index_source, "index-sink-input", index_sink_input, NULL); @@ -280,34 +260,11 @@ pulse_monitor_set_enabled (PulseMonitor *monitor, gboolean enabled) return TRUE; } -const gchar * -pulse_monitor_get_name (PulseMonitor *monitor) -{ - g_return_val_if_fail (PULSE_IS_MONITOR (monitor), NULL); - - return monitor->priv->name; -} - -gboolean -pulse_monitor_set_name (PulseMonitor *monitor, const gchar *name) -{ - g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE); - - if (g_strcmp0 (name, monitor->priv->name) != 0) { - g_free (monitor->priv->name); - monitor->priv->name = g_strdup (name); - - g_object_notify_by_pspec (G_OBJECT (monitor), properties[PROP_NAME]); - } - return TRUE; -} - static gboolean stream_connect (PulseMonitor *monitor) { pa_sample_spec spec; pa_buffer_attr attr; - const gchar *name; gchar *idx; int ret; @@ -320,14 +277,9 @@ stream_connect (PulseMonitor *monitor) spec.format = PA_SAMPLE_FLOAT32; spec.rate = 25; - if (monitor->priv->name != NULL) - name = monitor->priv->name; - else - name = "Peak detect"; - monitor->priv->stream = pa_stream_new_with_proplist (monitor->priv->context, - name, + _("Peak detect"), &spec, NULL, monitor->priv->proplist); diff --git a/backends/pulse/pulse-monitor.h b/backends/pulse/pulse-monitor.h index 41147f5..e371ec3 100644 --- a/backends/pulse/pulse-monitor.h +++ b/backends/pulse/pulse-monitor.h @@ -23,6 +23,8 @@ #include <pulse/pulseaudio.h> +#include "pulse-types.h" + G_BEGIN_DECLS #define PULSE_TYPE_MONITOR \ @@ -38,7 +40,6 @@ G_BEGIN_DECLS #define PULSE_MONITOR_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_MONITOR, PulseMonitorClass)) -typedef struct _PulseMonitor PulseMonitor; typedef struct _PulseMonitorClass PulseMonitorClass; typedef struct _PulseMonitorPrivate PulseMonitorPrivate; @@ -54,7 +55,7 @@ struct _PulseMonitorClass { GObjectClass parent_class; - /* Signals */ + /*< private >*/ void (*value) (PulseMonitor *monitor, gdouble value); }; @@ -63,7 +64,6 @@ GType pulse_monitor_get_type (void) G_GNUC_CONST; PulseMonitor *pulse_monitor_new (pa_context *context, pa_proplist *proplist, - const gchar *name, guint32 index_source, guint32 index_sink_input); @@ -71,10 +71,6 @@ gboolean pulse_monitor_get_enabled (PulseMonitor *monitor); gboolean pulse_monitor_set_enabled (PulseMonitor *monitor, gboolean enabled); -const gchar * pulse_monitor_get_name (PulseMonitor *monitor); -gboolean pulse_monitor_set_name (PulseMonitor *monitor, - const gchar *name); - G_END_DECLS #endif /* PULSE_MONITOR_H */ diff --git a/backends/pulse/pulse-port-switch.c b/backends/pulse/pulse-port-switch.c new file mode 100644 index 0000000..08f1543 --- /dev/null +++ b/backends/pulse/pulse-port-switch.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "pulse-connection.h" +#include "pulse-port.h" +#include "pulse-port-switch.h" +#include "pulse-stream.h" + +struct _PulsePortSwitchPrivate +{ + GList *ports; + PulseStream *stream; +}; + +enum { + PROP_0, + PROP_STREAM, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void pulse_port_switch_class_init (PulsePortSwitchClass *klass); + +static void pulse_port_switch_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void pulse_port_switch_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void pulse_port_switch_init (PulsePortSwitch *swtch); +static void pulse_port_switch_dispose (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (PulsePortSwitch, pulse_port_switch, MATE_MIXER_TYPE_SWITCH) + +static gboolean pulse_port_switch_set_active_option (MateMixerSwitch *mms, + MateMixerSwitchOption *mmso); + +static const GList *pulse_port_switch_list_options (MateMixerSwitch *mms); + +static gint compare_ports (gconstpointer a, + gconstpointer b); +static gint compare_port_name (gconstpointer a, + gconstpointer b); + +static void +pulse_port_switch_class_init (PulsePortSwitchClass *klass) +{ + GObjectClass *object_class; + MateMixerSwitchClass *switch_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_port_switch_dispose; + object_class->get_property = pulse_port_switch_get_property; + object_class->set_property = pulse_port_switch_set_property; + + switch_class = MATE_MIXER_SWITCH_CLASS (klass); + switch_class->set_active_option = pulse_port_switch_set_active_option; + switch_class->list_options = pulse_port_switch_list_options; + + properties[PROP_STREAM] = + g_param_spec_object ("stream", + "Stream", + "PulseAudio stream", + PULSE_TYPE_STREAM, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (PulsePortSwitchPrivate)); +} + +static void +pulse_port_switch_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + PulsePortSwitch *swtch; + + swtch = PULSE_PORT_SWITCH (object); + + switch (param_id) { + case PROP_STREAM: + g_value_set_object (value, swtch->priv->stream); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_port_switch_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + PulsePortSwitch *swtch; + + swtch = PULSE_PORT_SWITCH (object); + + switch (param_id) { + case PROP_STREAM: + /* Construct-only object */ + swtch->priv->stream = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_port_switch_init (PulsePortSwitch *swtch) +{ + swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch, + PULSE_TYPE_PORT_SWITCH, + PulsePortSwitchPrivate); +} + +static void +pulse_port_switch_dispose (GObject *object) +{ + PulsePortSwitch *swtch; + + swtch = PULSE_PORT_SWITCH (object); + + g_clear_object (&swtch->priv->stream); + + G_OBJECT_CLASS (pulse_port_switch_parent_class)->dispose (object); +} + +PulseStream * +pulse_port_switch_get_stream (PulsePortSwitch *swtch) +{ + g_return_val_if_fail (PULSE_IS_PORT_SWITCH (swtch), NULL); + + return swtch->priv->stream; +} + +void +pulse_port_switch_add_port (PulsePortSwitch *swtch, PulsePort *port) +{ + g_return_if_fail (PULSE_IS_PORT_SWITCH (swtch)); + g_return_if_fail (PULSE_IS_PORT (port)); + + swtch->priv->ports = g_list_insert_sorted (swtch->priv->ports, + port, + compare_ports); +} + +void +pulse_port_switch_set_active_port (PulsePortSwitch *swtch, PulsePort *port) +{ + g_return_if_fail (PULSE_IS_PORT_SWITCH (swtch)); + g_return_if_fail (PULSE_IS_PORT (port)); + + _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (swtch), + MATE_MIXER_SWITCH_OPTION (port)); +} + +void +pulse_port_switch_set_active_port_by_name (PulsePortSwitch *swtch, const gchar *name) +{ + GList *item; + + g_return_if_fail (PULSE_IS_PORT_SWITCH (swtch)); + g_return_if_fail (name != NULL); + + item = g_list_find_custom (swtch->priv->ports, name, compare_port_name); + if G_UNLIKELY (item == NULL) { + g_debug ("Invalid switch port name %s", name); + return; + } + pulse_port_switch_set_active_port (swtch, PULSE_PORT (item->data)); +} + +static gboolean +pulse_port_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso) +{ + PulsePortSwitchClass *klass; + + g_return_val_if_fail (PULSE_IS_PORT_SWITCH (mms), FALSE); + g_return_val_if_fail (PULSE_IS_PORT (mmso), FALSE); + + klass = PULSE_PORT_SWITCH_GET_CLASS (PULSE_PORT_SWITCH (mms)); + + return klass->set_active_port (PULSE_PORT_SWITCH (mms), + PULSE_PORT (mmso)); +} + +static const GList * +pulse_port_switch_list_options (MateMixerSwitch *swtch) +{ + g_return_val_if_fail (PULSE_IS_PORT_SWITCH (swtch), NULL); + + return PULSE_PORT_SWITCH (swtch)->priv->ports; +} + +static gint +compare_ports (gconstpointer a, gconstpointer b) +{ + return pulse_port_get_priority (PULSE_PORT (b)) - + pulse_port_get_priority (PULSE_PORT (a)); +} + +static gint +compare_port_name (gconstpointer a, gconstpointer b) +{ + PulsePort *port = PULSE_PORT (a); + const gchar *name = (const gchar *) b; + + return strcmp (pulse_port_get_name (port), name); +} diff --git a/backends/pulse/pulse-port-switch.h b/backends/pulse/pulse-port-switch.h new file mode 100644 index 0000000..6ccef38 --- /dev/null +++ b/backends/pulse/pulse-port-switch.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_PORT_SWITCH_H +#define PULSE_PORT_SWITCH_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_PORT_SWITCH \ + (pulse_port_switch_get_type ()) +#define PULSE_PORT_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_PORT_SWITCH, PulsePortSwitch)) +#define PULSE_IS_PORT_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_PORT_SWITCH)) +#define PULSE_PORT_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_PORT_SWITCH, PulsePortSwitchClass)) +#define PULSE_IS_PORT_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_PORT_SWITCH)) +#define PULSE_PORT_SWITCH_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_PORT_SWITCH, PulsePortSwitchClass)) + +typedef struct _PulsePortSwitchClass PulsePortSwitchClass; +typedef struct _PulsePortSwitchPrivate PulsePortSwitchPrivate; + +struct _PulsePortSwitch +{ + MateMixerSwitch parent; + + /*< private >*/ + PulsePortSwitchPrivate *priv; +}; + +struct _PulsePortSwitchClass +{ + MateMixerSwitchClass parent_class; + + /*< private >*/ + gboolean (*set_active_port) (PulsePortSwitch *swtch, + PulsePort *port); +}; + +GType pulse_port_switch_get_type (void) G_GNUC_CONST; + +PulseStream *pulse_port_switch_get_stream (PulsePortSwitch *swtch); + +void pulse_port_switch_add_port (PulsePortSwitch *swtch, + PulsePort *port); + +void pulse_port_switch_set_active_port (PulsePortSwitch *swtch, + PulsePort *port); + +void pulse_port_switch_set_active_port_by_name (PulsePortSwitch *swtch, + const gchar *name); + +G_END_DECLS + +#endif /* PULSE_PORT_SWITCH_H */ diff --git a/backends/pulse/pulse-port.c b/backends/pulse/pulse-port.c new file mode 100644 index 0000000..f427448 --- /dev/null +++ b/backends/pulse/pulse-port.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-port.h" + +struct _PulsePortPrivate +{ + guint priority; +}; + +static void pulse_port_class_init (PulsePortClass *klass); +static void pulse_port_init (PulsePort *port); + +G_DEFINE_TYPE (PulsePort, pulse_port, MATE_MIXER_TYPE_SWITCH_OPTION) + +static void +pulse_port_class_init (PulsePortClass *klass) +{ + g_type_class_add_private (klass, sizeof (PulsePortPrivate)); +} + +static void +pulse_port_init (PulsePort *port) +{ + port->priv = G_TYPE_INSTANCE_GET_PRIVATE (port, + PULSE_TYPE_PORT, + PulsePortPrivate); +} + +PulsePort * +pulse_port_new (const gchar *name, + const gchar *label, + const gchar *icon, + guint priority) +{ + PulsePort *port; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (label != NULL, NULL); + + port = g_object_new (PULSE_TYPE_PORT, + "name", name, + "label", label, + "icon", icon, + NULL); + + port->priv->priority = priority; + return port; +} + +const gchar * +pulse_port_get_name (PulsePort *port) +{ + g_return_val_if_fail (PULSE_IS_PORT (port), NULL); + + return mate_mixer_switch_option_get_name (MATE_MIXER_SWITCH_OPTION (port)); +} + +guint +pulse_port_get_priority (PulsePort *port) +{ + g_return_val_if_fail (PULSE_IS_PORT (port), 0); + + return port->priv->priority; +} diff --git a/backends/pulse/pulse-port.h b/backends/pulse/pulse-port.h new file mode 100644 index 0000000..241fa2d --- /dev/null +++ b/backends/pulse/pulse-port.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_PORT_H +#define PULSE_PORT_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_PORT \ + (pulse_port_get_type ()) +#define PULSE_PORT(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_PORT, PulsePort)) +#define PULSE_IS_PORT(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_PORT)) +#define PULSE_PORT_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_PORT, PulsePortClass)) +#define PULSE_IS_PORT_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_PORT)) +#define PULSE_PORT_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_PORT, PulsePortClass)) + +typedef struct _PulsePortClass PulsePortClass; +typedef struct _PulsePortPrivate PulsePortPrivate; + +struct _PulsePort +{ + MateMixerSwitchOption parent; + + /*< private >*/ + PulsePortPrivate *priv; +}; + +struct _PulsePortClass +{ + MateMixerSwitchOptionClass parent; +}; + +GType pulse_port_get_type (void) G_GNUC_CONST; + +PulsePort * pulse_port_new (const gchar *name, + const gchar *label, + const gchar *icon, + guint priority); + +const gchar *pulse_port_get_name (PulsePort *port); +guint pulse_port_get_priority (PulsePort *port); + +G_END_DECLS + +#endif /* PULSE_PORT_H */ diff --git a/backends/pulse/pulse-sink-control.c b/backends/pulse/pulse-sink-control.c new file mode 100644 index 0000000..500cef0 --- /dev/null +++ b/backends/pulse/pulse-sink-control.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-monitor.h" +#include "pulse-stream-control.h" +#include "pulse-sink.h" +#include "pulse-sink-control.h" + +static void pulse_sink_control_class_init (PulseSinkControlClass *klass); +static void pulse_sink_control_init (PulseSinkControl *control); + +G_DEFINE_TYPE (PulseSinkControl, pulse_sink_control, PULSE_TYPE_STREAM_CONTROL); + +static gboolean pulse_sink_control_set_mute (PulseStreamControl *psc, + gboolean mute); +static gboolean pulse_sink_control_set_volume (PulseStreamControl *psc, + pa_cvolume *cvolume); +static PulseMonitor *pulse_sink_control_create_monitor (PulseStreamControl *psc); + +static void +pulse_sink_control_class_init (PulseSinkControlClass *klass) +{ + PulseStreamControlClass *control_class; + + control_class = PULSE_STREAM_CONTROL_CLASS (klass); + control_class->set_mute = pulse_sink_control_set_mute; + control_class->set_volume = pulse_sink_control_set_volume; + control_class->create_monitor = pulse_sink_control_create_monitor; +} + +static void +pulse_sink_control_init (PulseSinkControl *control) +{ +} + +PulseSinkControl * +pulse_sink_control_new (PulseSink *sink, + const pa_sink_info *info) +{ + PulseSinkControl *control; + MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE | + MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE; + MateMixerStreamControlRole role; + guint32 index; + + g_return_val_if_fail (PULSE_IS_SINK (sink), NULL); + g_return_val_if_fail (info != NULL, NULL); + + if (info->active_port != NULL) + role = MATE_MIXER_STREAM_CONTROL_ROLE_PORT; + else + role = MATE_MIXER_STREAM_CONTROL_ROLE_MASTER; + + /* Build the flag list */ + if (info->flags & PA_SINK_DECIBEL_VOLUME) + flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; + + index = pulse_sink_get_index_monitor (sink); + if (index != PA_INVALID_INDEX) + flags |= MATE_MIXER_STREAM_CONTROL_HAS_MONITOR; + + control = g_object_new (PULSE_TYPE_SINK_CONTROL, + "name", info->name, + "label", info->description, + "flags", flags, + "role", role, + "stream", sink, + NULL); + + pulse_sink_control_update (control, info); + return control; +} + +void +pulse_sink_control_update (PulseSinkControl *control, const pa_sink_info *info) +{ + g_return_if_fail (PULSE_IS_SINK_CONTROL (control)); + g_return_if_fail (info != NULL); + + /* Let all the information update before emitting notify signals */ + g_object_freeze_notify (G_OBJECT (control)); + + _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (control), + info->mute ? TRUE : FALSE); + + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (control), + &info->channel_map); + + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (control), + &info->volume, + info->base_volume); + + g_object_thaw_notify (G_OBJECT (control)); +} + +static gboolean +pulse_sink_control_set_mute (PulseStreamControl *psc, gboolean mute) +{ + g_return_val_if_fail (PULSE_IS_SINK_CONTROL (psc), FALSE); + + return pulse_connection_set_sink_mute (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc), + mute); +} + +static gboolean +pulse_sink_control_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume) +{ + g_return_val_if_fail (PULSE_IS_SINK_CONTROL (psc), FALSE); + g_return_val_if_fail (cvolume != NULL, FALSE); + + return pulse_connection_set_sink_volume (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc), + cvolume); +} + +static PulseMonitor * +pulse_sink_control_create_monitor (PulseStreamControl *psc) +{ + PulseSink *sink; + guint32 index; + + g_return_val_if_fail (PULSE_IS_SINK_CONTROL (psc), NULL); + + sink = PULSE_SINK (mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (psc))); + + index = pulse_sink_get_index_monitor (sink); + if G_UNLIKELY (index == PA_INVALID_INDEX) { + g_debug ("Monitor of stream control %s is not available", + mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (psc))); + return NULL; + } + + return pulse_connection_create_monitor (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + index, + PA_INVALID_INDEX); +} diff --git a/backends/pulse/pulse-sink-control.h b/backends/pulse/pulse-sink-control.h new file mode 100644 index 0000000..e9570f4 --- /dev/null +++ b/backends/pulse/pulse-sink-control.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SINK_CONTROL_H +#define PULSE_SINK_CONTROL_H + +#include <glib.h> +#include <glib-object.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-stream-control.h" +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SINK_CONTROL \ + (pulse_sink_control_get_type ()) +#define PULSE_SINK_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SINK_CONTROL, PulseSinkControl)) +#define PULSE_IS_SINK_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SINK_CONTROL)) +#define PULSE_SINK_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SINK_CONTROL, PulseSinkControlClass)) +#define PULSE_IS_SINK_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK_CONTROL)) +#define PULSE_SINK_CONTROL_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK_CONTROL, PulseSinkControlClass)) + +typedef struct _PulseSinkControlClass PulseSinkControlClass; +typedef struct _PulseSinkControlPrivate PulseSinkControlPrivate; + +struct _PulseSinkControl +{ + PulseStreamControl parent; +}; + +struct _PulseSinkControlClass +{ + PulseStreamControlClass parent_class; +}; + +GType pulse_sink_control_get_type (void) G_GNUC_CONST; + +PulseSinkControl *pulse_sink_control_new (PulseSink *sink, + const pa_sink_info *info); + +void pulse_sink_control_update (PulseSinkControl *control, + const pa_sink_info *info); + +G_END_DECLS + +#endif /* PULSE_SINK_CONTROL_H */ diff --git a/backends/pulse/pulse-sink-input.c b/backends/pulse/pulse-sink-input.c index 1d5f9c2..eab85b8 100644 --- a/backends/pulse/pulse-sink-input.c +++ b/backends/pulse/pulse-sink-input.c @@ -15,57 +15,41 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#include <string.h> #include <glib.h> #include <glib-object.h> - -#include <libmatemixer/matemixer-client-stream.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" -#include "pulse-client-stream.h" #include "pulse-helpers.h" #include "pulse-monitor.h" #include "pulse-sink.h" #include "pulse-sink-input.h" #include "pulse-stream.h" +#include "pulse-stream-control.h" static void pulse_sink_input_class_init (PulseSinkInputClass *klass); static void pulse_sink_input_init (PulseSinkInput *input); -G_DEFINE_TYPE (PulseSinkInput, pulse_sink_input, PULSE_TYPE_CLIENT_STREAM); - -static void pulse_sink_input_reload (PulseStream *pstream); +G_DEFINE_TYPE (PulseSinkInput, pulse_sink_input, PULSE_TYPE_STREAM_CONTROL); -static gboolean pulse_sink_input_set_mute (PulseStream *pstream, - gboolean mute); -static gboolean pulse_sink_input_set_volume (PulseStream *pstream, - pa_cvolume *cvolume); -static gboolean pulse_sink_input_set_parent (PulseClientStream *pclient, - PulseStream *parent); -static gboolean pulse_sink_input_remove (PulseClientStream *pclient); -static PulseMonitor *pulse_sink_input_create_monitor (PulseStream *pstream); +static gboolean pulse_sink_input_set_mute (PulseStreamControl *psc, + gboolean mute); +static gboolean pulse_sink_input_set_volume (PulseStreamControl *psc, + pa_cvolume *cvolume); +static PulseMonitor *pulse_sink_input_create_monitor (PulseStreamControl *psc); static void pulse_sink_input_class_init (PulseSinkInputClass *klass) { - PulseStreamClass *stream_class; - PulseClientStreamClass *client_class; - - stream_class = PULSE_STREAM_CLASS (klass); + PulseStreamControlClass *control_class; - stream_class->reload = pulse_sink_input_reload; - stream_class->set_mute = pulse_sink_input_set_mute; - stream_class->set_volume = pulse_sink_input_set_volume; - stream_class->create_monitor = pulse_sink_input_create_monitor; - - client_class = PULSE_CLIENT_STREAM_CLASS (klass); - - client_class->set_parent = pulse_sink_input_set_parent; - client_class->remove = pulse_sink_input_remove; + control_class = PULSE_STREAM_CONTROL_CLASS (klass); + control_class->set_mute = pulse_sink_input_set_mute; + control_class->set_volume = pulse_sink_input_set_volume; + control_class->create_monitor = pulse_sink_input_create_monitor; } static void @@ -73,230 +57,171 @@ pulse_sink_input_init (PulseSinkInput *input) { } -PulseStream * -pulse_sink_input_new (PulseConnection *connection, - const pa_sink_input_info *info, - PulseStream *parent) +PulseSinkInput * +pulse_sink_input_new (PulseSink *sink, const pa_sink_input_info *info) { - PulseSinkInput *input; + PulseSinkInput *input; + gchar *name; + const gchar *prop; + const gchar *label = NULL; + MateMixerAppInfo *app_info = NULL; - g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (info != NULL, NULL); + MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE | + MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE | + MATE_MIXER_STREAM_CONTROL_HAS_MONITOR; + MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN; - /* Consider the sink input index as unchanging parameter */ - input = g_object_new (PULSE_TYPE_SINK_INPUT, - "connection", connection, - "index", info->index, - NULL); + MateMixerStreamControlMediaRole media_role = MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN; - /* Other data may change at any time, so let's make a use of our update function */ - pulse_sink_input_update (PULSE_STREAM (input), info, parent); - - return PULSE_STREAM (input); -} - -gboolean -pulse_sink_input_update (PulseStream *pstream, - const pa_sink_input_info *info, - PulseStream *parent) -{ - MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT | - MATE_MIXER_STREAM_CLIENT | - MATE_MIXER_STREAM_HAS_MUTE | - MATE_MIXER_STREAM_HAS_MONITOR; - PulseClientStream *pclient; - const gchar *prop; - const gchar *description = NULL; - gchar *name; - - g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), FALSE); - g_return_val_if_fail (info != NULL, FALSE); - - pclient = PULSE_CLIENT_STREAM (pstream); - - /* Let all the information update before emitting notify signals */ - g_object_freeze_notify (G_OBJECT (pstream)); + g_return_val_if_fail (PULSE_IS_SINK (sink), NULL); + g_return_val_if_fail (info != NULL, NULL); /* Many mixer applications query the Pulse client list and use the client * name here, but we use the name only as an identifier, so let's avoid * this unnecessary overhead and use a custom name. * Also make sure to make the name unique by including the PulseAudio index. */ - name = g_strdup_printf ("pulse-stream-client-output-%lu", (gulong) info->index); - - pulse_stream_update_name (pstream, name); - g_free (name); - - prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE); - if (prop != NULL) { - MateMixerClientStreamRole role = pulse_convert_media_role_name (prop); - - if (role == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT) { - /* The event description seems to provide much better readable - * description for event streams */ - prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION); - - if (G_LIKELY (prop != NULL)) - description = prop; - } - pulse_client_stream_update_role (pclient, role); - } else - pulse_client_stream_update_role (pclient, MATE_MIXER_CLIENT_STREAM_ROLE_NONE); - - if (description == NULL) - description = info->name; - - pulse_stream_update_description (pstream, description); - pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE); - - if (info->client != PA_INVALID_INDEX) - pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_APPLICATION); - else - pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_NO_FLAGS); - - if (G_LIKELY (parent != NULL)) { - if (pulse_sink_get_monitor_index (parent) != PA_INVALID_INDEX) - flags |= MATE_MIXER_STREAM_HAS_MONITOR; - - pulse_client_stream_update_parent (pclient, MATE_MIXER_STREAM (parent)); - } else - pulse_client_stream_update_parent (pclient, NULL); + name = g_strdup_printf ("pulse-output-control-%lu", (gulong) info->index); #if PA_CHECK_VERSION(1, 0, 0) if (info->has_volume) { flags |= - MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME; + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; if (info->volume_writable) - flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME; + flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE; } - - /* Flags needed before volume */ - pulse_stream_update_flags (pstream, flags); - pulse_stream_update_channel_map (pstream, &info->channel_map); - - if (info->has_volume) - pulse_stream_update_volume (pstream, &info->volume, 0); - else - pulse_stream_update_volume (pstream, NULL, 0); #else /* Pre-1.0 PulseAudio does not include the has_volume and volume_writable * fields, but does include the volume info, so let's give it a try */ flags |= - MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME | - MATE_MIXER_STREAM_CAN_SET_VOLUME; + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE | + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; +#endif - /* Flags needed before volume */ - pulse_stream_update_flags (pstream, flags); - pulse_stream_update_channel_map (pstream, &info->channel_map); + if (info->client != PA_INVALID_INDEX) { + app_info = _mate_mixer_app_info_new (); - pulse_stream_update_volume (pstream, &info->volume, 0); -#endif + role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION; - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME); - if (prop != NULL) - pulse_client_stream_update_app_name (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME); + if (prop != NULL) + _mate_mixer_app_info_set_name (app_info, prop); - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID); - if (prop != NULL) - pulse_client_stream_update_app_id (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID); + if (prop != NULL) + _mate_mixer_app_info_set_id (app_info, prop); - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION); - if (prop != NULL) - pulse_client_stream_update_app_version (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION); + if (prop != NULL) + _mate_mixer_app_info_set_version (app_info, prop); - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME); - if (prop != NULL) - pulse_client_stream_update_app_icon (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME); + if (prop != NULL) + _mate_mixer_app_info_set_icon (app_info, prop); + } - // XXX needs to fix monitor if parent changes + prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE); + if (prop != NULL) { + media_role = pulse_convert_media_role_name (prop); - g_object_thaw_notify (G_OBJECT (pstream)); - return TRUE; -} + if (media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT) { + /* The event description seems to provide much better readable + * description for event streams */ + prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION); + if (prop != NULL) + label = prop; + } + } -static void -pulse_sink_input_reload (PulseStream *pstream) -{ - g_return_if_fail (PULSE_IS_SINK_INPUT (pstream)); + if (label == NULL) + label = info->name; - pulse_connection_load_sink_input_info (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); -} + input = g_object_new (PULSE_TYPE_SINK_INPUT, + "name", name, + "label", label, + "flags", flags, + "role", role, + "media-role", media_role, + "index", info->index, + "stream", sink, + NULL); + g_free (name); -static gboolean -pulse_sink_input_set_mute (PulseStream *pstream, gboolean mute) -{ - g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), FALSE); + if (app_info != NULL) + pulse_stream_control_set_app_info (PULSE_STREAM_CONTROL (input), app_info); - return pulse_connection_set_sink_input_mute (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - mute); + pulse_sink_input_update (input, info); + return input; } -static gboolean -pulse_sink_input_set_volume (PulseStream *pstream, pa_cvolume *cvolume) +void +pulse_sink_input_update (PulseSinkInput *input, const pa_sink_input_info *info) { - g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), FALSE); - g_return_val_if_fail (cvolume != NULL, FALSE); + g_return_if_fail (PULSE_IS_SINK_INPUT (input)); + g_return_if_fail (info != NULL); - return pulse_connection_set_sink_input_volume (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - cvolume); -} + /* Let all the information update before emitting notify signals */ + g_object_freeze_notify (G_OBJECT (input)); -static gboolean -pulse_sink_input_set_parent (PulseClientStream *pclient, PulseStream *parent) -{ - PulseStream *pstream; + _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (input), + info->mute ? TRUE : FALSE); - g_return_val_if_fail (PULSE_IS_SINK_INPUT (pclient), FALSE); +#if PA_CHECK_VERSION(1, 0, 0) + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (input), &info->channel_map); - pstream = PULSE_STREAM (pclient); + if (info->has_volume) + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (input), &info->volume, 0); + else + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (input), NULL, 0); +#else + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (input), &info->channel_map); + pulse_stream_control_set_volume (PULSE_STREAM_CONTROL (input), &info->volume, 0); +#endif - return pulse_connection_move_sink_input (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - pulse_stream_get_index (parent)); + g_object_thaw_notify (G_OBJECT (input)); } static gboolean -pulse_sink_input_remove (PulseClientStream *pclient) +pulse_sink_input_set_mute (PulseStreamControl *psc, gboolean mute) { - PulseStream *pstream; + g_return_val_if_fail (PULSE_IS_SINK_INPUT (psc), FALSE); - g_return_val_if_fail (PULSE_IS_SINK_INPUT (pclient), FALSE); + return pulse_connection_set_sink_input_mute (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + pulse_stream_control_get_index (psc), + mute); +} - pstream = PULSE_STREAM (pclient); +static gboolean +pulse_sink_input_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume) +{ + g_return_val_if_fail (PULSE_IS_SINK_INPUT (psc), FALSE); + g_return_val_if_fail (cvolume != NULL, FALSE); - return pulse_connection_kill_sink_input (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); + return pulse_connection_set_sink_input_volume (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + pulse_stream_control_get_index (psc), + cvolume); } static PulseMonitor * -pulse_sink_input_create_monitor (PulseStream *pstream) +pulse_sink_input_create_monitor (PulseStreamControl *psc) { - MateMixerStream *parent; - guint32 index; - - g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), NULL); + PulseSink *sink; + guint32 index; - parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream)); - if (G_UNLIKELY (parent == NULL)) { - g_debug ("Not creating monitor for client stream %s as it is not available", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream))); - return NULL; - } + g_return_val_if_fail (PULSE_IS_SINK_INPUT (psc), NULL); - index = pulse_sink_get_monitor_index (PULSE_STREAM (parent)); + sink = PULSE_SINK (mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (psc))); - if (G_UNLIKELY (index == PA_INVALID_INDEX)) { - g_debug ("Not creating monitor for client stream %s as it is not available", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream))); + index = pulse_sink_get_index_monitor (sink); + if G_UNLIKELY (index == PA_INVALID_INDEX) { + g_debug ("Monitor of stream control %s is not available", + mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (psc))); return NULL; } - return pulse_connection_create_monitor (pulse_stream_get_connection (pstream), + return pulse_connection_create_monitor (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), index, - pulse_stream_get_index (pstream)); + pulse_stream_control_get_index (psc)); } diff --git a/backends/pulse/pulse-sink-input.h b/backends/pulse/pulse-sink-input.h index 1e5004b..127eab6 100644 --- a/backends/pulse/pulse-sink-input.h +++ b/backends/pulse/pulse-sink-input.h @@ -23,9 +23,8 @@ #include <pulse/pulseaudio.h> -#include "pulse-client-stream.h" -#include "pulse-connection.h" -#include "pulse-stream.h" +#include "pulse-stream-control.h" +#include "pulse-types.h" G_BEGIN_DECLS @@ -42,28 +41,25 @@ G_BEGIN_DECLS #define PULSE_SINK_INPUT_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK_INPUT, PulseSinkInputClass)) -typedef struct _PulseSinkInput PulseSinkInput; typedef struct _PulseSinkInputClass PulseSinkInputClass; struct _PulseSinkInput { - PulseClientStream parent; + PulseStreamControl parent; }; struct _PulseSinkInputClass { - PulseClientStreamClass parent_class; + PulseStreamControlClass parent_class; }; -GType pulse_sink_input_get_type (void) G_GNUC_CONST; +GType pulse_sink_input_get_type (void) G_GNUC_CONST; -PulseStream *pulse_sink_input_new (PulseConnection *connection, - const pa_sink_input_info *info, - PulseStream *parent); +PulseSinkInput *pulse_sink_input_new (PulseSink *sink, + const pa_sink_input_info *info); -gboolean pulse_sink_input_update (PulseStream *pstream, - const pa_sink_input_info *info, - PulseStream *parent); +void pulse_sink_input_update (PulseSinkInput *input, + const pa_sink_input_info *info); G_END_DECLS diff --git a/backends/pulse/pulse-sink-switch.c b/backends/pulse/pulse-sink-switch.c new file mode 100644 index 0000000..0e08dac --- /dev/null +++ b/backends/pulse/pulse-sink-switch.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "pulse-connection.h" +#include "pulse-port.h" +#include "pulse-port-switch.h" +#include "pulse-sink-switch.h" +#include "pulse-stream.h" + +static void pulse_sink_switch_class_init (PulseSinkSwitchClass *klass); +static void pulse_sink_switch_init (PulseSinkSwitch *swtch); + +G_DEFINE_TYPE (PulseSinkSwitch, pulse_sink_switch, PULSE_TYPE_PORT_SWITCH) + +static gboolean pulse_sink_switch_set_active_port (PulsePortSwitch *swtch, + PulsePort *port); + +static void +pulse_sink_switch_class_init (PulseSinkSwitchClass *klass) +{ + PulsePortSwitchClass *switch_class; + + switch_class = PULSE_PORT_SWITCH_CLASS (klass); + switch_class->set_active_port = pulse_sink_switch_set_active_port; +} + +static void +pulse_sink_switch_init (PulseSinkSwitch *swtch) +{ +} + +PulsePortSwitch * +pulse_sink_switch_new (const gchar *name, const gchar *label, PulseSink *sink) +{ + return g_object_new (PULSE_TYPE_SINK_SWITCH, + "name", name, + "label", label, + "role", MATE_MIXER_SWITCH_ROLE_PORT, + "stream", sink, + NULL); +} + +static gboolean +pulse_sink_switch_set_active_port (PulsePortSwitch *swtch, PulsePort *port) +{ + PulseStream *stream; + + g_return_val_if_fail (PULSE_IS_SINK_SWITCH (swtch), FALSE); + g_return_val_if_fail (PULSE_IS_PORT (port), FALSE); + + stream = pulse_port_switch_get_stream (swtch); + + return pulse_connection_set_sink_port (pulse_stream_get_connection (stream), + pulse_stream_get_index (stream), + pulse_port_get_name (port)); +} diff --git a/backends/pulse/pulse-sink-switch.h b/backends/pulse/pulse-sink-switch.h new file mode 100644 index 0000000..71ed4c9 --- /dev/null +++ b/backends/pulse/pulse-sink-switch.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SINK_SWITCH_H +#define PULSE_SINK_SWITCH_H + +#include <glib.h> +#include <glib-object.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SINK_SWITCH \ + (pulse_sink_switch_get_type ()) +#define PULSE_SINK_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SINK_SWITCH, PulseSinkSwitch)) +#define PULSE_IS_SINK_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SINK_SWITCH)) +#define PULSE_SINK_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SINK_SWITCH, PulseSinkSwitchClass)) +#define PULSE_IS_SINK_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK_SWITCH)) +#define PULSE_SINK_SWITCH_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK_SWITCH, PulseSinkSwitchClass)) + +typedef struct _PulseSinkSwitchClass PulseSinkSwitchClass; +typedef struct _PulseSinkSwitchPrivate PulseSinkSwitchPrivate; + +struct _PulseSinkSwitch +{ + PulsePortSwitch parent; +}; + +struct _PulseSinkSwitchClass +{ + PulsePortSwitchClass parent_class; +}; + +GType pulse_sink_switch_get_type (void) G_GNUC_CONST; + +PulsePortSwitch *pulse_sink_switch_new (const gchar *name, + const gchar *label, + PulseSink *sink); + +G_END_DECLS + +#endif /* PULSE_SINK_SWITCH_H */ diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c index 0f828b1..d2f0280 100644 --- a/backends/pulse/pulse-sink.c +++ b/backends/pulse/pulse-sink.c @@ -16,62 +16,57 @@ */ #include <glib.h> +#include <glib/gi18n.h> #include <glib-object.h> - -#include <libmatemixer/matemixer-port.h> -#include <libmatemixer/matemixer-port-private.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" #include "pulse-device.h" #include "pulse-monitor.h" +#include "pulse-port.h" +#include "pulse-port-switch.h" #include "pulse-stream.h" #include "pulse-sink.h" +#include "pulse-sink-control.h" +#include "pulse-sink-input.h" +#include "pulse-sink-switch.h" struct _PulseSinkPrivate { - guint32 index_monitor; + guint32 monitor; + GHashTable *inputs; + PulsePortSwitch *pswitch; + GList *streams_list; + GList *switches_list; + PulseSinkControl *control; }; static void pulse_sink_class_init (PulseSinkClass *klass); static void pulse_sink_init (PulseSink *sink); +static void pulse_sink_dispose (GObject *object); +static void pulse_sink_finalize (GObject *object); G_DEFINE_TYPE (PulseSink, pulse_sink, PULSE_TYPE_STREAM); -static void pulse_sink_reload (PulseStream *pstream); - -static gboolean pulse_sink_set_mute (PulseStream *pstream, - gboolean mute); -static gboolean pulse_sink_set_volume (PulseStream *pstream, - pa_cvolume *cvolume); -static gboolean pulse_sink_set_active_port (PulseStream *pstream, - MateMixerPort *port); - -static gboolean pulse_sink_suspend (PulseStream *pstream); -static gboolean pulse_sink_resume (PulseStream *pstream); - -static PulseMonitor *pulse_sink_create_monitor (PulseStream *pstream); - -static void update_ports (PulseStream *pstream, - pa_sink_port_info **ports, - pa_sink_port_info *active); +static const GList *pulse_sink_list_controls (MateMixerStream *mms); +static const GList *pulse_sink_list_switches (MateMixerStream *mms); static void pulse_sink_class_init (PulseSinkClass *klass) { - PulseStreamClass *stream_class; + GObjectClass *object_class; + MateMixerStreamClass *stream_class; - stream_class = PULSE_STREAM_CLASS (klass); + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_sink_dispose; + object_class->finalize = pulse_sink_finalize; - stream_class->reload = pulse_sink_reload; - stream_class->set_mute = pulse_sink_set_mute; - stream_class->set_volume = pulse_sink_set_volume; - stream_class->set_active_port = pulse_sink_set_active_port; - stream_class->suspend = pulse_sink_suspend; - stream_class->resume = pulse_sink_resume; - stream_class->create_monitor = pulse_sink_create_monitor; + stream_class = MATE_MIXER_STREAM_CLASS (klass); + stream_class->list_controls = pulse_sink_list_controls; + stream_class->list_switches = pulse_sink_list_switches; g_type_class_add_private (klass, sizeof (PulseSinkPrivate)); } @@ -83,247 +78,190 @@ pulse_sink_init (PulseSink *sink) PULSE_TYPE_SINK, PulseSinkPrivate); - sink->priv->index_monitor = PA_INVALID_INDEX; + sink->priv->inputs = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); + + sink->priv->monitor = PA_INVALID_INDEX; } -PulseStream * -pulse_sink_new (PulseConnection *connection, - const pa_sink_info *info, - PulseDevice *device) +static void +pulse_sink_dispose (GObject *object) { - PulseStream *stream; + PulseSink *sink; - g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (info != NULL, NULL); + sink = PULSE_SINK (object); - /* Consider the sink index as unchanging parameter */ - stream = g_object_new (PULSE_TYPE_SINK, - "connection", connection, - "index", info->index, - NULL); + g_clear_object (&sink->priv->control); + g_clear_object (&sink->priv->pswitch); - /* Other data may change at any time, so let's make a use of our update function */ - pulse_sink_update (stream, info, device); + g_hash_table_remove_all (sink->priv->inputs); - return stream; + G_OBJECT_CLASS (pulse_sink_parent_class)->dispose (object); } -guint32 -pulse_sink_get_monitor_index (PulseStream *pstream) +static void +pulse_sink_finalize (GObject *object) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), PA_INVALID_INDEX); + PulseSink *sink; + + sink = PULSE_SINK (object); - return PULSE_SINK (pstream)->priv->index_monitor; + g_hash_table_unref (sink->priv->inputs); + + G_OBJECT_CLASS (pulse_sink_parent_class)->finalize (object); } -gboolean -pulse_sink_update (PulseStream *pstream, const pa_sink_info *info, PulseDevice *device) +PulseSink * +pulse_sink_new (PulseConnection *connection, + const pa_sink_info *info, + PulseDevice *device) { - MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT | - MATE_MIXER_STREAM_HAS_MUTE | - MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_CAN_SET_VOLUME | - MATE_MIXER_STREAM_CAN_SUSPEND; PulseSink *sink; - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); - g_return_val_if_fail (info != NULL, FALSE); - - /* Let all the information update before emitting notify signals */ - g_object_freeze_notify (G_OBJECT (pstream)); - - pulse_stream_update_name (pstream, info->name); - pulse_stream_update_description (pstream, info->description); - pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE); - - /* Stream state */ - switch (info->state) { - case PA_SINK_RUNNING: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_RUNNING); - break; - case PA_SINK_IDLE: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE); - break; - case PA_SINK_SUSPENDED: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED); - break; - default: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_UNKNOWN); - break; - } - - /* Build the flag list */ - if (info->flags & PA_SINK_DECIBEL_VOLUME) - flags |= MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME; - if (info->flags & PA_SINK_FLAT_VOLUME) - flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME; - - sink = PULSE_SINK (pstream); - - if (sink->priv->index_monitor == PA_INVALID_INDEX) - sink->priv->index_monitor = info->monitor_source; + g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (info != NULL, NULL); - if (sink->priv->index_monitor != PA_INVALID_INDEX) - flags |= MATE_MIXER_STREAM_HAS_MONITOR; + sink = g_object_new (PULSE_TYPE_SINK, + "name", info->name, + "label", info->description, + "device", device, + "direction", MATE_MIXER_DIRECTION_OUTPUT, + "connection", connection, + "index", info->index, + NULL); + + sink->priv->control = pulse_sink_control_new (sink, info); + + if (info->n_ports > 0) { + pa_sink_port_info **ports = info->ports; + + /* Create the port switch */ + sink->priv->pswitch = pulse_sink_switch_new ("port", _("Port"), sink); + + while (*ports != NULL) { + pa_sink_port_info *p = *ports++; + PulsePort *port; + const gchar *icon = NULL; + + /* A port may include an icon but in PulseAudio sink and source ports + * the property is not included, for this reason ports are also read from + * devices where the icons may be present */ + if (device != NULL) { + port = pulse_device_get_port (PULSE_DEVICE (device), p->name); + if (port != NULL) + icon = mate_mixer_switch_option_get_icon (MATE_MIXER_SWITCH_OPTION (port)); + } - /* Flags must be updated before volume */ - pulse_stream_update_flags (pstream, flags); + port = pulse_port_new (p->name, + p->description, + icon, + p->priority); - pulse_stream_update_channel_map (pstream, &info->channel_map); - pulse_stream_update_volume (pstream, &info->volume, info->base_volume); + pulse_port_switch_add_port (sink->priv->pswitch, port); - pulse_stream_update_device (pstream, MATE_MIXER_DEVICE (device)); + if (p == info->active_port) + pulse_port_switch_set_active_port (sink->priv->pswitch, port); + } - /* Ports must be updated after device */ - if (info->ports != NULL) { - update_ports (pstream, info->ports, info->active_port); + g_debug ("Created port list for sink %s", info->name); } - g_object_thaw_notify (G_OBJECT (pstream)); - return TRUE; -} - -static void -pulse_sink_reload (PulseStream *pstream) -{ - g_return_if_fail (PULSE_IS_SINK (pstream)); + pulse_sink_update (sink, info); - pulse_connection_load_sink_info (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); + _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (sink), + MATE_MIXER_STREAM_CONTROL (sink->priv->control)); + return sink; } -static gboolean -pulse_sink_set_mute (PulseStream *pstream, gboolean mute) +void +pulse_sink_add_input (PulseSink *sink, const pa_sink_input_info *info) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); - - return pulse_connection_set_sink_mute (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - mute); + PulseSinkInput *input; + + /* This function is used for both creating and refreshing sink inputs */ + input = g_hash_table_lookup (sink->priv->inputs, GINT_TO_POINTER (info->index)); + if (input == NULL) { + const gchar *name; + + input = pulse_sink_input_new (sink, info); + g_hash_table_insert (sink->priv->inputs, + GINT_TO_POINTER (info->index), + input); + + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (input)); + g_signal_emit_by_name (G_OBJECT (sink), + "control-added", + name); + } else + pulse_sink_input_update (input, info); } -static gboolean -pulse_sink_set_volume (PulseStream *pstream, pa_cvolume *cvolume) +void +pulse_sink_remove_input (PulseSink *sink, guint32 index) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); - g_return_val_if_fail (cvolume != NULL, FALSE); + PulseSinkInput *input; + const gchar *name; - return pulse_connection_set_sink_volume (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - cvolume); -} + input = g_hash_table_lookup (sink->priv->inputs, GINT_TO_POINTER (index)); + if G_UNLIKELY (input == NULL) + return; -static gboolean -pulse_sink_set_active_port (PulseStream *pstream, MateMixerPort *port) -{ - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (input)); - return pulse_connection_set_sink_port (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - mate_mixer_port_get_name (port)); + g_hash_table_remove (sink->priv->inputs, GINT_TO_POINTER (index)); + g_signal_emit_by_name (G_OBJECT (sink), + "control-removed", + name); } -static gboolean -pulse_sink_suspend (PulseStream *pstream) +void +pulse_sink_update (PulseSink *sink, const pa_sink_info *info) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); + g_return_if_fail (PULSE_IS_SINK (sink)); + g_return_if_fail (info != NULL); - return pulse_connection_suspend_sink (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - TRUE); + /* The switch doesn't allow being unset, PulseAudio should always include + * the active port name if the are any ports available */ + if (info->active_port != NULL) + pulse_port_switch_set_active_port_by_name (sink->priv->pswitch, + info->active_port->name); + + sink->priv->monitor = info->monitor_source; } -static gboolean -pulse_sink_resume (PulseStream *pstream) +guint32 +pulse_sink_get_index_monitor (PulseSink *sink) { - g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE); + g_return_val_if_fail (PULSE_IS_SINK (sink), 0); - return pulse_connection_suspend_sink (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - FALSE); + return sink->priv->monitor; } -static PulseMonitor * -pulse_sink_create_monitor (PulseStream *pstream) +static const GList * +pulse_sink_list_controls (MateMixerStream *mms) { - guint32 index; + GList *list; - g_return_val_if_fail (PULSE_IS_SINK (pstream), NULL); + g_return_val_if_fail (PULSE_IS_SINK (mms), NULL); - index = pulse_sink_get_monitor_index (pstream); + // XXX + list = g_hash_table_get_values (PULSE_SINK (mms)->priv->inputs); + if (list != NULL) + g_list_foreach (list, (GFunc) g_object_ref, NULL); - if (G_UNLIKELY (index == PA_INVALID_INDEX)) { - g_debug ("Not creating monitor for stream %s: not available", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream))); - return NULL; - } - - return pulse_connection_create_monitor (pulse_stream_get_connection (pstream), - index, - PA_INVALID_INDEX); + return g_list_prepend (list, g_object_ref (PULSE_SINK (mms)->priv->control)); } -static void -update_ports (PulseStream *pstream, - pa_sink_port_info **ports, - pa_sink_port_info *active) +static const GList * +pulse_sink_list_switches (MateMixerStream *mms) { - MateMixerPort *port; - MateMixerDevice *device; - GHashTable *hash; - - hash = pulse_stream_get_ports (pstream); - - while (*ports != NULL) { - MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS; - pa_sink_port_info *info = *ports; - const gchar *icon = NULL; - - device = mate_mixer_stream_get_device (MATE_MIXER_STREAM (pstream)); - if (device != NULL) { - port = mate_mixer_device_get_port (device, info->name); - - if (port != NULL) { - flags = mate_mixer_port_get_flags (port); - icon = mate_mixer_port_get_icon (port); - } - } - -#if PA_CHECK_VERSION(2, 0, 0) - if (info->available == PA_PORT_AVAILABLE_YES) - flags |= MATE_MIXER_PORT_AVAILABLE; - else - flags &= ~MATE_MIXER_PORT_AVAILABLE; -#endif - - port = g_hash_table_lookup (hash, info->name); - - if (port != NULL) { - /* Update existing port */ - _mate_mixer_port_update_description (port, info->description); - _mate_mixer_port_update_icon (port, icon); - _mate_mixer_port_update_priority (port, info->priority); - _mate_mixer_port_update_flags (port, flags); - } else { - /* Add previously unknown port to the hash table */ - port = _mate_mixer_port_new (info->name, - info->description, - icon, - info->priority, - flags); - - g_hash_table_insert (hash, g_strdup (info->name), port); - } - - ports++; - } + g_return_val_if_fail (PULSE_IS_SINK (mms), NULL); - /* Active port */ - if (G_LIKELY (active != NULL)) - port = g_hash_table_lookup (hash, active->name); - else - port = NULL; + // XXX + if (PULSE_SINK (mms)->priv->pswitch != NULL) + return g_list_prepend (NULL, PULSE_SINK (mms)->priv->pswitch); - pulse_stream_update_active_port (pstream, port); + return NULL; } diff --git a/backends/pulse/pulse-sink.h b/backends/pulse/pulse-sink.h index c0631ca..5eaeaa0 100644 --- a/backends/pulse/pulse-sink.h +++ b/backends/pulse/pulse-sink.h @@ -23,9 +23,8 @@ #include <pulse/pulseaudio.h> -#include "pulse-connection.h" -#include "pulse-device.h" #include "pulse-stream.h" +#include "pulse-types.h" G_BEGIN_DECLS @@ -42,7 +41,6 @@ G_BEGIN_DECLS #define PULSE_SINK_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK, PulseSinkClass)) -typedef struct _PulseSink PulseSink; typedef struct _PulseSinkClass PulseSinkClass; typedef struct _PulseSinkPrivate PulseSinkPrivate; @@ -59,17 +57,21 @@ struct _PulseSinkClass PulseStreamClass parent_class; }; -GType pulse_sink_get_type (void) G_GNUC_CONST; +GType pulse_sink_get_type (void) G_GNUC_CONST; -PulseStream *pulse_sink_new (PulseConnection *connection, - const pa_sink_info *info, - PulseDevice *device); +PulseSink *pulse_sink_new (PulseConnection *connection, + const pa_sink_info *info, + PulseDevice *device); -guint32 pulse_sink_get_monitor_index (PulseStream *pstream); +void pulse_sink_add_input (PulseSink *sink, + const pa_sink_input_info *info); -gboolean pulse_sink_update (PulseStream *pstream, - const pa_sink_info *info, - PulseDevice *device); +void pulse_sink_remove_input (PulseSink *sink, guint32 index); + +void pulse_sink_update (PulseSink *sink, + const pa_sink_info *info); + +guint32 pulse_sink_get_index_monitor (PulseSink *sink); G_END_DECLS diff --git a/backends/pulse/pulse-source-control.c b/backends/pulse/pulse-source-control.c new file mode 100644 index 0000000..3ed1573 --- /dev/null +++ b/backends/pulse/pulse-source-control.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-monitor.h" +#include "pulse-stream-control.h" +#include "pulse-source.h" +#include "pulse-source-control.h" + +static void pulse_source_control_class_init (PulseSourceControlClass *klass); +static void pulse_source_control_init (PulseSourceControl *control); + +G_DEFINE_TYPE (PulseSourceControl, pulse_source_control, PULSE_TYPE_STREAM_CONTROL); + +static gboolean pulse_source_control_set_mute (PulseStreamControl *psc, + gboolean mute); +static gboolean pulse_source_control_set_volume (PulseStreamControl *psc, + pa_cvolume *cvolume); +static PulseMonitor *pulse_source_control_create_monitor (PulseStreamControl *psc); + +static void +pulse_source_control_class_init (PulseSourceControlClass *klass) +{ + PulseStreamControlClass *control_class; + + control_class = PULSE_STREAM_CONTROL_CLASS (klass); + control_class->set_mute = pulse_source_control_set_mute; + control_class->set_volume = pulse_source_control_set_volume; + control_class->create_monitor = pulse_source_control_create_monitor; +} + +static void +pulse_source_control_init (PulseSourceControl *control) +{ +} + +PulseSourceControl * +pulse_source_control_new (PulseSource *source, + const pa_source_info *info) +{ + PulseSourceControl *control; + MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE | + MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE | + MATE_MIXER_STREAM_CONTROL_HAS_MONITOR; + MateMixerStreamControlRole role; + + g_return_val_if_fail (PULSE_IS_SOURCE (source), NULL); + g_return_val_if_fail (info != NULL, NULL); + + if (info->active_port != NULL) + role = MATE_MIXER_STREAM_CONTROL_ROLE_PORT; + else + role = MATE_MIXER_STREAM_CONTROL_ROLE_MASTER; + + /* Build the flag list */ + if (info->flags & PA_SOURCE_DECIBEL_VOLUME) + flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; + + control = g_object_new (PULSE_TYPE_SOURCE_CONTROL, + "name", info->name, + "label", info->description, + "flags", flags, + "role", role, + "stream", source, + NULL); + + pulse_source_control_update (control, info); + return control; +} + +void +pulse_source_control_update (PulseSourceControl *control, const pa_source_info *info) +{ + g_return_if_fail (PULSE_IS_SOURCE_CONTROL (control)); + g_return_if_fail (info != NULL); + + /* Let all the information update before emitting notify signals */ + g_object_freeze_notify (G_OBJECT (control)); + + _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (control), + info->mute ? TRUE : FALSE); + + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (control), + &info->channel_map); + + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (control), + &info->volume, + info->base_volume); + + g_object_thaw_notify (G_OBJECT (control)); +} + +static gboolean +pulse_source_control_set_mute (PulseStreamControl *psc, gboolean mute) +{ + g_return_val_if_fail (PULSE_IS_SOURCE_CONTROL (psc), FALSE); + + return pulse_connection_set_source_mute (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc), + mute); +} + +static gboolean +pulse_source_control_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume) +{ + g_return_val_if_fail (PULSE_IS_SOURCE_CONTROL (psc), FALSE); + g_return_val_if_fail (cvolume != NULL, FALSE); + + return pulse_connection_set_source_volume (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc), + cvolume); +} + +static PulseMonitor * +pulse_source_control_create_monitor (PulseStreamControl *psc) +{ + guint32 index; + + g_return_val_if_fail (PULSE_IS_SOURCE_CONTROL (psc), NULL); + + index = PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc); + if G_UNLIKELY (index == PA_INVALID_INDEX) { + g_debug ("Monitor of stream control %s is not available", + mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (psc))); + return NULL; + } + + return pulse_connection_create_monitor (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + index, + PA_INVALID_INDEX); +} diff --git a/backends/pulse/pulse-source-control.h b/backends/pulse/pulse-source-control.h new file mode 100644 index 0000000..a8d659f --- /dev/null +++ b/backends/pulse/pulse-source-control.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SOURCE_CONTROL_H +#define PULSE_SOURCE_CONTROL_H + +#include <glib.h> +#include <glib-object.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-stream-control.h" +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SOURCE_CONTROL \ + (pulse_source_control_get_type ()) +#define PULSE_SOURCE_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE_CONTROL, PulseSourceControl)) +#define PULSE_IS_SOURCE_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE_CONTROL)) +#define PULSE_SOURCE_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE_CONTROL, PulseSourceControlClass)) +#define PULSE_IS_SOURCE_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_CONTROL)) +#define PULSE_SOURCE_CONTROL_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_CONTROL, PulseSourceControlClass)) + +typedef struct _PulseSourceControlClass PulseSourceControlClass; +typedef struct _PulseSourceControlPrivate PulseSourceControlPrivate; + +struct _PulseSourceControl +{ + PulseStreamControl parent; +}; + +struct _PulseSourceControlClass +{ + PulseStreamControlClass parent_class; +}; + +GType pulse_source_control_get_type (void) G_GNUC_CONST; + +PulseSourceControl *pulse_source_control_new (PulseSource *source, + const pa_source_info *info); + +void pulse_source_control_update (PulseSourceControl *control, + const pa_source_info *info); + +G_END_DECLS + +#endif /* PULSE_SOURCE_CONTROL_H */ diff --git a/backends/pulse/pulse-source-output.c b/backends/pulse/pulse-source-output.c index 6cbd888..69fc3e4 100644 --- a/backends/pulse/pulse-source-output.c +++ b/backends/pulse/pulse-source-output.c @@ -17,56 +17,39 @@ #include <glib.h> #include <glib-object.h> -#include <string.h> - -#include <libmatemixer/matemixer-client-stream.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" -#include "pulse-client-stream.h" #include "pulse-helpers.h" #include "pulse-monitor.h" -#include "pulse-stream.h" #include "pulse-source.h" #include "pulse-source-output.h" +#include "pulse-stream.h" +#include "pulse-stream-control.h" static void pulse_source_output_class_init (PulseSourceOutputClass *klass); static void pulse_source_output_init (PulseSourceOutput *output); -G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_CLIENT_STREAM); - -static void pulse_source_output_reload (PulseStream *pstream); - -static gboolean pulse_source_output_set_mute (PulseStream *pstream, - gboolean mute); -static gboolean pulse_source_output_set_volume (PulseStream *pstream, - pa_cvolume *cvolume); +G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_STREAM_CONTROL); -static gboolean pulse_source_output_set_parent (PulseClientStream *pclient, - PulseStream *parent); -static gboolean pulse_source_output_remove (PulseClientStream *pclient); - -static PulseMonitor *pulse_source_output_create_monitor (PulseStream *pstream); +static gboolean pulse_source_output_set_mute (PulseStreamControl *psc, + gboolean mute); +static gboolean pulse_source_output_set_volume (PulseStreamControl *psc, + pa_cvolume *cvolume); +static PulseMonitor *pulse_source_output_create_monitor (PulseStreamControl *psc); static void pulse_source_output_class_init (PulseSourceOutputClass *klass) { - PulseStreamClass *stream_class; - PulseClientStreamClass *client_class; - - stream_class = PULSE_STREAM_CLASS (klass); - - stream_class->reload = pulse_source_output_reload; - stream_class->set_mute = pulse_source_output_set_mute; - stream_class->set_volume = pulse_source_output_set_volume; - stream_class->create_monitor = pulse_source_output_create_monitor; - - client_class = PULSE_CLIENT_STREAM_CLASS (klass); + PulseStreamControlClass *control_class; - client_class->set_parent = pulse_source_output_set_parent; - client_class->remove = pulse_source_output_remove; + control_class = PULSE_STREAM_CONTROL_CLASS (klass); + control_class->set_mute = pulse_source_output_set_mute; + control_class->set_volume = pulse_source_output_set_volume; + control_class->create_monitor = pulse_source_output_create_monitor; } static void @@ -74,210 +57,157 @@ pulse_source_output_init (PulseSourceOutput *output) { } -PulseStream * -pulse_source_output_new (PulseConnection *connection, - const pa_source_output_info *info, - PulseStream *parent) +PulseSourceOutput * +pulse_source_output_new (PulseSource *source, + const pa_source_output_info *info) { PulseSourceOutput *output; + gchar *name; + const gchar *prop; + MateMixerAppInfo *app_info = NULL; - g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (info != NULL, NULL); - - /* Consider the sink input index as unchanging parameter */ - output = g_object_new (PULSE_TYPE_SOURCE_OUTPUT, - "connection", connection, - "index", info->index, - NULL); - - /* Other data may change at any time, so let's make a use of our update function */ - pulse_source_output_update (PULSE_STREAM (output), info, parent); + MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE | + MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE | + MATE_MIXER_STREAM_CONTROL_HAS_MONITOR; + MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN; - return PULSE_STREAM (output); -} + MateMixerStreamControlMediaRole media_role = MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN; -gboolean -pulse_source_output_update (PulseStream *pstream, - const pa_source_output_info *info, - PulseStream *parent) -{ - MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT | - MATE_MIXER_STREAM_CLIENT; - PulseClientStream *pclient; - const gchar *prop; - const gchar *description = NULL; - gchar *name; + g_return_val_if_fail (PULSE_IS_SOURCE (source), NULL); + g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), FALSE); - g_return_val_if_fail (info != NULL, FALSE); + /* Many mixer applications query the Pulse client list and use the client + * name here, but we use the name only as an identifier, so let's avoid + * this unnecessary overhead and use a custom name. + * Also make sure to make the name unique by including the PulseAudio index. */ + name = g_strdup_printf ("pulse-input-control-%lu", (gulong) info->index); - pclient = PULSE_CLIENT_STREAM (pstream); +#if PA_CHECK_VERSION(1, 0, 0) + if (info->has_volume) { + flags |= + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; - /* Let all the information update before emitting notify signals */ - g_object_freeze_notify (G_OBJECT (pstream)); + if (info->volume_writable) + flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE; + } +#else + /* Pre-1.0 PulseAudio does not include the has_volume and volume_writable + * fields, but does include the volume info, so let's give it a try */ + flags |= + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE | + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL; +#endif - /* Many other mixer applications query the Pulse client list and use the - * client name here, but we use the name only as an identifier, so let's avoid - * this unnecessary overhead and use a custom name. - * Also make sure to make the name unique by including the Pulse index. */ - name = g_strdup_printf ("pulse-stream-client-input-%lu", (gulong) info->index); + if (info->client != PA_INVALID_INDEX) { + app_info = _mate_mixer_app_info_new (); - pulse_stream_update_name (pstream, name); - g_free (name); + role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION; - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME); - if (prop != NULL) - pulse_client_stream_update_app_name (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME); + if (prop != NULL) + _mate_mixer_app_info_set_name (app_info, prop); - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID); - if (prop != NULL) - pulse_client_stream_update_app_id (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID); + if (prop != NULL) + _mate_mixer_app_info_set_id (app_info, prop); - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION); - if (prop != NULL) - pulse_client_stream_update_app_version (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION); + if (prop != NULL) + _mate_mixer_app_info_set_version (app_info, prop); - prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME); - if (prop != NULL) - pulse_client_stream_update_app_icon (pclient, prop); + prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME); + if (prop != NULL) + _mate_mixer_app_info_set_icon (app_info, prop); + } prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE); - if (prop != NULL) { - MateMixerClientStreamRole role = pulse_convert_media_role_name (prop); + if (prop != NULL) + media_role = pulse_convert_media_role_name (prop); - if (role == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT) { - /* The event description seems to provide much better readable - * description for event streams */ - prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION); + output = g_object_new (PULSE_TYPE_SOURCE_OUTPUT, + "name", name, + "label", info->name, + "flags", flags, + "role", role, + "media-role", media_role, + "index", info->index, + "stream", source, + NULL); + g_free (name); - if (G_LIKELY (prop != NULL)) - description = prop; - } - pulse_client_stream_update_role (pclient, role); - } else - pulse_client_stream_update_role (pclient, MATE_MIXER_CLIENT_STREAM_ROLE_NONE); + if (app_info != NULL) + pulse_stream_control_set_app_info (PULSE_STREAM_CONTROL (output), app_info); - if (description == NULL) - description = info->name; + pulse_source_output_update (output, info); + return output; +} - pulse_stream_update_description (pstream, description); +void +pulse_source_output_update (PulseSourceOutput *output, + const pa_source_output_info *info) +{ + g_return_if_fail (PULSE_IS_SOURCE_OUTPUT (output)); + g_return_if_fail (info != NULL); - if (info->client != PA_INVALID_INDEX) - pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_APPLICATION); - else - pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_NO_FLAGS); + /* Let all the information update before emitting notify signals */ + g_object_freeze_notify (G_OBJECT (output)); - if (G_LIKELY (parent != NULL)) { - pulse_client_stream_update_parent (pclient, MATE_MIXER_STREAM (parent)); - flags |= MATE_MIXER_STREAM_HAS_MONITOR; - } else - pulse_client_stream_update_parent (pclient, NULL); + _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (output), + info->mute ? TRUE : FALSE); #if PA_CHECK_VERSION(1, 0, 0) - if (info->has_volume) { - flags |= MATE_MIXER_STREAM_HAS_VOLUME; - - if (info->volume_writable) - flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME; - } - - flags |= MATE_MIXER_STREAM_HAS_MUTE; - - /* Flags needed before volume */ - pulse_stream_update_flags (pstream, flags); - pulse_stream_update_channel_map (pstream, &info->channel_map); - pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE); + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (output), + &info->channel_map); if (info->has_volume) - pulse_stream_update_volume (pstream, &info->volume, 0); + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (output), + &info->volume, + 0); else - pulse_stream_update_volume (pstream, NULL, 0); + pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (output), + NULL, + 0); #else - /* Flags needed before volume */ - pulse_stream_update_flags (pstream, flags); + pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (output), + &info->channel_map); - pulse_stream_update_channel_map (pstream, &info->channel_map); - pulse_stream_update_volume (pstream, NULL, 0); + pulse_stream_control_set_volume (PULSE_STREAM_CONTROL (output), + &info->volume, + 0); #endif - // XXX needs to fix monitor if parent changes - - g_object_thaw_notify (G_OBJECT (pstream)); - return TRUE; -} - -static void -pulse_source_output_reload (PulseStream *pstream) -{ - g_return_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream)); - - pulse_connection_load_source_output_info (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); + g_object_thaw_notify (G_OBJECT (output)); } static gboolean -pulse_source_output_set_mute (PulseStream *pstream, gboolean mute) +pulse_source_output_set_mute (PulseStreamControl *psc, gboolean mute) { - g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), FALSE); + g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (psc), FALSE); - return pulse_connection_set_source_output_mute (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), + return pulse_connection_set_source_output_mute (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + pulse_stream_control_get_index (psc), mute); } static gboolean -pulse_source_output_set_volume (PulseStream *pstream, pa_cvolume *cvolume) +pulse_source_output_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume) { - g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), FALSE); + g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (psc), FALSE); g_return_val_if_fail (cvolume != NULL, FALSE); - return pulse_connection_set_source_output_volume (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), + return pulse_connection_set_source_output_volume (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + pulse_stream_control_get_index (psc), cvolume); } -static gboolean -pulse_source_output_set_parent (PulseClientStream *pclient, PulseStream *parent) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pclient), FALSE); - - pstream = PULSE_STREAM (pclient); - - return pulse_connection_move_sink_input (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - pulse_stream_get_index (parent)); -} - -static gboolean -pulse_source_output_remove (PulseClientStream *pclient) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pclient), FALSE); - - pstream = PULSE_STREAM (pclient); - - return pulse_connection_kill_source_output (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); -} - static PulseMonitor * -pulse_source_output_create_monitor (PulseStream *pstream) +pulse_source_output_create_monitor (PulseStreamControl *psc) { - MateMixerStream *parent; - - g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), NULL); - - parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream)); - if (G_UNLIKELY (parent == NULL)) { - g_debug ("Not creating monitor for client stream %s: not available", - mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream))); - return NULL; - } + g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (psc), NULL); - return pulse_connection_create_monitor (pulse_stream_get_connection (pstream), - pulse_stream_get_index (PULSE_STREAM (parent)), + return pulse_connection_create_monitor (PULSE_STREAM_CONTROL_GET_CONNECTION (psc), + PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc), PA_INVALID_INDEX); } diff --git a/backends/pulse/pulse-source-output.h b/backends/pulse/pulse-source-output.h index 845d439..1037f94 100644 --- a/backends/pulse/pulse-source-output.h +++ b/backends/pulse/pulse-source-output.h @@ -23,48 +23,43 @@ #include <pulse/pulseaudio.h> -#include "pulse-client-stream.h" -#include "pulse-connection.h" -#include "pulse-stream.h" +#include "pulse-stream-control.h" +#include "pulse-types.h" G_BEGIN_DECLS -#define PULSE_TYPE_SOURCE_OUTPUT \ +#define PULSE_TYPE_SOURCE_OUTPUT \ (pulse_source_output_get_type ()) -#define PULSE_SOURCE_OUTPUT(o) \ +#define PULSE_SOURCE_OUTPUT(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutput)) -#define PULSE_IS_SOURCE_OUTPUT(o) \ +#define PULSE_IS_SOURCE_OUTPUT(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE_OUTPUT)) -#define PULSE_SOURCE_OUTPUT_CLASS(k) \ +#define PULSE_SOURCE_OUTPUT_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass)) -#define PULSE_IS_SOURCE_OUTPUT_CLASS(k) \ +#define PULSE_IS_SOURCE_OUTPUT_CLASS(k) \ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_OUTPUT)) -#define PULSE_SOURCE_OUTPUT_GET_CLASS(o) \ +#define PULSE_SOURCE_OUTPUT_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass)) -typedef struct _PulseSourceOutput PulseSourceOutput; -typedef struct _PulseSourceOutputClass PulseSourceOutputClass; -typedef struct _PulseSourceOutputPrivate PulseSourceOutputPrivate; +typedef struct _PulseSourceOutputClass PulseSourceOutputClass; struct _PulseSourceOutput { - PulseClientStream parent; + PulseStreamControl parent; }; struct _PulseSourceOutputClass { - PulseClientStreamClass parent_class; + PulseStreamControlClass parent_class; }; -GType pulse_source_output_get_type (void) G_GNUC_CONST; +GType pulse_source_output_get_type (void) G_GNUC_CONST; -PulseStream *pulse_source_output_new (PulseConnection *connection, - const pa_source_output_info *info, - PulseStream *parent); +PulseSourceOutput *pulse_source_output_new (PulseSource *source, + const pa_source_output_info *info); -gboolean pulse_source_output_update (PulseStream *pstream, - const pa_source_output_info *info, - PulseStream *parent); +void pulse_source_output_update (PulseSourceOutput *output, + const pa_source_output_info *info); G_END_DECLS diff --git a/backends/pulse/pulse-source-switch.c b/backends/pulse/pulse-source-switch.c new file mode 100644 index 0000000..178702e --- /dev/null +++ b/backends/pulse/pulse-source-switch.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include "pulse-connection.h" +#include "pulse-port.h" +#include "pulse-port-switch.h" +#include "pulse-source-switch.h" +#include "pulse-stream.h" + +static void pulse_source_switch_class_init (PulseSourceSwitchClass *klass); +static void pulse_source_switch_init (PulseSourceSwitch *swtch); + +G_DEFINE_TYPE (PulseSourceSwitch, pulse_source_switch, PULSE_TYPE_PORT_SWITCH) + +static gboolean pulse_source_switch_set_active_port (PulsePortSwitch *swtch, + PulsePort *port); + +static void +pulse_source_switch_class_init (PulseSourceSwitchClass *klass) +{ + PulsePortSwitchClass *switch_class; + + switch_class = PULSE_PORT_SWITCH_CLASS (klass); + switch_class->set_active_port = pulse_source_switch_set_active_port; +} + +static void +pulse_source_switch_init (PulseSourceSwitch *swtch) +{ +} + +PulsePortSwitch * +pulse_source_switch_new (const gchar *name, const gchar *label, PulseSource *source) +{ + return g_object_new (PULSE_TYPE_SOURCE_SWITCH, + "name", name, + "label", label, + "role", MATE_MIXER_SWITCH_ROLE_PORT, + "stream", source, + NULL); +} + +static gboolean +pulse_source_switch_set_active_port (PulsePortSwitch *swtch, PulsePort *port) +{ + PulseStream *stream; + + g_return_val_if_fail (PULSE_IS_SOURCE_SWITCH (swtch), FALSE); + g_return_val_if_fail (PULSE_IS_PORT (port), FALSE); + + stream = pulse_port_switch_get_stream (swtch); + + return pulse_connection_set_source_port (pulse_stream_get_connection (stream), + pulse_stream_get_index (stream), + pulse_port_get_name (port)); +} diff --git a/backends/pulse/pulse-source-switch.h b/backends/pulse/pulse-source-switch.h new file mode 100644 index 0000000..408e872 --- /dev/null +++ b/backends/pulse/pulse-source-switch.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_SOURCE_SWITCH_H +#define PULSE_SOURCE_SWITCH_H + +#include <glib.h> +#include <glib-object.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_SOURCE_SWITCH \ + (pulse_source_switch_get_type ()) +#define PULSE_SOURCE_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE_SWITCH, PulseSourceSwitch)) +#define PULSE_IS_SOURCE_SWITCH(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE_SWITCH)) +#define PULSE_SOURCE_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE_SWITCH, PulseSourceSwitchClass)) +#define PULSE_IS_SOURCE_SWITCH_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_SWITCH)) +#define PULSE_SOURCE_SWITCH_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_SWITCH, PulseSourceSwitchClass)) + +typedef struct _PulseSourceSwitchClass PulseSourceSwitchClass; +typedef struct _PulseSourceSwitchPrivate PulseSourceSwitchPrivate; + +struct _PulseSourceSwitch +{ + PulsePortSwitch parent; +}; + +struct _PulseSourceSwitchClass +{ + PulsePortSwitchClass parent_class; +}; + +GType pulse_source_switch_get_type (void) G_GNUC_CONST; + +PulsePortSwitch *pulse_source_switch_new (const gchar *name, + const gchar *label, + PulseSource *source); + +G_END_DECLS + +#endif /* PULSE_SOURCE_SWITCH_H */ diff --git a/backends/pulse/pulse-source.c b/backends/pulse/pulse-source.c index e7dce6f..0d095a7 100644 --- a/backends/pulse/pulse-source.c +++ b/backends/pulse/pulse-source.c @@ -16,253 +16,238 @@ */ #include <glib.h> +#include <glib/gi18n.h> #include <glib-object.h> - -#include <libmatemixer/matemixer-port.h> -#include <libmatemixer/matemixer-port-private.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" #include "pulse-device.h" #include "pulse-monitor.h" +#include "pulse-port.h" +#include "pulse-port-switch.h" #include "pulse-stream.h" #include "pulse-source.h" +#include "pulse-source-control.h" +#include "pulse-source-output.h" +#include "pulse-source-switch.h" + +struct _PulseSourcePrivate +{ + GHashTable *outputs; + PulsePortSwitch *pswitch; + PulseSourceControl *control; +}; static void pulse_source_class_init (PulseSourceClass *klass); static void pulse_source_init (PulseSource *source); +static void pulse_source_dispose (GObject *object); +static void pulse_source_finalize (GObject *object); G_DEFINE_TYPE (PulseSource, pulse_source, PULSE_TYPE_STREAM); -static void pulse_source_reload (PulseStream *pstream); +static const GList *pulse_source_list_controls (MateMixerStream *mms); +static const GList *pulse_source_list_switches (MateMixerStream *mms); -static gboolean pulse_source_set_mute (PulseStream *pstream, - gboolean mute); -static gboolean pulse_source_set_volume (PulseStream *pstream, - pa_cvolume *cvolume); -static gboolean pulse_source_set_active_port (PulseStream *pstream, - MateMixerPort *port); +static void +pulse_source_class_init (PulseSourceClass *klass) +{ + GObjectClass *object_class; + MateMixerStreamClass *stream_class; -static PulseMonitor *pulse_source_create_monitor (PulseStream *pstream); + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_source_dispose; + object_class->finalize = pulse_source_finalize; -static void update_ports (PulseStream *pstream, - pa_source_port_info **ports, - pa_source_port_info *active); + stream_class = MATE_MIXER_STREAM_CLASS (klass); + stream_class->list_controls = pulse_source_list_controls; + stream_class->list_switches = pulse_source_list_switches; + + g_type_class_add_private (klass, sizeof (PulseSourcePrivate)); +} static void -pulse_source_class_init (PulseSourceClass *klass) +pulse_source_init (PulseSource *source) +{ + source->priv = G_TYPE_INSTANCE_GET_PRIVATE (source, + PULSE_TYPE_SOURCE, + PulseSourcePrivate); + + source->priv->outputs = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); +} + +static void +pulse_source_dispose (GObject *object) { - PulseStreamClass *stream_class; + PulseSource *source; - stream_class = PULSE_STREAM_CLASS (klass); + source = PULSE_SOURCE (object); - stream_class->reload = pulse_source_reload; - stream_class->set_mute = pulse_source_set_mute; - stream_class->set_volume = pulse_source_set_volume; - stream_class->set_active_port = pulse_source_set_active_port; - stream_class->create_monitor = pulse_source_create_monitor; + g_clear_object (&source->priv->control); + g_clear_object (&source->priv->pswitch); + + g_hash_table_remove_all (source->priv->outputs); + + G_OBJECT_CLASS (pulse_source_parent_class)->dispose (object); } static void -pulse_source_init (PulseSource *source) +pulse_source_finalize (GObject *object) { + PulseSource *source; + + source = PULSE_SOURCE (object); + + g_hash_table_unref (source->priv->outputs); + + G_OBJECT_CLASS (pulse_source_parent_class)->finalize (object); } -PulseStream * +PulseSource * pulse_source_new (PulseConnection *connection, const pa_source_info *info, PulseDevice *device) { - PulseStream *stream; + PulseSource *source; g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); g_return_val_if_fail (info != NULL, NULL); - /* Consider the sink index as unchanging parameter */ - stream = g_object_new (PULSE_TYPE_SOURCE, + source = g_object_new (PULSE_TYPE_SOURCE, + "name", info->name, + "label", info->description, + "device", device, + "direction", MATE_MIXER_DIRECTION_INPUT, "connection", connection, "index", info->index, NULL); - /* Other data may change at any time, so let's make a use of our update function */ - pulse_source_update (stream, info, device); + source->priv->control = pulse_source_control_new (source, info); - return stream; -} + if (info->n_ports > 0) { + pa_source_port_info **ports = info->ports; -gboolean -pulse_source_update (PulseStream *pstream, - const pa_source_info *info, - PulseDevice *device) -{ - MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT | - MATE_MIXER_STREAM_HAS_MUTE | - MATE_MIXER_STREAM_HAS_MONITOR | - MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_CAN_SET_VOLUME | - MATE_MIXER_STREAM_CAN_SUSPEND; - - g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE); - g_return_val_if_fail (info != NULL, FALSE); - - /* Let all the information update before emitting notify signals */ - g_object_freeze_notify (G_OBJECT (pstream)); - - pulse_stream_update_name (pstream, info->name); - pulse_stream_update_description (pstream, info->description); - pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE); - - /* Stream state */ - switch (info->state) { - case PA_SOURCE_RUNNING: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_RUNNING); - break; - case PA_SOURCE_IDLE: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE); - break; - case PA_SOURCE_SUSPENDED: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED); - break; - default: - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_UNKNOWN); - break; - } + /* Create the port switch */ + source->priv->pswitch = pulse_source_switch_new ("port", _("Port"), source); + + while (*ports != NULL) { + pa_source_port_info *p = *ports++; + PulsePort *port; + const gchar *icon = NULL; - /* Build the flag list */ - if (info->flags & PA_SINK_DECIBEL_VOLUME) - flags |= MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME; - if (info->flags & PA_SINK_FLAT_VOLUME) - flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME; + /* A port may include an icon but in PulseAudio sink and source ports + * the property is not included, for this reason ports are also read from + * devices where the icons may be present */ + if (device != NULL) { + port = pulse_device_get_port (PULSE_DEVICE (device), p->name); + if (port != NULL) + icon = mate_mixer_switch_option_get_icon (MATE_MIXER_SWITCH_OPTION (port)); + } - /* Flags must be updated before volume */ - pulse_stream_update_flags (pstream, flags); + port = pulse_port_new (p->name, + p->description, + icon, + p->priority); - pulse_stream_update_channel_map (pstream, &info->channel_map); - pulse_stream_update_volume (pstream, &info->volume, info->base_volume); + pulse_port_switch_add_port (source->priv->pswitch, port); - pulse_stream_update_device (pstream, MATE_MIXER_DEVICE (device)); + if (p == info->active_port) + pulse_port_switch_set_active_port (source->priv->pswitch, port); + } - /* Ports must be updated after device */ - if (info->ports != NULL) { - update_ports (pstream, info->ports, info->active_port); + g_debug ("Created port list for source %s", info->name); } - g_object_thaw_notify (G_OBJECT (pstream)); - return TRUE; -} + pulse_source_update (source, info); -static void -pulse_source_reload (PulseStream *pstream) -{ - g_return_if_fail (PULSE_IS_SOURCE (pstream)); - - pulse_connection_load_source_info (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream)); + _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (source), + MATE_MIXER_STREAM_CONTROL (source->priv->control)); + return source; } -static gboolean -pulse_source_set_mute (PulseStream *pstream, gboolean mute) +void +pulse_source_add_output (PulseSource *source, const pa_source_output_info *info) { - g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE); - - return pulse_connection_set_source_mute (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - mute); + PulseSourceOutput *output; + + /* This function is used for both creating and refreshing source outputs */ + output = g_hash_table_lookup (source->priv->outputs, GINT_TO_POINTER (info->index)); + if (output == NULL) { + const gchar *name; + + output = pulse_source_output_new (source, info); + g_hash_table_insert (source->priv->outputs, + GINT_TO_POINTER (info->index), + output); + + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (output)); + g_signal_emit_by_name (G_OBJECT (source), + "control-added", + name); + } else + pulse_source_output_update (output, info); } -static gboolean -pulse_source_set_volume (PulseStream *pstream, pa_cvolume *cvolume) +void +pulse_source_remove_output (PulseSource *source, guint32 index) { - g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE); - g_return_val_if_fail (cvolume != NULL, FALSE); + PulseSourceOutput *output; + const gchar *name; - return pulse_connection_set_source_volume (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - cvolume); -} + output = g_hash_table_lookup (source->priv->outputs, GINT_TO_POINTER (index)); + if G_UNLIKELY (output == NULL) + return; -static gboolean -pulse_source_set_active_port (PulseStream *pstream, MateMixerPort *port) -{ - g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); + name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (output)); - return pulse_connection_set_source_port (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - mate_mixer_port_get_name (port)); + g_hash_table_remove (source->priv->outputs, GINT_TO_POINTER (index)); + g_signal_emit_by_name (G_OBJECT (source), + "control-removed", + name); } -static PulseMonitor * -pulse_source_create_monitor (PulseStream *pstream) +void +pulse_source_update (PulseSource *source, + const pa_source_info *info) { - g_return_val_if_fail (PULSE_IS_SOURCE (pstream), NULL); - - return pulse_connection_create_monitor (pulse_stream_get_connection (pstream), - pulse_stream_get_index (pstream), - PA_INVALID_INDEX); + g_return_if_fail (PULSE_IS_SOURCE (source)); + g_return_if_fail (info != NULL); + + /* The switch doesn't allow being unset, PulseAudio should always include + * the active port name if the are any ports available */ + if (info->active_port != NULL) + pulse_port_switch_set_active_port_by_name (source->priv->pswitch, + info->active_port->name); } -static void -update_ports (PulseStream *pstream, - pa_source_port_info **ports, - pa_source_port_info *active) +static const GList * +pulse_source_list_controls (MateMixerStream *mms) { - MateMixerPort *port; - MateMixerDevice *device; - GHashTable *hash; - - hash = pulse_stream_get_ports (pstream); + GList *list; - while (*ports != NULL) { - MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS; - pa_source_port_info *info = *ports; - const gchar *icon = NULL; + g_return_val_if_fail (PULSE_IS_SOURCE (mms), NULL); - device = mate_mixer_stream_get_device (MATE_MIXER_STREAM (pstream)); - if (device != NULL) { - port = mate_mixer_device_get_port (device, info->name); - - if (port != NULL) { - flags = mate_mixer_port_get_flags (port); - icon = mate_mixer_port_get_icon (port); - } - } + // XXX + list = g_hash_table_get_values (PULSE_SOURCE (mms)->priv->outputs); + if (list != NULL) + g_list_foreach (list, (GFunc) g_object_ref, NULL); -#if PA_CHECK_VERSION(2, 0, 0) - if (info->available == PA_PORT_AVAILABLE_YES) - flags |= MATE_MIXER_PORT_AVAILABLE; - else - flags &= ~MATE_MIXER_PORT_AVAILABLE; -#endif - - port = g_hash_table_lookup (hash, info->name); - - if (port != NULL) { - /* Update existing port */ - _mate_mixer_port_update_description (port, info->description); - _mate_mixer_port_update_icon (port, icon); - _mate_mixer_port_update_priority (port, info->priority); - _mate_mixer_port_update_flags (port, flags); - } else { - /* Add previously unknown port to the hash table */ - port = _mate_mixer_port_new (info->name, - info->description, - icon, - info->priority, - flags); - - g_hash_table_insert (hash, g_strdup (info->name), port); - } + return g_list_prepend (list, g_object_ref (PULSE_SOURCE (mms)->priv->control)); +} - ports++; - } +static const GList * +pulse_source_list_switches (MateMixerStream *mms) +{ + g_return_val_if_fail (PULSE_IS_SOURCE (mms), NULL); - /* Active port */ - if (G_LIKELY (active != NULL)) - port = g_hash_table_lookup (hash, active->name); - else - port = NULL; + // XXX + if (PULSE_SOURCE (mms)->priv->pswitch != NULL) + return g_list_prepend (NULL, PULSE_SOURCE (mms)->priv->pswitch); - pulse_stream_update_active_port (pstream, port); + return NULL; } diff --git a/backends/pulse/pulse-source.h b/backends/pulse/pulse-source.h index 9abf6d8..fdc3d5e 100644 --- a/backends/pulse/pulse-source.h +++ b/backends/pulse/pulse-source.h @@ -23,9 +23,8 @@ #include <pulse/pulseaudio.h> -#include "pulse-connection.h" -#include "pulse-device.h" #include "pulse-stream.h" +#include "pulse-types.h" G_BEGIN_DECLS @@ -42,12 +41,15 @@ G_BEGIN_DECLS #define PULSE_SOURCE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE, PulseSourceClass)) -typedef struct _PulseSource PulseSource; typedef struct _PulseSourceClass PulseSourceClass; +typedef struct _PulseSourcePrivate PulseSourcePrivate; struct _PulseSource { PulseStream parent; + + /*< private >*/ + PulseSourcePrivate *priv; }; struct _PulseSourceClass @@ -55,15 +57,20 @@ struct _PulseSourceClass PulseStreamClass parent_class; }; -GType pulse_source_get_type (void) G_GNUC_CONST; +GType pulse_source_get_type (void) G_GNUC_CONST; + +PulseSource *pulse_source_new (PulseConnection *connection, + const pa_source_info *info, + PulseDevice *device); + +void pulse_source_add_output (PulseSource *source, + const pa_source_output_info *info); -PulseStream *pulse_source_new (PulseConnection *connection, - const pa_source_info *info, - PulseDevice *device); +void pulse_source_remove_output (PulseSource *source, + guint32 index); -gboolean pulse_source_update (PulseStream *pstream, - const pa_source_info *info, - PulseDevice *device); +void pulse_source_update (PulseSource *source, + const pa_source_info *info); G_END_DECLS diff --git a/backends/pulse/pulse-stream-control.c b/backends/pulse/pulse-stream-control.c new file mode 100644 index 0000000..fa17e6b --- /dev/null +++ b/backends/pulse/pulse-stream-control.c @@ -0,0 +1,744 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-connection.h" +#include "pulse-helpers.h" +#include "pulse-monitor.h" +#include "pulse-stream-control.h" + +struct _PulseStreamControlPrivate +{ + guint32 index; + guint volume; + pa_cvolume cvolume; + pa_volume_t base_volume; + pa_channel_map channel_map; + PulseConnection *connection; + PulseMonitor *monitor; + MateMixerAppInfo *app_info; +}; + +enum { + PROP_0, + PROP_INDEX, + PROP_CONNECTION, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +static void pulse_stream_control_class_init (PulseStreamControlClass *klass); + +static void pulse_stream_control_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void pulse_stream_control_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); + +static void pulse_stream_control_init (PulseStreamControl *control); +static void pulse_stream_control_dispose (GObject *object); +static void pulse_stream_control_finalize (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (PulseStreamControl, pulse_stream_control, MATE_MIXER_TYPE_STREAM_CONTROL) + +static MateMixerAppInfo * pulse_stream_control_get_app_info (MateMixerStreamControl *mmsc); + +static gboolean pulse_stream_control_set_mute (MateMixerStreamControl *mmsc, + gboolean mute); + +static guint pulse_stream_control_get_num_channels (MateMixerStreamControl *mmsc); + +static guint pulse_stream_control_get_volume (MateMixerStreamControl *mmsc); +static gboolean pulse_stream_control_set_volume (MateMixerStreamControl *mmsc, + guint volume); + +static gdouble pulse_stream_control_get_decibel (MateMixerStreamControl *mmsc); +static gboolean pulse_stream_control_set_decibel (MateMixerStreamControl *mmsc, + gdouble decibel); + +static guint pulse_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, + guint channel); +static gboolean pulse_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, + guint channel, + guint volume); + +static gdouble pulse_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc, + guint channel); +static gboolean pulse_stream_control_set_channel_decibel (MateMixerStreamControl *mmsc, + guint channel, + gdouble decibel); + +static MateMixerChannelPosition pulse_stream_control_get_channel_position (MateMixerStreamControl *mmsc, + guint channel); +static gboolean pulse_stream_control_has_channel_position (MateMixerStreamControl *mmsc, + MateMixerChannelPosition position); + +static gboolean pulse_stream_control_set_balance (MateMixerStreamControl *mmsc, + gfloat balance); + +static gboolean pulse_stream_control_set_fade (MateMixerStreamControl *mmsc, + gfloat fade); + +static gboolean pulse_stream_control_get_monitor_enabled (MateMixerStreamControl *mmsc); +static gboolean pulse_stream_control_set_monitor_enabled (MateMixerStreamControl *mmsc, + gboolean enabled); + +static guint pulse_stream_control_get_min_volume (MateMixerStreamControl *mmsc); +static guint pulse_stream_control_get_max_volume (MateMixerStreamControl *mmsc); +static guint pulse_stream_control_get_normal_volume (MateMixerStreamControl *mmsc); +static guint pulse_stream_control_get_base_volume (MateMixerStreamControl *mmsc); + +static void on_monitor_value (PulseMonitor *monitor, + gdouble value, + PulseStreamControl *control); + +static void set_balance_fade (PulseStreamControl *control); + +static gboolean set_cvolume (PulseStreamControl *control, + pa_cvolume *cvolume); + +static void +pulse_stream_control_class_init (PulseStreamControlClass *klass) +{ + GObjectClass *object_class; + MateMixerStreamControlClass *control_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = pulse_stream_control_dispose; + object_class->finalize = pulse_stream_control_finalize; + object_class->get_property = pulse_stream_control_get_property; + object_class->set_property = pulse_stream_control_set_property; + + control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass); + control_class->get_app_info = pulse_stream_control_get_app_info; + control_class->set_mute = pulse_stream_control_set_mute; + control_class->get_num_channels = pulse_stream_control_get_num_channels; + control_class->get_volume = pulse_stream_control_get_volume; + control_class->set_volume = pulse_stream_control_set_volume; + control_class->get_decibel = pulse_stream_control_get_decibel; + control_class->set_decibel = pulse_stream_control_set_decibel; + control_class->get_channel_volume = pulse_stream_control_get_channel_volume; + control_class->set_channel_volume = pulse_stream_control_set_channel_volume; + control_class->get_channel_decibel = pulse_stream_control_get_channel_decibel; + control_class->set_channel_decibel = pulse_stream_control_set_channel_decibel; + control_class->get_channel_position = pulse_stream_control_get_channel_position; + control_class->has_channel_position = pulse_stream_control_has_channel_position; + control_class->set_balance = pulse_stream_control_set_balance; + control_class->set_fade = pulse_stream_control_set_fade; + control_class->get_monitor_enabled = pulse_stream_control_get_monitor_enabled; + control_class->set_monitor_enabled = pulse_stream_control_set_monitor_enabled; + control_class->get_min_volume = pulse_stream_control_get_min_volume; + control_class->get_max_volume = pulse_stream_control_get_max_volume; + control_class->get_normal_volume = pulse_stream_control_get_normal_volume; + control_class->get_base_volume = pulse_stream_control_get_base_volume; + + properties[PROP_INDEX] = + g_param_spec_uint ("index", + "Index", + "Index of the stream control", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_CONNECTION] = + g_param_spec_object ("connection", + "Connection", + "PulseAudio connection", + PULSE_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + g_type_class_add_private (object_class, sizeof (PulseStreamControlPrivate)); +} + +static void +pulse_stream_control_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + PulseStreamControl *control; + + control = PULSE_STREAM_CONTROL (object); + + switch (param_id) { + case PROP_INDEX: + g_value_set_uint (value, control->priv->index); + break; + case PROP_CONNECTION: + g_value_set_object (value, control->priv->connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_stream_control_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + PulseStreamControl *control; + + control = PULSE_STREAM_CONTROL (object); + + switch (param_id) { + case PROP_INDEX: + control->priv->index = g_value_get_uint (value); + break; + case PROP_CONNECTION: + control->priv->connection = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +pulse_stream_control_init (PulseStreamControl *control) +{ + control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control, + PULSE_TYPE_STREAM_CONTROL, + PulseStreamControlPrivate); + + /* Initialize empty volume and channel map structures, they will be used + * if the stream does not support volume */ + pa_cvolume_init (&control->priv->cvolume); + + pa_channel_map_init (&control->priv->channel_map); +} + +static void +pulse_stream_control_dispose (GObject *object) +{ + PulseStreamControl *control; + + control = PULSE_STREAM_CONTROL (object); + + g_clear_object (&control->priv->monitor); + g_clear_object (&control->priv->connection); + + G_OBJECT_CLASS (pulse_stream_control_parent_class)->dispose (object); +} + +static void +pulse_stream_control_finalize (GObject *object) +{ + PulseStreamControl *control; + + control = PULSE_STREAM_CONTROL (object); + + if (control->priv->app_info != NULL) + _mate_mixer_app_info_free (control->priv->app_info); + + G_OBJECT_CLASS (pulse_stream_control_parent_class)->finalize (object); +} + +guint32 +pulse_stream_control_get_index (PulseStreamControl *control) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), 0); + + return control->priv->index; +} + +PulseConnection * +pulse_stream_control_get_connection (PulseStreamControl *control) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL); + + return control->priv->connection; +} + +PulseMonitor * +pulse_stream_control_get_monitor (PulseStreamControl *control) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL); + + return control->priv->monitor; +} + +const pa_cvolume * +pulse_stream_control_get_cvolume (PulseStreamControl *control) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL); + + return &control->priv->cvolume; +} + +const pa_channel_map * +pulse_stream_control_get_channel_map (PulseStreamControl *control) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL); + + return &control->priv->channel_map; +} + +void +pulse_stream_control_set_app_info (PulseStreamControl *control, MateMixerAppInfo *info) +{ + g_return_if_fail (PULSE_IS_STREAM_CONTROL (control)); + + if G_UNLIKELY (control->priv->app_info) + _mate_mixer_app_info_free (control->priv->app_info); + + control->priv->app_info = info; +} + +void +pulse_stream_control_set_channel_map (PulseStreamControl *control, const pa_channel_map *map) +{ + MateMixerStreamControlFlags flags; + + g_return_if_fail (PULSE_IS_STREAM_CONTROL (control)); + g_return_if_fail (map != NULL); + + flags = mate_mixer_stream_control_get_flags (MATE_MIXER_STREAM_CONTROL (control)); + + if (pa_channel_map_valid (map)) { + if (pa_channel_map_can_balance (map)) + flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; + else + flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_BALANCE; + + if (pa_channel_map_can_fade (map)) + flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE; + else + flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_FADE; + + control->priv->channel_map = *map; + } else { + flags &= ~(MATE_MIXER_STREAM_CONTROL_CAN_BALANCE | MATE_MIXER_STREAM_CONTROL_CAN_FADE); + + /* If the channel map is not valid, create an empty channel map, which + * also won't validate, but at least we know what it is */ + pa_channel_map_init (&control->priv->channel_map); + } + + _mate_mixer_stream_control_set_flags (MATE_MIXER_STREAM_CONTROL (control), flags); +} + +void +pulse_stream_control_set_cvolume (PulseStreamControl *control, + const pa_cvolume *cvolume, + pa_volume_t base_volume) +{ + MateMixerStreamControlFlags flags; + + g_return_if_fail (PULSE_IS_STREAM_CONTROL (control)); + + /* The base volume is not a property */ + control->priv->base_volume = base_volume; + + flags = mate_mixer_stream_control_get_flags (MATE_MIXER_STREAM_CONTROL (control)); + + g_object_freeze_notify (G_OBJECT (control)); + + if (cvolume != NULL && pa_cvolume_valid (cvolume)) { + /* Decibel volume and volume settability flags must be provided by + * the implementation */ + flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE; + + if (pa_cvolume_equal (&control->priv->cvolume, cvolume) == 0) { + control->priv->cvolume = *cvolume; + control->priv->volume = (guint) pa_cvolume_max (&control->priv->cvolume); + + g_object_notify (G_OBJECT (control), "volume"); + } + } else { + flags &= ~(MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE | + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL); + + /* If the cvolume is not valid, create an empty cvolume, which also + * won't validate, but at least we know what it is */ + pa_cvolume_init (&control->priv->cvolume); + + if (control->priv->volume != (guint) PA_VOLUME_MUTED) { + control->priv->volume = (guint) PA_VOLUME_MUTED; + + g_object_notify (G_OBJECT (control), "volume"); + } + } + + _mate_mixer_stream_control_set_flags (MATE_MIXER_STREAM_CONTROL (control), flags); + + /* Changing volume may change the balance and fade values as well */ + set_balance_fade (control); + + g_object_thaw_notify (G_OBJECT (control)); +} + +static MateMixerAppInfo * +pulse_stream_control_get_app_info (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), NULL); + + return PULSE_STREAM_CONTROL (mmsc)->priv->app_info; +} + +static gboolean +pulse_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + return PULSE_STREAM_CONTROL_GET_CLASS (mmsc)->set_mute (PULSE_STREAM_CONTROL (mmsc), mute); +} + +static guint +pulse_stream_control_get_num_channels (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), 0); + + return PULSE_STREAM_CONTROL (mmsc)->priv->channel_map.channels; +} + +static guint +pulse_stream_control_get_volume (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED); + + return PULSE_STREAM_CONTROL (mmsc)->priv->volume; +} + +static gboolean +pulse_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume) +{ + PulseStreamControl *control; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + cvolume = control->priv->cvolume; + + if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL) + return FALSE; + + return set_cvolume (control, &cvolume); +} + +static gdouble +pulse_stream_control_get_decibel (MateMixerStreamControl *mmsc) +{ + gdouble value; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY); + + value = pa_sw_volume_to_dB (pulse_stream_control_get_volume (mmsc)); + + /* PA_VOLUME_MUTED is converted to PA_DECIBEL_MININFTY */ + return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value; +} + +static gboolean +pulse_stream_control_set_decibel (MateMixerStreamControl *mmsc, gdouble decibel) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + return pulse_stream_control_set_volume (mmsc, + pa_sw_volume_from_dB (decibel)); +} + +static guint +pulse_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint channel) +{ + PulseStreamControl *control; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (channel >= control->priv->cvolume.channels) + return (guint) PA_VOLUME_MUTED; + + return (guint) control->priv->cvolume.values[channel]; +} + +static gboolean +pulse_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, guint channel, guint volume) +{ + PulseStreamControl *control; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (channel >= control->priv->cvolume.channels) + return FALSE; + + /* This is safe, because the cvolume is validated by set_cvolume() */ + cvolume = control->priv->cvolume; + cvolume.values[channel] = (pa_volume_t) volume; + + return set_cvolume (control, &cvolume); +} + +static gdouble +pulse_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc, guint channel) +{ + PulseStreamControl *control; + gdouble value; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (channel >= control->priv->cvolume.channels) + return -MATE_MIXER_INFINITY; + + value = pa_sw_volume_to_dB (control->priv->cvolume.values[channel]); + + return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value; +} + +static gboolean +pulse_stream_control_set_channel_decibel (MateMixerStreamControl *mmsc, + guint channel, + gdouble decibel) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + return pulse_stream_control_set_channel_volume (mmsc, + channel, + pa_sw_volume_from_dB (decibel)); +} + +static MateMixerChannelPosition +pulse_stream_control_get_channel_position (MateMixerStreamControl *mmsc, guint channel) +{ + PulseStreamControl *control; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), MATE_MIXER_CHANNEL_UNKNOWN); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (channel >= control->priv->channel_map.channels) + return MATE_MIXER_CHANNEL_UNKNOWN; + + return pulse_convert_position_to_pulse (control->priv->channel_map.map[channel]); +} + +static gboolean +pulse_stream_control_has_channel_position (MateMixerStreamControl *mmsc, + MateMixerChannelPosition position) +{ + PulseStreamControl *control; + pa_channel_position_t p; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + + /* Handle invalid position as a special case, otherwise this function would + * return TRUE for e.g. unknown index in a default channel map */ + p = pulse_convert_position_to_pulse (position); + + if (p == PA_CHANNEL_POSITION_INVALID) + return FALSE; + + if (pa_channel_map_has_position (&control->priv->channel_map, p) != 0) + return TRUE; + else + return FALSE; +} + +static gboolean +pulse_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance) +{ + PulseStreamControl *control; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + cvolume = control->priv->cvolume; + + if (pa_cvolume_set_balance (&cvolume, &control->priv->channel_map, balance) == NULL) + return FALSE; + + return set_cvolume (control, &cvolume); +} + +static gboolean +pulse_stream_control_set_fade (MateMixerStreamControl *mmsc, gfloat fade) +{ + PulseStreamControl *control; + pa_cvolume cvolume; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + cvolume = control->priv->cvolume; + + if (pa_cvolume_set_fade (&cvolume, &control->priv->channel_map, fade) == NULL) + return FALSE; + + return set_cvolume (control, &cvolume); +} + +static gboolean +pulse_stream_control_get_monitor_enabled (MateMixerStreamControl *mmsc) +{ + PulseStreamControl *control; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (control->priv->monitor != NULL) + return pulse_monitor_get_enabled (control->priv->monitor); + + return FALSE; +} + +static gboolean +pulse_stream_control_set_monitor_enabled (MateMixerStreamControl *mmsc, gboolean enabled) +{ + PulseStreamControl *control; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (enabled == TRUE) { + if (control->priv->monitor == NULL) { + control->priv->monitor = + PULSE_STREAM_CONTROL_GET_CLASS (control)->create_monitor (control); + + if G_UNLIKELY (control->priv->monitor == NULL) + return FALSE; + + g_signal_connect (G_OBJECT (control->priv->monitor), + "value", + G_CALLBACK (on_monitor_value), + control); + } + } else { + if (control->priv->monitor == NULL) + return FALSE; + } + return pulse_monitor_set_enabled (control->priv->monitor, enabled); +} + +static guint +pulse_stream_control_get_min_volume (MateMixerStreamControl *mmsc) +{ + return (guint) PA_VOLUME_MUTED; +} + +static guint +pulse_stream_control_get_max_volume (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED); + + return (guint) PA_VOLUME_UI_MAX; +} + +static guint +pulse_stream_control_get_normal_volume (MateMixerStreamControl *mmsc) +{ + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED); + + return (guint) PA_VOLUME_NORM; +} + +static guint +pulse_stream_control_get_base_volume (MateMixerStreamControl *mmsc) +{ + PulseStreamControl *control; + + g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED); + + control = PULSE_STREAM_CONTROL (mmsc); + + if (control->priv->base_volume > 0) + return control->priv->base_volume; + else + return (guint) PA_VOLUME_NORM; +} + +static void +on_monitor_value (PulseMonitor *monitor, gdouble value, PulseStreamControl *control) +{ + g_signal_emit_by_name (G_OBJECT (control), + "monitor-value", + value); +} + +static void +set_balance_fade (PulseStreamControl *control) +{ + gfloat value; + + /* PulseAudio returns the default 0.0f value on error, so skip checking validity + * of the channel map and cvolume */ + value = pa_cvolume_get_balance (&control->priv->cvolume, + &control->priv->channel_map); + + _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (control), value); + + value = pa_cvolume_get_fade (&control->priv->cvolume, + &control->priv->channel_map); + + _mate_mixer_stream_control_set_fade (MATE_MIXER_STREAM_CONTROL (control), value); +} + +static gboolean +set_cvolume (PulseStreamControl *control, pa_cvolume *cvolume) +{ + PulseStreamControlClass *klass; + + if (pa_cvolume_valid (cvolume) == 0) + return FALSE; + if (pa_cvolume_equal (cvolume, &control->priv->cvolume) != 0) + return TRUE; + + klass = PULSE_STREAM_CONTROL_GET_CLASS (control); + + if (klass->set_volume (control, cvolume) == FALSE) + return FALSE; + + control->priv->cvolume = *cvolume; + control->priv->volume = (guint) pa_cvolume_max (cvolume); + + g_object_notify (G_OBJECT (control), "volume"); + + /* Changing volume may change the balance and fade values as well */ + set_balance_fade (control); + return TRUE; +} diff --git a/backends/pulse/pulse-stream-control.h b/backends/pulse/pulse-stream-control.h new file mode 100644 index 0000000..abc3f98 --- /dev/null +++ b/backends/pulse/pulse-stream-control.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_STREAM_CONTROL_H +#define PULSE_STREAM_CONTROL_H + +#include <glib.h> +#include <glib-object.h> +#include <libmatemixer/matemixer.h> + +#include <pulse/pulseaudio.h> + +#include "pulse-types.h" + +G_BEGIN_DECLS + +#define PULSE_TYPE_STREAM_CONTROL \ + (pulse_stream_control_get_type ()) +#define PULSE_STREAM_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_STREAM_CONTROL, PulseStreamControl)) +#define PULSE_IS_STREAM_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_STREAM_CONTROL)) +#define PULSE_STREAM_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_STREAM_CONTROL, PulseStreamControlClass)) +#define PULSE_IS_STREAM_CONTROL_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_STREAM_CONTROL)) +#define PULSE_STREAM_CONTROL_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_STREAM_CONTROL, PulseStreamControlClass)) + +#define PULSE_STREAM_CONTROL_GET_CONNECTION(psc) \ + (pulse_stream_get_connection (PULSE_STREAM (mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (psc))))) +#define PULSE_STREAM_CONTROL_GET_STREAM_INDEX(psc) \ + (pulse_stream_get_index (PULSE_STREAM (mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (psc))))) + +typedef struct _PulseStreamControlClass PulseStreamControlClass; +typedef struct _PulseStreamControlPrivate PulseStreamControlPrivate; + +struct _PulseStreamControl +{ + MateMixerStreamControl parent; + + /*< private >*/ + PulseStreamControlPrivate *priv; +}; + +struct _PulseStreamControlClass +{ + MateMixerStreamControlClass parent_class; + + /*< private >*/ + gboolean (*set_mute) (PulseStreamControl *control, + gboolean mute); + gboolean (*set_volume) (PulseStreamControl *control, + pa_cvolume *volume); + + PulseMonitor *(*create_monitor) (PulseStreamControl *control); +}; + +GType pulse_stream_control_get_type (void) G_GNUC_CONST; + +guint32 pulse_stream_control_get_index (PulseStreamControl *control); + +PulseConnection * pulse_stream_control_get_connection (PulseStreamControl *control); +PulseMonitor * pulse_stream_control_get_monitor (PulseStreamControl *control); + +const pa_cvolume * pulse_stream_control_get_cvolume (PulseStreamControl *control); +const pa_channel_map *pulse_stream_control_get_channel_map (PulseStreamControl *control); + +void pulse_stream_control_set_app_info (PulseStreamControl *stream, + MateMixerAppInfo *info); + +void pulse_stream_control_set_channel_map (PulseStreamControl *control, + const pa_channel_map *map); +void pulse_stream_control_set_cvolume (PulseStreamControl *control, + const pa_cvolume *cvolume, + pa_volume_t base_volume); + +G_END_DECLS + +#endif /* PULSE_STREAM_CONTROL_H */ diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c index fb738ad..752c3e6 100644 --- a/backends/pulse/pulse-stream.c +++ b/backends/pulse/pulse-stream.c @@ -15,62 +15,34 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#include <string.h> #include <glib.h> #include <glib-object.h> - -#include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-stream.h> -#include <libmatemixer/matemixer-port.h> +#include <libmatemixer/matemixer.h> +#include <libmatemixer/matemixer-private.h> #include <pulse/pulseaudio.h> #include "pulse-connection.h" +#include "pulse-device.h" #include "pulse-helpers.h" #include "pulse-monitor.h" +#include "pulse-port.h" #include "pulse-stream.h" struct _PulseStreamPrivate { - guint32 index; - gchar *name; - gchar *description; - MateMixerDevice *device; - MateMixerStreamFlags flags; - MateMixerStreamState state; - gboolean mute; - guint volume; - pa_cvolume cvolume; - pa_volume_t base_volume; - pa_channel_map channel_map; - gfloat balance; - gfloat fade; - GHashTable *ports; - GList *ports_list; - MateMixerPort *port; - PulseConnection *connection; - PulseMonitor *monitor; - gchar *monitor_name; + guint32 index; + PulseConnection *connection; }; enum { PROP_0, - PROP_NAME, - PROP_DESCRIPTION, - PROP_DEVICE, - PROP_FLAGS, - PROP_STATE, - PROP_MUTE, - PROP_VOLUME, - PROP_BALANCE, - PROP_FADE, - PROP_ACTIVE_PORT, PROP_INDEX, - PROP_CONNECTION + PROP_CONNECTION, + N_PROPERTIES }; -static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface); +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; static void pulse_stream_class_init (PulseStreamClass *klass); @@ -83,174 +55,45 @@ static void pulse_stream_set_property (GObject *object, const GValue *value, GParamSpec *pspec); -static void pulse_stream_init (PulseStream *pstream); +static void pulse_stream_init (PulseStream *stream); static void pulse_stream_dispose (GObject *object); -static void pulse_stream_finalize (GObject *object); - -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseStream, pulse_stream, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM, - mate_mixer_stream_interface_init)) - -static const gchar * pulse_stream_get_name (MateMixerStream *stream); -static const gchar * pulse_stream_get_description (MateMixerStream *stream); -static MateMixerDevice * pulse_stream_get_device (MateMixerStream *stream); -static MateMixerStreamFlags pulse_stream_get_flags (MateMixerStream *stream); -static MateMixerStreamState pulse_stream_get_state (MateMixerStream *stream); - -static gboolean pulse_stream_get_mute (MateMixerStream *stream); -static gboolean pulse_stream_set_mute (MateMixerStream *stream, - gboolean mute); - -static guint pulse_stream_get_num_channels (MateMixerStream *stream); - -static guint pulse_stream_get_volume (MateMixerStream *stream); -static gboolean pulse_stream_set_volume (MateMixerStream *stream, - guint volume); - -static gdouble pulse_stream_get_decibel (MateMixerStream *stream); -static gboolean pulse_stream_set_decibel (MateMixerStream *stream, - gdouble decibel); - -static guint pulse_stream_get_channel_volume (MateMixerStream *stream, - guint channel); -static gboolean pulse_stream_set_channel_volume (MateMixerStream *stream, - guint channel, - guint volume); - -static gdouble pulse_stream_get_channel_decibel (MateMixerStream *stream, - guint channel); -static gboolean pulse_stream_set_channel_decibel (MateMixerStream *stream, - guint channel, - gdouble decibel); - -static MateMixerChannelPosition pulse_stream_get_channel_position (MateMixerStream *stream, - guint channel); -static gboolean pulse_stream_has_channel_position (MateMixerStream *stream, - MateMixerChannelPosition position); - -static gfloat pulse_stream_get_balance (MateMixerStream *stream); -static gboolean pulse_stream_set_balance (MateMixerStream *stream, - gfloat balance); - -static gfloat pulse_stream_get_fade (MateMixerStream *stream); -static gboolean pulse_stream_set_fade (MateMixerStream *stream, - gfloat fade); - -static gboolean pulse_stream_suspend (MateMixerStream *stream); -static gboolean pulse_stream_resume (MateMixerStream *stream); -static gboolean pulse_stream_monitor_start (MateMixerStream *stream); -static void pulse_stream_monitor_stop (MateMixerStream *stream); -static gboolean pulse_stream_monitor_is_running (MateMixerStream *stream); -static gboolean pulse_stream_monitor_set_name (MateMixerStream *stream, - const gchar *name); - -static const GList * pulse_stream_list_ports (MateMixerStream *stream); - -static MateMixerPort * pulse_stream_get_active_port (MateMixerStream *stream); -static gboolean pulse_stream_set_active_port (MateMixerStream *stream, - MateMixerPort *port); - -static guint pulse_stream_get_min_volume (MateMixerStream *stream); -static guint pulse_stream_get_max_volume (MateMixerStream *stream); -static guint pulse_stream_get_normal_volume (MateMixerStream *stream); -static guint pulse_stream_get_base_volume (MateMixerStream *stream); - -static void on_monitor_value (PulseMonitor *monitor, - gdouble value, - PulseStream *pstream); - -static gboolean update_balance_fade (PulseStream *pstream); - -static gboolean set_cvolume (PulseStream *pstream, - pa_cvolume *cvolume); - -static gint compare_ports (gconstpointer a, - gconstpointer b); - -static void -mate_mixer_stream_interface_init (MateMixerStreamInterface *iface) -{ - iface->get_name = pulse_stream_get_name; - iface->get_description = pulse_stream_get_description; - iface->get_device = pulse_stream_get_device; - iface->get_flags = pulse_stream_get_flags; - iface->get_state = pulse_stream_get_state; - iface->get_mute = pulse_stream_get_mute; - iface->set_mute = pulse_stream_set_mute; - iface->get_num_channels = pulse_stream_get_num_channels; - iface->get_volume = pulse_stream_get_volume; - iface->set_volume = pulse_stream_set_volume; - iface->get_decibel = pulse_stream_get_decibel; - iface->set_decibel = pulse_stream_set_decibel; - iface->get_channel_volume = pulse_stream_get_channel_volume; - iface->set_channel_volume = pulse_stream_set_channel_volume; - iface->get_channel_decibel = pulse_stream_get_channel_decibel; - iface->set_channel_decibel = pulse_stream_set_channel_decibel; - iface->get_channel_position = pulse_stream_get_channel_position; - iface->has_channel_position = pulse_stream_has_channel_position; - iface->get_balance = pulse_stream_get_balance; - iface->set_balance = pulse_stream_set_balance; - iface->get_fade = pulse_stream_get_fade; - iface->set_fade = pulse_stream_set_fade; - iface->suspend = pulse_stream_suspend; - iface->resume = pulse_stream_resume; - iface->monitor_start = pulse_stream_monitor_start; - iface->monitor_stop = pulse_stream_monitor_stop; - iface->monitor_is_running = pulse_stream_monitor_is_running; - iface->monitor_set_name = pulse_stream_monitor_set_name; - iface->list_ports = pulse_stream_list_ports; - iface->get_active_port = pulse_stream_get_active_port; - iface->set_active_port = pulse_stream_set_active_port; - iface->get_min_volume = pulse_stream_get_min_volume; - iface->get_max_volume = pulse_stream_get_max_volume; - iface->get_normal_volume = pulse_stream_get_normal_volume; - iface->get_base_volume = pulse_stream_get_base_volume; -} +G_DEFINE_ABSTRACT_TYPE (PulseStream, pulse_stream, MATE_MIXER_TYPE_STREAM) static void pulse_stream_class_init (PulseStreamClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + MateMixerStreamClass *stream_class; object_class = G_OBJECT_CLASS (klass); object_class->dispose = pulse_stream_dispose; - object_class->finalize = pulse_stream_finalize; object_class->get_property = pulse_stream_get_property; object_class->set_property = pulse_stream_set_property; - g_object_class_install_property (object_class, - PROP_INDEX, - g_param_spec_uint ("index", - "Index", - "Stream index", - 0, - G_MAXUINT, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, - PROP_CONNECTION, - g_param_spec_object ("connection", - "Connection", - "PulseAudio connection", - PULSE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_override_property (object_class, PROP_NAME, "name"); - g_object_class_override_property (object_class, PROP_DESCRIPTION, "description"); - g_object_class_override_property (object_class, PROP_DEVICE, "device"); - g_object_class_override_property (object_class, PROP_FLAGS, "flags"); - g_object_class_override_property (object_class, PROP_STATE, "state"); - g_object_class_override_property (object_class, PROP_MUTE, "mute"); - g_object_class_override_property (object_class, PROP_VOLUME, "volume"); - g_object_class_override_property (object_class, PROP_BALANCE, "balance"); - g_object_class_override_property (object_class, PROP_FADE, "fade"); - g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port"); + stream_class = MATE_MIXER_STREAM_CLASS (klass); + + properties[PROP_INDEX] = + g_param_spec_uint ("index", + "Index", + "Index of the stream", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_CONNECTION] = + g_param_spec_object ("connection", + "Connection", + "PulseAudio connection", + PULSE_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); g_type_class_add_private (object_class, sizeof (PulseStreamPrivate)); } @@ -261,46 +104,16 @@ pulse_stream_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - PulseStream *pstream; + PulseStream *stream; - pstream = PULSE_STREAM (object); + stream = PULSE_STREAM (object); switch (param_id) { - case PROP_NAME: - g_value_set_string (value, pstream->priv->name); - break; - case PROP_DESCRIPTION: - g_value_set_string (value, pstream->priv->description); - break; - case PROP_DEVICE: - g_value_set_object (value, pstream->priv->device); - break; - case PROP_FLAGS: - g_value_set_flags (value, pstream->priv->flags); - break; - case PROP_STATE: - g_value_set_enum (value, pstream->priv->state); - break; - case PROP_MUTE: - g_value_set_boolean (value, pstream->priv->mute); - break; - case PROP_VOLUME: - g_value_set_uint (value, pstream->priv->volume); - break; - case PROP_BALANCE: - g_value_set_float (value, pstream->priv->balance); - break; - case PROP_FADE: - g_value_set_float (value, pstream->priv->fade); - break; - case PROP_ACTIVE_PORT: - g_value_set_object (value, pstream->priv->port); - break; case PROP_INDEX: - g_value_set_uint (value, pstream->priv->index); + g_value_set_uint (value, stream->priv->index); break; case PROP_CONNECTION: - g_value_set_object (value, pstream->priv->connection); + g_value_set_object (value, stream->priv->connection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -314,17 +127,16 @@ pulse_stream_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - PulseStream *pstream; + PulseStream *stream; - pstream = PULSE_STREAM (object); + stream = PULSE_STREAM (object); switch (param_id) { case PROP_INDEX: - pstream->priv->index = g_value_get_uint (value); + stream->priv->index = g_value_get_uint (value); break; case PROP_CONNECTION: - /* Construct-only object */ - pstream->priv->connection = g_value_dup_object (value); + stream->priv->connection = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -333,962 +145,51 @@ pulse_stream_set_property (GObject *object, } static void -pulse_stream_init (PulseStream *pstream) +pulse_stream_init (PulseStream *stream) { - pstream->priv = G_TYPE_INSTANCE_GET_PRIVATE (pstream, - PULSE_TYPE_STREAM, - PulseStreamPrivate); - - pstream->priv->ports = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); - - /* Initialize empty volume and channel map structures, they will be used - * if the stream does not support volume */ - pa_cvolume_init (&pstream->priv->cvolume); - - pa_channel_map_init (&pstream->priv->channel_map); + stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, + PULSE_TYPE_STREAM, + PulseStreamPrivate); } static void pulse_stream_dispose (GObject *object) { - PulseStream *pstream; + PulseStream *stream; - pstream = PULSE_STREAM (object); + stream = PULSE_STREAM (object); - if (pstream->priv->ports_list != NULL) { - g_list_free_full (pstream->priv->ports_list, g_object_unref); - pstream->priv->ports_list = NULL; - } - g_hash_table_remove_all (pstream->priv->ports); - - g_clear_object (&pstream->priv->port); - g_clear_object (&pstream->priv->device); - g_clear_object (&pstream->priv->monitor); - g_clear_object (&pstream->priv->connection); + g_clear_object (&stream->priv->connection); G_OBJECT_CLASS (pulse_stream_parent_class)->dispose (object); } -static void -pulse_stream_finalize (GObject *object) -{ - PulseStream *pstream; - - pstream = PULSE_STREAM (object); - - g_free (pstream->priv->name); - g_free (pstream->priv->description); - g_free (pstream->priv->monitor_name); - - g_hash_table_destroy (pstream->priv->ports); - - G_OBJECT_CLASS (pulse_stream_parent_class)->finalize (object); -} - guint32 -pulse_stream_get_index (PulseStream *pstream) +pulse_stream_get_index (PulseStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (pstream), 0); + g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); - return pstream->priv->index; + return stream->priv->index; } PulseConnection * -pulse_stream_get_connection (PulseStream *pstream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); - - return pstream->priv->connection; -} - -PulseMonitor * -pulse_stream_get_monitor (PulseStream *pstream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); - - return pstream->priv->monitor; -} - -const pa_cvolume * -pulse_stream_get_cvolume (PulseStream *pstream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); - - return &pstream->priv->cvolume; -} - -const pa_channel_map * -pulse_stream_get_channel_map (PulseStream *pstream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); - - return &pstream->priv->channel_map; -} - -GHashTable * -pulse_stream_get_ports (PulseStream *pstream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL); - - return pstream->priv->ports; -} - -gboolean -pulse_stream_update_name (PulseStream *pstream, const gchar *name) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - /* Allow the name to be NULL */ - if (g_strcmp0 (name, pstream->priv->name) != 0) { - g_free (pstream->priv->name); - pstream->priv->name = g_strdup (name); - - g_object_notify (G_OBJECT (pstream), "name"); - return TRUE; - } - return FALSE; -} - -gboolean -pulse_stream_update_description (PulseStream *pstream, const gchar *description) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - /* Allow the description to be NULL */ - if (g_strcmp0 (description, pstream->priv->description) != 0) { - g_free (pstream->priv->description); - pstream->priv->description = g_strdup (description); - - g_object_notify (G_OBJECT (pstream), "description"); - return TRUE; - } - return FALSE; -} - -gboolean -pulse_stream_update_device (PulseStream *pstream, MateMixerDevice *device) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - if (pstream->priv->device != device) { - g_clear_object (&pstream->priv->device); - - if (G_LIKELY (device != NULL)) - pstream->priv->device = g_object_ref (device); - - g_object_notify (G_OBJECT (pstream), "device"); - return TRUE; - } - return FALSE; -} - -gboolean -pulse_stream_update_flags (PulseStream *pstream, MateMixerStreamFlags flags) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - if (pstream->priv->flags != flags) { - pstream->priv->flags = flags; - - g_object_notify (G_OBJECT (pstream), "flags"); - return TRUE; - } - return FALSE; -} - -gboolean -pulse_stream_update_state (PulseStream *pstream, MateMixerStreamState state) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - if (pstream->priv->state != state) { - pstream->priv->state = state; - - g_object_notify (G_OBJECT (pstream), "state"); - return TRUE; - } - return FALSE; -} - -gboolean -pulse_stream_update_channel_map (PulseStream *pstream, const pa_channel_map *map) -{ - MateMixerStreamFlags flags; - - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - g_return_val_if_fail (map != NULL, FALSE); - - flags = pstream->priv->flags; - - if (pa_channel_map_valid (map)) { - if (pa_channel_map_can_balance (map)) - flags |= MATE_MIXER_STREAM_CAN_BALANCE; - else - flags &= ~MATE_MIXER_STREAM_CAN_BALANCE; - - if (pa_channel_map_can_fade (map)) - flags |= MATE_MIXER_STREAM_CAN_FADE; - else - flags &= ~MATE_MIXER_STREAM_CAN_FADE; - - pstream->priv->channel_map = *map; - } else { - flags &= ~(MATE_MIXER_STREAM_CAN_BALANCE | MATE_MIXER_STREAM_CAN_FADE); - - /* If the channel map is not valid, create an empty channel map, which - * also won't validate, but at least we know what it is */ - pa_channel_map_init (&pstream->priv->channel_map); - } - - pulse_stream_update_flags (pstream, flags); - return TRUE; -} - -gboolean -pulse_stream_update_volume (PulseStream *pstream, - const pa_cvolume *cvolume, - pa_volume_t base_volume) -{ - MateMixerStreamFlags flags; - - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - /* The base volume is not a property */ - pstream->priv->base_volume = base_volume; - - flags = pstream->priv->flags; - - if (cvolume != NULL && pa_cvolume_valid (cvolume)) { - /* Decibel volume and volume settability flags must be provided by - * the implementation */ - flags |= MATE_MIXER_STREAM_HAS_VOLUME; - - if (pa_cvolume_equal (&pstream->priv->cvolume, cvolume) == 0) { - pstream->priv->cvolume = *cvolume; - pstream->priv->volume = (guint) pa_cvolume_max (&pstream->priv->cvolume); - - g_object_notify (G_OBJECT (pstream), "volume"); - } - } else { - flags &= ~(MATE_MIXER_STREAM_HAS_VOLUME | - MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME | - MATE_MIXER_STREAM_CAN_SET_VOLUME); - - /* If the cvolume is not valid, create an empty cvolume, which also - * won't validate, but at least we know what it is */ - pa_cvolume_init (&pstream->priv->cvolume); - - if (pstream->priv->volume != (guint) PA_VOLUME_MUTED) { - pstream->priv->volume = (guint) PA_VOLUME_MUTED; - - g_object_notify (G_OBJECT (pstream), "volume"); - } - } - - pulse_stream_update_flags (pstream, flags); - - /* Changing volume may change the balance and fade values as well */ - update_balance_fade (pstream); - return TRUE; -} - -gboolean -pulse_stream_update_mute (PulseStream *pstream, gboolean mute) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - - if (pstream->priv->mute != mute) { - pstream->priv->mute = mute; - - g_object_notify (G_OBJECT (pstream), "mute"); - return TRUE; - } - return FALSE; -} - -gboolean -pulse_stream_update_active_port (PulseStream *pstream, MateMixerPort *port) -{ - g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); - - if (pstream->priv->port != port) { - if (pstream->priv->port != NULL) - g_clear_object (&pstream->priv->port); - - if (port != NULL) - pstream->priv->port = g_object_ref (port); - - g_object_notify (G_OBJECT (pstream), "active-port"); - return TRUE; - } - return FALSE; -} - -static const gchar * -pulse_stream_get_name (MateMixerStream *stream) +pulse_stream_get_connection (PulseStream *stream) { g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); - return PULSE_STREAM (stream)->priv->name; + return stream->priv->connection; } -static const gchar * -pulse_stream_get_description (MateMixerStream *stream) +PulseDevice * +pulse_stream_get_device (PulseStream *stream) { - g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); - - return PULSE_STREAM (stream)->priv->description; -} + MateMixerDevice *device; -static MateMixerDevice * -pulse_stream_get_device (MateMixerStream *stream) -{ g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); - return PULSE_STREAM (stream)->priv->device; -} - -static MateMixerStreamFlags -pulse_stream_get_flags (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_NO_FLAGS); - - return PULSE_STREAM (stream)->priv->flags; -} - -static MateMixerStreamState -pulse_stream_get_state (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_STATE_UNKNOWN); - - return PULSE_STREAM (stream)->priv->state; -} - -static gboolean -pulse_stream_get_mute (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MUTE)) - return FALSE; - - return pstream->priv->mute; -} - -static gboolean -pulse_stream_set_mute (MateMixerStream *stream, gboolean mute) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MUTE)) - return FALSE; - - if (pstream->priv->mute != mute) { - PulseStreamClass *klass = PULSE_STREAM_GET_CLASS (pstream); - - if (klass->set_mute (pstream, mute) == FALSE) - return FALSE; - - pstream->priv->mute = mute; - - g_object_notify (G_OBJECT (stream), "mute"); - } - return TRUE; -} - -static guint -pulse_stream_get_num_channels (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0); - - return PULSE_STREAM (stream)->priv->channel_map.channels; -} - -static guint -pulse_stream_get_volume (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) - return (guint) PA_VOLUME_MUTED; - - return pstream->priv->volume; -} - -static gboolean -pulse_stream_set_volume (MateMixerStream *stream, guint volume) -{ - PulseStream *pstream; - pa_cvolume cvolume; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME)) - return FALSE; - - cvolume = pstream->priv->cvolume; - - if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL) - return FALSE; - - return set_cvolume (pstream, &cvolume); -} - -static gdouble -pulse_stream_get_decibel (MateMixerStream *stream) -{ - PulseStream *pstream; - gdouble value; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), -MATE_MIXER_INFINITY); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) - return -MATE_MIXER_INFINITY; - - value = pa_sw_volume_to_dB (pulse_stream_get_volume (stream)); - - /* PA_VOLUME_MUTED is converted to PA_DECIBEL_MININFTY */ - return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value; -} - -static gboolean -pulse_stream_set_decibel (MateMixerStream *stream, gdouble decibel) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) || - !(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME)) - return FALSE; - - return pulse_stream_set_volume (stream, pa_sw_volume_from_dB (decibel)); -} - -static guint -pulse_stream_get_channel_volume (MateMixerStream *stream, guint channel) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) - return (guint) PA_VOLUME_MUTED; - - if (channel >= pstream->priv->cvolume.channels) - return (guint) PA_VOLUME_MUTED; - - return (guint) pstream->priv->cvolume.values[channel]; -} - -static gboolean -pulse_stream_set_channel_volume (MateMixerStream *stream, guint channel, guint volume) -{ - PulseStream *pstream; - pa_cvolume cvolume; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME)) - return FALSE; - - if (channel >= pstream->priv->cvolume.channels) - return FALSE; - - /* This is safe, because the cvolume is validated by set_cvolume() */ - cvolume = pstream->priv->cvolume; - cvolume.values[channel] = (pa_volume_t) volume; - - return set_cvolume (pstream, &cvolume); -} - -static gdouble -pulse_stream_get_channel_decibel (MateMixerStream *stream, guint channel) -{ - PulseStream *pstream; - gdouble value; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), -MATE_MIXER_INFINITY); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME)) - return -MATE_MIXER_INFINITY; - - if (channel >= pstream->priv->cvolume.channels) - return -MATE_MIXER_INFINITY; - - value = pa_sw_volume_to_dB (pstream->priv->cvolume.values[channel]); - - return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value; -} - -static gboolean -pulse_stream_set_channel_decibel (MateMixerStream *stream, - guint channel, - gdouble decibel) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) || - !(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME)) - return FALSE; - - return pulse_stream_set_channel_volume (stream, channel, pa_sw_volume_from_dB (decibel)); -} - -static MateMixerChannelPosition -pulse_stream_get_channel_position (MateMixerStream *stream, guint channel) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_CHANNEL_UNKNOWN); - - pstream = PULSE_STREAM (stream); - - if (channel >= pstream->priv->channel_map.channels) - return MATE_MIXER_CHANNEL_UNKNOWN; - - return pulse_convert_position_to_pulse (pstream->priv->channel_map.map[channel]); -} - -static gboolean -pulse_stream_has_channel_position (MateMixerStream *stream, - MateMixerChannelPosition position) -{ - PulseStream *pstream; - pa_channel_position_t p; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - /* Handle invalid position as a special case, otherwise this function would - * return TRUE for e.g. unknown index in a default channel map */ - p = pulse_convert_position_to_pulse (position); - - if (p == PA_CHANNEL_POSITION_INVALID) - return FALSE; - - if (pa_channel_map_has_position (&pstream->priv->channel_map, p) != 0) - return TRUE; - else - return FALSE; -} - -static gfloat -pulse_stream_get_balance (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0f); - - return PULSE_STREAM (stream)->priv->balance; -} - -static gboolean -pulse_stream_set_balance (MateMixerStream *stream, gfloat balance) -{ - PulseStream *pstream; - pa_cvolume cvolume; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_BALANCE)) - return FALSE; - - cvolume = pstream->priv->cvolume; - - if (pa_cvolume_set_balance (&cvolume, &pstream->priv->channel_map, balance) == NULL) - return FALSE; - - return set_cvolume (pstream, &cvolume); -} - -static gfloat -pulse_stream_get_fade (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0f); - - return PULSE_STREAM (stream)->priv->fade; -} - -static gboolean -pulse_stream_set_fade (MateMixerStream *stream, gfloat fade) -{ - PulseStream *pstream; - pa_cvolume cvolume; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_FADE)) - return FALSE; - - cvolume = pstream->priv->cvolume; - - if (pa_cvolume_set_fade (&cvolume, &pstream->priv->channel_map, fade) == NULL) - return FALSE; - - return set_cvolume (pstream, &cvolume); -} - -static gboolean -pulse_stream_suspend (MateMixerStream *stream) -{ - PulseStream *pstream; - PulseStreamClass *klass; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND)) - return FALSE; - - if (pstream->priv->state == MATE_MIXER_STREAM_STATE_SUSPENDED) - return FALSE; - - klass = PULSE_STREAM_GET_CLASS (pstream); - - if (klass->suspend (pstream) == FALSE) - return FALSE; - - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED); - return TRUE; -} - -static gboolean -pulse_stream_resume (MateMixerStream *stream) -{ - PulseStream *pstream; - PulseStreamClass *klass; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND)) - return FALSE; - - if (pstream->priv->state != MATE_MIXER_STREAM_STATE_SUSPENDED) - return FALSE; - - klass = PULSE_STREAM_GET_CLASS (pstream); - - if (klass->resume (pstream) == FALSE) - return FALSE; - - /* The state when resumed should be either RUNNING or IDLE, let's assume - * IDLE for now and request an immediate update */ - pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE); - - klass->reload (pstream); - return TRUE; -} - -static gboolean -pulse_stream_monitor_start (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MONITOR)) - return FALSE; - - if (pstream->priv->monitor == NULL) { - pstream->priv->monitor = PULSE_STREAM_GET_CLASS (pstream)->create_monitor (pstream); - - if (G_UNLIKELY (pstream->priv->monitor == NULL)) - return FALSE; - - pulse_monitor_set_name (pstream->priv->monitor, - pstream->priv->monitor_name); - - g_signal_connect (G_OBJECT (pstream->priv->monitor), - "value", - G_CALLBACK (on_monitor_value), - pstream); - } - - return pulse_monitor_set_enabled (pstream->priv->monitor, TRUE); -} - -static void -pulse_stream_monitor_stop (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_if_fail (PULSE_IS_STREAM (stream)); - - pstream = PULSE_STREAM (stream); - - if (pstream->priv->monitor != NULL) - pulse_monitor_set_enabled (pstream->priv->monitor, FALSE); -} - -static gboolean -pulse_stream_monitor_is_running (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (pstream->priv->monitor != NULL) - return pulse_monitor_get_enabled (pstream->priv->monitor); - - return FALSE; -} - -static gboolean -pulse_stream_monitor_set_name (MateMixerStream *stream, const gchar *name) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - - pstream = PULSE_STREAM (stream); - - if (pstream->priv->monitor != NULL) - pulse_monitor_set_name (pstream->priv->monitor, name); - - pstream->priv->monitor_name = g_strdup (name); - return TRUE; -} - -static const GList * -pulse_stream_list_ports (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); - - pstream = PULSE_STREAM (stream); - - if (pstream->priv->ports_list == NULL) { - GList *list = g_hash_table_get_values (pstream->priv->ports); - - if (list != NULL) { - g_list_foreach (list, (GFunc) g_object_ref, NULL); - - pstream->priv->ports_list = g_list_sort (list, compare_ports); - } - } - - return (const GList *) pstream->priv->ports_list; -} - -static MateMixerPort * -pulse_stream_get_active_port (MateMixerStream *stream) -{ - g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL); - - return PULSE_STREAM (stream)->priv->port; -} - -static gboolean -pulse_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port) -{ - PulseStream *pstream; - PulseStreamClass *klass; - const gchar *name; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE); - - pstream = PULSE_STREAM (stream); - - /* Make sure the port comes from this stream */ - name = mate_mixer_port_get_name (port); - - if (g_hash_table_lookup (pstream->priv->ports, name) == NULL) { - g_warning ("Port %s does not belong to stream %s", - mate_mixer_port_get_name (port), - mate_mixer_stream_get_name (stream)); - return FALSE; - } - - klass = PULSE_STREAM_GET_CLASS (pstream); - - /* Change the port */ - if (klass->set_active_port (pstream, port) == FALSE) - return FALSE; - - if (pstream->priv->port != NULL) - g_object_unref (pstream->priv->port); - - pstream->priv->port = g_object_ref (port); - - g_object_notify (G_OBJECT (stream), "active-port"); - return TRUE; -} - -static guint -pulse_stream_get_min_volume (MateMixerStream *stream) -{ - return (guint) PA_VOLUME_MUTED; -} - -static guint -pulse_stream_get_max_volume (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) - return (guint) PA_VOLUME_MUTED; - - return (guint) PA_VOLUME_UI_MAX; -} - -static guint -pulse_stream_get_normal_volume (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) - return (guint) PA_VOLUME_MUTED; - - return (guint) PA_VOLUME_NORM; -} - -static guint -pulse_stream_get_base_volume (MateMixerStream *stream) -{ - PulseStream *pstream; - - g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED); - - pstream = PULSE_STREAM (stream); - - if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME)) - return (guint) PA_VOLUME_MUTED; - - if (pstream->priv->base_volume > 0) - return pstream->priv->base_volume; - else - return (guint) PA_VOLUME_NORM; -} - -static void -on_monitor_value (PulseMonitor *monitor, gdouble value, PulseStream *pstream) -{ - g_signal_emit_by_name (G_OBJECT (pstream), - "monitor-value", - value); -} - -static gboolean -update_balance_fade (PulseStream *pstream) -{ - gfloat fade; - gfloat balance; - gboolean changed = FALSE; - - /* The PulseAudio return the default 0.0f values on errors, so skip checking - * validity of the channel map and volume */ - balance = pa_cvolume_get_balance (&pstream->priv->cvolume, - &pstream->priv->channel_map); - - if (pstream->priv->balance != balance) { - pstream->priv->balance = balance; - - g_object_notify (G_OBJECT (pstream), "balance"); - changed = TRUE; - } - - fade = pa_cvolume_get_fade (&pstream->priv->cvolume, - &pstream->priv->channel_map); - - if (pstream->priv->fade != fade) { - pstream->priv->fade = fade; - - g_object_notify (G_OBJECT (pstream), "fade"); - changed = TRUE; - } - - return changed; -} - -static gboolean -set_cvolume (PulseStream *pstream, pa_cvolume *cvolume) -{ - PulseStreamClass *klass; - - if (pa_cvolume_valid (cvolume) == 0) - return FALSE; - if (pa_cvolume_equal (cvolume, &pstream->priv->cvolume) != 0) - return TRUE; - - klass = PULSE_STREAM_GET_CLASS (pstream); - - if (klass->set_volume (pstream, cvolume) == FALSE) - return FALSE; - - pstream->priv->cvolume = *cvolume; - pstream->priv->volume = (guint) pa_cvolume_max (cvolume); - - g_object_notify (G_OBJECT (pstream), "volume"); - - /* Changing volume may change the balance and fade values as well */ - update_balance_fade (pstream); - return TRUE; -} - -static gint -compare_ports (gconstpointer a, gconstpointer b) -{ - MateMixerPort *p1 = MATE_MIXER_PORT (a); - MateMixerPort *p2 = MATE_MIXER_PORT (b); + device = mate_mixer_stream_get_device (MATE_MIXER_STREAM (stream)); + if (device != NULL) + return PULSE_DEVICE (device); - gint ret = (gint) (mate_mixer_port_get_priority (p2) - - mate_mixer_port_get_priority (p1)); - if (ret != 0) - return ret; - else - return strcmp (mate_mixer_port_get_name (p1), - mate_mixer_port_get_name (p2)); + return NULL; } diff --git a/backends/pulse/pulse-stream.h b/backends/pulse/pulse-stream.h index e4c6a00..eafe457 100644 --- a/backends/pulse/pulse-stream.h +++ b/backends/pulse/pulse-stream.h @@ -20,15 +20,11 @@ #include <glib.h> #include <glib-object.h> - -#include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-port.h> -#include <libmatemixer/matemixer-stream.h> +#include <libmatemixer/matemixer.h> #include <pulse/pulseaudio.h> -#include "pulse-connection.h" -#include "pulse-monitor.h" +#include "pulse-types.h" G_BEGIN_DECLS @@ -45,13 +41,12 @@ G_BEGIN_DECLS #define PULSE_STREAM_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_STREAM, PulseStreamClass)) -typedef struct _PulseStream PulseStream; typedef struct _PulseStreamClass PulseStreamClass; typedef struct _PulseStreamPrivate PulseStreamPrivate; struct _PulseStream { - GObject parent; + MateMixerStream parent; /*< private >*/ PulseStreamPrivate *priv; @@ -59,58 +54,15 @@ struct _PulseStream struct _PulseStreamClass { - GObjectClass parent_class; - - /*< private >*/ - /* Virtual table */ - void (*reload) (PulseStream *stream); - - gboolean (*set_mute) (PulseStream *stream, - gboolean mute); - gboolean (*set_volume) (PulseStream *stream, - pa_cvolume *volume); - - gboolean (*set_active_port) (PulseStream *stream, - MateMixerPort *port); - - gboolean (*suspend) (PulseStream *stream); - gboolean (*resume) (PulseStream *stream); - - PulseMonitor *(*create_monitor) (PulseStream *stream); + MateMixerStreamClass parent_class; }; -GType pulse_stream_get_type (void) G_GNUC_CONST; - -guint32 pulse_stream_get_index (PulseStream *pstream); -PulseConnection * pulse_stream_get_connection (PulseStream *pstream); -PulseMonitor * pulse_stream_get_monitor (PulseStream *pstream); -GHashTable * pulse_stream_get_ports (PulseStream *pstream); - -const pa_cvolume * pulse_stream_get_cvolume (PulseStream *pstream); -const pa_channel_map *pulse_stream_get_channel_map (PulseStream *pstream); - -gboolean pulse_stream_update_name (PulseStream *pstream, - const gchar *name); -gboolean pulse_stream_update_description (PulseStream *pstream, - const gchar *description); -gboolean pulse_stream_update_device (PulseStream *pstream, - MateMixerDevice *device); -gboolean pulse_stream_update_flags (PulseStream *pstream, - MateMixerStreamFlags flags); -gboolean pulse_stream_update_state (PulseStream *pstream, - MateMixerStreamState state); - -gboolean pulse_stream_update_channel_map (PulseStream *pstream, - const pa_channel_map *map); -gboolean pulse_stream_update_volume (PulseStream *pstream, - const pa_cvolume *volume, - pa_volume_t base_volume); +GType pulse_stream_get_type (void) G_GNUC_CONST; -gboolean pulse_stream_update_mute (PulseStream *pstream, - gboolean mute); +guint32 pulse_stream_get_index (PulseStream *stream); +PulseConnection *pulse_stream_get_connection (PulseStream *stream); -gboolean pulse_stream_update_active_port (PulseStream *pstream, - MateMixerPort *port); +PulseDevice * pulse_stream_get_device (PulseStream *stream); G_END_DECLS diff --git a/backends/pulse/pulse-types.h b/backends/pulse/pulse-types.h new file mode 100644 index 0000000..c664268 --- /dev/null +++ b/backends/pulse/pulse-types.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PULSE_TYPES_H +#define PULSE_TYPES_H + +G_BEGIN_DECLS + +typedef struct _PulseBackend PulseBackend; +typedef struct _PulseConnection PulseConnection; +typedef struct _PulseDevice PulseDevice; +typedef struct _PulseDeviceProfile PulseDeviceProfile; +typedef struct _PulseDeviceSwitch PulseDeviceSwitch; +typedef struct _PulseExtStream PulseExtStream; +typedef struct _PulseMonitor PulseMonitor; +typedef struct _PulsePort PulsePort; +typedef struct _PulsePortSwitch PulsePortSwitch; +typedef struct _PulseSink PulseSink; +typedef struct _PulseSinkControl PulseSinkControl; +typedef struct _PulseSinkInput PulseSinkInput; +typedef struct _PulseSinkSwitch PulseSinkSwitch; +typedef struct _PulseSource PulseSource; +typedef struct _PulseSourceControl PulseSourceControl; +typedef struct _PulseSourceOutput PulseSourceOutput; +typedef struct _PulseSourceSwitch PulseSourceSwitch; +typedef struct _PulseStream PulseStream; +typedef struct _PulseStreamControl PulseStreamControl; + +G_END_DECLS + +#endif /* PULSE_TYPES_H */ diff --git a/configure.ac b/configure.ac index d3c2173..f2d70e0 100644 --- a/configure.ac +++ b/configure.ac @@ -41,7 +41,7 @@ AC_PROG_INSTALL # Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([string.h math.h sys/types.h unistd.h locale.h]) +AC_CHECK_HEADERS([string.h math.h sys/types.h unistd.h libintl.h locale.h]) # ======================================================================= # Libtool @@ -57,6 +57,18 @@ LT_PREREQ([2.2]) LT_INIT # ======================================================================= +# Gettext +# ======================================================================= +AM_GNU_GETTEXT_VERSION([0.18]) +AM_GNU_GETTEXT([external]) + +IT_PROG_INTLTOOL([0.35.0]) + +GETTEXT_PACKAGE=libmatemixer +AC_SUBST(GETTEXT_PACKAGE) +AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [Gettext Package]) + +# ======================================================================= # Check for required packages # ======================================================================= PKG_PROG_PKG_CONFIG @@ -257,6 +269,7 @@ docs/Makefile docs/reference/Makefile docs/reference/version.xml examples/Makefile +po/Makefile.in ]) AC_OUTPUT diff --git a/examples/monitor.c b/examples/monitor.c index 71d8b61..87e41f3 100644 --- a/examples/monitor.c +++ b/examples/monitor.c @@ -66,48 +66,60 @@ get_stream_control_role_string (MateMixerStreamControlRole role) return "Master"; case MATE_MIXER_STREAM_CONTROL_ROLE_PCM: return "PCM"; + case MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER: + return "Speaker"; + case MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE: + return "Microphone"; + case MATE_MIXER_STREAM_CONTROL_ROLE_PORT: + return "Port"; + case MATE_MIXER_STREAM_CONTROL_ROLE_BOOST: + return "Boost"; case MATE_MIXER_STREAM_CONTROL_ROLE_BASS: return "Bass"; case MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE: return "Treble"; case MATE_MIXER_STREAM_CONTROL_ROLE_CD: return "CD"; - case MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER: - return "PC Speaker"; - case MATE_MIXER_STREAM_CONTROL_ROLE_PORT: - return "Port"; + case MATE_MIXER_STREAM_CONTROL_ROLE_VIDEO: + return "Video"; + case MATE_MIXER_STREAM_CONTROL_ROLE_MUSIC: + return "Music"; + default: + break; } return "Unknown"; } static const gchar * -create_role_string (MateMixerClientStreamRole role) +get_stream_control_media_role_string (MateMixerStreamControlMediaRole role) { switch (role) { - case MATE_MIXER_CLIENT_STREAM_ROLE_NONE: - return "None"; - case MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO: + case MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN: + return "Unknown"; + case MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_VIDEO: return "Video"; - case MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC: + case MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_MUSIC: return "Music"; - case MATE_MIXER_CLIENT_STREAM_ROLE_GAME: + case MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_GAME: return "Game"; - case MATE_MIXER_CLIENT_STREAM_ROLE_EVENT: + case MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT: return "Event"; - case MATE_MIXER_CLIENT_STREAM_ROLE_PHONE: + case MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PHONE: return "Phone"; - case MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION: + case MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ANIMATION: return "Animation"; - case MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION: + case MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PRODUCTION: return "Production"; - case MATE_MIXER_CLIENT_STREAM_ROLE_A11Y: + case MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_A11Y: return "A11y"; - case MATE_MIXER_CLIENT_STREAM_ROLE_TEST: + case MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_TEST: return "Test"; - case MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT: + case MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ABSTRACT: return "Abstract"; - case MATE_MIXER_CLIENT_STREAM_ROLE_FILTER: + case MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_FILTER: return "Filter"; + default: + break; } return "Unknown"; } @@ -145,9 +157,7 @@ create_volume_bar (MateMixerStreamControl *ctl, double *percent) static void print_devices (void) { - const GList *devices; - const GList *profiles; - MateMixerDeviceProfile *active_profile; + const GList *devices; devices = mate_mixer_context_list_devices (context); @@ -161,30 +171,6 @@ print_devices (void) mate_mixer_device_get_label (device), mate_mixer_device_get_icon (device)); - profiles = mate_mixer_device_list_profiles (device); - - active_profile = mate_mixer_device_get_active_profile (device); - while (profiles) { - MateMixerDeviceProfile *profile = MATE_MIXER_DEVICE_PROFILE (profiles->data); - - g_print (" |%c| Profile %s\n" - " |-| Label : %s\n" - " |-| Priority : %u\n" - " |-| Inputs : %u\n" - " |-| Outputs : %u\n\n", - (profile == active_profile) - ? 'A' - : '-', - mate_mixer_device_profile_get_name (profile), - mate_mixer_device_profile_get_label (profile), - mate_mixer_device_profile_get_priority (profile), - mate_mixer_device_profile_get_num_input_streams (profile), - mate_mixer_device_profile_get_num_output_streams (profile)); - - profiles = profiles->next; - } - g_print ("\n"); - const GList *switches = mate_mixer_device_list_switches (device); while (switches != NULL) { @@ -201,11 +187,12 @@ print_devices (void) while (options != NULL) { MateMixerSwitchOption *option = MATE_MIXER_SWITCH_OPTION (options->data); - g_print (" |%c| %s\n", + g_print (" |%c| %s (icon: %s)\n", (option == active) ? '*' : '-', - mate_mixer_switch_option_get_label (option)); + mate_mixer_switch_option_get_label (option), + mate_mixer_switch_option_get_icon (option)); options = options->next; } @@ -229,19 +216,12 @@ print_streams (void) MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); MateMixerStreamControl *ctl; MateMixerSwitch *swtch; - MateMixerClientStream *client = NULL; gchar *volume_bar; gdouble volume; const GList *controls; const GList *switches; - if (mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_CLIENT) { - /* The application-specific details are accessible through the client - * interface, which all client streams implement */ - client = MATE_MIXER_CLIENT_STREAM (stream); - } - controls = mate_mixer_stream_list_controls (stream); while (controls != NULL) { @@ -286,94 +266,28 @@ print_streams (void) options = mate_mixer_switch_list_options (swtch); + MateMixerSwitchOption *active = mate_mixer_switch_get_active_option (swtch); + g_print ("Switch %s\n", mate_mixer_switch_get_name (swtch)); while (options != NULL) { MateMixerSwitchOption *option = MATE_MIXER_SWITCH_OPTION (options->data); - g_print (" |%c| %s\n", - '-', - mate_mixer_switch_option_get_label (option)); + g_print (" |%c| %s (icon: %s)\n", + (option == active) + ? '*' + : '-', + mate_mixer_switch_option_get_label (option), + mate_mixer_switch_option_get_icon (option)); options = options->next; } - options = options->next; - } - - if (client != NULL) { - MateMixerClientStreamFlags client_flags; - - client_flags = mate_mixer_client_stream_get_flags (client); - - if (client_flags & MATE_MIXER_CLIENT_STREAM_APPLICATION) { - gchar *app = create_app_string (mate_mixer_client_stream_get_app_name (client), - mate_mixer_client_stream_get_app_id (client), - mate_mixer_client_stream_get_app_version (client)); - - g_print (" |-| Application : %s\n", app); - g_free (app); - } - } - - g_print ("\n"); - - streams = streams->next; - } -} - -static void -print_stored_streams (void) -{ - const GList *streams; - - streams = mate_mixer_context_list_stored_streams (context); - - while (streams) { - MateMixerStream *stream = MATE_MIXER_STREAM (streams->data); - MateMixerStreamControl *ctl; - MateMixerClientStream *client; - MateMixerClientStreamFlags client_flags; - MateMixerClientStreamRole client_role; - gchar *volume_bar; - gdouble volume; - - ctl = mate_mixer_stream_get_default_control (stream); - - client = MATE_MIXER_CLIENT_STREAM (stream); - client_flags = mate_mixer_client_stream_get_flags (client); - client_role = mate_mixer_client_stream_get_role (client); - - volume_bar = create_volume_bar (ctl, &volume); - - g_print ("Stored stream %s\n" - " |-| Role : %s\n" - " |-| Volume : %s %.1f %%\n" - " |-| Muted : %s\n" - " |-| Channels : %d\n" - " |-| Balance : %.1f\n" - " |-| Fade : %.1f\n", - mate_mixer_stream_get_name (stream), - create_role_string (client_role), - volume_bar, - volume, - mate_mixer_stream_control_get_mute (ctl) ? "Yes" : "No", - mate_mixer_stream_control_get_num_channels (ctl), - mate_mixer_stream_control_get_balance (ctl), - mate_mixer_stream_control_get_fade (ctl)); - - if (client_flags & MATE_MIXER_CLIENT_STREAM_APPLICATION) { - gchar *app = create_app_string (mate_mixer_client_stream_get_app_name (client), - mate_mixer_client_stream_get_app_id (client), - mate_mixer_client_stream_get_app_version (client)); - - g_print (" |-| Application : %s\n", app); - g_free (app); + switches = switches->next; } g_print ("\n"); - g_free (volume_bar); streams = streams->next; } @@ -387,7 +301,6 @@ connected (void) print_devices (); print_streams (); - print_stored_streams (); g_print ("Waiting for events. Hit CTRL+C to quit.\n"); } diff --git a/libmatemixer/Makefile.am b/libmatemixer/Makefile.am index 8858b90..d7075cb 100644 --- a/libmatemixer/Makefile.am +++ b/libmatemixer/Makefile.am @@ -10,11 +10,12 @@ libmatemixer_includedir = $(includedir)/libmatemixer libmatemixer_include_HEADERS = \ matemixer.h \ - matemixer-client-stream.h \ + matemixer-app-info.h \ matemixer-context.h \ matemixer-device.h \ - matemixer-device-profile.h \ matemixer-enums.h \ + matemixer-enum-types.h \ + matemixer-stored-control.h \ matemixer-stream.h \ matemixer-stream-control.h \ matemixer-switch.h \ @@ -28,19 +29,18 @@ libmatemixer_la_CFLAGS = $(GLIB_CFLAGS) libmatemixer_la_SOURCES = \ matemixer.c \ matemixer-private.h \ + matemixer-app-info.c \ + matemixer-app-info-private.h \ matemixer-backend.c \ matemixer-backend.h \ - matemixer-backend-private.h \ matemixer-backend-module.c \ matemixer-backend-module.h \ - matemixer-client-stream.c \ matemixer-context.c \ matemixer-device.c \ - matemixer-device-profile.c \ - matemixer-device-profile-private.h \ matemixer-enum-types.c \ - matemixer-enum-types.h \ + matemixer-stored-control.c \ matemixer-stream.c \ + matemixer-stream-private.h \ matemixer-stream-control.c \ matemixer-stream-control-private.h \ matemixer-switch.c \ diff --git a/libmatemixer/matemixer-app-info-private.h b/libmatemixer/matemixer-app-info-private.h new file mode 100644 index 0000000..486b163 --- /dev/null +++ b/libmatemixer/matemixer-app-info-private.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MATEMIXER_APP_INFO_PRIVATE_H +#define MATEMIXER_APP_INFO_PRIVATE_H + +#include <glib.h> + +#include "matemixer-types.h" + +G_BEGIN_DECLS + +struct _MateMixerAppInfo +{ + gchar *name; + gchar *id; + gchar *version; + gchar *icon; +}; + +MateMixerAppInfo *_mate_mixer_app_info_new (void); + +void _mate_mixer_app_info_set_name (MateMixerAppInfo *info, + const gchar *name); +void _mate_mixer_app_info_set_id (MateMixerAppInfo *info, + const gchar *id); +void _mate_mixer_app_info_set_version (MateMixerAppInfo *info, + const gchar *version); +void _mate_mixer_app_info_set_icon (MateMixerAppInfo *info, + const gchar *icon); + +MateMixerAppInfo *_mate_mixer_app_info_copy (MateMixerAppInfo *info); +void _mate_mixer_app_info_free (MateMixerAppInfo *info); + +G_END_DECLS + +#endif /* MATEMIXER_APP_INFO_PRIVATE_H */ diff --git a/libmatemixer/matemixer-app-info.c b/libmatemixer/matemixer-app-info.c new file mode 100644 index 0000000..369f148 --- /dev/null +++ b/libmatemixer/matemixer-app-info.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "matemixer-app-info.h" +#include "matemixer-app-info-private.h" + +G_DEFINE_BOXED_TYPE (MateMixerAppInfo, mate_mixer_app_info, + _mate_mixer_app_info_copy, + _mate_mixer_app_info_free) + +/** + * mate_mixer_app_info_get_name: + * @device: a #MateMixerAppInfo + */ +const gchar * +mate_mixer_app_info_get_name (MateMixerAppInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + + return info->name; +} + +/** + * mate_mixer_app_info_get_id: + * @device: a #MateMixerAppInfo + */ +const gchar * +mate_mixer_app_info_get_id (MateMixerAppInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + + return info->id; +} + +/** + * mate_mixer_app_info_get_version: + * @device: a #MateMixerAppInfo + */ +const gchar * +mate_mixer_app_info_get_version (MateMixerAppInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + + return info->version; +} + +/** + * mate_mixer_app_info_get_icon: + * @device: a #MateMixerAppInfo + */ +const gchar * +mate_mixer_app_info_get_icon (MateMixerAppInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + + return info->icon; +} + +MateMixerAppInfo * +_mate_mixer_app_info_new (void) +{ + return g_slice_new0 (MateMixerAppInfo); +} + +void +_mate_mixer_app_info_set_name (MateMixerAppInfo *info, const gchar *name) +{ + g_return_if_fail (info != NULL); + + g_free (info->name); + + info->name = g_strdup (name); +} + +void +_mate_mixer_app_info_set_id (MateMixerAppInfo *info, const gchar *id) +{ + g_return_if_fail (info != NULL); + + g_free (info->id); + + info->id = g_strdup (id); +} + +void +_mate_mixer_app_info_set_version (MateMixerAppInfo *info, const gchar *version) +{ + g_return_if_fail (info != NULL); + + g_free (info->version); + + info->version = g_strdup (version); +} + +void +_mate_mixer_app_info_set_icon (MateMixerAppInfo *info, const gchar *icon) +{ + g_return_if_fail (info != NULL); + + g_free (info->icon); + + info->icon = g_strdup (icon); +} + +MateMixerAppInfo * +_mate_mixer_app_info_copy (MateMixerAppInfo *info) +{ + MateMixerAppInfo *info2; + + info2 = _mate_mixer_app_info_new (); + info2->name = g_strdup (info->name); + info2->id = g_strdup (info->id); + info2->version = g_strdup (info->version); + info2->icon = g_strdup (info->icon); + + return info2; +} + +void +_mate_mixer_app_info_free (MateMixerAppInfo *info) +{ + g_free (info->name); + g_free (info->id); + g_free (info->version); + g_free (info->icon); + + g_slice_free (MateMixerAppInfo, info); +} diff --git a/libmatemixer/matemixer-backend-private.h b/libmatemixer/matemixer-app-info.h index b5de8ae..eedc1c7 100644 --- a/libmatemixer/matemixer-backend-private.h +++ b/libmatemixer/matemixer-app-info.h @@ -15,26 +15,26 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef MATEMIXER_BACKEND_PRIVATE_H -#define MATEMIXER_BACKEND_PRIVATE_H +#ifndef MATEMIXER_APP_INFO_H +#define MATEMIXER_APP_INFO_H #include <glib.h> +#include <glib-object.h> -#include "matemixer-backend.h" -#include "matemixer-enums.h" -#include "matemixer-stream.h" +#include "matemixer-types.h" G_BEGIN_DECLS -void _mate_mixer_backend_set_state (MateMixerBackend *backend, - MateMixerState state); +#define MATE_MIXER_TYPE_APP_INFO (mate_mixer_app_info_get_type ()) +#define MATE_MIXER_APP_INFO(o) ((MateMixerAppInfo *) o) -void _mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, - MateMixerStream *stream); +GType mate_mixer_app_info_get_type (void) G_GNUC_CONST; -void _mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, - MateMixerStream *stream); +const gchar *mate_mixer_app_info_get_name (MateMixerAppInfo *info); +const gchar *mate_mixer_app_info_get_id (MateMixerAppInfo *info); +const gchar *mate_mixer_app_info_get_version (MateMixerAppInfo *info); +const gchar *mate_mixer_app_info_get_icon (MateMixerAppInfo *info); G_END_DECLS -#endif /* MATEMIXER_BACKEND_PRIVATE_H */ +#endif /* MATEMIXER_APP_INFO_H */ diff --git a/libmatemixer/matemixer-backend-module.c b/libmatemixer/matemixer-backend-module.c index 0e7716e..bd71de0 100644 --- a/libmatemixer/matemixer-backend-module.c +++ b/libmatemixer/matemixer-backend-module.c @@ -19,7 +19,6 @@ #include <glib-object.h> #include <gmodule.h> -#include "matemixer-backend.h" #include "matemixer-backend-module.h" /* Initialize backend */ diff --git a/libmatemixer/matemixer-backend-module.h b/libmatemixer/matemixer-backend-module.h index e1dfd8d..dc95633 100644 --- a/libmatemixer/matemixer-backend-module.h +++ b/libmatemixer/matemixer-backend-module.h @@ -21,7 +21,7 @@ #include <glib.h> #include <glib-object.h> -#include <libmatemixer/matemixer-enums.h> +#include "matemixer-enums.h" G_BEGIN_DECLS diff --git a/libmatemixer/matemixer-backend.c b/libmatemixer/matemixer-backend.c index fab0883..b8177e3 100644 --- a/libmatemixer/matemixer-backend.c +++ b/libmatemixer/matemixer-backend.c @@ -20,16 +20,16 @@ #include <glib-object.h> #include "matemixer-backend.h" -#include "matemixer-backend-private.h" +#include "matemixer-device.h" #include "matemixer-enums.h" #include "matemixer-enum-types.h" #include "matemixer-stream.h" +#include "matemixer-stream-control.h" +#include "matemixer-stored-control.h" struct _MateMixerBackendPrivate { - GList *devices; - GList *streams; - GList *stored_streams; + GHashTable *devices; MateMixerStream *default_input; MateMixerStream *default_output; MateMixerState state; @@ -51,8 +51,8 @@ enum { DEVICE_REMOVED, STREAM_ADDED, STREAM_REMOVED, - STORED_STREAM_ADDED, - STORED_STREAM_REMOVED, + STORED_CONTROL_ADDED, + STORED_CONTROL_REMOVED, N_SIGNALS }; @@ -72,22 +72,19 @@ static void mate_mixer_backend_set_property (GObject *object, GParamSpec *pspec); static void mate_mixer_backend_dispose (GObject *object); +static void mate_mixer_backend_finalize (GObject *object); G_DEFINE_ABSTRACT_TYPE (MateMixerBackend, mate_mixer_backend, G_TYPE_OBJECT) -static void device_added (MateMixerBackend *backend, const gchar *name); -static void device_removed (MateMixerBackend *backend, const gchar *name); +static void device_added (MateMixerBackend *backend, + const gchar *name); +static void device_removed (MateMixerBackend *backend, + const gchar *name); -static void device_stream_added (MateMixerDevice *device, - const gchar *name); -static void device_stream_removed (MateMixerDevice *device, - const gchar *name); - -static void free_devices (MateMixerBackend *backend); -static void free_streams (MateMixerBackend *backend); -static void free_stored_streams (MateMixerBackend *backend); -static void stream_removed (MateMixerBackend *backend, - const gchar *name); +static void device_stream_added (MateMixerBackend *backend, + const gchar *name); +static void device_stream_removed (MateMixerBackend *backend, + const gchar *name); static void mate_mixer_backend_class_init (MateMixerBackendClass *klass) @@ -96,6 +93,7 @@ mate_mixer_backend_class_init (MateMixerBackendClass *klass) object_class = G_OBJECT_CLASS (klass); object_class->dispose = mate_mixer_backend_dispose; + object_class->finalize = mate_mixer_backend_finalize; object_class->get_property = mate_mixer_backend_get_property; object_class->set_property = mate_mixer_backend_set_property; @@ -129,7 +127,7 @@ mate_mixer_backend_class_init (MateMixerBackendClass *klass) signals[DEVICE_ADDED] = g_signal_new ("device-added", G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, + G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MateMixerBackendClass, device_added), NULL, NULL, @@ -141,7 +139,7 @@ mate_mixer_backend_class_init (MateMixerBackendClass *klass) signals[DEVICE_REMOVED] = g_signal_new ("device-removed", G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, + G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MateMixerBackendClass, device_removed), NULL, NULL, @@ -153,7 +151,7 @@ mate_mixer_backend_class_init (MateMixerBackendClass *klass) signals[STREAM_ADDED] = g_signal_new ("stream-added", G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, + G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MateMixerBackendClass, stream_added), NULL, NULL, @@ -165,7 +163,7 @@ mate_mixer_backend_class_init (MateMixerBackendClass *klass) signals[STREAM_REMOVED] = g_signal_new ("stream-removed", G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, + G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MateMixerBackendClass, stream_removed), NULL, NULL, @@ -174,11 +172,11 @@ mate_mixer_backend_class_init (MateMixerBackendClass *klass) 1, G_TYPE_STRING); - signals[STORED_STREAM_ADDED] = - g_signal_new ("stored-stream-added", + signals[STORED_CONTROL_ADDED] = + g_signal_new ("stored-control-added", G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendClass, stored_stream_added), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MateMixerBackendClass, stored_control_added), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -186,11 +184,11 @@ mate_mixer_backend_class_init (MateMixerBackendClass *klass) 1, G_TYPE_STRING); - signals[STORED_STREAM_REMOVED] = - g_signal_new ("stored-stream-removed", + signals[STORED_CONTROL_REMOVED] = + g_signal_new ("stored-control-removed", G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerBackendClass, stored_stream_removed), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MateMixerBackendClass, stored_control_removed), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -215,11 +213,9 @@ mate_mixer_backend_get_property (GObject *object, case PROP_STATE: g_value_set_enum (value, backend->priv->state); break; - case PROP_DEFAULT_INPUT_STREAM: g_value_set_object (value, backend->priv->default_input); break; - case PROP_DEFAULT_OUTPUT_STREAM: g_value_set_object (value, backend->priv->default_output); break; @@ -244,7 +240,6 @@ mate_mixer_backend_set_property (GObject *object, case PROP_DEFAULT_INPUT_STREAM: mate_mixer_backend_set_default_input_stream (backend, g_value_get_object (value)); break; - case PROP_DEFAULT_OUTPUT_STREAM: mate_mixer_backend_set_default_output_stream (backend, g_value_get_object (value)); break; @@ -262,10 +257,11 @@ mate_mixer_backend_init (MateMixerBackend *backend) MATE_MIXER_TYPE_BACKEND, MateMixerBackendPrivate); - g_signal_connect (G_OBJECT (backend), - "device-added", - G_CALLBACK (free_devices), - NULL); + backend->priv->devices = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + g_signal_connect (G_OBJECT (backend), "device-added", G_CALLBACK (device_added), @@ -273,32 +269,8 @@ mate_mixer_backend_init (MateMixerBackend *backend) g_signal_connect (G_OBJECT (backend), "device-removed", - G_CALLBACK (free_devices), - NULL); - g_signal_connect (G_OBJECT (backend), - "device-removed", G_CALLBACK (device_removed), NULL); - - g_signal_connect (G_OBJECT (backend), - "stream-added", - G_CALLBACK (free_streams), - NULL); - g_signal_connect (G_OBJECT (backend), - "stream-removed", - G_CALLBACK (free_streams), - NULL); - - g_signal_connect (G_OBJECT (backend), - "stored-stream-added", - G_CALLBACK (free_stored_streams), - NULL); - g_signal_connect (G_OBJECT (backend), - "stored-stream-removed", - G_CALLBACK (free_stored_streams), - NULL); - - // XXX also free when changing state } static void @@ -308,18 +280,28 @@ mate_mixer_backend_dispose (GObject *object) backend = MATE_MIXER_BACKEND (object); - free_devices (backend); - free_streams (backend); - free_stored_streams (backend); - g_clear_object (&backend->priv->default_input); g_clear_object (&backend->priv->default_output); + g_hash_table_remove_all (backend->priv->devices); + G_OBJECT_CLASS (mate_mixer_backend_parent_class)->dispose (object); } +static void +mate_mixer_backend_finalize (GObject *object) +{ + MateMixerBackend *backend; + + backend = MATE_MIXER_BACKEND (object); + + g_hash_table_unref (backend->priv->devices); + + G_OBJECT_CLASS (mate_mixer_backend_parent_class)->finalize (object); +} + void -mate_mixer_backend_set_data (MateMixerBackend *backend, MateMixerBackendData *data) +mate_mixer_backend_set_app_info (MateMixerBackend *backend, MateMixerAppInfo *info) { MateMixerBackendClass *klass; @@ -327,8 +309,21 @@ mate_mixer_backend_set_data (MateMixerBackend *backend, MateMixerBackendData *da klass = MATE_MIXER_BACKEND_GET_CLASS (backend); - if (klass->set_data != NULL) - klass->set_data (backend, data); + if (klass->set_app_info != NULL) + klass->set_app_info (backend, info); +} + +void +mate_mixer_backend_set_server_address (MateMixerBackend *backend, const gchar *address) +{ + MateMixerBackendClass *klass; + + g_return_if_fail (MATE_MIXER_IS_BACKEND (backend)); + + klass = MATE_MIXER_BACKEND_GET_CLASS (backend); + + if (klass->set_server_address != NULL) + klass->set_server_address (backend, address); } gboolean @@ -369,72 +364,117 @@ mate_mixer_backend_get_flags (MateMixerBackend *backend) return backend->priv->flags; } -const GList * -mate_mixer_backend_list_devices (MateMixerBackend *backend) +MateMixerDevice * +mate_mixer_backend_get_device (MateMixerBackend *backend, const gchar *name) { + const GList *list; + g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); + g_return_val_if_fail (name != NULL, NULL); - if (backend->priv->devices == NULL) { - MateMixerBackendClass *klass; + list = mate_mixer_backend_list_devices (backend); + while (list != NULL) { + MateMixerDevice *device = MATE_MIXER_DEVICE (list->data); - klass = MATE_MIXER_BACKEND_GET_CLASS (backend); + if (strcmp (name, mate_mixer_device_get_name (device)) == 0) + return device; - if (klass->list_devices != NULL) - backend->priv->devices = klass->list_devices (backend); + list = list->next; } - - return backend->priv->devices; + return NULL; } -const GList * -mate_mixer_backend_list_streams (MateMixerBackend *backend) +MateMixerStream * +mate_mixer_backend_get_stream (MateMixerBackend *backend, const gchar *name) { + const GList *list; + g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); + g_return_val_if_fail (name != NULL, NULL); - if (backend->priv->streams == NULL) { - MateMixerBackendClass *klass; + list = mate_mixer_backend_list_streams (backend); + while (list != NULL) { + MateMixerStream *stream = MATE_MIXER_STREAM (list->data); - klass = MATE_MIXER_BACKEND_GET_CLASS (backend); + if (strcmp (name, mate_mixer_stream_get_name (stream)) == 0) + return stream; - if (klass->list_streams != NULL) - backend->priv->streams = klass->list_streams (backend); + list = list->next; } + return NULL; +} + +MateMixerStoredControl * +mate_mixer_backend_get_stored_control (MateMixerBackend *backend, const gchar *name) +{ + const GList *list; + + g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); + g_return_val_if_fail (name != NULL, NULL); + + list = mate_mixer_backend_list_stored_controls (backend); + while (list != NULL) { + MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (list->data); - return backend->priv->streams; + if (strcmp (name, mate_mixer_stream_control_get_name (control)) == 0) + return MATE_MIXER_STORED_CONTROL (control); + + list = list->next; + } + return NULL; } const GList * -mate_mixer_backend_list_stored_streams (MateMixerBackend *backend) +mate_mixer_backend_list_devices (MateMixerBackend *backend) { + MateMixerBackendClass *klass; + g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); - if (backend->priv->stored_streams == NULL) { - MateMixerBackendClass *klass; + klass = MATE_MIXER_BACKEND_GET_CLASS (backend); - klass = MATE_MIXER_BACKEND_GET_CLASS (backend); + if G_LIKELY (klass->list_devices != NULL) + return klass->list_devices (backend); - if (klass->list_stored_streams != NULL) - backend->priv->stored_streams = klass->list_stored_streams (backend); - } + return NULL; +} + +const GList * +mate_mixer_backend_list_streams (MateMixerBackend *backend) +{ + MateMixerBackendClass *klass; + + g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); - return backend->priv->stored_streams; + klass = MATE_MIXER_BACKEND_GET_CLASS (backend); + + if G_LIKELY (klass->list_streams != NULL) + return klass->list_streams (backend); + + return NULL; } -MateMixerStream * -mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend) +const GList * +mate_mixer_backend_list_stored_controls (MateMixerBackend *backend) { - MateMixerStream *stream = NULL; + MateMixerBackendClass *klass; g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); - g_object_get (G_OBJECT (backend), - "default-input-stream", &stream, - NULL); + klass = MATE_MIXER_BACKEND_GET_CLASS (backend); - if (stream != NULL) - g_object_unref (stream); + if (klass->list_stored_controls != NULL) + return klass->list_stored_controls (backend); - return stream; + return NULL; +} + +MateMixerStream * +mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend) +{ + g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); + + return backend->priv->default_input; } gboolean @@ -462,18 +502,9 @@ mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, MateMixerStream * mate_mixer_backend_get_default_output_stream (MateMixerBackend *backend) { - MateMixerStream *stream = NULL; - g_return_val_if_fail (MATE_MIXER_IS_BACKEND (backend), NULL); - g_object_get (G_OBJECT (backend), - "default-output-stream", &stream, - NULL); - - if (stream != NULL) - g_object_unref (stream); - - return stream; + return backend->priv->default_output; } gboolean @@ -503,87 +534,97 @@ device_added (MateMixerBackend *backend, const gchar *name) { MateMixerDevice *device; - // device = mate_mixer_backend_get_device (backend, name); + device = mate_mixer_backend_get_device (backend, name); + if G_UNLIKELY (device == NULL) { + g_warn_if_reached (); + return; + } -/* - g_signal_connect (G_OBJECT (device), - "stream-added", - G_CALLBACK (device_stream_added)); - */ + /* Keep the device in a hash table as it won't be possible to retrieve + * it when the remove signal is received */ + g_hash_table_insert (backend->priv->devices, + g_strdup (name), + g_object_ref (device)); + + /* Connect to the stream signals from devices so we can forward them on + * the backend */ + g_signal_connect_swapped (G_OBJECT (device), + "stream-added", + G_CALLBACK (device_stream_added), + backend); + g_signal_connect_swapped (G_OBJECT (device), + "stream-removed", + G_CALLBACK (device_stream_removed), + backend); } static void device_removed (MateMixerBackend *backend, const gchar *name) { + MateMixerDevice *device; + + device = g_hash_table_lookup (backend->priv->devices, name); + if G_UNLIKELY (device == NULL) { + g_warn_if_reached (); + return; + } + + g_signal_handlers_disconnect_by_func (G_OBJECT (device), + G_CALLBACK (device_stream_added), + backend); + g_signal_handlers_disconnect_by_func (G_OBJECT (device), + G_CALLBACK (device_stream_removed), + backend); + + g_hash_table_remove (backend->priv->devices, name); } static void -device_stream_added (MateMixerDevice *device, const gchar *name) +device_stream_added (MateMixerBackend *backend, const gchar *name) { - g_signal_emit (G_OBJECT (device), + g_signal_emit (G_OBJECT (backend), signals[STREAM_ADDED], 0, name); } static void -device_stream_removed (MateMixerDevice *device, const gchar *name) +device_stream_removed (MateMixerBackend *backend, const gchar *name) { - g_signal_emit (G_OBJECT (device), + g_signal_emit (G_OBJECT (backend), signals[STREAM_REMOVED], 0, name); } -static void -free_devices (MateMixerBackend *backend) -{ - if (backend->priv->devices == NULL) - return; - - g_list_free_full (backend->priv->devices, g_object_unref); - - backend->priv->devices = NULL; -} - -static void -free_streams (MateMixerBackend *backend) +/* Protected functions */ +void +_mate_mixer_backend_set_state (MateMixerBackend *backend, MateMixerState state) { - if (backend->priv->streams == NULL) - return; - - g_list_free_full (backend->priv->streams, g_object_unref); - - backend->priv->streams = NULL; -} + g_return_if_fail (MATE_MIXER_IS_BACKEND (backend)); -static void -free_stored_streams (MateMixerBackend *backend) -{ - if (backend->priv->stored_streams == NULL) + if (backend->priv->state == state) return; - g_list_free_full (backend->priv->stored_streams, g_object_unref); + backend->priv->state = state; - backend->priv->stored_streams = NULL; + g_object_notify_by_pspec (G_OBJECT (backend), properties[PROP_STATE]); } -/* Protected */ void -_mate_mixer_backend_set_state (MateMixerBackend *backend, MateMixerState state) +_mate_mixer_backend_set_flags (MateMixerBackend *backend, MateMixerBackendFlags flags) { - if (backend->priv->state == state) - return; - - backend->priv->state = state; + g_return_if_fail (MATE_MIXER_IS_BACKEND (backend)); - g_object_notify_by_pspec (G_OBJECT (backend), properties[PROP_STATE]); + backend->priv->flags = flags; } void _mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, MateMixerStream *stream) { + g_return_if_fail (MATE_MIXER_IS_BACKEND (backend)); + if (backend->priv->default_input == stream) return; @@ -598,13 +639,16 @@ _mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, g_debug ("Default input stream changed to %s", (stream != NULL) ? mate_mixer_stream_get_name (stream) : "none"); - g_object_notify_by_pspec (G_OBJECT (backend), properties[PROP_DEFAULT_INPUT_STREAM]); + g_object_notify_by_pspec (G_OBJECT (backend), + properties[PROP_DEFAULT_INPUT_STREAM]); } void _mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, MateMixerStream *stream) { + g_return_if_fail (MATE_MIXER_IS_BACKEND (backend)); + if (backend->priv->default_output == stream) return; @@ -619,5 +663,6 @@ _mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, g_debug ("Default output stream changed to %s", (stream != NULL) ? mate_mixer_stream_get_name (stream) : "none"); - g_object_notify_by_pspec (G_OBJECT (backend), properties[PROP_DEFAULT_OUTPUT_STREAM]); + g_object_notify_by_pspec (G_OBJECT (backend), + properties[PROP_DEFAULT_OUTPUT_STREAM]); } diff --git a/libmatemixer/matemixer-backend.h b/libmatemixer/matemixer-backend.h index 1c918c9..41ffb40 100644 --- a/libmatemixer/matemixer-backend.h +++ b/libmatemixer/matemixer-backend.h @@ -21,8 +21,8 @@ #include <glib.h> #include <glib-object.h> -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-types.h> +#include "matemixer-enums.h" +#include "matemixer-types.h" G_BEGIN_DECLS @@ -41,7 +41,6 @@ G_BEGIN_DECLS typedef struct _MateMixerBackend MateMixerBackend; typedef struct _MateMixerBackendClass MateMixerBackendClass; -typedef struct _MateMixerBackendData MateMixerBackendData; typedef struct _MateMixerBackendPrivate MateMixerBackendPrivate; struct _MateMixerBackend @@ -57,67 +56,81 @@ struct _MateMixerBackendClass GObjectClass parent_class; /*< private >*/ - void (*set_data) (MateMixerBackend *backend, - MateMixerBackendData *data); + void (*set_app_info) (MateMixerBackend *backend, + MateMixerAppInfo *info); + void (*set_server_address) (MateMixerBackend *backend, + const gchar *address); - gboolean (*open) (MateMixerBackend *backend); - void (*close) (MateMixerBackend *backend); + gboolean (*open) (MateMixerBackend *backend); + void (*close) (MateMixerBackend *backend); - GList *(*list_devices) (MateMixerBackend *backend); - GList *(*list_streams) (MateMixerBackend *backend); - GList *(*list_stored_streams) (MateMixerBackend *backend); + const GList *(*list_devices) (MateMixerBackend *backend); + const GList *(*list_streams) (MateMixerBackend *backend); + const GList *(*list_stored_controls) (MateMixerBackend *backend); - gboolean (*set_default_input_stream) (MateMixerBackend *backend, - MateMixerStream *stream); - gboolean (*set_default_output_stream) (MateMixerBackend *backend, - MateMixerStream *stream); + gboolean (*set_default_input_stream) (MateMixerBackend *backend, + MateMixerStream *stream); + gboolean (*set_default_output_stream) (MateMixerBackend *backend, + MateMixerStream *stream); /* Signals */ - void (*device_added) (MateMixerBackend *backend, - const gchar *name); - void (*device_removed) (MateMixerBackend *backend, - const gchar *name); - void (*stream_added) (MateMixerBackend *backend, - const gchar *name); - void (*stream_removed) (MateMixerBackend *backend, - const gchar *name); - void (*stored_stream_added) (MateMixerBackend *backend, - const gchar *name); - void (*stored_stream_removed) (MateMixerBackend *backend, - const gchar *name); + void (*device_added) (MateMixerBackend *backend, + const gchar *name); + void (*device_removed) (MateMixerBackend *backend, + const gchar *name); + void (*stream_added) (MateMixerBackend *backend, + const gchar *name); + void (*stream_removed) (MateMixerBackend *backend, + const gchar *name); + void (*stored_control_added) (MateMixerBackend *backend, + const gchar *name); + void (*stored_control_removed) (MateMixerBackend *backend, + const gchar *name); }; -struct _MateMixerBackendData -{ - gchar *app_name; - gchar *app_id; - gchar *app_version; - gchar *app_icon; - gchar *server_address; -}; +GType mate_mixer_backend_get_type (void) G_GNUC_CONST; + +void mate_mixer_backend_set_app_info (MateMixerBackend *backend, + MateMixerAppInfo *info); +void mate_mixer_backend_set_server_address (MateMixerBackend *backend, + const gchar *address); + +gboolean mate_mixer_backend_open (MateMixerBackend *backend); +void mate_mixer_backend_close (MateMixerBackend *backend); + +MateMixerState mate_mixer_backend_get_state (MateMixerBackend *backend); +MateMixerBackendFlags mate_mixer_backend_get_flags (MateMixerBackend *backend); -GType mate_mixer_backend_get_type (void) G_GNUC_CONST; +MateMixerDevice * mate_mixer_backend_get_device (MateMixerBackend *backend, + const gchar *name); +MateMixerStream * mate_mixer_backend_get_stream (MateMixerBackend *backend, + const gchar *name); +MateMixerStoredControl *mate_mixer_backend_get_stored_control (MateMixerBackend *backend, + const gchar *name); -void mate_mixer_backend_set_data (MateMixerBackend *backend, - MateMixerBackendData *data); +const GList * mate_mixer_backend_list_devices (MateMixerBackend *backend); +const GList * mate_mixer_backend_list_streams (MateMixerBackend *backend); +const GList * mate_mixer_backend_list_stored_controls (MateMixerBackend *backend); -gboolean mate_mixer_backend_open (MateMixerBackend *backend); -void mate_mixer_backend_close (MateMixerBackend *backend); +MateMixerStream * mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend); +gboolean mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, + MateMixerStream *stream); -MateMixerState mate_mixer_backend_get_state (MateMixerBackend *backend); -MateMixerBackendFlags mate_mixer_backend_get_flags (MateMixerBackend *backend); +MateMixerStream * mate_mixer_backend_get_default_output_stream (MateMixerBackend *backend); +gboolean mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, + MateMixerStream *stream); -const GList * mate_mixer_backend_list_devices (MateMixerBackend *backend); -const GList * mate_mixer_backend_list_streams (MateMixerBackend *backend); -const GList * mate_mixer_backend_list_stored_streams (MateMixerBackend *backend); +/* Protected functions */ +void _mate_mixer_backend_set_state (MateMixerBackend *backend, + MateMixerState state); +void _mate_mixer_backend_set_flags (MateMixerBackend *backend, + MateMixerBackendFlags flags); -MateMixerStream * mate_mixer_backend_get_default_input_stream (MateMixerBackend *backend); -gboolean mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, - MateMixerStream *stream); +void _mate_mixer_backend_set_default_input_stream (MateMixerBackend *backend, + MateMixerStream *stream); -MateMixerStream * mate_mixer_backend_get_default_output_stream (MateMixerBackend *backend); -gboolean mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, - MateMixerStream *stream); +void _mate_mixer_backend_set_default_output_stream (MateMixerBackend *backend, + MateMixerStream *stream); G_END_DECLS diff --git a/libmatemixer/matemixer-client-stream.c b/libmatemixer/matemixer-client-stream.c deleted file mode 100644 index fc34622..0000000 --- a/libmatemixer/matemixer-client-stream.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include <glib.h> -#include <glib-object.h> - -#include "matemixer-client-stream.h" -#include "matemixer-enums.h" -#include "matemixer-enum-types.h" -#include "matemixer-stream.h" - -/** - * SECTION:matemixer-client-stream - * @short_description: Interface providing extra functionality for client streams - * @see_also: #MateMixerStream - * @include: libmatemixer/matemixer.h - * - * #MateMixerClientStream represents a special kind of stream, which belongs - * to a parent input or output stream. - * - * A typical example of a client stream is a stream provided by an application. - */ - -struct _MateMixerClientStreamPrivate -{ - gchar *app_name; - gchar *app_id; - gchar *app_version; - gchar *app_icon; - MateMixerStream *parent; - MateMixerClientStreamFlags client_flags; - MateMixerClientStreamRole client_role; -}; - -enum { - PROP_0, - PROP_CLIENT_FLAGS, - PROP_CLIENT_ROLE, - PROP_PARENT, - PROP_APP_NAME, - PROP_APP_ID, - PROP_APP_VERSION, - PROP_APP_ICON, - N_PROPERTIES -}; - -static GParamSpec *properties[N_PROPERTIES] = { NULL, }; - -static void mate_mixer_client_stream_class_init (MateMixerClientStreamClass *klass); - -static void mate_mixer_client_stream_init (MateMixerClientStream *client); - -static void mate_mixer_client_stream_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void mate_mixer_client_stream_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); - -static void mate_mixer_client_stream_dispose (GObject *object); -static void mate_mixer_client_stream_finalize (GObject *object); - -G_DEFINE_ABSTRACT_TYPE (MateMixerClientStream, mate_mixer_client_stream, MATE_MIXER_TYPE_STREAM) - -static void -mate_mixer_client_stream_class_init (MateMixerClientStreamClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = mate_mixer_client_stream_dispose; - object_class->finalize = mate_mixer_client_stream_finalize; - object_class->get_property = mate_mixer_client_stream_get_property; - object_class->set_property = mate_mixer_client_stream_set_property; - - properties[PROP_CLIENT_FLAGS] = - g_param_spec_flags ("client-flags", - "Client flags", - "Capability flags of the client stream", - MATE_MIXER_TYPE_CLIENT_STREAM_FLAGS, - MATE_MIXER_CLIENT_STREAM_NO_FLAGS, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_CLIENT_ROLE] = - g_param_spec_enum ("role", - "Role", - "Role of the client stream", - MATE_MIXER_TYPE_CLIENT_STREAM_ROLE, - MATE_MIXER_CLIENT_STREAM_ROLE_NONE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_PARENT] = - g_param_spec_object ("parent", - "Parent", - "Parent stream of the client stream", - MATE_MIXER_TYPE_STREAM, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_APP_NAME] = - g_param_spec_string ("app-name", - "App name", - "Name of the client stream application", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_APP_ID] = - g_param_spec_string ("app-id", - "App ID", - "Identifier of the client stream application", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_APP_VERSION] = - g_param_spec_string ("app-version", - "App version", - "Version of the client stream application", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_APP_ICON] = - g_param_spec_string ("app-icon", - "App icon", - "Icon name of the client stream application", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); -} - -static void -mate_mixer_client_stream_init (MateMixerClientStream *client) -{ - client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, - MATE_MIXER_TYPE_CLIENT_STREAM, - MateMixerClientStreamPrivate); -} - -static void -mate_mixer_client_stream_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ -} - -static void -mate_mixer_client_stream_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ -} - -static void -mate_mixer_client_stream_dispose (GObject *object) -{ - MateMixerClientStream *client; - - client = MATE_MIXER_CLIENT_STREAM (object); - - g_clear_object (&client->priv->parent); - - G_OBJECT_CLASS (mate_mixer_client_stream_parent_class)->dispose (object); -} - -static void -mate_mixer_client_stream_finalize (GObject *object) -{ - MateMixerClientStream *client; - - client = MATE_MIXER_CLIENT_STREAM (object); - - g_free (client->priv->app_name); - g_free (client->priv->app_id); - g_free (client->priv->app_version); - g_free (client->priv->app_icon); - - G_OBJECT_CLASS (mate_mixer_client_stream_parent_class)->finalize (object); -} - -/** - * mate_mixer_client_stream_get_flags: - * @client: a #MateMixerClientStream - * - */ -MateMixerClientStreamFlags -mate_mixer_client_stream_get_flags (MateMixerClientStream *client) -{ - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_NO_FLAGS); - - return client->priv->client_flags; -} - -/** - * mate_mixer_client_stream_get_role: - * @client: a #MateMixerClientStream - * - */ -MateMixerClientStreamRole -mate_mixer_client_stream_get_role (MateMixerClientStream *client) -{ - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_ROLE_NONE); - - return client->priv->client_role; -} - -/** - * mate_mixer_client_stream_get_parent: - * @client: a #MateMixerClientStream - * - * Gets the parent stream of the client stream. - * - * Returns: a #MateMixerStream or %NULL if the parent stream is not known. - */ -MateMixerStream * -mate_mixer_client_stream_get_parent (MateMixerClientStream *client) -{ - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); - - return client->priv->parent; -} - -/** - * mate_mixer_client_stream_set_parent: - * @client: a #MateMixerClientStream - * @parent: a #MateMixerStream - * - * Changes the parent stream of the client stream. The parent stream must be a - * non-client input or output stream. - * - * Returns: %TRUE on success or %FALSE on failure. - */ -gboolean -mate_mixer_client_stream_set_parent (MateMixerClientStream *client, MateMixerStream *parent) -{ - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_STREAM (parent), FALSE); - - if (client->priv->parent != parent) { - MateMixerClientStreamClass *klass; - - klass = MATE_MIXER_CLIENT_STREAM_GET_CLASS (client); - - if (klass->set_parent == NULL || - klass->set_parent (client, parent) == FALSE) - return FALSE; - - if (client->priv->parent != NULL) - g_object_unref (client->priv->parent); - - client->priv->parent = g_object_ref (parent); - } - - return TRUE; -} - -/** - * mate_mixer_client_stream_remove: - * @client: a #MateMixerClientStream - * - * Removes the client stream. - * - * Returns: %TRUE on success or %FALSE on failure. - */ -gboolean -mate_mixer_client_stream_remove (MateMixerClientStream *client) -{ - MateMixerClientStreamClass *klass; - - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), FALSE); - - klass = MATE_MIXER_CLIENT_STREAM_GET_CLASS (client); - - if (klass->remove != NULL) - return klass->remove (client); - - return FALSE; -} - -/** - * mate_mixer_client_stream_get_app_name: - * @client: a #MateMixerClientStream - * - * Gets the name of the application in case the stream is an application - * stream. - * - * Returns: a string on success, or %NULL if the stream is not an application - * stream or if the application does not provide a name. - */ -const gchar * -mate_mixer_client_stream_get_app_name (MateMixerClientStream *client) -{ - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); - - return client->priv->app_name; -} - -/** - * mate_mixer_client_stream_get_app_id: - * @client: a #MateMixerClientStream - * - * Gets the identifier (e.g. org.example.app) of the application in case the - * stream is an application stream. - * - * Returns: a string on success, or %NULL if the stream is not an application - * stream or if the application does not provide an identifier. - */ -const gchar * -mate_mixer_client_stream_get_app_id (MateMixerClientStream *client) -{ - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); - - return client->priv->app_id; -} - -/** - * mate_mixer_client_stream_get_app_version: - * @client: a #MateMixerClientStream - * - * Gets the version of the application in case the stream is an application - * stream. - * - * Returns: a string on success, or %NULL if the stream is not an application - * stream or if the application does not provide a version string. - */ -const gchar * -mate_mixer_client_stream_get_app_version (MateMixerClientStream *client) -{ - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); - - return client->priv->app_version; -} - -/** - * mate_mixer_client_stream_get_app_icon: - * @client: a #MateMixerClientStream - * - * Gets the XDG icon name of the application in case the stream is an - * application stream. - * - * Returns: a string on success, or %NULL if the stream is not an application - * stream or if the application does not provide an icon name. - */ -const gchar * -mate_mixer_client_stream_get_app_icon (MateMixerClientStream *client) -{ - g_return_val_if_fail (MATE_MIXER_IS_CLIENT_STREAM (client), NULL); - - return client->priv->app_icon; -} diff --git a/libmatemixer/matemixer-client-stream.h b/libmatemixer/matemixer-client-stream.h deleted file mode 100644 index 43ab3f0..0000000 --- a/libmatemixer/matemixer-client-stream.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MATEMIXER_CLIENT_STREAM_H -#define MATEMIXER_CLIENT_STREAM_H - -#include <glib.h> -#include <glib-object.h> - -#include <libmatemixer/matemixer-enums.h> -#include <libmatemixer/matemixer-types.h> - -G_BEGIN_DECLS - -#define MATE_MIXER_TYPE_CLIENT_STREAM \ - (mate_mixer_client_stream_get_type ()) -#define MATE_MIXER_CLIENT_STREAM(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_CLIENT_STREAM, MateMixerClientStream)) -#define MATE_MIXER_IS_CLIENT_STREAM(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_CLIENT_STREAM)) -#define MATE_MIXER_CLIENT_STREAM_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_CLIENT_STREAM, MateMixerClientStreamClass)) -#define MATE_MIXER_IS_CLIENT_STREAM_CLASS(k) \ - (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_CLIENT_STREAM)) -#define MATE_MIXER_CLIENT_STREAM_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_CLIENT_STREAM, MateMixerClientStreamClass)) - -typedef struct _MateMixerClientStreamClass MateMixerClientStreamClass; -typedef struct _MateMixerClientStreamPrivate MateMixerClientStreamPrivate; - -struct _MateMixerClientStream -{ - GObject *parent; - - /*< private >*/ - MateMixerClientStreamPrivate *priv; -}; - -struct _MateMixerClientStreamClass -{ - GTypeInterface parent_iface; - - /*< private >*/ - MateMixerClientStreamFlags (*get_flags) (MateMixerClientStream *client); - MateMixerClientStreamRole (*get_role) (MateMixerClientStream *client); - - MateMixerStream *(*get_parent) (MateMixerClientStream *client); - gboolean (*set_parent) (MateMixerClientStream *client, - MateMixerStream *stream); - - gboolean (*remove) (MateMixerClientStream *client); - - const gchar *(*get_app_name) (MateMixerClientStream *client); - const gchar *(*get_app_id) (MateMixerClientStream *client); - const gchar *(*get_app_version) (MateMixerClientStream *client); - const gchar *(*get_app_icon) (MateMixerClientStream *client); -}; - -GType mate_mixer_client_stream_get_type (void) G_GNUC_CONST; - -MateMixerClientStreamFlags mate_mixer_client_stream_get_flags (MateMixerClientStream *client); -MateMixerClientStreamRole mate_mixer_client_stream_get_role (MateMixerClientStream *client); - -MateMixerStream * mate_mixer_client_stream_get_parent (MateMixerClientStream *client); -gboolean mate_mixer_client_stream_set_parent (MateMixerClientStream *client, - MateMixerStream *parent); - -gboolean mate_mixer_client_stream_remove (MateMixerClientStream *client); - -const gchar * mate_mixer_client_stream_get_app_name (MateMixerClientStream *client); -const gchar * mate_mixer_client_stream_get_app_id (MateMixerClientStream *client); -const gchar * mate_mixer_client_stream_get_app_version (MateMixerClientStream *client); -const gchar * mate_mixer_client_stream_get_app_icon (MateMixerClientStream *client); - -G_END_DECLS - -#endif /* MATEMIXER_CLIENT_STREAM_H */ diff --git a/libmatemixer/matemixer-context.c b/libmatemixer/matemixer-context.c index a485a06..bb28b18 100644 --- a/libmatemixer/matemixer-context.c +++ b/libmatemixer/matemixer-context.c @@ -22,7 +22,6 @@ #include "matemixer.h" #include "matemixer-backend.h" #include "matemixer-backend-module.h" -#include "matemixer-client-stream.h" #include "matemixer-context.h" #include "matemixer-enums.h" #include "matemixer-enum-types.h" @@ -38,9 +37,10 @@ struct _MateMixerContextPrivate { gboolean backend_chosen; + gchar *server_address; MateMixerState state; MateMixerBackend *backend; - MateMixerBackendData backend_data; + MateMixerAppInfo *app_info; MateMixerBackendType backend_type; MateMixerBackendModule *module; }; @@ -65,8 +65,8 @@ enum { DEVICE_REMOVED, STREAM_ADDED, STREAM_REMOVED, - STORED_STREAM_ADDED, - STORED_STREAM_REMOVED, + STORED_CONTROL_ADDED, + STORED_CONTROL_REMOVED, N_SIGNALS }; @@ -107,10 +107,10 @@ static void on_backend_stream_removed (MateMixerBackend *backe const gchar *name, MateMixerContext *context); -static void on_backend_stored_stream_added (MateMixerBackend *backend, +static void on_backend_stored_control_added (MateMixerBackend *backend, const gchar *name, MateMixerContext *context); -static void on_backend_stored_stream_removed (MateMixerBackend *backend, +static void on_backend_stored_control_removed (MateMixerBackend *backend, const gchar *name, MateMixerContext *context); @@ -303,17 +303,17 @@ mate_mixer_context_class_init (MateMixerContextClass *klass) G_TYPE_STRING); /** - * MateMixerContext::stored-stream-added: + * MateMixerContext::stored-control-added: * @context: a #MateMixerContext - * @name: name of the added stored stream + * @name: name of the added stored control * - * The signal is emitted each time a stored stream is created. + * The signal is emitted each time a stored control is created. */ - signals[STORED_STREAM_ADDED] = + signals[STORED_CONTROL_ADDED] = g_signal_new ("stored-control-added", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerContextClass, stored_stream_added), + G_STRUCT_OFFSET (MateMixerContextClass, stored_control_added), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -322,17 +322,17 @@ mate_mixer_context_class_init (MateMixerContextClass *klass) G_TYPE_STRING); /** - * MateMixerContext::stream-removed: + * MateMixerContext::stored-control-removed: * @context: a #MateMixerContext - * @name: name of the removed stream + * @name: name of the removed control * - * The signal is emitted each time a stream is removed. + * The signal is emitted each time a control is removed. */ - signals[STORED_STREAM_REMOVED] = - g_signal_new ("stored-stream-removed", + signals[STORED_CONTROL_REMOVED] = + g_signal_new ("stored-control-removed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerContextClass, stored_stream_removed), + G_STRUCT_OFFSET (MateMixerContextClass, stored_control_removed), NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -355,19 +355,19 @@ mate_mixer_context_get_property (GObject *object, switch (param_id) { case PROP_APP_NAME: - g_value_set_string (value, context->priv->backend_data.app_name); + g_value_set_string (value, mate_mixer_app_info_get_name (context->priv->app_info)); break; case PROP_APP_ID: - g_value_set_string (value, context->priv->backend_data.app_id); + g_value_set_string (value, mate_mixer_app_info_get_id (context->priv->app_info)); break; case PROP_APP_VERSION: - g_value_set_string (value, context->priv->backend_data.app_version); + g_value_set_string (value, mate_mixer_app_info_get_version (context->priv->app_info)); break; case PROP_APP_ICON: - g_value_set_string (value, context->priv->backend_data.app_icon); + g_value_set_string (value, mate_mixer_app_info_get_icon (context->priv->app_info)); break; case PROP_SERVER_ADDRESS: - g_value_set_string (value, context->priv->backend_data.server_address); + g_value_set_string (value, context->priv->server_address); break; case PROP_STATE: g_value_set_enum (value, context->priv->state); @@ -378,6 +378,7 @@ mate_mixer_context_get_property (GObject *object, case PROP_DEFAULT_OUTPUT_STREAM: g_value_set_object (value, mate_mixer_context_get_default_output_stream (context)); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -416,6 +417,7 @@ mate_mixer_context_set_property (GObject *object, case PROP_DEFAULT_OUTPUT_STREAM: mate_mixer_context_set_default_output_stream (context, g_value_get_object (value)); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -428,12 +430,18 @@ mate_mixer_context_init (MateMixerContext *context) context->priv = G_TYPE_INSTANCE_GET_PRIVATE (context, MATE_MIXER_TYPE_CONTEXT, MateMixerContextPrivate); + + context->priv->app_info = _mate_mixer_app_info_new (); } static void mate_mixer_context_dispose (GObject *object) { - close_context (MATE_MIXER_CONTEXT (object)); + MateMixerContext *context; + + context = MATE_MIXER_CONTEXT (object); + + close_context (context); G_OBJECT_CLASS (mate_mixer_context_parent_class)->dispose (object); } @@ -445,11 +453,9 @@ mate_mixer_context_finalize (GObject *object) context = MATE_MIXER_CONTEXT (object); - g_free (context->priv->backend_data.app_name); - g_free (context->priv->backend_data.app_id); - g_free (context->priv->backend_data.app_version); - g_free (context->priv->backend_data.app_icon); - g_free (context->priv->backend_data.server_address); + _mate_mixer_app_info_free (context->priv->app_info); + + g_free (context->priv->server_address); G_OBJECT_CLASS (mate_mixer_context_parent_class)->finalize (object); } @@ -504,7 +510,7 @@ mate_mixer_context_set_backend_type (MateMixerContext *context, context->priv->state == MATE_MIXER_STATE_READY) return FALSE; - modules = _mate_mixer_get_modules (); + modules = _mate_mixer_list_modules (); while (modules != NULL) { module = MATE_MIXER_BACKEND_MODULE (modules->data); @@ -542,13 +548,11 @@ mate_mixer_context_set_app_name (MateMixerContext *context, const gchar *app_nam context->priv->state == MATE_MIXER_STATE_READY) return FALSE; - if (g_strcmp0 (context->priv->backend_data.app_name, app_name) != 0) { - g_free (context->priv->backend_data.app_name); + _mate_mixer_app_info_set_name (context->priv->app_info, app_name); - context->priv->backend_data.app_name = g_strdup (app_name); + g_object_notify_by_pspec (G_OBJECT (context), + properties[PROP_APP_NAME]); - g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_APP_NAME]); - } return TRUE; } @@ -575,13 +579,11 @@ mate_mixer_context_set_app_id (MateMixerContext *context, const gchar *app_id) context->priv->state == MATE_MIXER_STATE_READY) return FALSE; - if (g_strcmp0 (context->priv->backend_data.app_id, app_id) != 0) { - g_free (context->priv->backend_data.app_id); + _mate_mixer_app_info_set_id (context->priv->app_info, app_id); - context->priv->backend_data.app_id = g_strdup (app_id); + g_object_notify_by_pspec (G_OBJECT (context), + properties[PROP_APP_ID]); - g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_APP_ID]); - } return TRUE; } @@ -608,13 +610,11 @@ mate_mixer_context_set_app_version (MateMixerContext *context, const gchar *app_ context->priv->state == MATE_MIXER_STATE_READY) return FALSE; - if (g_strcmp0 (context->priv->backend_data.app_version, app_version) != 0) { - g_free (context->priv->backend_data.app_version); + _mate_mixer_app_info_set_version (context->priv->app_info, app_version); - context->priv->backend_data.app_version = g_strdup (app_version); + g_object_notify_by_pspec (G_OBJECT (context), + properties[PROP_APP_VERSION]); - g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_APP_VERSION]); - } return TRUE; } @@ -641,13 +641,11 @@ mate_mixer_context_set_app_icon (MateMixerContext *context, const gchar *app_ico context->priv->state == MATE_MIXER_STATE_READY) return FALSE; - if (g_strcmp0 (context->priv->backend_data.app_icon, app_icon) != 0) { - g_free (context->priv->backend_data.app_icon); + _mate_mixer_app_info_set_icon (context->priv->app_info, app_icon); - context->priv->backend_data.app_icon = g_strdup (app_icon); + g_object_notify_by_pspec (G_OBJECT (context), + properties[PROP_APP_ICON]); - g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_APP_ICON]); - } return TRUE; } @@ -675,13 +673,13 @@ mate_mixer_context_set_server_address (MateMixerContext *context, const gchar *a context->priv->state == MATE_MIXER_STATE_READY) return FALSE; - if (g_strcmp0 (context->priv->backend_data.server_address, address) != 0) { - g_free (context->priv->backend_data.server_address); + g_free (context->priv->server_address); - context->priv->backend_data.server_address = g_strdup (address); + context->priv->server_address = g_strdup (address); + + g_object_notify_by_pspec (G_OBJECT (context), + properties[PROP_SERVER_ADDRESS]); - g_object_notify_by_pspec (G_OBJECT (context), properties[PROP_SERVER_ADDRESS]); - } return TRUE; } @@ -727,7 +725,7 @@ mate_mixer_context_open (MateMixerContext *context) /* We are going to choose the first backend to try. It will be either the one * specified by the application or the one with the highest priority */ - modules = _mate_mixer_get_modules (); + modules = _mate_mixer_list_modules (); if (context->priv->backend_type != MATE_MIXER_BACKEND_UNKNOWN) { while (modules != NULL) { @@ -758,7 +756,8 @@ mate_mixer_context_open (MateMixerContext *context) context->priv->module = g_object_ref (module); context->priv->backend = g_object_new (info->g_type, NULL); - mate_mixer_backend_set_data (context->priv->backend, &context->priv->backend_data); + mate_mixer_backend_set_app_info (context->priv->backend, context->priv->app_info); + mate_mixer_backend_set_server_address (context->priv->backend, context->priv->server_address); g_debug ("Trying to open backend %s", info->name); @@ -848,21 +847,13 @@ mate_mixer_context_get_state (MateMixerContext *context) MateMixerDevice * mate_mixer_context_get_device (MateMixerContext *context, const gchar *name) { - GList *list; - g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); g_return_val_if_fail (name != NULL, NULL); - list = (GList *) mate_mixer_context_list_devices (context); - while (list != NULL) { - MateMixerDevice *device = MATE_MIXER_DEVICE (list->data); - - if (strcmp (name, mate_mixer_device_get_name (device)) == 0) - return device; + if (context->priv->state != MATE_MIXER_STATE_READY) + return NULL; - list = list->next; - } - return NULL; + return mate_mixer_backend_get_device (MATE_MIXER_BACKEND (context->priv->backend), name); } /** @@ -877,50 +868,34 @@ mate_mixer_context_get_device (MateMixerContext *context, const gchar *name) MateMixerStream * mate_mixer_context_get_stream (MateMixerContext *context, const gchar *name) { - GList *list; - g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); g_return_val_if_fail (name != NULL, NULL); - list = (GList *) mate_mixer_context_list_streams (context); - while (list != NULL) { - MateMixerStream *stream = MATE_MIXER_STREAM (list->data); - - if (strcmp (name, mate_mixer_stream_get_name (stream)) == 0) - return stream; + if (context->priv->state != MATE_MIXER_STATE_READY) + return NULL; - list = list->next; - } - return NULL; + return mate_mixer_backend_get_stream (MATE_MIXER_BACKEND (context->priv->backend), name); } /** - * mate_mixer_context_get_stored_stream: + * mate_mixer_context_get_stored_control: * @context: a #MateMixerContext - * @name: a stream name + * @name: a stored control name * - * Gets the stream with the given name. + * Gets the stored control with the given name. * - * Returns: a #MateMixerStream or %NULL if there is no such stream. + * Returns: a #MateMixerStoredControl or %NULL if there is no such control. */ -MateMixerStream * -mate_mixer_context_get_stored_stream (MateMixerContext *context, const gchar *name) +MateMixerStoredControl * +mate_mixer_context_get_stored_control (MateMixerContext *context, const gchar *name) { - GList *list; - g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); g_return_val_if_fail (name != NULL, NULL); - list = (GList *) mate_mixer_context_list_stored_streams (context); - while (list != NULL) { - MateMixerStream *stream = MATE_MIXER_STREAM (list->data); - - if (strcmp (name, mate_mixer_stream_get_name (stream)) == 0) - return stream; + if (context->priv->state != MATE_MIXER_STATE_READY) + return NULL; - list = list->next; - } - return NULL; + return mate_mixer_backend_get_stored_control (MATE_MIXER_BACKEND (context->priv->backend), name); } /** @@ -972,19 +947,19 @@ mate_mixer_context_list_streams (MateMixerContext *context) } /** - * mate_mixer_context_list_stored_streams: + * mate_mixer_context_list_stored_controls: * @context: a #MateMixerContext * */ const GList * -mate_mixer_context_list_stored_streams (MateMixerContext *context) +mate_mixer_context_list_stored_controls (MateMixerContext *context) { g_return_val_if_fail (MATE_MIXER_IS_CONTEXT (context), NULL); if (context->priv->state != MATE_MIXER_STATE_READY) return NULL; - return mate_mixer_backend_list_stored_streams (MATE_MIXER_BACKEND (context->priv->backend)); + return mate_mixer_backend_list_stored_controls (MATE_MIXER_BACKEND (context->priv->backend)); } /** @@ -1028,11 +1003,7 @@ mate_mixer_context_set_default_input_stream (MateMixerContext *context, if (context->priv->state != MATE_MIXER_STATE_READY) return FALSE; - if (MATE_MIXER_IS_CLIENT_STREAM (stream)) { - g_warning ("Unable to set client stream as the default input stream"); - return FALSE; - } - if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_INPUT)) { + if (mate_mixer_stream_get_direction (stream) != MATE_MIXER_DIRECTION_INPUT) { g_warning ("Unable to set non-input stream as the default input stream"); return FALSE; } @@ -1081,11 +1052,7 @@ mate_mixer_context_set_default_output_stream (MateMixerContext *context, if (context->priv->state != MATE_MIXER_STATE_READY) return FALSE; - if (MATE_MIXER_IS_CLIENT_STREAM (stream)) { - g_warning ("Unable to set client stream as the default output stream"); - return FALSE; - } - if (!(mate_mixer_stream_get_flags (stream) & MATE_MIXER_STREAM_OUTPUT)) { + if (mate_mixer_stream_get_direction (stream) != MATE_MIXER_DIRECTION_OUTPUT) { g_warning ("Unable to set non-output stream as the default output stream"); return FALSE; } @@ -1231,23 +1198,23 @@ on_backend_stream_removed (MateMixerBackend *backend, } static void -on_backend_stored_stream_added (MateMixerBackend *backend, - const gchar *name, - MateMixerContext *context) +on_backend_stored_control_added (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context) { g_signal_emit (G_OBJECT (context), - signals[STORED_STREAM_ADDED], + signals[STORED_CONTROL_ADDED], 0, name); } static void -on_backend_stored_stream_removed (MateMixerBackend *backend, - const gchar *name, - MateMixerContext *context) +on_backend_stored_control_removed (MateMixerBackend *backend, + const gchar *name, + MateMixerContext *context) { g_signal_emit (G_OBJECT (context), - signals[STORED_STREAM_REMOVED], + signals[STORED_CONTROL_REMOVED], 0, name); } @@ -1276,7 +1243,7 @@ try_next_backend (MateMixerContext *context) const GList *modules; const MateMixerBackendInfo *info = NULL; - modules = _mate_mixer_get_modules (); + modules = _mate_mixer_list_modules (); while (modules != NULL) { if (context->priv->module == modules->data) { @@ -1301,7 +1268,8 @@ try_next_backend (MateMixerContext *context) context->priv->module = g_object_ref (module); context->priv->backend = g_object_new (info->g_type, NULL); - mate_mixer_backend_set_data (context->priv->backend, &context->priv->backend_data); + mate_mixer_backend_set_app_info (context->priv->backend, context->priv->app_info); + mate_mixer_backend_set_server_address (context->priv->backend, context->priv->server_address); g_debug ("Trying to open backend %s", info->name); @@ -1359,20 +1327,20 @@ change_state (MateMixerContext *context, MateMixerState state) G_CALLBACK (on_backend_stream_removed), context); g_signal_connect (G_OBJECT (context->priv->backend), - "stored-stream-added", - G_CALLBACK (on_backend_stored_stream_added), + "stored-control-added", + G_CALLBACK (on_backend_stored_control_added), context); g_signal_connect (G_OBJECT (context->priv->backend), - "stored-stream-removed", - G_CALLBACK (on_backend_stored_stream_removed), + "stored-control-removed", + G_CALLBACK (on_backend_stored_control_removed), context); g_signal_connect (G_OBJECT (context->priv->backend), - "notify::default-input", + "notify::default-input-stream", G_CALLBACK (on_backend_default_input_stream_notify), context); g_signal_connect (G_OBJECT (context->priv->backend), - "notify::default-output", + "notify::default-output-stream", G_CALLBACK (on_backend_default_output_stream_notify), context); diff --git a/libmatemixer/matemixer-context.h b/libmatemixer/matemixer-context.h index 3370570..30d9363 100644 --- a/libmatemixer/matemixer-context.h +++ b/libmatemixer/matemixer-context.h @@ -66,64 +66,64 @@ struct _MateMixerContextClass GObjectClass parent_class; /*< private >*/ - void (*device_added) (MateMixerContext *context, - const gchar *name); - void (*device_removed) (MateMixerContext *context, - const gchar *name); - void (*stream_added) (MateMixerContext *context, - const gchar *name); - void (*stream_removed) (MateMixerContext *context, - const gchar *name); - void (*stored_stream_added) (MateMixerContext *context, - const gchar *name); - void (*stored_stream_removed) (MateMixerContext *context, - const gchar *name); + void (*device_added) (MateMixerContext *context, + const gchar *name); + void (*device_removed) (MateMixerContext *context, + const gchar *name); + void (*stream_added) (MateMixerContext *context, + const gchar *name); + void (*stream_removed) (MateMixerContext *context, + const gchar *name); + void (*stored_control_added) (MateMixerContext *context, + const gchar *name); + void (*stored_control_removed) (MateMixerContext *context, + const gchar *name); }; -GType mate_mixer_context_get_type (void) G_GNUC_CONST; - -MateMixerContext * mate_mixer_context_new (void); - -gboolean mate_mixer_context_set_backend_type (MateMixerContext *context, - MateMixerBackendType backend_type); -gboolean mate_mixer_context_set_app_name (MateMixerContext *context, - const gchar *app_name); -gboolean mate_mixer_context_set_app_id (MateMixerContext *context, - const gchar *app_id); -gboolean mate_mixer_context_set_app_version (MateMixerContext *context, - const gchar *app_version); -gboolean mate_mixer_context_set_app_icon (MateMixerContext *context, - const gchar *app_icon); -gboolean mate_mixer_context_set_server_address (MateMixerContext *context, - const gchar *address); - -gboolean mate_mixer_context_open (MateMixerContext *context); -void mate_mixer_context_close (MateMixerContext *context); - -MateMixerState mate_mixer_context_get_state (MateMixerContext *context); - -MateMixerDevice * mate_mixer_context_get_device (MateMixerContext *context, - const gchar *name); -MateMixerStream * mate_mixer_context_get_stream (MateMixerContext *context, - const gchar *name); -MateMixerStream * mate_mixer_context_get_stored_stream (MateMixerContext *context, - const gchar *name); - -const GList * mate_mixer_context_list_devices (MateMixerContext *context); -const GList * mate_mixer_context_list_streams (MateMixerContext *context); -const GList * mate_mixer_context_list_stored_streams (MateMixerContext *context); - -MateMixerStream * mate_mixer_context_get_default_input_stream (MateMixerContext *context); -gboolean mate_mixer_context_set_default_input_stream (MateMixerContext *context, - MateMixerStream *stream); - -MateMixerStream * mate_mixer_context_get_default_output_stream (MateMixerContext *context); -gboolean mate_mixer_context_set_default_output_stream (MateMixerContext *context, - MateMixerStream *stream); - -const gchar * mate_mixer_context_get_backend_name (MateMixerContext *context); -MateMixerBackendType mate_mixer_context_get_backend_type (MateMixerContext *context); -MateMixerBackendFlags mate_mixer_context_get_backend_flags (MateMixerContext *context); +GType mate_mixer_context_get_type (void) G_GNUC_CONST; + +MateMixerContext * mate_mixer_context_new (void); + +gboolean mate_mixer_context_set_backend_type (MateMixerContext *context, + MateMixerBackendType backend_type); +gboolean mate_mixer_context_set_app_name (MateMixerContext *context, + const gchar *app_name); +gboolean mate_mixer_context_set_app_id (MateMixerContext *context, + const gchar *app_id); +gboolean mate_mixer_context_set_app_version (MateMixerContext *context, + const gchar *app_version); +gboolean mate_mixer_context_set_app_icon (MateMixerContext *context, + const gchar *app_icon); +gboolean mate_mixer_context_set_server_address (MateMixerContext *context, + const gchar *address); + +gboolean mate_mixer_context_open (MateMixerContext *context); +void mate_mixer_context_close (MateMixerContext *context); + +MateMixerState mate_mixer_context_get_state (MateMixerContext *context); + +MateMixerDevice * mate_mixer_context_get_device (MateMixerContext *context, + const gchar *name); +MateMixerStream * mate_mixer_context_get_stream (MateMixerContext *context, + const gchar *name); +MateMixerStoredControl *mate_mixer_context_get_stored_control (MateMixerContext *context, + const gchar *name); + +const GList * mate_mixer_context_list_devices (MateMixerContext *context); +const GList * mate_mixer_context_list_streams (MateMixerContext *context); +const GList * mate_mixer_context_list_stored_controls (MateMixerContext *context); + +MateMixerStream * mate_mixer_context_get_default_input_stream (MateMixerContext *context); +gboolean mate_mixer_context_set_default_input_stream (MateMixerContext *context, + MateMixerStream *stream); + +MateMixerStream * mate_mixer_context_get_default_output_stream (MateMixerContext *context); +gboolean mate_mixer_context_set_default_output_stream (MateMixerContext *context, + MateMixerStream *stream); + +const gchar * mate_mixer_context_get_backend_name (MateMixerContext *context); +MateMixerBackendType mate_mixer_context_get_backend_type (MateMixerContext *context); +MateMixerBackendFlags mate_mixer_context_get_backend_flags (MateMixerContext *context); G_END_DECLS diff --git a/libmatemixer/matemixer-device-profile-private.h b/libmatemixer/matemixer-device-profile-private.h deleted file mode 100644 index 44f2853..0000000 --- a/libmatemixer/matemixer-device-profile-private.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MATEMIXER_DEVICE_PROFILE_PRIVATE_H -#define MATEMIXER_DEVICE_PROFILE_PRIVATE_H - -#include <glib.h> - -#include "matemixer-device-profile.h" - -G_BEGIN_DECLS - -MateMixerDeviceProfile *_mate_mixer_device_profile_new (const gchar *name, - const gchar *description, - guint priority, - guint input_streams, - guint output_streams); - -gboolean _mate_mixer_device_profile_set_label (MateMixerDeviceProfile *profile, - const gchar *label); -gboolean _mate_mixer_device_profile_set_priority (MateMixerDeviceProfile *profile, - guint priority); -gboolean _mate_mixer_device_profile_set_num_input_streams (MateMixerDeviceProfile *profile, - guint num); -gboolean _mate_mixer_device_profile_set_num_output_streams (MateMixerDeviceProfile *profile, - guint num); - -G_END_DECLS - -#endif /* MATEMIXER_DEVICE_PROFILE_PRIVATE_H */ diff --git a/libmatemixer/matemixer-device-profile.c b/libmatemixer/matemixer-device-profile.c deleted file mode 100644 index d841ff2..0000000 --- a/libmatemixer/matemixer-device-profile.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include <glib.h> -#include <glib-object.h> - -#include "matemixer-device-profile.h" -#include "matemixer-device-profile-private.h" - -/** - * SECTION:matemixer-device-profile - * @short_description: Device profile - * @include: libmatemixer/matemixer.h - */ - -struct _MateMixerDeviceProfilePrivate -{ - gchar *name; - gchar *label; - guint priority; - guint num_input_streams; - guint num_output_streams; -}; - -enum { - PROP_0, - PROP_NAME, - PROP_LABEL, - PROP_PRIORITY, - PROP_NUM_INPUT_STREAMS, - PROP_NUM_OUTPUT_STREAMS, - N_PROPERTIES -}; - -static GParamSpec *properties[N_PROPERTIES] = { NULL, }; - -static void mate_mixer_device_profile_class_init (MateMixerDeviceProfileClass *klass); - -static void mate_mixer_device_profile_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void mate_mixer_device_profile_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); - -static void mate_mixer_device_profile_init (MateMixerDeviceProfile *profile); -static void mate_mixer_device_profile_finalize (GObject *object); - -G_DEFINE_TYPE (MateMixerDeviceProfile, mate_mixer_device_profile, G_TYPE_OBJECT); - -static void -mate_mixer_device_profile_class_init (MateMixerDeviceProfileClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->finalize = mate_mixer_device_profile_finalize; - object_class->get_property = mate_mixer_device_profile_get_property; - object_class->set_property = mate_mixer_device_profile_set_property; - - properties[PROP_NAME] = - g_param_spec_string ("name", - "Name", - "Name of the profile", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_LABEL] = - g_param_spec_string ("label", - "Label", - "Label of the profile", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_PRIORITY] = - g_param_spec_uint ("priority", - "Priority", - "Priority of the profile", - 0, - G_MAXUINT, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_NUM_INPUT_STREAMS] = - g_param_spec_uint ("num-input-streams", - "Number of input streams", - "Number of input streams in the profile", - 0, - G_MAXUINT, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_NUM_OUTPUT_STREAMS] = - g_param_spec_uint ("num-output-streams", - "Number of output streams", - "Number of output streams in the profile", - 0, - G_MAXUINT, - 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, N_PROPERTIES, properties); - - g_type_class_add_private (object_class, sizeof (MateMixerDeviceProfilePrivate)); -} - -static void -mate_mixer_device_profile_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - MateMixerDeviceProfile *profile; - - profile = MATE_MIXER_DEVICE_PROFILE (object); - - switch (param_id) { - case PROP_NAME: - g_value_set_string (value, profile->priv->name); - break; - - case PROP_LABEL: - g_value_set_string (value, profile->priv->label); - break; - - case PROP_PRIORITY: - g_value_set_uint (value, profile->priv->priority); - break; - - case PROP_NUM_INPUT_STREAMS: - g_value_set_uint (value, profile->priv->num_input_streams); - break; - - case PROP_NUM_OUTPUT_STREAMS: - g_value_set_uint (value, profile->priv->num_output_streams); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -mate_mixer_device_profile_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - MateMixerDeviceProfile *profile; - - profile = MATE_MIXER_DEVICE_PROFILE (object); - - switch (param_id) { - case PROP_NAME: - /* Construct-only string */ - profile->priv->name = g_strdup (g_value_get_string (value)); - break; - - case PROP_LABEL: - /* Construct-only string */ - profile->priv->label = g_strdup (g_value_get_string (value)); - break; - - case PROP_PRIORITY: - profile->priv->priority = g_value_get_uint (value); - break; - - case PROP_NUM_INPUT_STREAMS: - profile->priv->num_input_streams = g_value_get_uint (value); - break; - - case PROP_NUM_OUTPUT_STREAMS: - profile->priv->num_output_streams = g_value_get_uint (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -mate_mixer_device_profile_init (MateMixerDeviceProfile *profile) -{ - profile->priv = G_TYPE_INSTANCE_GET_PRIVATE (profile, - MATE_MIXER_TYPE_DEVICE_PROFILE, - MateMixerDeviceProfilePrivate); -} - -static void -mate_mixer_device_profile_finalize (GObject *object) -{ - MateMixerDeviceProfile *profile; - - profile = MATE_MIXER_DEVICE_PROFILE (object); - - g_free (profile->priv->name); - g_free (profile->priv->label); - - G_OBJECT_CLASS (mate_mixer_device_profile_parent_class)->finalize (object); -} - -/** - * mate_mixer_device_profile_get_name: - * @profile: a #MateMixerDeviceProfile - */ -const gchar * -mate_mixer_device_profile_get_name (MateMixerDeviceProfile *profile) -{ - g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), NULL); - - return profile->priv->name; -} - -/** - * mate_mixer_device_profile_get_label: - * @profile: a #MateMixerDeviceProfile - */ -const gchar * -mate_mixer_device_profile_get_label (MateMixerDeviceProfile *profile) -{ - g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), NULL); - - return profile->priv->label; -} - -/** - * mate_mixer_device_profile_get_priority: - * @profile: a #MateMixerDeviceProfile - */ -guint -mate_mixer_device_profile_get_priority (MateMixerDeviceProfile *profile) -{ - g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), 0); - - return profile->priv->priority; -} - -/** - * mate_mixer_device_profile_get_num_input_streams: - * @profile: a #MateMixerDeviceProfile - */ -guint -mate_mixer_device_profile_get_num_input_streams (MateMixerDeviceProfile *profile) -{ - g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), 0); - - return profile->priv->num_input_streams; -} - -/** - * mate_mixer_device_profile_get_num_output_streams: - * @profile: a #MateMixerDeviceProfile - */ -guint -mate_mixer_device_profile_get_num_output_streams (MateMixerDeviceProfile *profile) -{ - g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), 0); - - return profile->priv->num_output_streams; -} - -MateMixerDeviceProfile * -_mate_mixer_device_profile_new (const gchar *name, - const gchar *label, - guint priority, - guint input_streams, - guint output_streams) -{ - return g_object_new (MATE_MIXER_TYPE_DEVICE_PROFILE, - "name", name, - "label", label, - "priority", priority, - "num-input-streams", input_streams, - "num-output-streams", output_streams, - NULL); -} - -gboolean -_mate_mixer_device_profile_set_label (MateMixerDeviceProfile *profile, - const gchar *label) -{ - g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); - - if (g_strcmp0 (profile->priv->label, label) != 0) { - g_free (profile->priv->label); - - profile->priv->label = g_strdup (label); - - g_object_notify_by_pspec (G_OBJECT (profile), properties[PROP_LABEL]); - return TRUE; - } - - return FALSE; -} - -gboolean -_mate_mixer_device_profile_set_priority (MateMixerDeviceProfile *profile, - guint priority) -{ - g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); - - if (profile->priv->priority != priority) { - profile->priv->priority = priority; - - g_object_notify_by_pspec (G_OBJECT (profile), properties[PROP_PRIORITY]); - return TRUE; - } - - return FALSE; -} - -gboolean -_mate_mixer_device_profile_set_num_input_streams (MateMixerDeviceProfile *profile, - guint num) -{ - g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); - - if (profile->priv->num_input_streams != num) { - profile->priv->num_input_streams = num; - - g_object_notify_by_pspec (G_OBJECT (profile), properties[PROP_NUM_INPUT_STREAMS]); - return TRUE; - } - - return FALSE; -} - -gboolean -_mate_mixer_device_profile_set_num_output_streams (MateMixerDeviceProfile *profile, - guint num) -{ - g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); - - if (profile->priv->num_output_streams != num) { - profile->priv->num_output_streams = num; - - g_object_notify_by_pspec (G_OBJECT (profile), properties[PROP_NUM_OUTPUT_STREAMS]); - return TRUE; - } - - return FALSE; -} diff --git a/libmatemixer/matemixer-device-profile.h b/libmatemixer/matemixer-device-profile.h deleted file mode 100644 index 8e4221a..0000000 --- a/libmatemixer/matemixer-device-profile.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2014 Michal Ratajsky <[email protected]> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef MATEMIXER_DEVICE_PROFILE_H -#define MATEMIXER_DEVICE_PROFILE_H - -#include <glib.h> -#include <glib-object.h> -#include <libmatemixer/matemixer-types.h> - -G_BEGIN_DECLS - -#define MATE_MIXER_TYPE_DEVICE_PROFILE \ - (mate_mixer_device_profile_get_type ()) -#define MATE_MIXER_DEVICE_PROFILE(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_DEVICE_PROFILE, MateMixerDeviceProfile)) -#define MATE_MIXER_IS_DEVICE_PROFILE(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_DEVICE_PROFILE)) -#define MATE_MIXER_DEVICE_PROFILE_CLASS(k) \ - (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_DEVICE_PROFILE, MateMixerDeviceProfileClass)) -#define MATE_MIXER_IS_DEVICE_PROFILE_CLASS(k) \ - (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_DEVICE_PROFILE)) -#define MATE_MIXER_DEVICE_PROFILE_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_DEVICE_PROFILE, MateMixerDeviceProfileClass)) - -typedef struct _MateMixerDeviceProfileClass MateMixerDeviceProfileClass; -typedef struct _MateMixerDeviceProfilePrivate MateMixerDeviceProfilePrivate; - -struct _MateMixerDeviceProfile -{ - GObject parent; - - /*< private >*/ - MateMixerDeviceProfilePrivate *priv; -}; - -struct _MateMixerDeviceProfileClass -{ - GObjectClass parent_class; -}; - -GType mate_mixer_device_profile_get_type (void) G_GNUC_CONST; - -const gchar *mate_mixer_device_profile_get_name (MateMixerDeviceProfile *profile); -const gchar *mate_mixer_device_profile_get_label (MateMixerDeviceProfile *profile); -guint mate_mixer_device_profile_get_priority (MateMixerDeviceProfile *profile); - -guint mate_mixer_device_profile_get_num_input_streams (MateMixerDeviceProfile *profile); -guint mate_mixer_device_profile_get_num_output_streams (MateMixerDeviceProfile *profile); - -G_END_DECLS - -#endif /* MATEMIXER_DEVICE_PROFILE_H */ diff --git a/libmatemixer/matemixer-device.c b/libmatemixer/matemixer-device.c index 229110f..8144388 100644 --- a/libmatemixer/matemixer-device.c +++ b/libmatemixer/matemixer-device.c @@ -20,7 +20,6 @@ #include <glib-object.h> #include "matemixer-device.h" -#include "matemixer-device-profile.h" #include "matemixer-stream.h" #include "matemixer-switch.h" @@ -32,13 +31,9 @@ struct _MateMixerDevicePrivate { - gchar *name; - gchar *label; - gchar *icon; - GList *streams; - GList *switches; - GList *profiles; - MateMixerDeviceProfile *profile; + gchar *name; + gchar *label; + gchar *icon; }; enum { @@ -46,7 +41,6 @@ enum { PROP_NAME, PROP_LABEL, PROP_ICON, - PROP_ACTIVE_PROFILE, N_PROPERTIES }; @@ -74,17 +68,14 @@ static void mate_mixer_device_set_property (GObject *object, GParamSpec *pspec); static void mate_mixer_device_init (MateMixerDevice *device); -static void mate_mixer_device_dispose (GObject *object); static void mate_mixer_device_finalize (GObject *object); G_DEFINE_ABSTRACT_TYPE (MateMixerDevice, mate_mixer_device, G_TYPE_OBJECT) -static MateMixerStream * mate_mixer_device_real_get_stream (MateMixerDevice *device, - const gchar *name); -static MateMixerSwitch * mate_mixer_device_real_get_switch (MateMixerDevice *device, - const gchar *name); -static MateMixerDeviceProfile *mate_mixer_device_real_get_profile (MateMixerDevice *device, - const gchar *name); +static MateMixerStream *mate_mixer_device_real_get_stream (MateMixerDevice *device, + const gchar *name); +static MateMixerSwitch *mate_mixer_device_real_get_switch (MateMixerDevice *device, + const gchar *name); static void mate_mixer_device_class_init (MateMixerDeviceClass *klass) @@ -93,10 +84,8 @@ mate_mixer_device_class_init (MateMixerDeviceClass *klass) klass->get_stream = mate_mixer_device_real_get_stream; klass->get_switch = mate_mixer_device_real_get_switch; - klass->get_profile = mate_mixer_device_real_get_profile; object_class = G_OBJECT_CLASS (klass); - object_class->dispose = mate_mixer_device_dispose; object_class->finalize = mate_mixer_device_finalize; object_class->get_property = mate_mixer_device_get_property; object_class->set_property = mate_mixer_device_set_property; @@ -128,14 +117,6 @@ mate_mixer_device_class_init (MateMixerDeviceClass *klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - properties[PROP_ACTIVE_PROFILE] = - g_param_spec_object ("active-profile", - "Active profile", - "The currently active profile of the device", - MATE_MIXER_TYPE_DEVICE_PROFILE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - g_object_class_install_properties (object_class, N_PROPERTIES, properties); signals[STREAM_ADDED] = @@ -209,10 +190,6 @@ mate_mixer_device_get_property (GObject *object, case PROP_ICON: g_value_set_string (value, device->priv->icon); break; - case PROP_ACTIVE_PROFILE: - g_value_set_object (value, device->priv->profile); - break; - default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -258,31 +235,6 @@ mate_mixer_device_init (MateMixerDevice *device) } static void -mate_mixer_device_dispose (GObject *object) -{ - MateMixerDevice *device; - - device = MATE_MIXER_DEVICE (object); - - if (device->priv->streams != NULL) { - g_list_free_full (device->priv->streams, g_object_unref); - device->priv->streams = NULL; - } - if (device->priv->switches != NULL) { - g_list_free_full (device->priv->switches, g_object_unref); - device->priv->switches = NULL; - } - if (device->priv->profiles != NULL) { - g_list_free_full (device->priv->profiles, g_object_unref); - device->priv->profiles = NULL; - } - - g_clear_object (&device->priv->profile); - - G_OBJECT_CLASS (mate_mixer_device_parent_class)->dispose (object); -} - -static void mate_mixer_device_finalize (GObject *object) { MateMixerDevice *device; @@ -309,7 +261,7 @@ mate_mixer_device_get_name (MateMixerDevice *device) } /** - * mate_mixer_device_get_description: + * mate_mixer_device_get_label: * @device: a #MateMixerDevice */ const gchar * @@ -333,20 +285,9 @@ mate_mixer_device_get_icon (MateMixerDevice *device) } /** - * mate_mixer_device_get_profile: - * @device: a #MateMixerDevice - * @name: a profile name - */ -MateMixerDeviceProfile * -mate_mixer_device_get_profile (MateMixerDevice *device, const gchar *name) -{ - return MATE_MIXER_DEVICE_GET_CLASS (device)->get_profile (device, name); -} - -/** * mate_mixer_device_get_stream: * @device: a #MateMixerDevice - * @name: a profile name + * @name: a stream name */ MateMixerStream * mate_mixer_device_get_stream (MateMixerDevice *device, const gchar *name) @@ -357,7 +298,7 @@ mate_mixer_device_get_stream (MateMixerDevice *device, const gchar *name) /** * mate_mixer_device_get_switch: * @device: a #MateMixerDevice - * @name: a profile name + * @name: a switch name */ MateMixerSwitch * mate_mixer_device_get_switch (MateMixerDevice *device, const gchar *name) @@ -372,16 +313,16 @@ mate_mixer_device_get_switch (MateMixerDevice *device, const gchar *name) const GList * mate_mixer_device_list_streams (MateMixerDevice *device) { + MateMixerDeviceClass *klass; + g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - if (device->priv->streams == NULL) { - MateMixerDeviceClass *klass = MATE_MIXER_DEVICE_GET_CLASS (device); + klass = MATE_MIXER_DEVICE_GET_CLASS (device); - if (klass->list_streams != NULL) - device->priv->streams = klass->list_streams (device); - } + if G_LIKELY (klass->list_streams != NULL) + return klass->list_streams (device); - return (const GList *) device->priv->streams; + return NULL; } /** @@ -391,77 +332,16 @@ mate_mixer_device_list_streams (MateMixerDevice *device) const GList * mate_mixer_device_list_switches (MateMixerDevice *device) { - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - - if (device->priv->switches == NULL) { - MateMixerDeviceClass *klass = MATE_MIXER_DEVICE_GET_CLASS (device); - - if (klass->list_switches != NULL) - device->priv->switches = klass->list_switches (device); - } - - return (const GList *) device->priv->switches; -} - -/** - * mate_mixer_device_list_profiles: - * @device: a #MateMixerDevice - */ -const GList * -mate_mixer_device_list_profiles (MateMixerDevice *device) -{ - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - - if (device->priv->profiles == NULL) { - MateMixerDeviceClass *klass = MATE_MIXER_DEVICE_GET_CLASS (device); - - if (klass->list_profiles != NULL) - device->priv->profiles = klass->list_profiles (device); - } + MateMixerDeviceClass *klass; - return (const GList *) device->priv->profiles; -} - -/** - * mate_mixer_device_get_active_profile: - * @device: a #MateMixerDevice - */ -MateMixerDeviceProfile * -mate_mixer_device_get_active_profile (MateMixerDevice *device) -{ g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - return device->priv->profile; -} - -/** - * mate_mixer_device_set_active_profile: - * @device: a #MateMixerDevice - * @profile: a #MateMixerDeviceProfile - */ -gboolean -mate_mixer_device_set_active_profile (MateMixerDevice *device, - MateMixerDeviceProfile *profile) -{ - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), FALSE); - g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE); - - if (profile != device->priv->profile) { - MateMixerDeviceClass *klass; - - klass = MATE_MIXER_DEVICE_GET_CLASS (device); + klass = MATE_MIXER_DEVICE_GET_CLASS (device); - if (klass->set_active_profile == NULL || - klass->set_active_profile (device, profile) == FALSE) - return FALSE; + if G_LIKELY (klass->list_switches != NULL) + return klass->list_switches (device); - if (G_LIKELY (device->priv->profile != NULL)) - g_object_unref (device->priv->profile); - - device->priv->profile = g_object_ref (profile); - } - - return TRUE; + return NULL; } static MateMixerStream * @@ -503,23 +383,3 @@ mate_mixer_device_real_get_switch (MateMixerDevice *device, const gchar *name) } return NULL; } - -static MateMixerDeviceProfile * -mate_mixer_device_real_get_profile (MateMixerDevice *device, const gchar *name) -{ - const GList *list; - - g_return_val_if_fail (MATE_MIXER_IS_DEVICE (device), NULL); - g_return_val_if_fail (name != NULL, NULL); - - list = mate_mixer_device_list_profiles (device); - while (list != NULL) { - MateMixerDeviceProfile *profile = MATE_MIXER_DEVICE_PROFILE (list->data); - - if (strcmp (name, mate_mixer_device_profile_get_name (profile)) == 0) - return profile; - - list = list->next; - } - return NULL; -} diff --git a/libmatemixer/matemixer-device.h b/libmatemixer/matemixer-device.h index 885460c..51d2a14 100644 --- a/libmatemixer/matemixer-device.h +++ b/libmatemixer/matemixer-device.h @@ -21,7 +21,7 @@ #include <glib.h> #include <glib-object.h> -#include "matemixer-types.h" +#include <libmatemixer/matemixer-types.h> G_BEGIN_DECLS @@ -41,6 +41,12 @@ G_BEGIN_DECLS typedef struct _MateMixerDeviceClass MateMixerDeviceClass; typedef struct _MateMixerDevicePrivate MateMixerDevicePrivate; +/** + * MateMixerDevice: + * + * The #MateMixerDevice structure contains only private data and should only + * be accessed using the provided API. + */ struct _MateMixerDevice { GObject object; @@ -49,59 +55,49 @@ struct _MateMixerDevice MateMixerDevicePrivate *priv; }; +/** + * MateMixerDeviceClass: + * + * The class structure of #MateMixerDevice. + */ struct _MateMixerDeviceClass { GObjectClass parent_class; /*< private >*/ + MateMixerStream *(*get_stream) (MateMixerDevice *device, + const gchar *name); + MateMixerSwitch *(*get_switch) (MateMixerDevice *device, + const gchar *name); - MateMixerStream *(*get_stream) (MateMixerDevice *device, - const gchar *name); - MateMixerSwitch *(*get_switch) (MateMixerDevice *device, - const gchar *name); - MateMixerDeviceProfile *(*get_profile) (MateMixerDevice *device, - const gchar *name); - - GList *(*list_streams) (MateMixerDevice *device); - GList *(*list_switches) (MateMixerDevice *device); - GList *(*list_profiles) (MateMixerDevice *device); - - gboolean (*set_active_profile) (MateMixerDevice *device, - MateMixerDeviceProfile *profile); + const GList *(*list_streams) (MateMixerDevice *device); + const GList *(*list_switches) (MateMixerDevice *device); /* Signals */ - void (*stream_added) (MateMixerDevice *device, - const gchar *name); - void (*stream_removed) (MateMixerDevice *device, - const gchar *name); - void (*switch_added) (MateMixerDevice *device, - const gchar *name); - void (*switch_removed) (MateMixerDevice *device, - const gchar *name); + void (*stream_added) (MateMixerDevice *device, + const gchar *name); + void (*stream_removed) (MateMixerDevice *device, + const gchar *name); + void (*switch_added) (MateMixerDevice *device, + const gchar *name); + void (*switch_removed) (MateMixerDevice *device, + const gchar *name); }; -GType mate_mixer_device_get_type (void) G_GNUC_CONST; - -const gchar * mate_mixer_device_get_name (MateMixerDevice *device); -const gchar * mate_mixer_device_get_label (MateMixerDevice *device); -const gchar * mate_mixer_device_get_icon (MateMixerDevice *device); - -MateMixerStream * mate_mixer_device_get_stream (MateMixerDevice *device, - const gchar *name); +GType mate_mixer_device_get_type (void) G_GNUC_CONST; -MateMixerSwitch * mate_mixer_device_get_switch (MateMixerDevice *device, - const gchar *name); +const gchar * mate_mixer_device_get_name (MateMixerDevice *device); +const gchar * mate_mixer_device_get_label (MateMixerDevice *device); +const gchar * mate_mixer_device_get_icon (MateMixerDevice *device); -MateMixerDeviceProfile *mate_mixer_device_get_profile (MateMixerDevice *device, - const gchar *name); +MateMixerStream *mate_mixer_device_get_stream (MateMixerDevice *device, + const gchar *name); -const GList * mate_mixer_device_list_streams (MateMixerDevice *device); -const GList * mate_mixer_device_list_switches (MateMixerDevice *device); -const GList * mate_mixer_device_list_profiles (MateMixerDevice *device); +MateMixerSwitch *mate_mixer_device_get_switch (MateMixerDevice *device, + const gchar *name); -MateMixerDeviceProfile *mate_mixer_device_get_active_profile (MateMixerDevice *device); -gboolean mate_mixer_device_set_active_profile (MateMixerDevice *device, - MateMixerDeviceProfile *profile); +const GList * mate_mixer_device_list_streams (MateMixerDevice *device); +const GList * mate_mixer_device_list_switches (MateMixerDevice *device); G_END_DECLS diff --git a/libmatemixer/matemixer-enum-types.c b/libmatemixer/matemixer-enum-types.c index 035be3d..ea63f50 100644 --- a/libmatemixer/matemixer-enum-types.c +++ b/libmatemixer/matemixer-enum-types.c @@ -53,6 +53,8 @@ mate_mixer_backend_type_get_type (void) static const GEnumValue values[] = { { MATE_MIXER_BACKEND_UNKNOWN, "MATE_MIXER_BACKEND_UNKNOWN", "unknown" }, { MATE_MIXER_BACKEND_PULSEAUDIO, "MATE_MIXER_BACKEND_PULSEAUDIO", "pulseaudio" }, + { MATE_MIXER_BACKEND_ALSA, "MATE_MIXER_BACKEND_ALSA", "alsa" }, + { MATE_MIXER_BACKEND_OSS, "MATE_MIXER_BACKEND_OSS", "oss" }, { MATE_MIXER_BACKEND_NULL, "MATE_MIXER_BACKEND_NULL", "null" }, { 0, NULL, NULL } }; @@ -64,62 +66,39 @@ mate_mixer_backend_type_get_type (void) } GType -mate_mixer_port_flags_get_type (void) +mate_mixer_backend_flags_get_type (void) { static GType etype = 0; if (etype == 0) { static const GFlagsValue values[] = { - { MATE_MIXER_PORT_NO_FLAGS, "MATE_MIXER_PORT_NO_FLAGS", "no-flags" }, - { MATE_MIXER_PORT_AVAILABLE, "MATE_MIXER_PORT_AVAILABLE", "available" }, - { MATE_MIXER_PORT_INPUT, "MATE_MIXER_PORT_INPUT", "input" }, - { MATE_MIXER_PORT_OUTPUT, "MATE_MIXER_PORT_OUTPUT", "output" }, + { MATE_MIXER_BACKEND_NO_FLAGS, "MATE_MIXER_STREAM_NO_FLAGS", "no-flags" }, + { MATE_MIXER_BACKEND_HAS_APPLICATION_CONTROLS, "MATE_MIXER_BACKEND_HAS_APPLICATION_CONTROLS", "has-application-controls" }, + { MATE_MIXER_BACKEND_CAN_SET_DEFAULT_INPUT_STREAM, "MATE_MIXER_BACKEND_CAN_SET_DEFAULT_INPUT_STREAM", "can-set-default-input-stream" }, + { MATE_MIXER_BACKEND_CAN_SET_DEFAULT_OUTPUT_STREAM, "MATE_MIXER_BACKEND_CAN_SET_DEFAULT_OUTPUT_STREAM", "can-set-default-output-stream" }, { 0, NULL, NULL } }; etype = g_flags_register_static ( - g_intern_static_string ("MateMixerPortFlags"), + g_intern_static_string ("MateMixerBackendFlags"), values); } return etype; } GType -mate_mixer_stream_flags_get_type (void) -{ - static GType etype = 0; - - if (etype == 0) { - static const GFlagsValue values[] = { - { MATE_MIXER_STREAM_NO_FLAGS, "MATE_MIXER_STREAM_NO_FLAGS", "no-flags" }, - { MATE_MIXER_STREAM_INPUT, "MATE_MIXER_STREAM_INPUT", "input" }, - { MATE_MIXER_STREAM_OUTPUT, "MATE_MIXER_STREAM_OUTPUT", "output" }, - { MATE_MIXER_STREAM_CLIENT, "MATE_MIXER_STREAM_CLIENT", "client" }, - { MATE_MIXER_STREAM_HAS_MONITOR, "MATE_MIXER_STREAM_HAS_MONITOR", "has-monitor" }, - { MATE_MIXER_STREAM_CAN_SUSPEND, "MATE_MIXER_STREAM_CAN_SUSPEND", "can-suspend" }, - { 0, NULL, NULL } - }; - etype = g_flags_register_static ( - g_intern_static_string ("MateMixerStreamFlags"), - values); - } - return etype; -} - -GType -mate_mixer_stream_state_get_type (void) +mate_mixer_direction_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { - { MATE_MIXER_STREAM_STATE_UNKNOWN, "MATE_MIXER_STREAM_STATE_UNKNOWN", "unknown" }, - { MATE_MIXER_STREAM_STATE_RUNNING, "MATE_MIXER_STREAM_STATE_RUNNING", "running" }, - { MATE_MIXER_STREAM_STATE_IDLE, "MATE_MIXER_STREAM_STATE_IDLE", "idle" }, - { MATE_MIXER_STREAM_STATE_SUSPENDED, "MATE_MIXER_STREAM_STATE_SUSPENDED", "suspended" }, + { MATE_MIXER_DIRECTION_UNKNOWN, "MATE_MIXER_DIRECTION_UNKNOWN", "unknown" }, + { MATE_MIXER_DIRECTION_INPUT, "MATE_MIXER_DIRECTION_INPUT", "input" }, + { MATE_MIXER_DIRECTION_OUTPUT, "MATE_MIXER_DIRECTION_OUTPUT", "output" }, { 0, NULL, NULL } }; etype = g_enum_register_static ( - g_intern_static_string ("MateMixerStreamState"), + g_intern_static_string ("MateMixerDirection"), values); } return etype; @@ -133,13 +112,16 @@ mate_mixer_stream_control_flags_get_type (void) if (etype == 0) { static const GFlagsValue values[] = { { MATE_MIXER_STREAM_CONTROL_NO_FLAGS, "MATE_MIXER_STREAM_CONTROL_NO_FLAGS", "no-flags" }, - { MATE_MIXER_STREAM_CONTROL_HAS_MUTE, "MATE_MIXER_STREAM_CONTROL_HAS_MUTE", "has-mute" }, - { MATE_MIXER_STREAM_CONTROL_HAS_VOLUME, "MATE_MIXER_STREAM_CONTROL_HAS_VOLUME", "has-volume" }, - { MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL, "MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL", "has-decibel" }, - { MATE_MIXER_STREAM_CONTROL_HAS_FLAT_VOLUME, "MATE_MIXER_STREAM_CONTROL_HAS_FLAT_VOLUME", "has-flat-volume" }, - { MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME, "MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME", "can-set-volume" }, + { MATE_MIXER_STREAM_CONTROL_MUTE_READABLE, "MATE_MIXER_STREAM_CONTROL_MUTE_READABLE", "mute-readable" }, + { MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE, "MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE", "mute-writable" }, + { MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE, "MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE", "volume-readable" }, + { MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE, "MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE", "volume-writable" }, { MATE_MIXER_STREAM_CONTROL_CAN_BALANCE, "MATE_MIXER_STREAM_CONTROL_CAN_BALANCE", "can-balance" }, { MATE_MIXER_STREAM_CONTROL_CAN_FADE, "MATE_MIXER_STREAM_CONTROL_CAN_FADE", "can-fade" }, + { MATE_MIXER_STREAM_CONTROL_MOVABLE, "MATE_MIXER_STREAM_CONTROL_MOVABLE", "movable" }, + { MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL, "MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL", "has-decibel" }, + { MATE_MIXER_STREAM_CONTROL_HAS_MONITOR, "MATE_MIXER_STREAM_CONTROL_HAS_MONITOR", "has-monitor" }, + { MATE_MIXER_STREAM_CONTROL_STORED, "MATE_MIXER_STREAM_CONTROL_STORED", "stored" }, { 0, NULL, NULL } }; etype = g_flags_register_static ( @@ -158,12 +140,17 @@ mate_mixer_stream_control_role_get_type (void) static const GEnumValue values[] = { { MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, "MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN", "unknown" }, { MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, "MATE_MIXER_STREAM_CONTROL_ROLE_MASTER", "master" }, - { MATE_MIXER_STREAM_CONTROL_ROLE_PORT, "MATE_MIXER_STREAM_CONTROL_ROLE_PORT", "port" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION, "MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION", "application" }, { MATE_MIXER_STREAM_CONTROL_ROLE_PCM, "MATE_MIXER_STREAM_CONTROL_ROLE_PCM", "pcm" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, "MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER", "speaker" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, "MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE", "microphone" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_PORT, "MATE_MIXER_STREAM_CONTROL_ROLE_PORT", "port" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, "MATE_MIXER_STREAM_CONTROL_ROLE_BOOST", "boost" }, { MATE_MIXER_STREAM_CONTROL_ROLE_BASS, "MATE_MIXER_STREAM_CONTROL_ROLE_BASS", "bass" }, { MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, "MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE", "treble" }, { MATE_MIXER_STREAM_CONTROL_ROLE_CD, "MATE_MIXER_STREAM_CONTROL_ROLE_CD", "cd" }, - { MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, "MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER", "speaker" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_VIDEO, "MATE_MIXER_STREAM_CONTROL_ROLE_VIDEO", "video" }, + { MATE_MIXER_STREAM_CONTROL_ROLE_MUSIC, "MATE_MIXER_STREAM_CONTROL_ROLE_MUSIC", "music" }, { 0, NULL, NULL } }; etype = g_enum_register_static ( @@ -174,47 +161,67 @@ mate_mixer_stream_control_role_get_type (void) } GType -mate_mixer_client_stream_flags_get_type (void) +mate_mixer_stream_control_media_role_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + { MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN, "MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN", "unknown" }, + { MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_VIDEO, "MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_VIDEO", "video" }, + { MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_MUSIC, "MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_MUSIC", "music" }, + { MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_GAME, "MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_GAME", "game" }, + { MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT, "MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT", "event" }, + { MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PHONE, "MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PHONE", "phone" }, + { MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ANIMATION, "MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ANIMATION", "animation" }, + { MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PRODUCTION, "MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PRODUCTION", "production" }, + { MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_A11Y, "MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_A11Y", "a11y" }, + { MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_TEST, "MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_TEST", "test" }, + { MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ABSTRACT, "MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ABSTRACT", "abstract" }, + { MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_FILTER, "MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_FILTER", "filter" }, + { 0, NULL, NULL } + }; + etype = g_enum_register_static ( + g_intern_static_string ("MateMixerStreamControlMediaRole"), + values); + } + return etype; +} + +GType +mate_mixer_switch_flags_get_type (void) { static GType etype = 0; if (etype == 0) { static const GFlagsValue values[] = { - { MATE_MIXER_CLIENT_STREAM_NO_FLAGS, "MATE_MIXER_CLIENT_STREAM_NO_FLAGS", "no-flags" }, - { MATE_MIXER_CLIENT_STREAM_APPLICATION, "MATE_MIXER_CLIENT_STREAM_APPLICATION", "application" }, - { MATE_MIXER_CLIENT_STREAM_CACHED, "MATE_MIXER_CLIENT_STREAM_CACHED", "cached" }, + { MATE_MIXER_SWITCH_NO_FLAGS, "MATE_MIXER_SWITCH_NO_FLAGS", "no-flags" }, + { MATE_MIXER_SWITCH_TOGGLE, "MATE_MIXER_SWITCH_TOGGLE", "toggle" }, + { MATE_MIXER_SWITCH_ALLOWS_NO_ACTIVE_OPTION, "MATE_MIXER_SWITCH_ALLOWS_NO_ACTIVE_OPTION", "allows-no-active-option" }, { 0, NULL, NULL } }; etype = g_flags_register_static ( - g_intern_static_string ("MateMixerClientStreamFlags"), + g_intern_static_string ("MateMixerSwitchFlags"), values); } return etype; } GType -mate_mixer_client_stream_role_get_type (void) +mate_mixer_switch_role_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { - { MATE_MIXER_CLIENT_STREAM_ROLE_NONE, "MATE_MIXER_CLIENT_STREAM_ROLE_NONE", "none" }, - { MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO, "MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO", "video" }, - { MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC, "MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC", "music" }, - { MATE_MIXER_CLIENT_STREAM_ROLE_GAME, "MATE_MIXER_CLIENT_STREAM_ROLE_GAME", "game" }, - { MATE_MIXER_CLIENT_STREAM_ROLE_EVENT, "MATE_MIXER_CLIENT_STREAM_ROLE_EVENT", "event" }, - { MATE_MIXER_CLIENT_STREAM_ROLE_PHONE, "MATE_MIXER_CLIENT_STREAM_ROLE_PHONE", "phone" }, - { MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION, "MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION", "animation" }, - { MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION, "MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION", "production" }, - { MATE_MIXER_CLIENT_STREAM_ROLE_A11Y, "MATE_MIXER_CLIENT_STREAM_ROLE_A11Y", "a11y" }, - { MATE_MIXER_CLIENT_STREAM_ROLE_TEST, "MATE_MIXER_CLIENT_STREAM_ROLE_TEST", "test" }, - { MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT, "MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT", "abstract" }, - { MATE_MIXER_CLIENT_STREAM_ROLE_FILTER, "MATE_MIXER_CLIENT_STREAM_ROLE_FILTER", "filter" }, + { MATE_MIXER_SWITCH_ROLE_UNKNOWN, "MATE_MIXER_SWITCH_ROLE_UNKNOWN", "unknown" }, + { MATE_MIXER_SWITCH_ROLE_DEVICE_PROFILE, "MATE_MIXER_SWITCH_ROLE_DEVICE_PROFILE", "device-profile" }, + { MATE_MIXER_SWITCH_ROLE_PORT, "MATE_MIXER_SWITCH_ROLE_PORT", "port" }, + { MATE_MIXER_SWITCH_ROLE_BOOST, "MATE_MIXER_SWITCH_ROLE_BOOST", "boost" }, { 0, NULL, NULL } }; etype = g_enum_register_static ( - g_intern_static_string ("MateMixerClientStreamRole"), + g_intern_static_string ("MateMixerSwitchRole"), values); } return etype; diff --git a/libmatemixer/matemixer-enum-types.h b/libmatemixer/matemixer-enum-types.h index f2193be..f96dc85 100644 --- a/libmatemixer/matemixer-enum-types.h +++ b/libmatemixer/matemixer-enum-types.h @@ -34,26 +34,26 @@ GType mate_mixer_state_get_type (void) G_GNUC_CONST; #define MATE_MIXER_TYPE_BACKEND_TYPE (mate_mixer_backend_type_get_type ()) GType mate_mixer_backend_type_get_type (void) G_GNUC_CONST; -#define MATE_MIXER_TYPE_PORT_FLAGS (mate_mixer_port_flags_get_type ()) -GType mate_mixer_port_flags_get_type (void) G_GNUC_CONST; +#define MATE_MIXER_TYPE_BACKEND_FLAGS (mate_mixer_backend_flags_get_type ()) +GType mate_mixer_backend_flags_get_type (void) G_GNUC_CONST; -#define MATE_MIXER_TYPE_STREAM_FLAGS (mate_mixer_stream_flags_get_type ()) -GType mate_mixer_stream_flags_get_type (void) G_GNUC_CONST; - -#define MATE_MIXER_TYPE_STREAM_STATE (mate_mixer_stream_state_get_type ()) -GType mate_mixer_stream_state_get_type (void) G_GNUC_CONST; +#define MATE_MIXER_TYPE_DIRECTION (mate_mixer_direction_get_type ()) +GType mate_mixer_direction_get_type (void) G_GNUC_CONST; #define MATE_MIXER_TYPE_STREAM_CONTROL_FLAGS (mate_mixer_stream_control_flags_get_type ()) GType mate_mixer_stream_control_flags_get_type (void) G_GNUC_CONST; #define MATE_MIXER_TYPE_STREAM_CONTROL_ROLE (mate_mixer_stream_control_role_get_type ()) -GType mate_mixer_stream_control_role_get_type (void); +GType mate_mixer_stream_control_role_get_type (void) G_GNUC_CONST; + +#define MATE_MIXER_TYPE_STREAM_CONTROL_MEDIA_ROLE (mate_mixer_stream_control_media_role_get_type ()) +GType mate_mixer_stream_control_media_role_get_type (void) G_GNUC_CONST; -#define MATE_MIXER_TYPE_CLIENT_STREAM_FLAGS (mate_mixer_client_stream_flags_get_type ()) -GType mate_mixer_client_stream_flags_get_type (void) G_GNUC_CONST; +#define MATE_MIXER_TYPE_SWITCH_FLAGS (mate_mixer_switch_flags_get_type ()) +GType mate_mixer_switch_flags_get_type (void) G_GNUC_CONST; -#define MATE_MIXER_TYPE_CLIENT_STREAM_ROLE (mate_mixer_client_stream_role_get_type ()) -GType mate_mixer_client_stream_role_get_type (void) G_GNUC_CONST; +#define MATE_MIXER_TYPE_SWITCH_ROLE (mate_mixer_switch_role_get_type ()) +GType mate_mixer_switch_role_get_type (void) G_GNUC_CONST; #define MATE_MIXER_TYPE_CHANNEL_POSITION (mate_mixer_channel_position_get_type ()) GType mate_mixer_channel_position_get_type (void) G_GNUC_CONST; diff --git a/libmatemixer/matemixer-enums.h b/libmatemixer/matemixer-enums.h index 8a88b1c..18bbbcf 100644 --- a/libmatemixer/matemixer-enums.h +++ b/libmatemixer/matemixer-enums.h @@ -63,59 +63,25 @@ typedef enum { MATE_MIXER_BACKEND_NULL } MateMixerBackendType; +/** + * MateMixerBackendFlags: + */ + typedef enum { /*< flags >*/ MATE_MIXER_BACKEND_NO_FLAGS = 0, + MATE_MIXER_BACKEND_HAS_APPLICATION_CONTROLS, MATE_MIXER_BACKEND_CAN_SET_DEFAULT_INPUT_STREAM, MATE_MIXER_BACKEND_CAN_SET_DEFAULT_OUTPUT_STREAM } MateMixerBackendFlags; /** - * MateMixerPortFlags: - * @MATE_MIXER_PORT_NO_FLAGS: - * @MATE_MIXER_PORT_AVAILABLE: - * @MATE_MIXER_PORT_INPUT: - * @MATE_MIXER_PORT_OUTPUT: - */ -typedef enum { /*< flags >*/ - MATE_MIXER_PORT_NO_FLAGS = 0, - MATE_MIXER_PORT_AVAILABLE = 1 << 0, - MATE_MIXER_PORT_INPUT = 1 << 1, - MATE_MIXER_PORT_OUTPUT = 1 << 2 -} MateMixerPortFlags; - -/** - * MateMixerStreamFlags: - * @MATE_MIXER_STREAM_NO_FLAGS: - * @MATE_MIXER_STREAM_INPUT: - * @MATE_MIXER_STREAM_OUTPUT: - * @MATE_MIXER_STREAM_CLIENT: - * @MATE_MIXER_STREAM_HAS_MONITOR: - * @MATE_MIXER_STREAM_CAN_SUSPEND: - */ -typedef enum { /*< flags >*/ - MATE_MIXER_STREAM_NO_FLAGS = 0, - MATE_MIXER_STREAM_INPUT = 1 << 0, - MATE_MIXER_STREAM_OUTPUT = 1 << 1, - MATE_MIXER_STREAM_CLIENT = 1 << 2, - MATE_MIXER_STREAM_HAS_MONITOR = 1 << 3, - MATE_MIXER_STREAM_CAN_SUSPEND = 1 << 4, - MATE_MIXER_STREAM_PORTS_FIXED = 1 << 5, - MATE_MIXER_STREAM_PORTS_EXCLUSIVE = 1 << 6, -} MateMixerStreamFlags; - -/** - * MateMixerStreamState: - * @MATE_MIXER_STREAM_STATE_UNKNOWN: - * @MATE_MIXER_STREAM_STATE_RUNNING: - * @MATE_MIXER_STREAM_STATE_IDLE: - * @MATE_MIXER_STREAM_STATE_SUSPENDED: + * MateMixerDirection: */ typedef enum { - MATE_MIXER_STREAM_STATE_UNKNOWN, - MATE_MIXER_STREAM_STATE_RUNNING, - MATE_MIXER_STREAM_STATE_IDLE, - MATE_MIXER_STREAM_STATE_SUSPENDED -} MateMixerStreamState; + MATE_MIXER_DIRECTION_UNKNOWN, + MATE_MIXER_DIRECTION_INPUT, + MATE_MIXER_DIRECTION_OUTPUT, +} MateMixerDirection; /** * MateMixerStreamControlFlags: @@ -129,69 +95,62 @@ typedef enum { * @MATE_MIXER_STREAM_CONTROL_CAN_FADE: */ typedef enum { - MATE_MIXER_STREAM_CONTROL_NO_FLAGS = 0, - MATE_MIXER_STREAM_CONTROL_HAS_MUTE = 1 << 0, - MATE_MIXER_STREAM_CONTROL_HAS_VOLUME = 1 << 1, - MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL = 1 << 2, - MATE_MIXER_STREAM_CONTROL_HAS_FLAT_VOLUME = 1 << 3, - MATE_MIXER_STREAM_CONTROL_CAN_SET_MUTE = 1 << 4, - MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME = 1 << 5, - MATE_MIXER_STREAM_CONTROL_CAN_BALANCE = 1 << 6, - MATE_MIXER_STREAM_CONTROL_CAN_FADE = 1 << 7 + MATE_MIXER_STREAM_CONTROL_NO_FLAGS = 0, + MATE_MIXER_STREAM_CONTROL_MUTE_READABLE = 1 << 0, + MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE = 1 << 1, + MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE = 1 << 2, + MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE = 1 << 3, + MATE_MIXER_STREAM_CONTROL_CAN_BALANCE = 1 << 4, + MATE_MIXER_STREAM_CONTROL_CAN_FADE = 1 << 5, + MATE_MIXER_STREAM_CONTROL_MOVABLE = 1 << 6, + MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL = 1 << 7, + MATE_MIXER_STREAM_CONTROL_HAS_MONITOR = 1 << 8, + MATE_MIXER_STREAM_CONTROL_STORED = 1 << 9 } MateMixerStreamControlFlags; typedef enum { MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, - MATE_MIXER_STREAM_CONTROL_ROLE_PORT, + MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION, MATE_MIXER_STREAM_CONTROL_ROLE_PCM, + MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, + MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, + MATE_MIXER_STREAM_CONTROL_ROLE_PORT, + MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, MATE_MIXER_STREAM_CONTROL_ROLE_BASS, MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, MATE_MIXER_STREAM_CONTROL_ROLE_CD, - MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, + MATE_MIXER_STREAM_CONTROL_ROLE_VIDEO, + MATE_MIXER_STREAM_CONTROL_ROLE_MUSIC } MateMixerStreamControlRole; -/** - * MateMixerClientStreamFlags: - * @MATE_MIXER_CLIENT_STREAM_NO_FLAGS: - * @MATE_MIXER_CLIENT_STREAM_APPLICATION: - * @MATE_MIXER_CLIENT_STREAM_CACHED: - */ +typedef enum { + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN, + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_VIDEO, + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_MUSIC, + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_GAME, + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT, + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PHONE, + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ANIMATION, + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PRODUCTION, + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_A11Y, + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_TEST, + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ABSTRACT, + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_FILTER +} MateMixerStreamControlMediaRole; + typedef enum { /*< flags >*/ - MATE_MIXER_CLIENT_STREAM_NO_FLAGS = 0, - MATE_MIXER_CLIENT_STREAM_APPLICATION = 1 << 0, - MATE_MIXER_CLIENT_STREAM_CACHED = 1 << 1, -} MateMixerClientStreamFlags; + MATE_MIXER_SWITCH_NO_FLAGS = 0, + MATE_MIXER_SWITCH_TOGGLE = 1 << 0, + MATE_MIXER_SWITCH_ALLOWS_NO_ACTIVE_OPTION = 1 << 1 +} MateMixerSwitchFlags; -/** - * MateMixerClientStreamRole: - * @MATE_MIXER_CLIENT_STREAM_ROLE_NONE: - * @MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO: - * @MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC: - * @MATE_MIXER_CLIENT_STREAM_ROLE_GAME: - * @MATE_MIXER_CLIENT_STREAM_ROLE_EVENT: - * @MATE_MIXER_CLIENT_STREAM_ROLE_PHONE: - * @MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION: - * @MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION: - * @MATE_MIXER_CLIENT_STREAM_ROLE_A11Y: - * @MATE_MIXER_CLIENT_STREAM_ROLE_TEST: - * @MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT: - * @MATE_MIXER_CLIENT_STREAM_ROLE_FILTER: - */ typedef enum { - MATE_MIXER_CLIENT_STREAM_ROLE_NONE, - MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO, - MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC, - MATE_MIXER_CLIENT_STREAM_ROLE_GAME, - MATE_MIXER_CLIENT_STREAM_ROLE_EVENT, - MATE_MIXER_CLIENT_STREAM_ROLE_PHONE, - MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION, - MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION, - MATE_MIXER_CLIENT_STREAM_ROLE_A11Y, - MATE_MIXER_CLIENT_STREAM_ROLE_TEST, - MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT, - MATE_MIXER_CLIENT_STREAM_ROLE_FILTER -} MateMixerClientStreamRole; + MATE_MIXER_SWITCH_ROLE_UNKNOWN, + MATE_MIXER_SWITCH_ROLE_DEVICE_PROFILE, + MATE_MIXER_SWITCH_ROLE_PORT, + MATE_MIXER_SWITCH_ROLE_BOOST +} MateMixerSwitchRole; /** * MateMixerChannelPosition: diff --git a/libmatemixer/matemixer-private.h b/libmatemixer/matemixer-private.h index 4229000..41cd2e7 100644 --- a/libmatemixer/matemixer-private.h +++ b/libmatemixer/matemixer-private.h @@ -20,12 +20,15 @@ #include <glib.h> +#include "matemixer-enums.h" + +#include "matemixer-app-info-private.h" +#include "matemixer-backend.h" #include "matemixer-backend-module.h" -#include "matemixer-backend-private.h" -#include "matemixer-device-profile-private.h" +#include "matemixer-stream-private.h" #include "matemixer-stream-control-private.h" -#include "matemixer-switch-option-private.h" #include "matemixer-switch-private.h" +#include "matemixer-switch-option-private.h" G_BEGIN_DECLS @@ -103,7 +106,9 @@ G_BEGIN_DECLS #define MATE_MIXER_CHANNEL_MASK_HAS_FRONT(m) ((m) & MATE_MIXER_CHANNEL_MASK_FRONT) #define MATE_MIXER_CHANNEL_MASK_HAS_BACK(m) ((m) & MATE_MIXER_CHANNEL_MASK_BACK) -const GList *_mate_mixer_get_modules (void); +G_GNUC_INTERNAL +const GList *_mate_mixer_list_modules (void); + guint32 _mate_mixer_create_channel_mask (MateMixerChannelPosition *positions, guint n); G_END_DECLS diff --git a/libmatemixer/matemixer-stored-control.c b/libmatemixer/matemixer-stored-control.c new file mode 100644 index 0000000..eb2a448 --- /dev/null +++ b/libmatemixer/matemixer-stored-control.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib-object.h> + +#include "matemixer-enums.h" +#include "matemixer-enum-types.h" +#include "matemixer-stream-control.h" +#include "matemixer-stored-control.h" + +G_DEFINE_INTERFACE (MateMixerStoredControl, mate_mixer_stored_control, MATE_MIXER_TYPE_STREAM_CONTROL) + +static void +mate_mixer_stored_control_default_init (MateMixerStoredControlInterface *iface) +{ + g_object_interface_install_property (iface, + g_param_spec_enum ("direction", + "Direction", + "Direction of the stored control", + MATE_MIXER_TYPE_DIRECTION, + MATE_MIXER_DIRECTION_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +/** + * mate_mixer_stored_control_get_direction: + * @control: a #MateMixerStoredControl + */ +MateMixerDirection +mate_mixer_stored_control_get_direction (MateMixerStoredControl *control) +{ + g_return_val_if_fail (MATE_MIXER_IS_STORED_CONTROL (control), MATE_MIXER_DIRECTION_UNKNOWN); + + return MATE_MIXER_STORED_CONTROL_GET_INTERFACE (control)->get_direction (control); +} diff --git a/libmatemixer/matemixer-stored-control.h b/libmatemixer/matemixer-stored-control.h new file mode 100644 index 0000000..0c9c982 --- /dev/null +++ b/libmatemixer/matemixer-stored-control.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MATEMIXER_STORED_CONTROL_H +#define MATEMIXER_STORED_CONTROL_H + +#include <glib.h> +#include <glib-object.h> + +#include <libmatemixer/matemixer-enums.h> +#include <libmatemixer/matemixer-types.h> + +G_BEGIN_DECLS + +#define MATE_MIXER_TYPE_STORED_CONTROL \ + (mate_mixer_stored_control_get_type ()) +#define MATE_MIXER_STORED_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_STORED_CONTROL, MateMixerStoredControl)) +#define MATE_MIXER_IS_STORED_CONTROL(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_STORED_CONTROL)) +#define MATE_MIXER_STORED_CONTROL_GET_INTERFACE(o) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((o), MATE_MIXER_TYPE_STORED_CONTROL, MateMixerStoredControlInterface)) + +typedef struct _MateMixerStoredControlInterface MateMixerStoredControlInterface; + +/** + * MateMixerStoredControlInterface: + * + * The interface structure of #MateMixerStoredControl. + */ +struct _MateMixerStoredControlInterface +{ + GTypeInterface parent_iface; + + /*< private >*/ + MateMixerDirection (*get_direction) (MateMixerStoredControl *control); +}; + +GType mate_mixer_stored_control_get_type (void) G_GNUC_CONST; + +MateMixerDirection mate_mixer_stored_control_get_direction (MateMixerStoredControl *control); + +G_END_DECLS + +#endif /* MATEMIXER_STORED_CONTROL_H */ diff --git a/libmatemixer/matemixer-stream-control-private.h b/libmatemixer/matemixer-stream-control-private.h index 49454b3..7802d41 100644 --- a/libmatemixer/matemixer-stream-control-private.h +++ b/libmatemixer/matemixer-stream-control-private.h @@ -19,6 +19,7 @@ #define MATEMIXER_STREAM_CONTROL_PRIVATE_H #include <glib.h> + #include "matemixer-enums.h" #include "matemixer-types.h" @@ -27,6 +28,9 @@ G_BEGIN_DECLS void _mate_mixer_stream_control_set_flags (MateMixerStreamControl *control, MateMixerStreamControlFlags flags); +void _mate_mixer_stream_control_set_stream (MateMixerStreamControl *control, + MateMixerStream *stream); + void _mate_mixer_stream_control_set_mute (MateMixerStreamControl *control, gboolean mute); diff --git a/libmatemixer/matemixer-stream-control.c b/libmatemixer/matemixer-stream-control.c index ace584a..b2b2dad 100644 --- a/libmatemixer/matemixer-stream-control.c +++ b/libmatemixer/matemixer-stream-control.c @@ -20,6 +20,7 @@ #include "matemixer-enums.h" #include "matemixer-enum-types.h" +#include "matemixer-stream.h" #include "matemixer-stream-control.h" #include "matemixer-stream-control-private.h" @@ -30,13 +31,15 @@ struct _MateMixerStreamControlPrivate { - gchar *name; - gchar *label; - gboolean mute; - gfloat balance; - gfloat fade; - MateMixerStreamControlFlags flags; - MateMixerStreamControlRole role; + gchar *name; + gchar *label; + gboolean mute; + gfloat balance; + gfloat fade; + MateMixerStream *stream; + MateMixerStreamControlFlags flags; + MateMixerStreamControlRole role; + MateMixerStreamControlMediaRole media_role; }; enum { @@ -45,6 +48,8 @@ enum { PROP_LABEL, PROP_FLAGS, PROP_ROLE, + PROP_MEDIA_ROLE, + PROP_STREAM, PROP_MUTE, PROP_VOLUME, PROP_BALANCE, @@ -54,6 +59,13 @@ enum { static GParamSpec *properties[N_PROPERTIES] = { NULL, }; +enum { + MONITOR_VALUE, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0, }; + static void mate_mixer_stream_control_class_init (MateMixerStreamControlClass *klass); static void mate_mixer_stream_control_get_property (GObject *object, @@ -66,6 +78,7 @@ static void mate_mixer_stream_control_set_property (GObject GParamSpec *pspec); static void mate_mixer_stream_control_init (MateMixerStreamControl *control); +static void mate_mixer_stream_control_dispose (GObject *object); static void mate_mixer_stream_control_finalize (GObject *object); G_DEFINE_ABSTRACT_TYPE (MateMixerStreamControl, mate_mixer_stream_control, G_TYPE_OBJECT) @@ -76,6 +89,7 @@ mate_mixer_stream_control_class_init (MateMixerStreamControlClass *klass) GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); + object_class->dispose = mate_mixer_stream_control_dispose; object_class->finalize = mate_mixer_stream_control_finalize; object_class->get_property = mate_mixer_stream_control_get_property; object_class->set_property = mate_mixer_stream_control_set_property; @@ -110,13 +124,32 @@ mate_mixer_stream_control_class_init (MateMixerStreamControlClass *klass) properties[PROP_ROLE] = g_param_spec_enum ("role", - "Role", - "Role of the stream control", - MATE_MIXER_TYPE_STREAM_CONTROL_ROLE, - MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); + "Role", + "Role of the stream control", + MATE_MIXER_TYPE_STREAM_CONTROL_ROLE, + MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_MEDIA_ROLE] = + g_param_spec_enum ("media-role", + "Media role", + "Media role of the stream control", + MATE_MIXER_TYPE_STREAM_CONTROL_MEDIA_ROLE, + MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_STREAM] = + g_param_spec_object ("stream", + "Stream", + "Stream which owns the control", + MATE_MIXER_TYPE_STREAM, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); properties[PROP_MUTE] = g_param_spec_boolean ("mute", @@ -158,6 +191,18 @@ mate_mixer_stream_control_class_init (MateMixerStreamControlClass *klass) g_object_class_install_properties (object_class, N_PROPERTIES, properties); + signals[MONITOR_VALUE] = + g_signal_new ("monitor-value", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MateMixerStreamControlClass, monitor_value), + NULL, + NULL, + g_cclosure_marshal_VOID__DOUBLE, + G_TYPE_NONE, + 1, + G_TYPE_DOUBLE); + g_type_class_add_private (object_class, sizeof (MateMixerStreamControlPrivate)); } @@ -184,6 +229,12 @@ mate_mixer_stream_control_get_property (GObject *object, case PROP_ROLE: g_value_set_enum (value, control->priv->role); break; + case PROP_MEDIA_ROLE: + g_value_set_enum (value, control->priv->media_role); + break; + case PROP_STREAM: + g_value_set_object (value, control->priv->stream); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -216,6 +267,18 @@ mate_mixer_stream_control_set_property (GObject *object, case PROP_ROLE: control->priv->role = g_value_get_enum (value); break; + case PROP_MEDIA_ROLE: + control->priv->media_role = g_value_get_enum (value); + break; + case PROP_STREAM: + /* Construct-only object */ + control->priv->stream = g_value_get_object (value); + + if (control->priv->stream != NULL) { + g_object_add_weak_pointer (G_OBJECT (control->priv->stream), + (gpointer *) &control->priv->stream); + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); @@ -232,6 +295,18 @@ mate_mixer_stream_control_init (MateMixerStreamControl *control) } static void +mate_mixer_stream_control_dispose (GObject *object) +{ + MateMixerStreamControl *control; + + control = MATE_MIXER_STREAM_CONTROL (object); + + g_clear_object (&control->priv->stream); + + G_OBJECT_CLASS (mate_mixer_stream_control_parent_class)->dispose (object); +} + +static void mate_mixer_stream_control_finalize (GObject *object) { MateMixerStreamControl *control; @@ -244,6 +319,10 @@ mate_mixer_stream_control_finalize (GObject *object) G_OBJECT_CLASS (mate_mixer_stream_control_parent_class)->finalize (object); } +/** + * mate_mixer_stream_control_get_name: + * @control: a #MateMixerStreamControl + */ const gchar * mate_mixer_stream_control_get_name (MateMixerStreamControl *control) { @@ -252,6 +331,10 @@ mate_mixer_stream_control_get_name (MateMixerStreamControl *control) return control->priv->name; } +/** + * mate_mixer_stream_control_get_label: + * @control: a #MateMixerStreamControl + */ const gchar * mate_mixer_stream_control_get_label (MateMixerStreamControl *control) { @@ -260,6 +343,10 @@ mate_mixer_stream_control_get_label (MateMixerStreamControl *control) return control->priv->label; } +/** + * mate_mixer_stream_control_get_flags: + * @control: a #MateMixerStreamControl + */ MateMixerStreamControlFlags mate_mixer_stream_control_get_flags (MateMixerStreamControl *control) { @@ -268,6 +355,10 @@ mate_mixer_stream_control_get_flags (MateMixerStreamControl *control) return control->priv->flags; } +/** + * mate_mixer_stream_control_get_role: + * @control: a #MateMixerStreamControl + */ MateMixerStreamControlRole mate_mixer_stream_control_get_role (MateMixerStreamControl *control) { @@ -276,6 +367,79 @@ mate_mixer_stream_control_get_role (MateMixerStreamControl *control) return control->priv->role; } +/** + * mate_mixer_stream_control_get_media_role: + * @control: a #MateMixerStreamControl + */ +MateMixerStreamControlMediaRole +mate_mixer_stream_control_get_media_role (MateMixerStreamControl *control) +{ + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN); + + return control->priv->media_role; +} + +/** + * mate_mixer_stream_control_get_app_info: + * @control: a #MateMixerStreamControl + */ +MateMixerAppInfo * +mate_mixer_stream_control_get_app_info (MateMixerStreamControl *control) +{ + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), NULL); + + if (control->priv->role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) { + MateMixerStreamControlClass *klass; + + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + + if G_LIKELY (klass->get_app_info != NULL) + return klass->get_app_info (control); + } + return NULL; +} + +/** + * mate_mixer_stream_control_get_stream: + * @control: a #MateMixerStreamControl + */ +MateMixerStream * +mate_mixer_stream_control_get_stream (MateMixerStreamControl *control) +{ + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), NULL); + + return control->priv->stream; +} + +/** + * mate_mixer_stream_control_set_stream: + * @control: a #MateMixerStreamControl + */ +gboolean +mate_mixer_stream_control_set_stream (MateMixerStreamControl *control, + MateMixerStream *stream) +{ + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0); + + if (control->priv->stream != stream) { + MateMixerStreamControlClass *klass; + + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + + if (klass->set_stream == NULL || + klass->set_stream (control, stream) == FALSE) + return FALSE; + + _mate_mixer_stream_control_set_stream (control, stream); + } + + return TRUE; +} + +/** + * mate_mixer_stream_control_get_mute: + * @control: a #MateMixerStreamControl + */ gboolean mate_mixer_stream_control_get_mute (MateMixerStreamControl *control) { @@ -284,6 +448,10 @@ mate_mixer_stream_control_get_mute (MateMixerStreamControl *control) return control->priv->mute; } +/** + * mate_mixer_stream_control_set_mute: + * @control: a #MateMixerStreamControl + */ gboolean mate_mixer_stream_control_set_mute (MateMixerStreamControl *control, gboolean mute) { @@ -292,17 +460,21 @@ mate_mixer_stream_control_set_mute (MateMixerStreamControl *control, gboolean mu if (control->priv->mute == mute) return TRUE; - if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_MUTE) { + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE) { MateMixerStreamControlClass *klass; klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (G_LIKELY (klass->set_mute != NULL)) + if G_LIKELY (klass->set_mute != NULL) return klass->set_mute (control, mute); } return FALSE; } +/** + * mate_mixer_stream_control_get_num_channels: + * @control: a #MateMixerStreamControl + */ guint mate_mixer_stream_control_get_num_channels (MateMixerStreamControl *control) { @@ -318,38 +490,50 @@ mate_mixer_stream_control_get_num_channels (MateMixerStreamControl *control) return 0; } +/** + * mate_mixer_stream_control_get_volume: + * @control: a #MateMixerStreamControl + */ guint mate_mixer_stream_control_get_volume (MateMixerStreamControl *control) { - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0); + MateMixerStreamControlClass *klass; - if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_VOLUME) { - MateMixerStreamControlClass *klass; + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0); - klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) { if G_LIKELY (klass->get_volume != NULL) return klass->get_volume (control); } - return 0; + return klass->get_min_volume (control); } +/** + * mate_mixer_stream_control_set_volume: + * @control: a #MateMixerStreamControl + */ gboolean mate_mixer_stream_control_set_volume (MateMixerStreamControl *control, guint volume) { g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_VOLUME) { + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE) { MateMixerStreamControlClass *klass; klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (G_LIKELY (klass->set_volume != NULL)) + if G_LIKELY (klass->set_volume != NULL) return klass->set_volume (control, volume); } return FALSE; } +/** + * mate_mixer_stream_control_get_decibel: + * @control: a #MateMixerStreamControl + */ gdouble mate_mixer_stream_control_get_decibel (MateMixerStreamControl *control) { @@ -360,28 +544,57 @@ mate_mixer_stream_control_get_decibel (MateMixerStreamControl *control) klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (G_LIKELY (klass->get_decibel != NULL)) + if G_LIKELY (klass->get_decibel != NULL) return klass->get_decibel (control); } return -MATE_MIXER_INFINITY; } +/** + * mate_mixer_stream_control_set_decibel: + * @control: a #MateMixerStreamControl + */ gboolean mate_mixer_stream_control_set_decibel (MateMixerStreamControl *control, gdouble decibel) { g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL) { + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL && + control->priv->flags & MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE) { MateMixerStreamControlClass *klass; klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (G_LIKELY (klass->set_decibel != NULL)) + if G_LIKELY (klass->set_decibel != NULL) return klass->set_decibel (control, decibel); } return FALSE; } +/** + * mate_mixer_stream_control_has_channel_position: + * @control: a #MateMixerStreamControl + */ +gboolean +mate_mixer_stream_control_has_channel_position (MateMixerStreamControl *control, + MateMixerChannelPosition position) +{ + MateMixerStreamControlClass *klass; + + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); + + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + + if (klass->has_channel_position != NULL) + return klass->has_channel_position (control, position); + + return FALSE; +} + +/** + * mate_mixer_stream_control_get_channel_position: + * @control: a #MateMixerStreamControl + */ MateMixerChannelPosition mate_mixer_stream_control_get_channel_position (MateMixerStreamControl *control, guint channel) { @@ -397,22 +610,30 @@ mate_mixer_stream_control_get_channel_position (MateMixerStreamControl *control, return MATE_MIXER_CHANNEL_UNKNOWN; } +/** + * mate_mixer_stream_control_get_channel_volume: + * @control: a #MateMixerStreamControl + */ guint mate_mixer_stream_control_get_channel_volume (MateMixerStreamControl *control, guint channel) { g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), 0); - if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_VOLUME) { + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) { MateMixerStreamControlClass *klass; klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (G_LIKELY (klass->get_channel_volume != NULL)) + if G_LIKELY (klass->get_channel_volume != NULL) return klass->get_channel_volume (control, channel); } return 0; } +/** + * mate_mixer_stream_control_set_channel_volume: + * @control: a #MateMixerStreamControl + */ gboolean mate_mixer_stream_control_set_channel_volume (MateMixerStreamControl *control, guint channel, @@ -420,17 +641,21 @@ mate_mixer_stream_control_set_channel_volume (MateMixerStreamControl *control, { g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_VOLUME) { + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) { MateMixerStreamControlClass *klass; klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (G_LIKELY (klass->set_channel_volume != NULL)) + if G_LIKELY (klass->set_channel_volume != NULL) return klass->set_channel_volume (control, channel, volume); } return FALSE; } +/** + * mate_mixer_stream_control_get_channel_decibel: + * @control: a #MateMixerStreamControl + */ gdouble mate_mixer_stream_control_get_channel_decibel (MateMixerStreamControl *control, guint channel) { @@ -441,12 +666,16 @@ mate_mixer_stream_control_get_channel_decibel (MateMixerStreamControl *control, klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (G_LIKELY (klass->get_channel_decibel != NULL)) + if G_LIKELY (klass->get_channel_decibel != NULL) return klass->get_channel_decibel (control, channel); } return -MATE_MIXER_INFINITY; } +/** + * mate_mixer_stream_control_set_channel_decibel: + * @control: a #MateMixerStreamControl + */ gboolean mate_mixer_stream_control_set_channel_decibel (MateMixerStreamControl *control, guint channel, @@ -459,28 +688,16 @@ mate_mixer_stream_control_set_channel_decibel (MateMixerStreamControl *control, klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (G_LIKELY (klass->set_channel_decibel != NULL)) + if G_LIKELY (klass->set_channel_decibel != NULL) return klass->set_channel_decibel (control, channel, decibel); } return FALSE; } -gboolean -mate_mixer_stream_control_has_channel_position (MateMixerStreamControl *control, - MateMixerChannelPosition position) -{ - MateMixerStreamControlClass *klass; - - g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); - - klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - - if (klass->has_channel_position != NULL) - return klass->has_channel_position (control, position); - - return FALSE; -} - +/** + * mate_mixer_stream_control_get_balance: + * @control: a #MateMixerStreamControl + */ gfloat mate_mixer_stream_control_get_balance (MateMixerStreamControl *control) { @@ -492,6 +709,10 @@ mate_mixer_stream_control_get_balance (MateMixerStreamControl *control) return 0.0f; } +/** + * mate_mixer_stream_control_set_balance: + * @control: a #MateMixerStreamControl + */ gboolean mate_mixer_stream_control_set_balance (MateMixerStreamControl *control, gfloat balance) { @@ -511,6 +732,10 @@ mate_mixer_stream_control_set_balance (MateMixerStreamControl *control, gfloat b return FALSE; } +/** + * mate_mixer_stream_control_get_fade: + * @control: a #MateMixerStreamControl + */ gfloat mate_mixer_stream_control_get_fade (MateMixerStreamControl *control) { @@ -522,6 +747,10 @@ mate_mixer_stream_control_get_fade (MateMixerStreamControl *control) return 0.0f; } +/** + * mate_mixer_stream_control_set_fade: + * @control: a #MateMixerStreamControl + */ gboolean mate_mixer_stream_control_set_fade (MateMixerStreamControl *control, gfloat fade) { @@ -535,12 +764,59 @@ mate_mixer_stream_control_set_fade (MateMixerStreamControl *control, gfloat fade klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); - if (G_LIKELY (klass->set_fade != NULL)) + if (klass->set_fade != NULL) return klass->set_fade (control, fade); } return FALSE; } +/** + * mate_mixer_stream_control_get_monitor_enabled: + * @control: a #MateMixerStreamControl + */ +gboolean +mate_mixer_stream_control_get_monitor_enabled (MateMixerStreamControl *control) +{ + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); + + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_MONITOR) { + MateMixerStreamControlClass *klass; + + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + if (klass->get_monitor_enabled != NULL) + return klass->get_monitor_enabled (control); + } + + return FALSE; +} + +/** + * mate_mixer_stream_control_set_monitor_enabled: + * @control: a #MateMixerStreamControl + */ +gboolean +mate_mixer_stream_control_set_monitor_enabled (MateMixerStreamControl *control, gboolean enabled) +{ + g_return_val_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control), FALSE); + + if (enabled == mate_mixer_stream_control_get_monitor_enabled (control)) + return TRUE; + + if (control->priv->flags & MATE_MIXER_STREAM_CONTROL_HAS_MONITOR) { + MateMixerStreamControlClass *klass; + + klass = MATE_MIXER_STREAM_CONTROL_GET_CLASS (control); + if (klass->set_monitor_enabled != NULL) + return klass->set_monitor_enabled (control, enabled); + } + + return FALSE; +} + +/** + * mate_mixer_stream_control_get_min_volume: + * @control: a #MateMixerStreamControl + */ guint mate_mixer_stream_control_get_min_volume (MateMixerStreamControl *control) { @@ -556,6 +832,10 @@ mate_mixer_stream_control_get_min_volume (MateMixerStreamControl *control) return 0; } +/** + * mate_mixer_stream_control_get_max_volume: + * @control: a #MateMixerStreamControl + */ guint mate_mixer_stream_control_get_max_volume (MateMixerStreamControl *control) { @@ -571,6 +851,10 @@ mate_mixer_stream_control_get_max_volume (MateMixerStreamControl *control) return 0; } +/** + * mate_mixer_stream_control_get_normal_volume: + * @control: a #MateMixerStreamControl + */ guint mate_mixer_stream_control_get_normal_volume (MateMixerStreamControl *control) { @@ -586,6 +870,10 @@ mate_mixer_stream_control_get_normal_volume (MateMixerStreamControl *control) return 0; } +/** + * mate_mixer_stream_control_get_base_volume: + * @control: a #MateMixerStreamControl + */ guint mate_mixer_stream_control_get_base_volume (MateMixerStreamControl *control) { @@ -601,27 +889,77 @@ mate_mixer_stream_control_get_base_volume (MateMixerStreamControl *control) return 0; } +/* Protected functions */ void _mate_mixer_stream_control_set_flags (MateMixerStreamControl *control, MateMixerStreamControlFlags flags) { + g_return_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control)); + + if (control->priv->flags == flags) + return; + control->priv->flags = flags; + + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_FLAGS]); +} + +/* Protected functions */ +void +_mate_mixer_stream_control_set_stream (MateMixerStreamControl *control, + MateMixerStream *stream) +{ + g_return_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control)); + + if (control->priv->stream == stream) + return; + + if (control->priv->stream != NULL) + g_object_unref (control->priv->stream); + + if (stream != NULL) + control->priv->stream = g_object_ref (stream); + else + control->priv->stream = NULL; + + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_STREAM]); } void _mate_mixer_stream_control_set_mute (MateMixerStreamControl *control, gboolean mute) { + g_return_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control)); + + if (control->priv->mute == mute) + return; + control->priv->mute = mute; + + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_MUTE]); } void _mate_mixer_stream_control_set_balance (MateMixerStreamControl *control, gfloat balance) { + g_return_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control)); + + if (control->priv->balance == balance) + return; + control->priv->balance = balance; + + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_BALANCE]); } void _mate_mixer_stream_control_set_fade (MateMixerStreamControl *control, gfloat fade) { + g_return_if_fail (MATE_MIXER_IS_STREAM_CONTROL (control)); + + if (control->priv->fade == fade) + return; + control->priv->fade = fade; + + g_object_notify_by_pspec (G_OBJECT (control), properties[PROP_FADE]); } diff --git a/libmatemixer/matemixer-stream-control.h b/libmatemixer/matemixer-stream-control.h index 51d7f95..2adf97f 100644 --- a/libmatemixer/matemixer-stream-control.h +++ b/libmatemixer/matemixer-stream-control.h @@ -49,6 +49,12 @@ G_BEGIN_DECLS typedef struct _MateMixerStreamControlClass MateMixerStreamControlClass; typedef struct _MateMixerStreamControlPrivate MateMixerStreamControlPrivate; +/** + * MateMixerStreamControl: + * + * The #MateMixerStreamControl structure contains only private data and should only + * be accessed using the provided API. + */ struct _MateMixerStreamControl { GObject object; @@ -57,103 +63,132 @@ struct _MateMixerStreamControl MateMixerStreamControlPrivate *priv; }; +/** + * MateMixerStreamControlClass: + * + * The class structure of #MateMixerStreamControl. + */ struct _MateMixerStreamControlClass { GObjectClass parent_class; /*< private >*/ - gboolean (*set_mute) (MateMixerStreamControl *control, - gboolean mute); - - guint (*get_num_channels) (MateMixerStreamControl *control); - - guint (*get_volume) (MateMixerStreamControl *control); - gboolean (*set_volume) (MateMixerStreamControl *control, - guint volume); - - gdouble (*get_decibel) (MateMixerStreamControl *control); - gboolean (*set_decibel) (MateMixerStreamControl *control, - gdouble decibel); - - gboolean (*has_channel_position) (MateMixerStreamControl *control, - MateMixerChannelPosition position); - MateMixerChannelPosition (*get_channel_position) (MateMixerStreamControl *control, - guint channel); - - guint (*get_channel_volume) (MateMixerStreamControl *control, - guint channel); - gboolean (*set_channel_volume) (MateMixerStreamControl *control, - guint channel, - guint volume); - - gdouble (*get_channel_decibel) (MateMixerStreamControl *control, - guint channel); - gboolean (*set_channel_decibel) (MateMixerStreamControl *control, - guint channel, - gdouble decibel); - - gboolean (*set_balance) (MateMixerStreamControl *control, - gfloat balance); - - gboolean (*set_fade) (MateMixerStreamControl *control, - gfloat fade); - - guint (*get_min_volume) (MateMixerStreamControl *control); - guint (*get_max_volume) (MateMixerStreamControl *control); - guint (*get_normal_volume) (MateMixerStreamControl *control); - guint (*get_base_volume) (MateMixerStreamControl *control); + MateMixerAppInfo * (*get_app_info) (MateMixerStreamControl *control); + + gboolean (*set_stream) (MateMixerStreamControl *control, + MateMixerStream *stream); + + gboolean (*set_mute) (MateMixerStreamControl *control, + gboolean mute); + + guint (*get_num_channels) (MateMixerStreamControl *control); + + guint (*get_volume) (MateMixerStreamControl *control); + gboolean (*set_volume) (MateMixerStreamControl *control, + guint volume); + + gdouble (*get_decibel) (MateMixerStreamControl *control); + gboolean (*set_decibel) (MateMixerStreamControl *control, + gdouble decibel); + + gboolean (*has_channel_position) (MateMixerStreamControl *control, + MateMixerChannelPosition position); + MateMixerChannelPosition (*get_channel_position) (MateMixerStreamControl *control, + guint channel); + + guint (*get_channel_volume) (MateMixerStreamControl *control, + guint channel); + gboolean (*set_channel_volume) (MateMixerStreamControl *control, + guint channel, + guint volume); + + gdouble (*get_channel_decibel) (MateMixerStreamControl *control, + guint channel); + gboolean (*set_channel_decibel) (MateMixerStreamControl *control, + guint channel, + gdouble decibel); + + gboolean (*set_balance) (MateMixerStreamControl *control, + gfloat balance); + + gboolean (*set_fade) (MateMixerStreamControl *control, + gfloat fade); + + gboolean (*get_monitor_enabled) (MateMixerStreamControl *control); + gboolean (*set_monitor_enabled) (MateMixerStreamControl *control, + gboolean enabled); + + guint (*get_min_volume) (MateMixerStreamControl *control); + guint (*get_max_volume) (MateMixerStreamControl *control); + guint (*get_normal_volume) (MateMixerStreamControl *control); + guint (*get_base_volume) (MateMixerStreamControl *control); + + /* Signals */ + void (*monitor_value) (MateMixerStreamControl *control, + gdouble value); }; -GType mate_mixer_stream_control_get_type (void) G_GNUC_CONST; +GType mate_mixer_stream_control_get_type (void) G_GNUC_CONST; + +const gchar * mate_mixer_stream_control_get_name (MateMixerStreamControl *control); +const gchar * mate_mixer_stream_control_get_label (MateMixerStreamControl *control); +MateMixerStreamControlFlags mate_mixer_stream_control_get_flags (MateMixerStreamControl *control); +MateMixerStreamControlRole mate_mixer_stream_control_get_role (MateMixerStreamControl *control); +MateMixerStreamControlMediaRole mate_mixer_stream_control_get_media_role (MateMixerStreamControl *control); + +MateMixerAppInfo * mate_mixer_stream_control_get_app_info (MateMixerStreamControl *control); + +MateMixerStream * mate_mixer_stream_control_get_stream (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_set_stream (MateMixerStreamControl *control, + MateMixerStream *stream); -const gchar * mate_mixer_stream_control_get_name (MateMixerStreamControl *control); -const gchar * mate_mixer_stream_control_get_label (MateMixerStreamControl *control); -MateMixerStreamControlFlags mate_mixer_stream_control_get_flags (MateMixerStreamControl *control); -MateMixerStreamControlRole mate_mixer_stream_control_get_role (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_get_mute (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_set_mute (MateMixerStreamControl *control, + gboolean mute); -gboolean mate_mixer_stream_control_get_mute (MateMixerStreamControl *control); -gboolean mate_mixer_stream_control_set_mute (MateMixerStreamControl *control, - gboolean mute); +guint mate_mixer_stream_control_get_num_channels (MateMixerStreamControl *control); -guint mate_mixer_stream_control_get_num_channels (MateMixerStreamControl *control); +guint mate_mixer_stream_control_get_volume (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_set_volume (MateMixerStreamControl *control, + guint volume); -guint mate_mixer_stream_control_get_volume (MateMixerStreamControl *control); -gboolean mate_mixer_stream_control_set_volume (MateMixerStreamControl *control, - guint volume); +gdouble mate_mixer_stream_control_get_decibel (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_set_decibel (MateMixerStreamControl *control, + gdouble decibel); -gdouble mate_mixer_stream_control_get_decibel (MateMixerStreamControl *control); -gboolean mate_mixer_stream_control_set_decibel (MateMixerStreamControl *control, - gdouble decibel); +gboolean mate_mixer_stream_control_has_channel_position (MateMixerStreamControl *control, + MateMixerChannelPosition position); +MateMixerChannelPosition mate_mixer_stream_control_get_channel_position (MateMixerStreamControl *control, + guint channel); -gboolean mate_mixer_stream_control_has_channel_position (MateMixerStreamControl *control, - MateMixerChannelPosition position); -MateMixerChannelPosition mate_mixer_stream_control_get_channel_position (MateMixerStreamControl *control, - guint channel); +guint mate_mixer_stream_control_get_channel_volume (MateMixerStreamControl *control, + guint channel); +gboolean mate_mixer_stream_control_set_channel_volume (MateMixerStreamControl *control, + guint channel, + guint volume); -guint mate_mixer_stream_control_get_channel_volume (MateMixerStreamControl *control, - guint channel); -gboolean mate_mixer_stream_control_set_channel_volume (MateMixerStreamControl *control, - guint channel, - guint volume); +gdouble mate_mixer_stream_control_get_channel_decibel (MateMixerStreamControl *control, + guint channel); +gboolean mate_mixer_stream_control_set_channel_decibel (MateMixerStreamControl *control, + guint channel, + gdouble decibel); -gdouble mate_mixer_stream_control_get_channel_decibel (MateMixerStreamControl *control, - guint channel); -gboolean mate_mixer_stream_control_set_channel_decibel (MateMixerStreamControl *control, - guint channel, - gdouble decibel); +gfloat mate_mixer_stream_control_get_balance (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_set_balance (MateMixerStreamControl *control, + gfloat balance); -gfloat mate_mixer_stream_control_get_balance (MateMixerStreamControl *control); -gboolean mate_mixer_stream_control_set_balance (MateMixerStreamControl *control, - gfloat balance); +gfloat mate_mixer_stream_control_get_fade (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_set_fade (MateMixerStreamControl *control, + gfloat fade); -gfloat mate_mixer_stream_control_get_fade (MateMixerStreamControl *control); -gboolean mate_mixer_stream_control_set_fade (MateMixerStreamControl *control, - gfloat fade); +gboolean mate_mixer_stream_control_get_monitor_enabled (MateMixerStreamControl *control); +gboolean mate_mixer_stream_control_set_monitor_enabled (MateMixerStreamControl *control, + gboolean enabled); -guint mate_mixer_stream_control_get_min_volume (MateMixerStreamControl *control); -guint mate_mixer_stream_control_get_max_volume (MateMixerStreamControl *control); -guint mate_mixer_stream_control_get_normal_volume (MateMixerStreamControl *control); -guint mate_mixer_stream_control_get_base_volume (MateMixerStreamControl *control); +guint mate_mixer_stream_control_get_min_volume (MateMixerStreamControl *control); +guint mate_mixer_stream_control_get_max_volume (MateMixerStreamControl *control); +guint mate_mixer_stream_control_get_normal_volume (MateMixerStreamControl *control); +guint mate_mixer_stream_control_get_base_volume (MateMixerStreamControl *control); G_END_DECLS diff --git a/libmatemixer/matemixer-stream-private.h b/libmatemixer/matemixer-stream-private.h new file mode 100644 index 0000000..84243ae --- /dev/null +++ b/libmatemixer/matemixer-stream-private.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 Michal Ratajsky <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MATEMIXER_STREAM_PRIVATE_H +#define MATEMIXER_STREAM_PRIVATE_H + +#include <glib.h> + +#include "matemixer-types.h" + +G_BEGIN_DECLS + +void _mate_mixer_stream_set_default_control (MateMixerStream *stream, + MateMixerStreamControl *control); + +G_END_DECLS + +#endif /* MATEMIXER_STREAM_PRIVATE_H */ diff --git a/libmatemixer/matemixer-stream.c b/libmatemixer/matemixer-stream.c index 1902de3..03902bd 100644 --- a/libmatemixer/matemixer-stream.c +++ b/libmatemixer/matemixer-stream.c @@ -24,6 +24,7 @@ #include "matemixer-enum-types.h" #include "matemixer-stream.h" #include "matemixer-stream-control.h" +#include "matemixer-stream-private.h" #include "matemixer-switch.h" /** @@ -34,27 +35,29 @@ struct _MateMixerStreamPrivate { gchar *name; - GList *controls; - GList *switches; - gboolean monitor_enabled; + gchar *label; + MateMixerDirection direction; MateMixerDevice *device; - MateMixerStreamFlags flags; - MateMixerStreamState state; + MateMixerStreamControl *control; }; enum { PROP_0, PROP_NAME, + PROP_LABEL, + PROP_DIRECTION, PROP_DEVICE, - PROP_FLAGS, - PROP_STATE, + PROP_DEFAULT_CONTROL, N_PROPERTIES }; static GParamSpec *properties[N_PROPERTIES] = { NULL, }; enum { - MONITOR_VALUE, + CONTROL_ADDED, + CONTROL_REMOVED, + SWITCH_ADDED, + SWITCH_REMOVED, N_SIGNALS }; @@ -105,6 +108,25 @@ mate_mixer_stream_class_init (MateMixerStreamClass *klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + properties[PROP_LABEL] = + g_param_spec_string ("label", + "Label", + "Label of the stream", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_DIRECTION] = + g_param_spec_enum ("direction", + "Direction", + "Direction of the stream", + MATE_MIXER_TYPE_DIRECTION, + MATE_MIXER_DIRECTION_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + properties[PROP_DEVICE] = g_param_spec_object ("device", "Device", @@ -114,39 +136,64 @@ mate_mixer_stream_class_init (MateMixerStreamClass *klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - properties[PROP_FLAGS] = - g_param_spec_flags ("flags", - "Flags", - "Capability flags of the stream", - MATE_MIXER_TYPE_STREAM_FLAGS, - MATE_MIXER_STREAM_NO_FLAGS, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - properties[PROP_STATE] = - g_param_spec_enum ("state", - "State", - "Current state of the stream", - MATE_MIXER_TYPE_STREAM_STATE, - MATE_MIXER_STREAM_STATE_UNKNOWN, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); + properties[PROP_DEFAULT_CONTROL] = + g_param_spec_object ("default-control", + "Default control", + "Default control of the stream", + MATE_MIXER_TYPE_STREAM_CONTROL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPERTIES, properties); - signals[MONITOR_VALUE] = - g_signal_new ("monitor-value", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (MateMixerStreamClass, monitor_value), + signals[CONTROL_ADDED] = + g_signal_new ("control-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MateMixerStreamClass, control_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[CONTROL_REMOVED] = + g_signal_new ("control-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MateMixerStreamClass, control_removed), NULL, NULL, - g_cclosure_marshal_VOID__DOUBLE, + g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, - G_TYPE_DOUBLE); + G_TYPE_STRING); + + signals[SWITCH_ADDED] = + g_signal_new ("switch-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MateMixerStreamClass, switch_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals[SWITCH_REMOVED] = + g_signal_new ("switch-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MateMixerStreamClass, switch_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, + G_TYPE_STRING); g_type_class_add_private (object_class, sizeof (MateMixerStreamPrivate)); } @@ -165,14 +212,17 @@ mate_mixer_stream_get_property (GObject *object, case PROP_NAME: g_value_set_string (value, stream->priv->name); break; + case PROP_LABEL: + g_value_set_string (value, stream->priv->label); + break; + case PROP_DIRECTION: + g_value_set_enum (value, stream->priv->direction); + break; case PROP_DEVICE: g_value_set_object (value, stream->priv->device); break; - case PROP_FLAGS: - g_value_set_flags (value, stream->priv->flags); - break; - case PROP_STATE: - g_value_set_enum (value, stream->priv->state); + case PROP_DEFAULT_CONTROL: + g_value_set_object (value, stream->priv->control); break; default: @@ -196,15 +246,25 @@ mate_mixer_stream_set_property (GObject *object, /* Construct-only string */ stream->priv->name = g_value_dup_string (value); break; + case PROP_LABEL: + /* Construct-only string */ + stream->priv->label = g_value_dup_string (value); + break; + case PROP_DIRECTION: + stream->priv->direction = g_value_get_enum (value); + break; case PROP_DEVICE: /* Construct-only object */ - stream->priv->device = g_value_dup_object (value); - break; - case PROP_FLAGS: - stream->priv->flags = g_value_get_flags (value); + stream->priv->device = g_value_get_object (value); + + if (stream->priv->device != NULL) { + g_object_add_weak_pointer (G_OBJECT (stream->priv->device), + (gpointer *) &stream->priv->device); + } break; - case PROP_STATE: - stream->priv->state = g_value_get_enum (value); + case PROP_DEFAULT_CONTROL: + /* Construct-only object */ + stream->priv->control = g_value_dup_object (value); break; default: @@ -228,7 +288,7 @@ mate_mixer_stream_dispose (GObject *object) stream = MATE_MIXER_STREAM (object); - g_clear_object (&stream->priv->device); + g_clear_object (&stream->priv->control); G_OBJECT_CLASS (mate_mixer_stream_parent_class)->dispose (object); } @@ -245,6 +305,10 @@ mate_mixer_stream_finalize (GObject *object) G_OBJECT_CLASS (mate_mixer_stream_parent_class)->finalize (object); } +/** + * mate_mixer_stream_get_name: + * @stream: a #MateMixerStream + */ const gchar * mate_mixer_stream_get_name (MateMixerStream *stream) { @@ -253,30 +317,46 @@ mate_mixer_stream_get_name (MateMixerStream *stream) return stream->priv->name; } -MateMixerDevice * -mate_mixer_stream_get_device (MateMixerStream *stream) +/** + * mate_mixer_stream_get_label: + * @stream: a #MateMixerStream + */ +const gchar * +mate_mixer_stream_get_label (MateMixerStream *stream) { g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - return stream->priv->device; + return stream->priv->label; } -MateMixerStreamFlags -mate_mixer_stream_get_flags (MateMixerStream *stream) +/** + * mate_mixer_stream_get_direction: + * @stream: a #MateMixerStream + */ +MateMixerDirection +mate_mixer_stream_get_direction (MateMixerStream *stream) { - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_NO_FLAGS); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_DIRECTION_UNKNOWN); - return stream->priv->flags; + return stream->priv->direction; } -MateMixerStreamState -mate_mixer_stream_get_state (MateMixerStream *stream) +/** + * mate_mixer_stream_get_device: + * @stream: a #MateMixerStream + */ +MateMixerDevice * +mate_mixer_stream_get_device (MateMixerStream *stream) { - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), MATE_MIXER_STREAM_STATE_UNKNOWN); + g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - return stream->priv->state; + return stream->priv->device; } +/** + * mate_mixer_stream_get_control: + * @stream: a #MateMixerStream + */ MateMixerStreamControl * mate_mixer_stream_get_control (MateMixerStream *stream, const gchar *name) { @@ -286,6 +366,10 @@ mate_mixer_stream_get_control (MateMixerStream *stream, const gchar *name) return MATE_MIXER_STREAM_GET_CLASS (stream)->get_control (stream, name); } +/** + * mate_mixer_stream_get_switch: + * @stream: a #MateMixerStream + */ MateMixerSwitch * mate_mixer_stream_get_switch (MateMixerStream *stream, const gchar *name) { @@ -295,92 +379,40 @@ mate_mixer_stream_get_switch (MateMixerStream *stream, const gchar *name) return MATE_MIXER_STREAM_GET_CLASS (stream)->get_switch (stream, name); } +/** + * mate_mixer_stream_get_default_control: + * @stream: a #MateMixerStream + */ MateMixerStreamControl * mate_mixer_stream_get_default_control (MateMixerStream *stream) { g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - return MATE_MIXER_STREAM_GET_CLASS (stream)->get_default_control (stream); + return stream->priv->control; } +/** + * mate_mixer_stream_list_controls: + * @stream: a #MateMixerStream + */ const GList * mate_mixer_stream_list_controls (MateMixerStream *stream) { g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - if (stream->priv->controls == NULL) - stream->priv->controls = MATE_MIXER_STREAM_GET_CLASS (stream)->list_controls (stream); - - return (const GList *) stream->priv->controls; + return MATE_MIXER_STREAM_GET_CLASS (stream)->list_controls (stream); } +/** + * mate_mixer_stream_list_switches: + * @stream: a #MateMixerStream + */ const GList * mate_mixer_stream_list_switches (MateMixerStream *stream) { g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), NULL); - if (stream->priv->switches == NULL) { - MateMixerStreamClass *klass = MATE_MIXER_STREAM_GET_CLASS (stream); - - if (klass->list_switches != NULL) - stream->priv->switches = klass->list_switches (stream); - } - - return (const GList *) stream->priv->switches; -} - -gboolean -mate_mixer_stream_suspend (MateMixerStream *stream) -{ - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - if (stream->priv->state == MATE_MIXER_STREAM_STATE_SUSPENDED) - return TRUE; - - if (stream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND) - return MATE_MIXER_STREAM_GET_CLASS (stream)->suspend (stream); - - return FALSE; -} - -gboolean -mate_mixer_stream_resume (MateMixerStream *stream) -{ - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - if (stream->priv->state != MATE_MIXER_STREAM_STATE_SUSPENDED) - return TRUE; - - if (stream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND) - return MATE_MIXER_STREAM_GET_CLASS (stream)->resume (stream); - - return FALSE; -} - -gboolean -mate_mixer_stream_monitor_get_enabled (MateMixerStream *stream) -{ - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - return stream->priv->monitor_enabled; -} - -gboolean -mate_mixer_stream_monitor_set_enabled (MateMixerStream *stream, gboolean enabled) -{ - g_return_val_if_fail (MATE_MIXER_IS_STREAM (stream), FALSE); - - if (stream->priv->monitor_enabled == enabled) - return TRUE; - - if (stream->priv->flags & MATE_MIXER_STREAM_HAS_MONITOR) { - if (enabled) - return MATE_MIXER_STREAM_GET_CLASS (stream)->monitor_start (stream); - else - return MATE_MIXER_STREAM_GET_CLASS (stream)->monitor_stop (stream); - } - - return FALSE; + return MATE_MIXER_STREAM_GET_CLASS (stream)->list_switches (stream); } static MateMixerStreamControl * @@ -422,3 +454,34 @@ mate_mixer_stream_real_get_switch (MateMixerStream *stream, const gchar *name) } return NULL; } + +void +_mate_mixer_stream_set_default_control (MateMixerStream *stream, + MateMixerStreamControl *control) +{ + g_return_if_fail (MATE_MIXER_IS_STREAM (stream)); + g_return_if_fail (control == NULL || MATE_MIXER_IS_STREAM_CONTROL (control)); + + if (stream->priv->control == control) + return; + + if (stream->priv->control != NULL) + g_object_unref (stream->priv->control); + + /* The default control is allowed to be NULL */ + if (control != NULL) { + stream->priv->control = g_object_ref (control); + + g_debug ("Stream %s default control changed to %s", + mate_mixer_stream_get_name (stream), + mate_mixer_stream_control_get_name (control)); + } else { + stream->priv->control = NULL; + + g_debug ("Stream %s default control unset", + mate_mixer_stream_get_name (stream)); + } + + g_object_notify_by_pspec (G_OBJECT (stream), + properties[PROP_DEFAULT_CONTROL]); +} diff --git a/libmatemixer/matemixer-stream.h b/libmatemixer/matemixer-stream.h index e73579e..c030196 100644 --- a/libmatemixer/matemixer-stream.h +++ b/libmatemixer/matemixer-stream.h @@ -42,6 +42,12 @@ G_BEGIN_DECLS typedef struct _MateMixerStreamClass MateMixerStreamClass; typedef struct _MateMixerStreamPrivate MateMixerStreamPrivate; +/** + * MateMixerStream: + * + * The #MateMixerStream structure contains only private data and should only + * be accessed using the provided API. + */ struct _MateMixerStream { GObject object; @@ -50,39 +56,43 @@ struct _MateMixerStream MateMixerStreamPrivate *priv; }; +/** + * MateMixerStreamClass: + * + * The class structure of #MateMixerStream. + */ struct _MateMixerStreamClass { GObjectClass parent_class; /*< private >*/ - MateMixerStreamControl *(*get_control) (MateMixerStream *stream, - const gchar *name); - - MateMixerStreamControl *(*get_default_control) (MateMixerStream *stream); - - MateMixerSwitch *(*get_switch) (MateMixerStream *stream, - const gchar *name); + MateMixerStreamControl *(*get_control) (MateMixerStream *stream, + const gchar *name); + MateMixerSwitch *(*get_switch) (MateMixerStream *stream, + const gchar *name); - GList *(*list_controls) (MateMixerStream *stream); - GList *(*list_switches) (MateMixerStream *stream); - - gboolean (*suspend) (MateMixerStream *stream); - gboolean (*resume) (MateMixerStream *stream); - - gboolean (*monitor_start) (MateMixerStream *stream); - gboolean (*monitor_stop) (MateMixerStream *stream); + const GList *(*list_controls) (MateMixerStream *stream); + const GList *(*list_switches) (MateMixerStream *stream); /* Signals */ - void (*monitor_value) (MateMixerStream *stream, gdouble value); + void (*control_added) (MateMixerStream *stream, + const gchar *name); + void (*control_removed) (MateMixerStream *stream, + const gchar *name); + + void (*switch_added) (MateMixerStream *stream, + const gchar *name); + void (*switch_removed) (MateMixerStream *stream, + const gchar *name); }; GType mate_mixer_stream_get_type (void) G_GNUC_CONST; const gchar * mate_mixer_stream_get_name (MateMixerStream *stream); -MateMixerDevice * mate_mixer_stream_get_device (MateMixerStream *stream); -MateMixerStreamFlags mate_mixer_stream_get_flags (MateMixerStream *stream); -MateMixerStreamState mate_mixer_stream_get_state (MateMixerStream *stream); +const gchar * mate_mixer_stream_get_label (MateMixerStream *stream); +MateMixerDirection mate_mixer_stream_get_direction (MateMixerStream *stream); +MateMixerDevice * mate_mixer_stream_get_device (MateMixerStream *stream); MateMixerStreamControl *mate_mixer_stream_get_control (MateMixerStream *stream, const gchar *name); MateMixerSwitch * mate_mixer_stream_get_switch (MateMixerStream *stream, @@ -93,13 +103,6 @@ MateMixerStreamControl *mate_mixer_stream_get_default_control (MateMixerStream * const GList * mate_mixer_stream_list_controls (MateMixerStream *stream); const GList * mate_mixer_stream_list_switches (MateMixerStream *stream); -gboolean mate_mixer_stream_suspend (MateMixerStream *stream); -gboolean mate_mixer_stream_resume (MateMixerStream *stream); - -gboolean mate_mixer_stream_monitor_get_enabled (MateMixerStream *stream); -gboolean mate_mixer_stream_monitor_set_enabled (MateMixerStream *stream, - gboolean enabled); - G_END_DECLS #endif /* MATEMIXER_STREAM_H */ diff --git a/libmatemixer/matemixer-switch-option-private.h b/libmatemixer/matemixer-switch-option-private.h index dea7583..d084dd5 100644 --- a/libmatemixer/matemixer-switch-option-private.h +++ b/libmatemixer/matemixer-switch-option-private.h @@ -20,7 +20,7 @@ #include <glib.h> -#include "matemixer-switch-option.h" +#include "matemixer-types.h" G_BEGIN_DECLS diff --git a/libmatemixer/matemixer-switch-option.c b/libmatemixer/matemixer-switch-option.c index e924b46..fc0c5b6 100644 --- a/libmatemixer/matemixer-switch-option.c +++ b/libmatemixer/matemixer-switch-option.c @@ -121,6 +121,7 @@ mate_mixer_switch_option_get_property (GObject *object, case PROP_ICON: g_value_set_string (value, option->priv->icon); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -150,6 +151,7 @@ mate_mixer_switch_option_set_property (GObject *object, /* Construct-only string */ option->priv->icon = g_value_dup_string (value); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; diff --git a/libmatemixer/matemixer-switch-option.h b/libmatemixer/matemixer-switch-option.h index b02c42c..ae87718 100644 --- a/libmatemixer/matemixer-switch-option.h +++ b/libmatemixer/matemixer-switch-option.h @@ -20,6 +20,7 @@ #include <glib.h> #include <glib-object.h> + #include <libmatemixer/matemixer-types.h> G_BEGIN_DECLS @@ -40,6 +41,12 @@ G_BEGIN_DECLS typedef struct _MateMixerSwitchOptionClass MateMixerSwitchOptionClass; typedef struct _MateMixerSwitchOptionPrivate MateMixerSwitchOptionPrivate; +/** + * MateMixerSwitchOption: + * + * The #MateMixerSwitchOption structure contains only private data and should only + * be accessed using the provided API. + */ struct _MateMixerSwitchOption { GObject parent; @@ -48,6 +55,11 @@ struct _MateMixerSwitchOption MateMixerSwitchOptionPrivate *priv; }; +/** + * MateMixerSwitchOptionClass: + * + * The class structure of #MateMixerSwitchOption. + */ struct _MateMixerSwitchOptionClass { GObjectClass parent_class; diff --git a/libmatemixer/matemixer-switch-private.h b/libmatemixer/matemixer-switch-private.h index 42390a9..bd4761e 100644 --- a/libmatemixer/matemixer-switch-private.h +++ b/libmatemixer/matemixer-switch-private.h @@ -19,6 +19,7 @@ #define MATEMIXER_SWITCH_PRIVATE_H #include <glib.h> + #include "matemixer-types.h" G_BEGIN_DECLS diff --git a/libmatemixer/matemixer-switch.c b/libmatemixer/matemixer-switch.c index b30e405..461b49e 100644 --- a/libmatemixer/matemixer-switch.c +++ b/libmatemixer/matemixer-switch.c @@ -34,7 +34,8 @@ struct _MateMixerSwitchPrivate { gchar *name; gchar *label; - GList *options; + MateMixerSwitchFlags flags; + MateMixerSwitchRole role; MateMixerSwitchOption *active; }; @@ -42,6 +43,8 @@ enum { PROP_0, PROP_NAME, PROP_LABEL, + PROP_FLAGS, + PROP_ROLE, PROP_ACTIVE_OPTION, N_PROPERTIES }; @@ -60,6 +63,7 @@ static void mate_mixer_switch_set_property (GObject *object, GParamSpec *pspec); static void mate_mixer_switch_init (MateMixerSwitch *swtch); +static void mate_mixer_switch_dispose (GObject *object); static void mate_mixer_switch_finalize (GObject *object); G_DEFINE_ABSTRACT_TYPE (MateMixerSwitch, mate_mixer_switch, G_TYPE_OBJECT) @@ -75,6 +79,7 @@ mate_mixer_switch_class_init (MateMixerSwitchClass *klass) klass->get_option = mate_mixer_switch_real_get_option; object_class = G_OBJECT_CLASS (klass); + object_class->dispose = mate_mixer_switch_dispose; object_class->finalize = mate_mixer_switch_finalize; object_class->get_property = mate_mixer_switch_get_property; object_class->set_property = mate_mixer_switch_set_property; @@ -97,12 +102,33 @@ mate_mixer_switch_class_init (MateMixerSwitchClass *klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + properties[PROP_ROLE] = + g_param_spec_enum ("role", + "Role", + "Role of the switch", + MATE_MIXER_TYPE_SWITCH_ROLE, + MATE_MIXER_SWITCH_ROLE_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_FLAGS] = + g_param_spec_flags ("flags", + "Flags", + "Flags of the switch", + MATE_MIXER_TYPE_SWITCH_FLAGS, + MATE_MIXER_SWITCH_NO_FLAGS, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + properties[PROP_ACTIVE_OPTION] = g_param_spec_object ("active-option", "Active option", "Active option of the switch", MATE_MIXER_TYPE_SWITCH_OPTION, - G_PARAM_READABLE | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPERTIES, properties); @@ -127,6 +153,16 @@ mate_mixer_switch_get_property (GObject *object, case PROP_LABEL: g_value_set_string (value, swtch->priv->label); break; + case PROP_FLAGS: + g_value_set_flags (value, swtch->priv->flags); + break; + case PROP_ROLE: + g_value_set_enum (value, swtch->priv->role); + break; + case PROP_ACTIVE_OPTION: + g_value_set_object (value, swtch->priv->active); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -152,6 +188,17 @@ mate_mixer_switch_set_property (GObject *object, /* Construct-only string */ swtch->priv->label = g_value_dup_string (value); break; + case PROP_FLAGS: + swtch->priv->flags = g_value_get_flags (value); + break; + case PROP_ROLE: + swtch->priv->role = g_value_get_enum (value); + break; + case PROP_ACTIVE_OPTION: + /* Construct-only object */ + swtch->priv->active = g_value_dup_object (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -167,6 +214,18 @@ mate_mixer_switch_init (MateMixerSwitch *swtch) } static void +mate_mixer_switch_dispose (GObject *object) +{ + MateMixerSwitch *swtch; + + swtch = MATE_MIXER_SWITCH (object); + + g_clear_object (&swtch->priv->active); + + G_OBJECT_CLASS (mate_mixer_switch_parent_class)->dispose (object); +} + +static void mate_mixer_switch_finalize (GObject *object) { MateMixerSwitch *swtch; @@ -179,6 +238,10 @@ mate_mixer_switch_finalize (GObject *object) G_OBJECT_CLASS (mate_mixer_switch_parent_class)->finalize (object); } +/** + * mate_mixer_switch_get_name: + * @swtch: a #MateMixerSwitch + */ const gchar * mate_mixer_switch_get_name (MateMixerSwitch *swtch) { @@ -187,6 +250,10 @@ mate_mixer_switch_get_name (MateMixerSwitch *swtch) return swtch->priv->name; } +/** + * mate_mixer_switch_get_label: + * @swtch: a #MateMixerSwitch + */ const gchar * mate_mixer_switch_get_label (MateMixerSwitch *swtch) { @@ -195,6 +262,34 @@ mate_mixer_switch_get_label (MateMixerSwitch *swtch) return swtch->priv->label; } +/** + * mate_mixer_switch_get_flags: + * @swtch: a #MateMixerSwitch + */ +MateMixerSwitchFlags +mate_mixer_switch_get_flags (MateMixerSwitch *swtch) +{ + g_return_val_if_fail (MATE_MIXER_IS_SWITCH (swtch), MATE_MIXER_SWITCH_NO_FLAGS); + + return swtch->priv->flags; +} + +/** + * mate_mixer_switch_get_role: + * @swtch: a #MateMixerSwitch + */ +MateMixerSwitchRole +mate_mixer_switch_get_role (MateMixerSwitch *swtch) +{ + g_return_val_if_fail (MATE_MIXER_IS_SWITCH (swtch), MATE_MIXER_SWITCH_ROLE_UNKNOWN); + + return swtch->priv->role; +} + +/** + * mate_mixer_switch_get_option: + * @swtch: a #MateMixerSwitch + */ MateMixerSwitchOption * mate_mixer_switch_get_option (MateMixerSwitch *swtch, const gchar *name) { @@ -203,6 +298,10 @@ mate_mixer_switch_get_option (MateMixerSwitch *swtch, const gchar *name) return MATE_MIXER_SWITCH_GET_CLASS (swtch)->get_option (swtch, name); } +/** + * mate_mixer_switch_get_active_option: + * @swtch: a #MateMixerSwitch + */ MateMixerSwitchOption * mate_mixer_switch_get_active_option (MateMixerSwitch *swtch) { @@ -211,45 +310,48 @@ mate_mixer_switch_get_active_option (MateMixerSwitch *swtch) return swtch->priv->active; } +/** + * mate_mixer_switch_set_active_option: + * @swtch: a #MateMixerSwitch + */ gboolean mate_mixer_switch_set_active_option (MateMixerSwitch *swtch, MateMixerSwitchOption *option) { - MateMixerSwitchClass *klass; - g_return_val_if_fail (MATE_MIXER_IS_SWITCH (swtch), FALSE); - klass = MATE_MIXER_SWITCH_GET_CLASS (swtch); - if (klass->set_active_option != NULL) { - if (klass->set_active_option (swtch, option) == FALSE) + if (swtch->priv->active != option) { + MateMixerSwitchClass *klass; + + klass = MATE_MIXER_SWITCH_GET_CLASS (swtch); + if (klass->set_active_option == NULL || + klass->set_active_option (swtch, option) == FALSE) return FALSE; _mate_mixer_switch_set_active_option (swtch, option); - return TRUE; } - return FALSE; + return TRUE; } +/** + * mate_mixer_switch_get_name: + * @swtch: a #MateMixerSwitch + */ const GList * mate_mixer_switch_list_options (MateMixerSwitch *swtch) { g_return_val_if_fail (MATE_MIXER_IS_SWITCH (swtch), NULL); - if (swtch->priv->options == NULL) { - MateMixerSwitchClass *klass = MATE_MIXER_SWITCH_GET_CLASS (swtch); - - if (klass->list_options != NULL) - swtch->priv->options = klass->list_options (swtch); - } - return (const GList *) swtch->priv->options; + return MATE_MIXER_SWITCH_GET_CLASS (swtch)->list_options (swtch); } +/* Protected functions */ void _mate_mixer_switch_set_active_option (MateMixerSwitch *swtch, MateMixerSwitchOption *option) { g_return_if_fail (MATE_MIXER_IS_SWITCH (swtch)); - g_return_if_fail (MATE_MIXER_IS_SWITCH_OPTION (option)); + g_return_if_fail (option == NULL || MATE_MIXER_IS_SWITCH_OPTION (option)); if (swtch->priv->active == option) return; @@ -257,9 +359,13 @@ _mate_mixer_switch_set_active_option (MateMixerSwitch *swtch, if (swtch->priv->active != NULL) g_object_unref (swtch->priv->active); - swtch->priv->active = g_object_ref (option); + if (option != NULL) + swtch->priv->active = g_object_ref (option); + else + swtch->priv->active = NULL; - g_object_notify_by_pspec (G_OBJECT (swtch), properties[PROP_ACTIVE_OPTION]); + g_object_notify_by_pspec (G_OBJECT (swtch), + properties[PROP_ACTIVE_OPTION]); } static MateMixerSwitchOption * diff --git a/libmatemixer/matemixer-switch.h b/libmatemixer/matemixer-switch.h index 3035607..7f0109f 100644 --- a/libmatemixer/matemixer-switch.h +++ b/libmatemixer/matemixer-switch.h @@ -21,6 +21,7 @@ #include <glib.h> #include <glib-object.h> +#include <libmatemixer/matemixer-enums.h> #include <libmatemixer/matemixer-types.h> G_BEGIN_DECLS @@ -41,6 +42,12 @@ G_BEGIN_DECLS typedef struct _MateMixerSwitchClass MateMixerSwitchClass; typedef struct _MateMixerSwitchPrivate MateMixerSwitchPrivate; +/** + * MateMixerSwitch: + * + * The #MateMixerSwitch structure contains only private data and should only + * be accessed using the provided API. + */ struct _MateMixerSwitch { GObject object; @@ -49,6 +56,11 @@ struct _MateMixerSwitch MateMixerSwitchPrivate *priv; }; +/** + * MateMixerSwitchClass: + * + * The class structure of #MateMixerSwitch. + */ struct _MateMixerSwitchClass { GObjectClass parent_class; @@ -57,26 +69,28 @@ struct _MateMixerSwitchClass MateMixerSwitchOption *(*get_option) (MateMixerSwitch *swtch, const gchar *name); + const GList *(*list_options) (MateMixerSwitch *swtch); + gboolean (*set_active_option) (MateMixerSwitch *swtch, MateMixerSwitchOption *option); - - GList *(*list_options) (MateMixerSwitch *swtch); }; GType mate_mixer_switch_get_type (void) G_GNUC_CONST; const gchar * mate_mixer_switch_get_name (MateMixerSwitch *swtch); const gchar * mate_mixer_switch_get_label (MateMixerSwitch *swtch); +MateMixerSwitchFlags mate_mixer_switch_get_flags (MateMixerSwitch *swtch); +MateMixerSwitchRole mate_mixer_switch_get_role (MateMixerSwitch *swtch); MateMixerSwitchOption *mate_mixer_switch_get_option (MateMixerSwitch *swtch, const gchar *name); +const GList * mate_mixer_switch_list_options (MateMixerSwitch *swtch); + MateMixerSwitchOption *mate_mixer_switch_get_active_option (MateMixerSwitch *swtch); gboolean mate_mixer_switch_set_active_option (MateMixerSwitch *swtch, MateMixerSwitchOption *option); -const GList * mate_mixer_switch_list_options (MateMixerSwitch *swtch); - G_END_DECLS #endif /* MATEMIXER_SWITCH_H */ diff --git a/libmatemixer/matemixer-toggle.c b/libmatemixer/matemixer-toggle.c index 2eea599..0db6e3b 100644 --- a/libmatemixer/matemixer-toggle.c +++ b/libmatemixer/matemixer-toggle.c @@ -30,6 +30,7 @@ struct _MateMixerTogglePrivate { + GList *options; MateMixerSwitchOption *on; MateMixerSwitchOption *off; }; @@ -63,7 +64,7 @@ G_DEFINE_ABSTRACT_TYPE (MateMixerToggle, mate_mixer_toggle, MATE_MIXER_TYPE_SWIT static MateMixerSwitchOption *mate_mixer_toggle_get_option (MateMixerSwitch *swtch, const gchar *name); -static GList * mate_mixer_toggle_list_options (MateMixerSwitch *swtch); +static const GList * mate_mixer_toggle_list_options (MateMixerSwitch *swtch); static void mate_mixer_toggle_class_init (MateMixerToggleClass *klass) @@ -83,7 +84,7 @@ mate_mixer_toggle_class_init (MateMixerToggleClass *klass) properties[PROP_STATE] = g_param_spec_boolean ("state", "State", - "Current state", + "Current state of the toggle", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); @@ -131,6 +132,7 @@ mate_mixer_toggle_get_property (GObject *object, case PROP_STATE_OPTION_OFF: g_value_set_object (value, toggle->priv->off); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -156,6 +158,7 @@ mate_mixer_toggle_set_property (GObject *object, /* Construct-only object */ toggle->priv->off = g_value_dup_object (value); break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -177,12 +180,22 @@ mate_mixer_toggle_dispose (GObject *object) toggle = MATE_MIXER_TOGGLE (object); + if (toggle->priv->options != NULL) { + g_list_free_full (toggle->priv->options, g_object_unref); + toggle->priv->options = NULL; + } + + /* FIXME: crashes on ALSA without the polling thread */ g_clear_object (&toggle->priv->on); g_clear_object (&toggle->priv->off); G_OBJECT_CLASS (mate_mixer_toggle_parent_class)->dispose (object); } +/** + * mate_mixer_toggle_get_state: + * @toggle: a #MateMixerToggle + */ gboolean mate_mixer_toggle_get_state (MateMixerToggle *toggle) { @@ -197,6 +210,10 @@ mate_mixer_toggle_get_state (MateMixerToggle *toggle) return FALSE; } +/** + * mate_mixer_toggle_get_state_option: + * @toggle: a #MateMixerToggle + */ MateMixerSwitchOption * mate_mixer_toggle_get_state_option (MateMixerToggle *toggle, gboolean state) { @@ -208,6 +225,10 @@ mate_mixer_toggle_get_state_option (MateMixerToggle *toggle, gboolean state) return toggle->priv->off; } +/** + * mate_mixer_toggle_set_state: + * @toggle: a #MateMixerToggle + */ gboolean mate_mixer_toggle_set_state (MateMixerToggle *toggle, gboolean state) { @@ -240,15 +261,20 @@ mate_mixer_toggle_get_option (MateMixerSwitch *swtch, const gchar *name) return NULL; } -static GList * +static const GList * mate_mixer_toggle_list_options (MateMixerSwitch *swtch) { - GList *list = NULL; + MateMixerToggle *toggle; g_return_val_if_fail (MATE_MIXER_IS_TOGGLE (swtch), NULL); - list = g_list_prepend (list, MATE_MIXER_TOGGLE (swtch)->priv->off); - list = g_list_prepend (list, MATE_MIXER_TOGGLE (swtch)->priv->on); + toggle = MATE_MIXER_TOGGLE (swtch); - return list; + if (toggle->priv->options == NULL) { + toggle->priv->options = g_list_prepend (toggle->priv->options, + toggle->priv->off); + toggle->priv->options = g_list_prepend (toggle->priv->options, + toggle->priv->on); + } + return toggle->priv->options; } diff --git a/libmatemixer/matemixer-toggle.h b/libmatemixer/matemixer-toggle.h index 1cedfc5..44946f7 100644 --- a/libmatemixer/matemixer-toggle.h +++ b/libmatemixer/matemixer-toggle.h @@ -25,22 +25,28 @@ G_BEGIN_DECLS -#define MATE_MIXER_TYPE_TOGGLE \ +#define MATE_MIXER_TYPE_TOGGLE \ (mate_mixer_toggle_get_type ()) -#define MATE_MIXER_TOGGLE(o) \ +#define MATE_MIXER_TOGGLE(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), MATE_MIXER_TYPE_TOGGLE, MateMixerToggle)) -#define MATE_MIXER_IS_TOGGLE(o) \ +#define MATE_MIXER_IS_TOGGLE(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), MATE_MIXER_TYPE_TOGGLE)) -#define MATE_MIXER_TOGGLE_CLASS(k) \ +#define MATE_MIXER_TOGGLE_CLASS(k) \ (G_TYPE_CHECK_CLASS_CAST ((k), MATE_MIXER_TYPE_TOGGLE, MateMixerToggleClass)) -#define MATE_MIXER_IS_TOGGLE_CLASS(k) \ +#define MATE_MIXER_IS_TOGGLE_CLASS(k) \ (G_TYPE_CHECK_CLASS_TYPE ((k), MATE_MIXER_TYPE_TOGGLE)) -#define MATE_MIXER_TOGGLE_GET_CLASS(o) \ +#define MATE_MIXER_TOGGLE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), MATE_MIXER_TYPE_TOGGLE, MateMixerToggleClass)) typedef struct _MateMixerToggleClass MateMixerToggleClass; typedef struct _MateMixerTogglePrivate MateMixerTogglePrivate; +/** + * MateMixerToggle: + * + * The #MateMixerToggle structure contains only private data and should only + * be accessed using the provided API. + */ struct _MateMixerToggle { MateMixerSwitch object; @@ -49,6 +55,11 @@ struct _MateMixerToggle MateMixerTogglePrivate *priv; }; +/** + * MateMixerToggleClass: + * + * The class structure of #MateMixerToggle. + */ struct _MateMixerToggleClass { MateMixerSwitchClass parent_class; diff --git a/libmatemixer/matemixer-types.h b/libmatemixer/matemixer-types.h index 6601a95..57a64a1 100644 --- a/libmatemixer/matemixer-types.h +++ b/libmatemixer/matemixer-types.h @@ -20,10 +20,11 @@ G_BEGIN_DECLS +typedef struct _MateMixerAppInfo MateMixerAppInfo; typedef struct _MateMixerDevice MateMixerDevice; -typedef struct _MateMixerClientStream MateMixerClientStream; typedef struct _MateMixerContext MateMixerContext; typedef struct _MateMixerDeviceProfile MateMixerDeviceProfile; +typedef struct _MateMixerStoredControl MateMixerStoredControl; typedef struct _MateMixerStream MateMixerStream; typedef struct _MateMixerStreamControl MateMixerStreamControl; typedef struct _MateMixerSwitch MateMixerSwitch; diff --git a/libmatemixer/matemixer.c b/libmatemixer/matemixer.c index fa83e3f..a5bca6f 100644 --- a/libmatemixer/matemixer.c +++ b/libmatemixer/matemixer.c @@ -100,13 +100,14 @@ mate_mixer_is_initialized (void) return initialized; } -/* Return a list of loaded backend modules */ +/* Internal: return a list of loaded backend modules */ const GList * -_mate_mixer_get_modules (void) +_mate_mixer_list_modules (void) { return (const GList *) modules; } +/* Internal: create a channel mask using the given list of channel positions */ guint32 _mate_mixer_create_channel_mask (MateMixerChannelPosition *positions, guint n) { diff --git a/libmatemixer/matemixer.h b/libmatemixer/matemixer.h index a6eac79..589bf0d 100644 --- a/libmatemixer/matemixer.h +++ b/libmatemixer/matemixer.h @@ -21,11 +21,14 @@ #include <glib.h> #include <glib-object.h> -#include <libmatemixer/matemixer-client-stream.h> +#include <libmatemixer/matemixer-types.h> + +#include <libmatemixer/matemixer-app-info.h> #include <libmatemixer/matemixer-context.h> #include <libmatemixer/matemixer-device.h> -#include <libmatemixer/matemixer-device-profile.h> #include <libmatemixer/matemixer-enums.h> +#include <libmatemixer/matemixer-enum-types.h> +#include <libmatemixer/matemixer-stored-control.h> #include <libmatemixer/matemixer-stream.h> #include <libmatemixer/matemixer-stream-control.h> #include <libmatemixer/matemixer-switch.h> diff --git a/po/ChangeLog b/po/ChangeLog new file mode 100644 index 0000000..f59ae48 --- /dev/null +++ b/po/ChangeLog @@ -0,0 +1,12 @@ +2014-08-16 gettextize <[email protected]> + + * Makefile.in.in: New file, from gettext-0.19.2. + * Rules-quot: New file, from gettext-0.19.2. + * boldquot.sed: New file, from gettext-0.19.2. + * [email protected]: New file, from gettext-0.19.2. + * [email protected]: New file, from gettext-0.19.2. + * insert-header.sin: New file, from gettext-0.19.2. + * quot.sed: New file, from gettext-0.19.2. + * remove-potcdate.sin: New file, from gettext-0.19.2. + * POTFILES.in: New file. + diff --git a/po/Makevars b/po/Makevars new file mode 100644 index 0000000..32692ab --- /dev/null +++ b/po/Makevars @@ -0,0 +1,41 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Free Software Foundation, Inc. + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..667e27c --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1 @@ +# List of source files which contain translatable strings. |