kmix: drop it

everything it attempts to do is done by other glue-code such as
pulseaudio or pipewire, alsa volume is usually restored on boot too

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2023-09-21 04:04:25 +03:00
parent fcf7c498bd
commit 41110f2399
162 changed files with 0 additions and 24839 deletions

View file

@ -22,7 +22,6 @@ kde4_optional_add_subdirectory(kcalc)
kde4_optional_add_subdirectory(kdeplasma-addons)
kde4_optional_add_subdirectory(kemu)
kde4_optional_add_subdirectory(kget)
kde4_optional_add_subdirectory(kmix)
kde4_optional_add_subdirectory(krdc)
kde4_optional_add_subdirectory(krfb)
kde4_optional_add_subdirectory(ksnapshot)

View file

@ -1,229 +0,0 @@
project(kmix)
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
include(FeatureSummary)
find_package(KDELibs4 4.23.0 REQUIRED)
include_directories(${KDE4_INCLUDES})
add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
endif()
kde4_optional_find_package(ALSA)
set_package_properties(ALSA PROPERTIES
DESCRIPTION "Advanced Linux Sound Architecture"
URL "https://alsa-project.org/wiki/Main_Page"
PURPOSE "Needed for KMix sound feedback"
)
configure_file(
apps/kmixremote.cmake
${CMAKE_CURRENT_BINARY_DIR}/kmixremote
@ONLY
)
# NetBSD
find_library(LIBOSSAUDIO_LIBRARY NAMES ossaudio)
add_definitions(-DKDE_DEFAULT_DEBUG_AREA=67100)
include_directories(/usr/lib/oss/include)
add_subdirectory( pics )
add_subdirectory( profiles )
#add_subdirectory( tests )
if (ALSA_FOUND)
add_definitions(-DHAVE_ALSA)
include_directories(${ALSA_INCLUDE_DIR})
endif (ALSA_FOUND)
set(kmix_adaptor_SRCS
dbus/dbusmixerwrapper.cpp
dbus/dbusmixsetwrapper.cpp
dbus/dbuscontrolwrapper.cpp
)
qt4_add_dbus_adaptor( kmix_adaptor_SRCS
dbus/org.kde.kmix.control.xml
dbus/dbuscontrolwrapper.h DBusControlWrapper
)
qt4_add_dbus_adaptor( kmix_adaptor_SRCS
dbus/org.kde.kmix.mixer.xml
dbus/dbusmixerwrapper.h DBusMixerWrapper
)
qt4_add_dbus_adaptor( kmix_adaptor_SRCS
dbus/org.kde.kmix.mixset.xml
dbus/dbusmixsetwrapper.h DBusMixSetWrapper
)
set(kmix_backend_SRCS
backends/mixer_backend.cpp
backends/mixer_mpris2.cpp
)
if(ALSA_FOUND)
set(kmix_backend_SRCS ${kmix_backend_SRCS}
backends/mixer_alsa9.cpp )
endif()
set(kmix_SRCS ${kmix_adaptor_SRCS} ${kmix_backend_SRCS}
apps/main.cpp
apps/kmix.cpp
apps/KMixApp.cpp
gui/kmixdockwidget.cpp
gui/kmixprefdlg.cpp
gui/viewbase.cpp
gui/viewdockareapopup.cpp
gui/viewsliders.cpp
gui/mixdevicewidget.cpp
gui/mdwmoveaction.cpp
gui/mdwslider.cpp
gui/mdwenum.cpp
gui/kmixerwidget.cpp
gui/ksmallslider.cpp
gui/verticaltext.cpp
gui/volumeslider.cpp
gui/kmixtoolbox.cpp
gui/dialogaddview.cpp
gui/dialogviewconfiguration.cpp
gui/dialogselectmaster.cpp
gui/dialogchoosebackends.cpp
gui/guiprofile.cpp
gui/osdwidget.cpp
core/MediaController.cpp
core/mixertoolbox.cpp
core/kmixdevicemanager.cpp
core/ControlManager.cpp
# core/ControlPool.cpp
core/GlobalConfig.cpp
core/MasterControl.cpp
core/mixer.cpp
core/mixset.cpp
core/mixdevice.cpp
core/mixdevicecomposite.cpp
core/volume.cpp
)
add_executable(kmix ${kmix_SRCS})
target_link_libraries(kmix
KDE4::solid
KDE4::kdeui
KDE4::plasma
${QT_QTXML_LIBRARY}
${X11_X11_LIB}
)
if(ALSA_FOUND)
target_link_libraries(kmix ${ALSA_LIBRARIES})
endif()
if(LIBOSSAUDIO_LIBRARY)
target_link_libraries(kmix ${LIBOSSAUDIO_LIBRARY})
endif()
install(
TARGETS kmix
DESTINATION ${KDE4_BIN_INSTALL_DIR}
)
########### next target ###############
set(kded_kmixd_SRCS ${kmix_adaptor_SRCS} ${kmix_backend_SRCS}
apps/kmixd.cpp
core/ControlManager.cpp
# core/ControlPool.cpp
core/GlobalConfig.cpp
core/MasterControl.cpp
core/MediaController.cpp
core/mixer.cpp
core/mixset.cpp
core/mixdevice.cpp
core/volume.cpp
core/mixertoolbox.cpp
core/kmixdevicemanager.cpp
)
#qt4_add_dbus_adaptor(kded_kmixd_SRCS org.kde.KMixD.xml kmixd.h Mixer)
kde4_add_plugin(kded_kmixd ${kded_kmixd_SRCS})
target_link_libraries(kded_kmixd
KDE4::kdeui
KDE4::solid
${QT_QTXML_LIBRARY}
)
if(ALSA_FOUND)
target_link_libraries(kded_kmixd ${ALSA_LIBRARIES})
endif()
if(LIBOSSAUDIO_LIBRARY)
target_link_libraries(kded_kmixd ${LIBOSSAUDIO_LIBRARY})
endif()
install(TARGETS kded_kmixd DESTINATION ${KDE4_PLUGIN_INSTALL_DIR})
#target_link_libraries( kmixd kded_kmixd )
#install(TARGETS kmixd DESTINATION ${KDE4_PLUGIN_INSTALL_DIR} )
install(FILES kmixd.desktop DESTINATION ${KDE4_SERVICES_INSTALL_DIR}/kded)
########### next target ###############
set(kmixctrl_SRCS ${kmix_adaptor_SRCS} ${kmix_backend_SRCS}
apps/kmixctrl.cpp
core/ControlManager.cpp
# core/ControlPool.cpp
core/GlobalConfig.cpp
core/MasterControl.cpp
core/MediaController.cpp
core/mixer.cpp
core/mixset.cpp
core/mixdevice.cpp
core/volume.cpp
core/mixertoolbox.cpp
core/kmixdevicemanager.cpp
)
# gui/guiprofile.cpp
add_executable(kmixctrl ${kmixctrl_SRCS})
target_link_libraries(kmixctrl
KDE4::kdeui
KDE4::solid
${QT_QTXML_LIBRARY}
)
if(ALSA_FOUND)
target_link_libraries(kmixctrl ${ALSA_LIBRARIES})
endif()
if(LIBOSSAUDIO_LIBRARY)
target_link_libraries(kmixctrl ${LIBOSSAUDIO_LIBRARY})
endif()
########### next target ###############
add_subdirectory( plasma )
install(
TARGETS kmixctrl
DESTINATION ${KDE4_BIN_INSTALL_DIR}
)
install(PROGRAMS kmix.desktop DESTINATION ${KDE4_XDG_APPS_INSTALL_DIR})
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/kmixremote DESTINATION ${KDE4_BIN_INSTALL_DIR})
install(FILES restore_kmix_volumes.desktop DESTINATION ${KDE4_AUTOSTART_INSTALL_DIR})
install(FILES kmix_autostart.desktop DESTINATION ${KDE4_AUTOSTART_INSTALL_DIR})
install(FILES kmixui.rc DESTINATION ${KDE4_DATA_INSTALL_DIR}/kmix)
install(FILES kmixctrl_restore.desktop DESTINATION ${KDE4_SERVICES_INSTALL_DIR})
install(FILES dbus/org.kde.kmix.control.xml DESTINATION ${KDE4_DBUS_INTERFACES_INSTALL_DIR})
install(FILES dbus/org.kde.kmix.mixer.xml DESTINATION ${KDE4_DBUS_INTERFACES_INSTALL_DIR})
install(FILES dbus/org.kde.kmix.mixset.xml DESTINATION ${KDE4_DBUS_INTERFACES_INSTALL_DIR})
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
endif()

View file

@ -1,339 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View file

@ -1,397 +0,0 @@
GNU Free Documentation License
Version 1.2, November 2002
Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
0. PREAMBLE
The purpose of this License is to make a manual, textbook, or other
functional and useful document "free" in the sense of freedom: to
assure everyone the effective freedom to copy and redistribute it,
with or without modifying it, either commercially or noncommercially.
Secondarily, this License preserves for the author and publisher a way
to get credit for their work, while not being considered responsible
for modifications made by others.
This License is a kind of "copyleft", which means that derivative
works of the document must themselves be free in the same sense. It
complements the GNU General Public License, which is a copyleft
license designed for free software.
We have designed this License in order to use it for manuals for free
software, because free software needs free documentation: a free
program should come with manuals providing the same freedoms that the
software does. But this License is not limited to software manuals;
it can be used for any textual work, regardless of subject matter or
whether it is published as a printed book. We recommend this License
principally for works whose purpose is instruction or reference.
1. APPLICABILITY AND DEFINITIONS
This License applies to any manual or other work, in any medium, that
contains a notice placed by the copyright holder saying it can be
distributed under the terms of this License. Such a notice grants a
world-wide, royalty-free license, unlimited in duration, to use that
work under the conditions stated herein. The "Document", below,
refers to any such manual or work. Any member of the public is a
licensee, and is addressed as "you". You accept the license if you
copy, modify or distribute the work in a way requiring permission
under copyright law.
A "Modified Version" of the Document means any work containing the
Document or a portion of it, either copied verbatim, or with
modifications and/or translated into another language.
A "Secondary Section" is a named appendix or a front-matter section of
the Document that deals exclusively with the relationship of the
publishers or authors of the Document to the Document's overall subject
(or to related matters) and contains nothing that could fall directly
within that overall subject. (Thus, if the Document is in part a
textbook of mathematics, a Secondary Section may not explain any
mathematics.) The relationship could be a matter of historical
connection with the subject or with related matters, or of legal,
commercial, philosophical, ethical or political position regarding
them.
The "Invariant Sections" are certain Secondary Sections whose titles
are designated, as being those of Invariant Sections, in the notice
that says that the Document is released under this License. If a
section does not fit the above definition of Secondary then it is not
allowed to be designated as Invariant. The Document may contain zero
Invariant Sections. If the Document does not identify any Invariant
Sections then there are none.
The "Cover Texts" are certain short passages of text that are listed,
as Front-Cover Texts or Back-Cover Texts, in the notice that says that
the Document is released under this License. A Front-Cover Text may
be at most 5 words, and a Back-Cover Text may be at most 25 words.
A "Transparent" copy of the Document means a machine-readable copy,
represented in a format whose specification is available to the
general public, that is suitable for revising the document
straightforwardly with generic text editors or (for images composed of
pixels) generic paint programs or (for drawings) some widely available
drawing editor, and that is suitable for input to text formatters or
for automatic translation to a variety of formats suitable for input
to text formatters. A copy made in an otherwise Transparent file
format whose markup, or absence of markup, has been arranged to thwart
or discourage subsequent modification by readers is not Transparent.
An image format is not Transparent if used for any substantial amount
of text. A copy that is not "Transparent" is called "Opaque".
Examples of suitable formats for Transparent copies include plain
ASCII without markup, Texinfo input format, LaTeX input format, SGML
or XML using a publicly available DTD, and standard-conforming simple
HTML, PostScript or PDF designed for human modification. Examples of
transparent image formats include PNG, XCF and JPG. Opaque formats
include proprietary formats that can be read and edited only by
proprietary word processors, SGML or XML for which the DTD and/or
processing tools are not generally available, and the
machine-generated HTML, PostScript or PDF produced by some word
processors for output purposes only.
The "Title Page" means, for a printed book, the title page itself,
plus such following pages as are needed to hold, legibly, the material
this License requires to appear in the title page. For works in
formats which do not have any title page as such, "Title Page" means
the text near the most prominent appearance of the work's title,
preceding the beginning of the body of the text.
A section "Entitled XYZ" means a named subunit of the Document whose
title either is precisely XYZ or contains XYZ in parentheses following
text that translates XYZ in another language. (Here XYZ stands for a
specific section name mentioned below, such as "Acknowledgements",
"Dedications", "Endorsements", or "History".) To "Preserve the Title"
of such a section when you modify the Document means that it remains a
section "Entitled XYZ" according to this definition.
The Document may include Warranty Disclaimers next to the notice which
states that this License applies to the Document. These Warranty
Disclaimers are considered to be included by reference in this
License, but only as regards disclaiming warranties: any other
implication that these Warranty Disclaimers may have is void and has
no effect on the meaning of this License.
2. VERBATIM COPYING
You may copy and distribute the Document in any medium, either
commercially or noncommercially, provided that this License, the
copyright notices, and the license notice saying this License applies
to the Document are reproduced in all copies, and that you add no other
conditions whatsoever to those of this License. You may not use
technical measures to obstruct or control the reading or further
copying of the copies you make or distribute. However, you may accept
compensation in exchange for copies. If you distribute a large enough
number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and
you may publicly display copies.
3. COPYING IN QUANTITY
If you publish printed copies (or copies in media that commonly have
printed covers) of the Document, numbering more than 100, and the
Document's license notice requires Cover Texts, you must enclose the
copies in covers that carry, clearly and legibly, all these Cover
Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
the back cover. Both covers must also clearly and legibly identify
you as the publisher of these copies. The front cover must present
the full title with all words of the title equally prominent and
visible. You may add other material on the covers in addition.
Copying with changes limited to the covers, as long as they preserve
the title of the Document and satisfy these conditions, can be treated
as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit
legibly, you should put the first ones listed (as many as fit
reasonably) on the actual cover, and continue the rest onto adjacent
pages.
If you publish or distribute Opaque copies of the Document numbering
more than 100, you must either include a machine-readable Transparent
copy along with each Opaque copy, or state in or with each Opaque copy
a computer-network location from which the general network-using
public has access to download using public-standard network protocols
a complete Transparent copy of the Document, free of added material.
If you use the latter option, you must take reasonably prudent steps,
when you begin distribution of Opaque copies in quantity, to ensure
that this Transparent copy will remain thus accessible at the stated
location until at least one year after the last time you distribute an
Opaque copy (directly or through your agents or retailers) of that
edition to the public.
It is requested, but not required, that you contact the authors of the
Document well before redistributing any large number of copies, to give
them a chance to provide you with an updated version of the Document.
4. MODIFICATIONS
You may copy and distribute a Modified Version of the Document under
the conditions of sections 2 and 3 above, provided that you release
the Modified Version under precisely this License, with the Modified
Version filling the role of the Document, thus licensing distribution
and modification of the Modified Version to whoever possesses a copy
of it. In addition, you must do these things in the Modified Version:
A. Use in the Title Page (and on the covers, if any) a title distinct
from that of the Document, and from those of previous versions
(which should, if there were any, be listed in the History section
of the Document). You may use the same title as a previous version
if the original publisher of that version gives permission.
B. List on the Title Page, as authors, one or more persons or entities
responsible for authorship of the modifications in the Modified
Version, together with at least five of the principal authors of the
Document (all of its principal authors, if it has fewer than five),
unless they release you from this requirement.
C. State on the Title page the name of the publisher of the
Modified Version, as the publisher.
D. Preserve all the copyright notices of the Document.
E. Add an appropriate copyright notice for your modifications
adjacent to the other copyright notices.
F. Include, immediately after the copyright notices, a license notice
giving the public permission to use the Modified Version under the
terms of this License, in the form shown in the Addendum below.
G. Preserve in that license notice the full lists of Invariant Sections
and required Cover Texts given in the Document's license notice.
H. Include an unaltered copy of this License.
I. Preserve the section Entitled "History", Preserve its Title, and add
to it an item stating at least the title, year, new authors, and
publisher of the Modified Version as given on the Title Page. If
there is no section Entitled "History" in the Document, create one
stating the title, year, authors, and publisher of the Document as
given on its Title Page, then add an item describing the Modified
Version as stated in the previous sentence.
J. Preserve the network location, if any, given in the Document for
public access to a Transparent copy of the Document, and likewise
the network locations given in the Document for previous versions
it was based on. These may be placed in the "History" section.
You may omit a network location for a work that was published at
least four years before the Document itself, or if the original
publisher of the version it refers to gives permission.
K. For any section Entitled "Acknowledgements" or "Dedications",
Preserve the Title of the section, and preserve in the section all
the substance and tone of each of the contributor acknowledgements
and/or dedications given therein.
L. Preserve all the Invariant Sections of the Document,
unaltered in their text and in their titles. Section numbers
or the equivalent are not considered part of the section titles.
M. Delete any section Entitled "Endorsements". Such a section
may not be included in the Modified Version.
N. Do not retitle any existing section to be Entitled "Endorsements"
or to conflict in title with any Invariant Section.
O. Preserve any Warranty Disclaimers.
If the Modified Version includes new front-matter sections or
appendices that qualify as Secondary Sections and contain no material
copied from the Document, you may at your option designate some or all
of these sections as invariant. To do this, add their titles to the
list of Invariant Sections in the Modified Version's license notice.
These titles must be distinct from any other section titles.
You may add a section Entitled "Endorsements", provided it contains
nothing but endorsements of your Modified Version by various
parties--for example, statements of peer review or that the text has
been approved by an organization as the authoritative definition of a
standard.
You may add a passage of up to five words as a Front-Cover Text, and a
passage of up to 25 words as a Back-Cover Text, to the end of the list
of Cover Texts in the Modified Version. Only one passage of
Front-Cover Text and one of Back-Cover Text may be added by (or
through arrangements made by) any one entity. If the Document already
includes a cover text for the same cover, previously added by you or
by arrangement made by the same entity you are acting on behalf of,
you may not add another; but you may replace the old one, on explicit
permission from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License
give permission to use their names for publicity for or to assert or
imply endorsement of any Modified Version.
5. COMBINING DOCUMENTS
You may combine the Document with other documents released under this
License, under the terms defined in section 4 above for modified
versions, provided that you include in the combination all of the
Invariant Sections of all of the original documents, unmodified, and
list them all as Invariant Sections of your combined work in its
license notice, and that you preserve all their Warranty Disclaimers.
The combined work need only contain one copy of this License, and
multiple identical Invariant Sections may be replaced with a single
copy. If there are multiple Invariant Sections with the same name but
different contents, make the title of each such section unique by
adding at the end of it, in parentheses, the name of the original
author or publisher of that section if known, or else a unique number.
Make the same adjustment to the section titles in the list of
Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections Entitled "History"
in the various original documents, forming one section Entitled
"History"; likewise combine any sections Entitled "Acknowledgements",
and any sections Entitled "Dedications". You must delete all sections
Entitled "Endorsements".
6. COLLECTIONS OF DOCUMENTS
You may make a collection consisting of the Document and other documents
released under this License, and replace the individual copies of this
License in the various documents with a single copy that is included in
the collection, provided that you follow the rules of this License for
verbatim copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute
it individually under this License, provided you insert a copy of this
License into the extracted document, and follow this License in all
other respects regarding verbatim copying of that document.
7. AGGREGATION WITH INDEPENDENT WORKS
A compilation of the Document or its derivatives with other separate
and independent documents or works, in or on a volume of a storage or
distribution medium, is called an "aggregate" if the copyright
resulting from the compilation is not used to limit the legal rights
of the compilation's users beyond what the individual works permit.
When the Document is included in an aggregate, this License does not
apply to the other works in the aggregate which are not themselves
derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these
copies of the Document, then if the Document is less than one half of
the entire aggregate, the Document's Cover Texts may be placed on
covers that bracket the Document within the aggregate, or the
electronic equivalent of covers if the Document is in electronic form.
Otherwise they must appear on printed covers that bracket the whole
aggregate.
8. TRANSLATION
Translation is considered a kind of modification, so you may
distribute translations of the Document under the terms of section 4.
Replacing Invariant Sections with translations requires special
permission from their copyright holders, but you may include
translations of some or all Invariant Sections in addition to the
original versions of these Invariant Sections. You may include a
translation of this License, and all the license notices in the
Document, and any Warranty Disclaimers, provided that you also include
the original English version of this License and the original versions
of those notices and disclaimers. In case of a disagreement between
the translation and the original version of this License or a notice
or disclaimer, the original version will prevail.
If a section in the Document is Entitled "Acknowledgements",
"Dedications", or "History", the requirement (section 4) to Preserve
its Title (section 1) will typically require changing the actual
title.
9. TERMINATION
You may not copy, modify, sublicense, or distribute the Document except
as expressly provided for under this License. Any other attempt to
copy, modify, sublicense or distribute the Document is void, and will
automatically terminate your rights under this License. However,
parties who have received copies, or rights, from you under this
License will not have their licenses terminated so long as such
parties remain in full compliance.
10. FUTURE REVISIONS OF THIS LICENSE
The Free Software Foundation may publish new, revised versions
of the GNU Free Documentation License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns. See
http://www.gnu.org/copyleft/.
Each version of the License is given a distinguishing version number.
If the Document specifies that a particular numbered version of this
License "or any later version" applies to it, you have the option of
following the terms and conditions either of that specified version or
of any later version that has been published (not as a draft) by the
Free Software Foundation. If the Document does not specify a version
number of this License, you may choose any version ever published (not
as a draft) by the Free Software Foundation.
ADDENDUM: How to use this License for your documents
To use this License in a document you have written, include a copy of
the License in the document and put the following copyright and
license notices just after the title page:
Copyright (c) YEAR YOUR NAME.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
replace the "with...Texts." line with this:
with the Invariant Sections being LIST THEIR TITLES, with the
Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
If you have Invariant Sections without Cover Texts, or some other
combination of the three, merge those two alternatives to suit the
situation.
If your document contains nontrivial examples of program code, we
recommend releasing these examples in parallel under your choice of
free software license, such as the GNU General Public License,
to permit their use in free software.

View file

@ -1,481 +0,0 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View file

@ -1,4 +0,0 @@
#! /bin/sh
$EXTRACTRC *.rc *.ui >> rc.cpp
$XGETTEXT `find . -name '*.cpp' | grep -v '/tests/'` -o $podir/kmix.pot
rm -f rc.cpp

View file

@ -1,36 +0,0 @@
These are the recommended test cases for KMix.
They should be tested before a new KDE version is being shipped.
01: Basic: Start KMix with existing profile (kmixrc)
02: Basic: Close KMix -> Dock
03: Basic: Close KMix -> Exit
04: Show/Hide Labels
05: Show/Hide Tickmarks
06: New Mixer Tab ("distribute" and "no distribute")
07: System tray volume control
08: Start KMix with no Mixer Hardware/Drivers available
09: Basic: Start KMix with NO existing profile (kmixrc)
10: Basic: KMix KControl module
11: kmixctrl --restore (with existing .kmixctrlrc) must restore volumes
12: kmixctrl --restore (without existing .kmixctrlrc) must NOT change volumes
13: "Switch-only" controls are available and work (e.g: IEC958': Capabilities: pswitch pswitch-joined cswitch cswitch-joined)
14: Rapid stream creation/deletion works properly (KMix shows and hides the controls, no crash). for i in 1 2 3 4 5 6 7 8 9 10; do paplay /Multimedia/Kennedy_berliner.ogg& done
-----------+----+----+----+----+----+----+----+----+----+----+----+----+
TestCase> | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
Version | | | | | | | | | | | | |
-----------+----+----+----+----+----+----+----+----+----+----+----+----+
CVS20030502| OK | OK | OK | OK | OK | OK | OK | | | | | |
CVS20031102| | | | | | na | | | | OK | | |
KDE3.2 | OK | | | | | na | | | | | | |
KDE3.4 | OK | OK | OK | OK | OK | na | OK | OK | OK | na | OK | OK |
-----------+----+----+----+----+----+----+----+----+----+----+----+----+
Test case results:
OK : Fully OK
: Not tested
xx : Failure
na : not applicable (Feature discontinued)

View file

@ -1,141 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright (C) 2000 Stefan Schimanski <schimmi@kde.org>
* Copyright (C) 2001 Preston Brown <pbrown@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "KMixApp.h"
#include "apps/kmix.h"
#include "core/ControlManager.h"
#include "core/GlobalConfig.h"
#include <kdebug.h>
bool KMixApp::_keepVisibility = false;
KMixApp::KMixApp()
: KUniqueApplication(), m_kmix( 0 )
{
GlobalConfig::init();
// We must disable QuitOnLastWindowClosed. Rationale:
// 1) The normal state of KMix is to only have the dock icon shown.
// 2a) The dock icon gets reconstructed, whenever a soundcard is hotplugged or unplugged.
// 2b) The dock icon gets reconstructed, when the user selects a new master.
// 3) During the reconstruction, it can easily happen that no window is present => KMix would quit
// => disable QuitOnLastWindowClosed
setQuitOnLastWindowClosed ( false );
}
KMixApp::~KMixApp()
{
ControlManager::instance().shutdownNow();
delete m_kmix;
}
int
KMixApp::newInstance()
{
// There are 3 cases for a new instance
//kDebug(67100) << "KMixApp::newInstance() isRestored()=" << isRestored() << "_keepVisibility=" << _keepVisibility;
static bool first = true;
if ( !first )
{ // There already exists an instance/window
/* !!! @bug : _keepVisibilty has the wrong value here.
It is supposed to have the value set by the command line
arg, and the keepVisibilty() method.
All looks fine, BUT(!!!) THIS code is NEVER entered in
the just started process.
KDE IPC (DBUS) has instead notified the already running
KMix process, about a newInstance(). So _keepVisibilty
has always the value of the first started KMix process.
This is a bug in KMix and must be fixed.
cesken, 2008-11-01
*/
kDebug(67100) << "KMixApp::newInstance() Instance exists";
if ( ! _keepVisibility && !isSessionRestored() ) {
kDebug(67100) << "KMixApp::newInstance() SHOW WINDOW (_keepVisibility=" << _keepVisibility << ", isSessionRestored=" << isSessionRestored();
// CASE 1: If KMix is running AND the *USER*
// starts it again, the KMix main window will be shown.
// If KMix is restored by SM or the --keepvisibilty is used, KMix will NOT
// explicitly be shown.
KUniqueApplication::newInstance();
// if ( !m_kmix ) {
// m_kmix->show();
// } else {
// kWarning(67100) << "KMixApp::newInstance() Window has not finished constructing yet so ignoring the show() request.";
// }
}
else {
// CASE 2: If KMix is running, AND ( session gets restored OR keepvisibilty command line switch )
kDebug(67100) << "KMixApp::newInstance() REGULAR_START _keepVisibility=" << _keepVisibility;
// Special case: Command line arg --keepVisibility was used:
// We don't want to change the visibiliy, thus we don't call show() here.
//
// Hint: --keepVisibility is a special option for applications that
// want to start a mixer service, but don't need to show the KMix
// GUI (like KMilo , KAlarm, ...).
// See (e.g.) Bug 58901 for deeper insight.
}
}
else
{
// CASE 3: KMix was not running yet => instanciate a new one
//kDebug(67100) << "KMixApp::newInstance() Instanciate: _keepVisibility=" << _keepVisibility ;
first = false; // NB See https://qa.mandriva.com/show_bug.cgi?id=56893#c3
// It is important to track this via a separate variable and not
// based on m_kmix to handle this race condition.
// Specific protection for the activation-prior-to-full-construction
// case exists above in the 'already running case'
GlobalConfig::init();
m_kmix = new KMixWindow(_keepVisibility);
//connect(this, SIGNAL(stopUpdatesOnVisibility()), m_kmix, SLOT(stopVisibilityUpdates()));
if ( isSessionRestored() && KMainWindow::canBeRestored(0) )
{
m_kmix->restore(0, false);
}
}
return 0;
}
void KMixApp::keepVisibility(bool val_keepVisibility) {
_keepVisibility = val_keepVisibility;
}
/*
void
KMixApp::quitExtended()
{
// This method is here to quit hold from the dock icon: When directly calling
// quit(), the main window will be hidden before saving the configuration.
// isVisible() would return on quit always false (which would be bad).
kDebug(67100) << "quitExtended ENTER";
emit stopUpdatesOnVisibility();
quit();
}
*/
#include "moc_KMixApp.cpp"

View file

@ -1,48 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KMixApp_h
#define KMixApp_h
#include <kuniqueapplication.h>
class KMixWindow;
class KMixApp : public KUniqueApplication
{
Q_OBJECT
public:
KMixApp();
~KMixApp();
int newInstance ();
public slots:
//void quitExtended(); // For a hack on visibility()
static void keepVisibility(bool);
/*
signals:
void stopUpdatesOnVisibility();
*/
private:
KMixWindow *m_kmix;
static bool _keepVisibility;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,163 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KMIX_H
#define KMIX_H
// Qt
#include <QString>
#include <QLabel>
#include <qlist.h>
#include <QVBoxLayout>
#include <QPushButton>
#include <QTimer>
// KDE
class KTabWidget;
class KAccel;
class KAction;
#include <kxmlguiwindow.h>
// KMix
#include "core/GlobalConfig.h"
#include "core/mixer.h"
class KMixDockWidget;
class KMixerWidget;
class KMixWindow;
class Mixer;
class OSDWidget;
class DialogSelectMaster;
class KMixWindow : public KXmlGuiWindow
{
Q_OBJECT
public:
KMixWindow(bool invisible);
~KMixWindow();
private:
void saveBaseConfig();
void saveViewConfig();
void loadConfig();
void loadBaseConfig();
void initPrefDlg();
void initActions();
void initActionsLate();
void initActionsAfterInitMixer();
//void recreateGUI();
void initWidgets();
//void setErrorMixerWidget();
virtual bool queryClose();
public slots:
void controlsChange(int changeType);
void quit();
void showSettings();
void showHelp();
void showAbout();
void toggleMenuBar();
void loadVolumes();
void loadVolumes(QString postfix);
void saveVolumes();
void saveVolumes(QString postfix);
void saveConfig();
virtual void applyPrefs();
void recreateGUI(bool saveView);
void recreateGUI(bool saveConfig, const QString& mixerId, bool forceNewTab);
void recreateGUIwithSavingView();
void newMixerShown(int tabIndex);
void slotSelectMaster();
private:
KMixerWidget* findKMWforTab( const QString& tabId );
void errorPopup(const QString& msg);
KAction* _actionShowMenubar;
private:
/**
* configSnapshot is used to hold the original state before modifications in the preferences dialog
*/
GlobalConfigData configDataSnapshot;
bool m_startVisible;
bool m_multiDriverMode; // Not officially supported.
bool m_autouseMultimediaKeys; // Due to message freeze, not in config dialog in KDE4.4
KTabWidget *m_wsMixers;
KMixDockWidget *m_dockWidget;
DialogSelectMaster *m_dsm;
QString m_hwInfoString;
QString m_defaultCardOnStart;
bool m_dontSetDefaultCardOnStart;
QLabel *m_errorLabel;
QList<QString> m_backendFilter;
void showVolumeDisplay();
void increaseOrDecreaseVolume(bool increase);
OSDWidget* osdWidget;
bool addMixerWidget(const QString& mixer_ID, QString guiprofId, int insertPosition);
void setInitialSize();
protected:
bool x11Event(XEvent *xevent) final;
private:
static QString getKmixctrlRcFilename(QString postfix);
bool profileExists(QString guiProfileId);
bool updateDocking();
void removeDock();
void updateTabsClosable();
private slots:
void slotHWInfo();
void slotKdeAudioSetupExec();
void slotConfigureCurrentView();
void plugged( const char* driverName, const QString& udi, QString& dev);
void unplugged( const QString& udi);
void hideOrClose();
void slotIncreaseVolume();
void slotDecreaseVolume();
void slotMute();
void slotSelectMasterClose(QObject*);
void newView();
void saveAndCloseView(int);
void loadVolumes1() { loadVolumes(QString("1")); }
void loadVolumes2() { loadVolumes(QString("2")); }
void loadVolumes3() { loadVolumes(QString("3")); }
void loadVolumes4() { loadVolumes(QString("4")); }
void saveVolumes1() { saveVolumes(QString("1")); }
void saveVolumes2() { saveVolumes(QString("2")); }
void saveVolumes3() { saveVolumes(QString("3")); }
void saveVolumes4() { saveVolumes(QString("4")); }
};
#endif // KMIX_H

View file

@ -1,87 +0,0 @@
/*
* kmixctrl - kmix volume save/restore utility
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "core/mixertoolbox.h"
#include <kapplication.h>
#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include <klocale.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kconfig.h>
#include <kdebug.h>
#include "gui/kmixtoolbox.h"
#include "core/GlobalConfig.h"
#include "core/mixer.h"
#include "core/version.h"
static const char description[] =
I18N_NOOP("kmixctrl - kmix volume save/restore utility");
int main(int argc, char *argv[])
{
KAboutData aboutData( "kmixctrl", 0, ki18n("KMixCtrl"),
APP_VERSION, ki18n(description), KAboutData::License_GPL,
ki18n("(c) 2000 by Stefan Schimanski"));
aboutData.addAuthor(ki18n("Stefan Schimanski"), KLocalizedString(), "1Stein@gmx.de");
KCmdLineArgs::init( argc, argv, &aboutData );
KGlobal::locale()->insertCatalog("kmix");
KCmdLineOptions options;
options.add("s");
options.add("save", ki18n("Save current volumes as default"));
options.add("r");
options.add("restore", ki18n("Restore default volumes"));
KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
QCoreApplication app (argc, argv);
GlobalConfig::init();
// create mixers
QString dummyStringHwinfo;
MixerToolBox::instance()->initMixer(false, QList<QString>(), dummyStringHwinfo);
// load volumes
if ( args->isSet("restore") )
{
for (int i=0; i<Mixer::mixers().count(); ++i) {
Mixer *mixer = (Mixer::mixers())[i];
mixer->volumeLoad( KGlobal::config().data() );
}
}
// save volumes
if ( args->isSet("save") )
{
for (int i=0; i<Mixer::mixers().count(); ++i) {
Mixer *mixer = (Mixer::mixers())[i];
mixer->volumeSave( KGlobal::config().data() );
}
}
MixerToolBox::instance()->deinitMixer();
return 0;
}

View file

@ -1,267 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 1996-2000 Christian Esken <esken@kde.org>
* Copyright 2000-2003 Christian Esken <esken@kde.org>, Stefan Schimanski <1Stein@gmx.de>
* Copyright 2002-2007 Christian Esken <esken@kde.org>, Helio Chissini de Castro <helio@conectiva.com.br>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kmixd.h"
#include <kaboutdata.h>
#include <kcmdlineargs.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kconfig.h>
#include <kaction.h>
#include <kstandardaction.h>
#include <kdebug.h>
#include <kxmlguifactory.h>
#include <kglobal.h>
#include <kactioncollection.h>
#include <ktoggleaction.h>
#include <KUniqueApplication>
#include <kpluginfactory.h>
#include <kpluginloader.h>
// KMix
#include "core/GlobalConfig.h"
#include "core/mixertoolbox.h"
#include "core/kmixdevicemanager.h"
#include "core/version.h"
K_PLUGIN_FACTORY(KMixDFactory, registerPlugin<KMixD>();)
K_EXPORT_PLUGIN(KMixDFactory("kmixd"))
/* KMixD
* Constructs a mixer window (KMix main window)
*/
KMixD::KMixD(QObject* parent, const QList<QVariant>&) :
KDEDModule(parent),
m_multiDriverMode (false), // -<- I never-ever want the multi-drivermode to be activated by accident
m_dontSetDefaultCardOnStart (false)
{
setObjectName( QLatin1String("KMixD" ));
// disable delete-on-close because KMix might just sit in the background waiting for cards to be plugged in
//setAttribute(Qt::WA_DeleteOnClose, false);
GlobalConfig::init();
//initActions(); // init actions first, so we can use them in the loadConfig() already
loadConfig(); // Load config before initMixer(), e.g. due to "MultiDriver" keyword
//initActionsLate(); // init actions that require a loaded config
//KGlobal::locale()->insertCatalog( QLatin1String( "kmix-controls" ));
//initWidgets();
//initPrefDlg();
MixerToolBox::instance()->initMixer(m_multiDriverMode, m_backendFilter, m_hwInfoString);
KMixDeviceManager *theKMixDeviceManager = KMixDeviceManager::instance();
theKMixDeviceManager->initHotplug();
connect(theKMixDeviceManager, SIGNAL(plugged(const char*,QString,QString&)), SLOT (plugged(const char*,QString,QString&)) );
connect(theKMixDeviceManager, SIGNAL(unplugged(QString)), SLOT (unplugged(QString)) );
}
KMixD::~KMixD()
{
MixerToolBox::instance()->deinitMixer();
}
/*
void KMixD::initActionsLate()
{
if ( m_autouseMultimediaKeys ) {
KAction* globalAction = actionCollection()->addAction("increase_volume");
globalAction->setText(i18n("Increase Volume"));
globalAction->setGlobalShortcut(KShortcut(Qt::Key_VolumeUp), ( KAction::ShortcutTypes)( KAction::ActiveShortcut | KAction::DefaultShortcut), KAction::NoAutoloading);
connect(globalAction, SIGNAL(triggered(bool)), SLOT(slotIncreaseVolume()));
globalAction = actionCollection()->addAction("decrease_volume");
globalAction->setText(i18n("Decrease Volume"));
globalAction->setGlobalShortcut(KShortcut(Qt::Key_VolumeDown));
connect(globalAction, SIGNAL(triggered(bool)), SLOT(slotDecreaseVolume()));
globalAction = actionCollection()->addAction("mute");
globalAction->setText(i18n("Mute"));
globalAction->setGlobalShortcut(KShortcut(Qt::Key_VolumeMute));
connect(globalAction, SIGNAL(triggered(bool)), SLOT(slotMute()));
}
}
*/
void KMixD::saveConfig()
{
kDebug() << "About to save config";
saveBaseConfig();
saveVolumes();
#warning We must Sync here, or we will lose configuration data. The reson for that is unknown.
kDebug() << "Saved config ... now syncing explicitly";
KGlobal::config()->sync();
kDebug() << "Saved config ... sync finished";
}
void KMixD::saveBaseConfig()
{
kDebug() << "About to save config (Base)";
KConfigGroup config(KGlobal::config(), "Global");
config.writeEntry( "DefaultCardOnStart", m_defaultCardOnStart );
config.writeEntry( "ConfigVersion", KMIX_CONFIG_VERSION );
config.writeEntry( "AutoUseMultimediaKeys", m_autouseMultimediaKeys );
Mixer* mixerMasterCard = Mixer::getGlobalMasterMixer();
if ( mixerMasterCard != 0 ) {
config.writeEntry( "MasterMixer", mixerMasterCard->id() );
}
std::shared_ptr<MixDevice> mdMaster = Mixer::getGlobalMasterMD();
if ( mdMaster ) {
config.writeEntry( "MasterMixerDevice", mdMaster->id() );
}
QString mixerIgnoreExpression = MixerToolBox::instance()->mixerIgnoreExpression();
config.writeEntry( "MixerIgnoreExpression", mixerIgnoreExpression );
kDebug() << "Config (Base) saving done";
}
/**
* Stores the volumes of all mixers Can be restored via loadVolumes() or
* the kmixctrl application.
*/
void KMixD::saveVolumes()
{
kDebug() << "About to save config (Volume)";
KConfig *cfg = new KConfig( QLatin1String( "kmixctrlrc" ) );
for ( int i=0; i<Mixer::mixers().count(); ++i)
{
Mixer *mixer = (Mixer::mixers())[i];
if ( mixer->isOpen() ) { // protect from unplugged devices (better do *not* save them)
mixer->volumeSave( cfg );
}
}
cfg->sync();
delete cfg;
kDebug() << "Config (Volume) saving done";
}
void KMixD::loadConfig()
{
loadBaseConfig();
//loadViewConfig(); // mw->loadConfig() explicitly called always after creating mw.
//loadVolumes(); // not in use
}
void KMixD::loadBaseConfig()
{
KConfigGroup config(KGlobal::config(), "Global");
m_multiDriverMode = config.readEntry("MultiDriver", false);
m_defaultCardOnStart = config.readEntry( "DefaultCardOnStart", "" );
m_autouseMultimediaKeys = config.readEntry( "AutoUseMultimediaKeys", true ); // currently not in use in kmixd
QString mixerMasterCard = config.readEntry( "MasterMixer", "" );
QString masterDev = config.readEntry( "MasterMixerDevice", "" );
//if ( ! mixerMasterCard.isEmpty() && ! masterDev.isEmpty() ) {
Mixer::setGlobalMaster(mixerMasterCard, masterDev, true);
//}
QString mixerIgnoreExpression = config.readEntry( "MixerIgnoreExpression", "Modem" );
m_backendFilter = config.readEntry<>( "Backends", QList<QString>() );
MixerToolBox::instance()->setMixerIgnoreExpression(mixerIgnoreExpression);
}
/**
* Loads the volumes of all mixers from kmixctrlrc.
* In other words:
* Restores the default voumes as stored via saveVolumes() or the
* execution of "kmixctrl --save"
*/
/* Currently this is not in use
void
KMixD::loadVolumes()
{
KConfig *cfg = new KConfig( QLatin1String( "kmixctrlrc" ), true );
for ( int i=0; i<Mixer::mixers().count(); ++i)
{
Mixer *mixer = (Mixer::mixers())[i];
mixer->volumeLoad( cfg );
}
delete cfg;
}
*/
void KMixD::plugged( const char* driverName, const QString& /*udi*/, QString& dev)
{
// kDebug(67100) << "Plugged: dev=" << dev << "(" << driverName << ") udi=" << udi << "\n";
QString driverNameString;
driverNameString = driverName;
int devNum = dev.toInt();
Mixer *mixer = new Mixer( driverNameString, devNum );
if ( mixer != 0 ) {
kDebug(67100) << "Plugged: dev=" << dev << "\n";
MixerToolBox::instance()->possiblyAddMixer(mixer);
}
}
void KMixD::unplugged( const QString& udi)
{
// kDebug(67100) << "Unplugged: udi=" <<udi << "\n";
for (int i=0; i<Mixer::mixers().count(); ++i) {
Mixer *mixer = (Mixer::mixers())[i];
// kDebug(67100) << "Try Match with:" << mixer->udi() << "\n";
if (mixer->udi() == udi ) {
kDebug(67100) << "Unplugged Match: Removing udi=" <<udi << "\n";
//KMixToolBox::notification("kmix/MasterFallback", "aaa");
bool globalMasterMixerDestroyed = ( mixer == Mixer::getGlobalMasterMixer() );
MixerToolBox::instance()->removeMixer(mixer);
// Check whether the Global Master disappeared, and select a new one if necessary
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
if ( globalMasterMixerDestroyed || md.get() == 0 ) {
// We don't know what the global master should be now.
// So lets play stupid, and just select the recommended master of the first device
if ( Mixer::mixers().count() > 0 ) {
std::shared_ptr<MixDevice> master = ((Mixer::mixers())[0])->getLocalMasterMD();
if ( master.get() != 0 ) {
QString localMaster = master->id();
Mixer::setGlobalMaster( ((Mixer::mixers())[0])->id(), localMaster, false);
QString text;
text = i18n("The soundcard containing the master device was unplugged. Changing to control %1 on card %2.",
master->readableName(),
((Mixer::mixers())[0])->readableName()
);
// KMixToolBox::notification("kmix/MasterFallback", text);
}
}
}
if ( Mixer::mixers().count() == 0 ) {
QString text;
text = i18n("The last soundcard was unplugged.");
// KMixToolBox::notification("kmix/MasterFallback", text);
}
break;
}
}
}
#include "moc_kmixd.cpp"

View file

@ -1,90 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KMIXD_H
#define KMIXD_H
// Qt
#include <QString>
#include <QtDBus/QtDBus>
#include <QList>
#include <QTimer>
// KDE
//class KAccel;
class KAction;
//#include <kxmlguiwindow.h>
#include <kdedmodule.h>
// KMix
//class KMixPrefDlg;
//class KMixDockWidget;
//class KMixWindow;
//class ViewDockAreaPopup;
#include "core/mixer.h"
//class OSDWidget;
class
KMixD : public KDEDModule, protected QDBusContext
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.KMixD")
public:
KMixD(QObject* parent, const QList<QVariant>&);
~KMixD();
private:
void saveBaseConfig();
void loadConfig();
void loadBaseConfig();
void initActions();
void initActionsLate();
public slots:
//void loadVolumes();
void saveVolumes();
//virtual void applyPrefs( KMixPrefDlg *prefDlg );
private:
//KAction* _actionShowMenubar;
bool m_multiDriverMode; // Not officially supported.
bool m_autouseMultimediaKeys; // Due to message freeze, not in config dialog in KDE4.4
QString m_hwInfoString;
QString m_defaultCardOnStart;
bool m_dontSetDefaultCardOnStart;
//void increaseOrDecreaseVolume(bool increase);
QList<QString> m_backendFilter;
private slots:
void saveConfig();
//void slotHWInfo();
void plugged( const char* driverName, const QString& udi, QString& dev);
void unplugged( const QString& udi);
//void slotIncreaseVolume();
//void slotDecreaseVolume();
//void slotMute();
};
#endif // KMIXD_H

View file

@ -1,136 +0,0 @@
#!/bin/sh
#################################################################################
# kmixremote - control kmix from a script.
#
# Set volume
# Get volume
# Mute
#################################################################################
qdbusbin="false"
usage()
{
echo "Usage:"
echo "List mixers # $0 list"
echo "List controls # $0 list <mixer>"
echo "Get Volume # $0 get [--master | <mixer> <control>]"
echo "Set Volume # $0 set [--master | <mixer> <control>] <0..100>"
echo "Mute/Unmute # $0 mute [--master | <mixer> <control>] true|false"
echo
}
exit_with_error()
{
echo "Error: $1"
echo
usage
exit 1
}
# Prints the mixer DBUS ID's on the console. leaving out the "/Mixers/" prefix
listMixers()
{
$qdbusbin org.kde.kmix /Mixers org.freedesktop.DBus.Properties.Get org.kde.KMix.MixSet mixers | cut -f3 -d/
errorCode=$?
if test $errorCode != 0; then
echo "Error $errorCode listing mixers. KMix is not running."
fi
}
# Prints the mixer control DBUS ID's of the given mixer on the console. leaving out the "/Mixers/" prefix
listControls()
{
$qdbusbin org.kde.kmix $1 org.freedesktop.DBus.Properties.Get org.kde.KMix.Mixer controls | cut -f4 -d/
errorCode=$?
if test $errorCode != 0; then
echo "Error $errorCode listing controls. KMix is not running."
fi
}
command=""
qdbuskatie="$(basename @QT_QDBUS_EXECUTABLE@)"
if type $qdbuskatie >/dev/null 2>&1 ; then
qdbusbin="$qdbuskatie"
elif type qdbus >/dev/null 2>&1 ; then
qdbusbin="qdbus"
else
exit_with_error "$0 requires qdbus, but it cannot be found. Please install or check \$PATH"
fi
# Read args
while true; do
arg=$1
if test -z "$arg"; then
break
fi
shift
if test "x--master" = "x$arg"; then
mixer="$($qdbusbin org.kde.kmix /Mixers org.kde.KMix.MixSet.currentMasterMixer)"
control="$($qdbusbin org.kde.kmix /Mixers org.kde.KMix.MixSet.currentMasterControl)"
elif test "x--help" = "x$arg" -o "x-h" = "x$arg"; then
usage
exit 0
else
# If not a specific option, then interpret as standad args, in this order: command, mixer, control and genericArg
if test -z "$command"; then
command=$arg
elif test -z "$mixer"; then
mixer="${arg}"
elif test -z "$control"; then
control=$arg
elif test -z "$genericArg"; then
genericArg=$arg
else
exit_with_error "Too many aguments"
fi
fi
#echo $arg
done
if test -z "$command"; then
usage
echo "<mixer> - The mixer to use. Select one from the following list:"
echo "-----------------------------------------------------------------"
listMixers
exit 0
elif test "xlist" = "x$command"; then
if test -z "$mixer"; then
listMixers
else
# List controls
listControls "/Mixers/${mixer}"
fi
exit 0
fi
# All following commands require a mixer
if test -z "$mixer"; then
exit_with_error "<mixer> argument missing"
fi
if test -z "$control"; then
exit_with_error "<control> argument missing"
fi
# All following commands require a mixer and a control
targetControl="/Mixers/${mixer}/${control}"
#echo "ARGS: $command $targetControl $genericArg"
# --- EXECUTE PHASE --------------------------------------------------------------------------------------------------
if test "xget" = "x$command"; then
# GET
$qdbusbin org.kde.kmix $targetControl org.freedesktop.DBus.Properties.Get org.kde.KMix.Control volume
elif test "xset" = "x$command"; then
# SET
$qdbusbin org.kde.kmix $targetControl org.freedesktop.DBus.Properties.Set org.kde.KMix.Control volume $genericArg
elif test "xmute" = "x$command"; then
# MUTE
$qdbusbin org.kde.kmix $targetControl org.freedesktop.DBus.Properties.Set org.kde.KMix.Control mute $genericArg
else
exit_with_error "No such command '$command'"
fi
exit 0

View file

@ -1,75 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2000 Stefan Schimanski <schimmi@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include <kdebug.h>
#include <klocale.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include "KMixApp.h"
#include "core/version.h"
static const char description[] =
I18N_NOOP("KMix - KDE's full featured mini mixer");
int main(int argc, char *argv[])
{
KAboutData aboutData( "kmix", 0, ki18n("KMix"),
APP_VERSION, ki18n(description), KAboutData::License_GPL,
ki18n("(c) 1996-2013 The KMix Authors"));
// Author Policy: Long-term maintainers and backend writers/maintainers go in the Authors list.
aboutData.addAuthor(ki18n("Christian Esken") , ki18n("Original author and current maintainer"), "esken@kde.org");
aboutData.addAuthor(ki18n("Helio Chissini de Castro"), ki18n("ALSA 0.9x port"), "helio@kde.org" );
aboutData.addAuthor(ki18n("Brian Hanson") , ki18n("Solaris support"), "bhanson@hotmail.com");
// The initial support was for ALSA 0.5. The new code is not based on it IIRC.
// aboutData.addAuthor(ki18n("Nick Lopez") , ki18n("Initial ALSA port"), "kimo_sabe@usa.net");
// Credit Policy: Authors who did a discrete part, like the Dataengine, OSD, help on specific platforms or soundcards.
aboutData.addCredit(ki18n("Igor Poboiko") , ki18n("Plasma Dataengine"), "igor.poboiko@gmail.com");
aboutData.addCredit(ki18n("Stefan Schimanski") , ki18n("Temporary maintainer"), "schimmi@kde.org");
aboutData.addCredit(ki18n("Sebestyen Zoltan") , ki18n("*BSD fixes"), "szoli@digo.inf.elte.hu");
aboutData.addCredit(ki18n("Lennart Augustsson"), ki18n("*BSD fixes"), "augustss@cs.chalmers.se");
aboutData.addCredit(ki18n("Nadeem Hasan") , ki18n("Mute and volume preview, other fixes"), "nhasan@kde.org");
aboutData.addCredit(ki18n("Erwin Mascher") , ki18n("Improving support for emu10k1 based soundcards"));
aboutData.addCredit(ki18n("Valentin Rusu") , ki18n("TerraTec DMX6Fire support"), "kde@rusu.info");
KCmdLineArgs::init( argc, argv, &aboutData );
KCmdLineOptions options;
options.add("keepvisibility", ki18n("Inhibits the unhiding of the KMix main window, if KMix is already running."));
KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
bool hasArgKeepvisibility = args->isSet("keepvisibility");
//kDebug(67100) << "hasArgKeepvisibility=" << hasArgKeepvisibility;
KMixApp::keepVisibility(hasArgKeepvisibility);
if (!KMixApp::start())
return 0;
KMixApp *app = new KMixApp();
int ret = app->exec();
delete app;
return ret;
}

View file

@ -1,96 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2000 Christian Esken
* esken@kde.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* This code is being #include'd from mixer.cpp */
#include "mixer_backend.h"
#include "core/mixer.h"
#include <QString>
#if defined(Q_OS_SOLARIS)
#define SUN_MIXER
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_DRAGONFLY) || defined(Q_OS_NETBSD) || defined(Q_OS_OPENBSD)
#define OSS_MIXER
#endif
// PORTING: add #ifdef PLATFORM , commands , #endif, add your new mixer below
// Compiled by its own!
//#include "backends/mixer_mpris2.cpp"
#if defined(SUN_MIXER)
#include "backends/mixer_sun.cpp"
#endif
// OSS 3 / 4
#if defined(OSS_MIXER)
#include "backends/mixer_oss.cpp"
#if !defined(Q_OS_NETBSD) && !defined(Q_OS_OPENBSD)
#include <sys/soundcard.h>
#else
#include <soundcard.h>
#endif
#if !defined(Q_OS_FREEBSD) && (SOUND_VERSION >= 0x040000)
#define OSS4_MIXER
#endif
#endif
#if defined(OSS4_MIXER)
#include "backends/mixer_oss4.cpp"
#endif
// Possibly encapsualte by #ifdef HAVE_DBUS
Mixer_Backend* MPRIS2_getMixer(Mixer *mixer, int device );
QString MPRIS2_getDriverName();
Mixer_Backend* ALSA_getMixer(Mixer *mixer, int device );
QString ALSA_getDriverName();
MixerFactory g_mixerFactories[] = {
#if defined(SUN_MIXER)
{ SUN_getMixer, SUN_getDriverName },
#endif
#if defined(HAVE_ALSA)
{ ALSA_getMixer, ALSA_getDriverName },
#endif
#if defined(OSS_MIXER)
{ OSS_getMixer, OSS_getDriverName },
#endif
#if defined(OSS4_MIXER)
{ OSS4_getMixer, OSS4_getDriverName },
#endif
// Make sure MPRIS2 is at the end. Implementation of SINGLE_PLUS_MPRIS2 in MixerToolBox is much easier.
// And also we make sure, streams are always the last backend, which is important for the default KMix GUI layout.
{ MPRIS2_getMixer, MPRIS2_getDriverName },
{ 0, 0 }
};

View file

@ -1,90 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MIXER_ALSA_H
#define MIXER_ALSA_H
// QT includes
#include <QList>
#include <QHash>
// Forward QT includes
#include <QString>
#include <QSocketNotifier>
#include "mixer_backend.h"
extern "C"
{
#include <alsa/asoundlib.h>
}
class Mixer_ALSA : public Mixer_Backend
{
public:
explicit Mixer_ALSA(Mixer *mixer, int device = -1 );
~Mixer_ALSA();
virtual int readVolumeFromHW( const QString& id, std::shared_ptr<MixDevice> md );
virtual int writeVolumeToHW ( const QString& id, std::shared_ptr<MixDevice> md );
virtual void setEnumIdHW( const QString& id, unsigned int);
virtual unsigned int enumIdHW(const QString& id);
virtual bool prepareUpdateFromHW();
virtual bool needsPolling() { return false; }
virtual QString getDriverName();
protected:
virtual int open();
virtual int close();
int id2num(const QString& id);
private:
int openAlsaDevice(const QString& devName);
void addEnumerated(snd_mixer_elem_t *elem, QList<QString*>&);
Volume* addVolume(snd_mixer_elem_t *elem, bool capture);
int setupAlsaPolling();
void deinitAlsaPolling();
virtual bool isRecsrcHW( const QString& id );
int identify( snd_mixer_selem_id_t *sid );
snd_mixer_elem_t* getMixerElem(int devnum);
virtual QString errorText(int mixer_error);
typedef QList<snd_mixer_selem_id_t *>AlsaMixerSidList;
AlsaMixerSidList mixer_sid_list;
typedef QList<snd_mixer_elem_t *> AlsaMixerElemList;
AlsaMixerElemList mixer_elem_list;
typedef QHash<QString,int> Id2numHash;
Id2numHash m_id2numHash;
bool _initialUpdate;
snd_mixer_t* _handle;
snd_ctl_t* ctl_handle;
QString devName;
struct pollfd *m_fds;
QList<QSocketNotifier*> m_sns;
//int m_count;
static bool warnOnce;
};
#endif

View file

@ -1,921 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
* Alsa 0.9x and 1.0 - Based on original alsamixer code
* from alsa-project ( www/alsa-project.org )
*
*
* Copyright (C) 2002 Helio Chissini de Castro <helio@conectiva.com.br>
* 2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// KMix
#include "mixer_alsa.h"
#include "core/kmixdevicemanager.h"
#include "core/mixer.h"
#include "core/volume.h"
// KDE
#include <kdebug.h>
#include <klocale.h>
// Qt
#include <QList>
// STD Headers
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <assert.h>
#include <qsocketnotifier.h>
//#include "core/mixer.h"
//class Mixer;
// #define if you want MUCH debugging output
//#define ALSA_SWITCH_DEBUG
//#define KMIX_ALSA_VOLUME_DEBUG
Mixer_Backend* ALSA_getMixer(Mixer *mixer, int device )
{
Mixer_Backend *l_mixer;
l_mixer = new Mixer_ALSA(mixer, device );
return l_mixer;
}
Mixer_ALSA::Mixer_ALSA( Mixer* mixer, int device ) : Mixer_Backend(mixer, device )
{
m_fds = 0;
_handle = 0;
ctl_handle = 0;
_initialUpdate = true;
}
Mixer_ALSA::~Mixer_ALSA()
{
close();
}
int Mixer_ALSA::identify( snd_mixer_selem_id_t *sid )
{
QString name = snd_mixer_selem_id_get_name( sid );
if (name.contains("master" , Qt::CaseInsensitive)) return MixDevice::VOLUME;
if (name.contains("master mono", Qt::CaseInsensitive)) return MixDevice::VOLUME;
if (name.contains("front" , Qt::CaseInsensitive) &&
!name.contains("mic" , Qt::CaseInsensitive)) return MixDevice::VOLUME;
if (name.contains("pc speaker" , Qt::CaseInsensitive)) return MixDevice::SPEAKER;
if (name.contains("capture" , Qt::CaseInsensitive)) return MixDevice::RECMONITOR;
if (name.contains("music" , Qt::CaseInsensitive)) return MixDevice::MIDI;
if (name.contains("Synth" , Qt::CaseInsensitive)) return MixDevice::MIDI;
if (name.contains("FM" , Qt::CaseInsensitive)) return MixDevice::MIDI;
if (name.contains("headphone" , Qt::CaseInsensitive)) return MixDevice::HEADPHONE;
if (name.contains("bass" , Qt::CaseInsensitive)) return MixDevice::BASS;
if (name.contains("treble" , Qt::CaseInsensitive)) return MixDevice::TREBLE;
if (name.contains("cd" , Qt::CaseInsensitive)) return MixDevice::CD;
if (name.contains("video" , Qt::CaseInsensitive)) return MixDevice::VIDEO;
if (name.contains("pcm" , Qt::CaseInsensitive)) return MixDevice::AUDIO;
if (name.contains("Wave" , Qt::CaseInsensitive)) return MixDevice::AUDIO;
if (name.contains("surround" , Qt::CaseInsensitive)) return MixDevice::SURROUND_BACK;
if (name.contains("center" , Qt::CaseInsensitive)) return MixDevice::SURROUND_CENTERFRONT;
if (name.contains("ac97" , Qt::CaseInsensitive)) return MixDevice::AC97;
if (name.contains("coaxial" , Qt::CaseInsensitive)) return MixDevice::DIGITAL;
if (name.contains("optical" , Qt::CaseInsensitive)) return MixDevice::DIGITAL;
if (name.contains("iec958" , Qt::CaseInsensitive)) return MixDevice::DIGITAL;
if (name.contains("digital" , Qt::CaseInsensitive)) return MixDevice::DIGITAL;
if (name.contains("mic boost" , Qt::CaseInsensitive)) return MixDevice::MICROPHONE_BOOST;
if (name.contains("Mic Front" , Qt::CaseInsensitive)) return MixDevice::MICROPHONE_FRONT;
if (name.contains("Front Mic" , Qt::CaseInsensitive)) return MixDevice::MICROPHONE_FRONT;
if (name.contains("mic" , Qt::CaseInsensitive)) return MixDevice::MICROPHONE;
if (name.contains("lfe" , Qt::CaseInsensitive)) return MixDevice::SURROUND_LFE;
if (name.contains("monitor" , Qt::CaseInsensitive)) return MixDevice::RECMONITOR;
if (name.contains("3d" , Qt::CaseInsensitive)) return MixDevice::SURROUND;
if (name.contains("side" , Qt::CaseInsensitive)) return MixDevice::SURROUND_BACK;
return MixDevice::EXTERNAL;
}
int Mixer_ALSA::open()
{
int masterChosenQuality = 0;
int err;
snd_mixer_elem_t *elem;
snd_mixer_selem_id_t *sid;
bool USE_ALSO_ALLOCA = true; // TODO remove alloca() when adding "delete sid" in the destructor or close()
if (USE_ALSO_ALLOCA) {
snd_mixer_selem_id_alloca( &sid );
}
// Determine a card name
if( m_devnum <= -1 || m_devnum > 31 )
devName = "default";
else
devName = QString( "hw:%1" ).arg( m_devnum );
// Open the card
err = openAlsaDevice(devName);
if ( err != 0 ) {
return err;
}
_udi = KMixDeviceManager::instance()->getUDI_ALSA(m_devnum);
if ( _udi.isEmpty() ) {
QString msg("No UDI found for '");
msg += devName;
msg += "'. Hotplugging not possible";
kWarning() << msg;
}
// Run a loop over all controls of the card
unsigned int idx = 0;
for ( elem = snd_mixer_first_elem( _handle ); elem; elem = snd_mixer_elem_next( elem ) ) {
// If element is not active, just skip
if ( ! snd_mixer_selem_is_active ( elem ) ) {
continue;
}
/* --- Create basic control structures: snd_mixer_selem_id_t*, ID, ... --------- */
// snd_mixer_selem_id_t*
// I believe we must malloc it ourself (just guessing due to missing ALSA documentation)
snd_mixer_selem_id_malloc ( &sid ); // !! Return code should be checked. Resource must be freed when unplugging card
snd_mixer_selem_get_id( elem, sid );
// Generate ID
QString mdID("%1:%2");
mdID = mdID.arg(snd_mixer_selem_id_get_name ( sid ) )
.arg(snd_mixer_selem_id_get_index( sid ) );
mdID.replace(' ','_'); // Any key/ID we use, must not uses spaces (rule)
MixDevice::ChannelType ct = (MixDevice::ChannelType)identify( sid );
/* ------------------------------------------------------------------------------- */
Volume* volPlay = 0;
Volume* volCapture = 0;
QList<QString*> enumList;
if ( snd_mixer_selem_is_enumerated(elem) ) {
// --- Enumerated ---
addEnumerated(elem, enumList);
} else {
volPlay = addVolume(elem, false);
volCapture = addVolume(elem, true );
}
QString readableName;
readableName = snd_mixer_selem_id_get_name( sid );
int controlInstanceIndex = snd_mixer_selem_id_get_index( sid );
if ( controlInstanceIndex > 0 ) {
// Add a number to the control name, like "PCM 2", when the index is > 0
QString idxString;
idxString.setNum(1+controlInstanceIndex);
readableName += ' ';
readableName += idxString;
}
// There can be an Enum-Control with the same name as a regular control. So we append a ".[cp]enum" prefix to always create a unique ID
QString finalMixdeviceID = mdID;
if ( ! enumList.isEmpty() ) {
if (snd_mixer_selem_is_enum_capture ( elem ) )
finalMixdeviceID = mdID + ".cenum"; // capture enum
else
finalMixdeviceID = mdID + ".penum"; // playback enum
}
m_id2numHash[finalMixdeviceID] = idx;
//kDebug() << "m_id2numHash[mdID] mdID=" << mdID << " idx=" << idx;
mixer_elem_list.append( elem );
mixer_sid_list.append( sid );
idx++;
MixDevice* mdNew = new MixDevice(_mixer, finalMixdeviceID, readableName, ct );
if ( volPlay != 0) {
mdNew->addPlaybackVolume(*volPlay);
delete volPlay;
}
if ( volCapture != 0) {
mdNew->addCaptureVolume (*volCapture);
delete volCapture;
}
if ( !enumList.isEmpty()) {
mdNew->addEnums(enumList);
qDeleteAll(enumList); // clear temporary list
}
std::shared_ptr<MixDevice> md = mdNew->addToPool();
m_mixDevices.append( md );
// --- Recommended master ----------------------------------------
if ( md->playbackVolume().hasVolume() ) {
if ( mdID == "Master:0" && masterChosenQuality < 100 ) {
// kDebug() << "Setting m_recommendedMaster to " << mdID;
m_recommendedMaster = md;
masterChosenQuality = 100;
} else if ( mdID == "PCM:0" && masterChosenQuality < 80) {
// kDebug() << "Setting m_recommendedMaster to " << mdID;
m_recommendedMaster = md;
masterChosenQuality = 80;
} else if ( mdID == "Front:0" && masterChosenQuality < 60) {
// kDebug() << "Setting m_recommendedMaster to " << mdID;
m_recommendedMaster = md;
masterChosenQuality = 60;
} else if ( mdID == "DAC:0" && masterChosenQuality < 50) {
// kDebug() << "Setting m_recommendedMaster to " << mdID;
m_recommendedMaster = md;
masterChosenQuality = 50;
} else if ( mdID == "Headphone:0" && masterChosenQuality < 40) {
// kDebug() << "Setting m_recommendedMaster to " << mdID;
m_recommendedMaster = md;
masterChosenQuality = 40;
} else if ( mdID == "Master Mono:0" && masterChosenQuality < 30) {
// kDebug() << "Setting m_recommendedMaster to " << mdID;
m_recommendedMaster = md;
masterChosenQuality = 30;
}
}
} // for all elems
m_isOpen = true; // return with success
setupAlsaPolling(); // For updates
return 0;
}
// warnOnce will make sure we only print the first ALSA device not found
bool Mixer_ALSA::warnOnce = true;
/**
* This opens a ALSA device for further interaction.
* As this is "slightly" more complicated than calling ::open(), it is put in a separate method.
*/
int Mixer_ALSA::openAlsaDevice(const QString& devName)
{
int err;
QString probeMessage;
probeMessage += "Trying ALSA Device '" + devName + "': ";
if ( ( err = snd_ctl_open ( &ctl_handle, devName.toAscii().data(), 0 ) ) < 0 ) {
if (Mixer_ALSA::warnOnce) {
Mixer_ALSA::warnOnce = false;
kDebug() << probeMessage << "not found: snd_ctl_open err=" << snd_strerror(err);
}
return Mixer::ERR_OPEN;
}
// Mixer name
snd_ctl_card_info_t *hw_info;
snd_ctl_card_info_alloca(&hw_info);
if ( ( err = snd_ctl_card_info ( ctl_handle, hw_info ) ) < 0 ) {
if (Mixer_ALSA::warnOnce) {
Mixer_ALSA::warnOnce = false;
kDebug() << probeMessage << "not found: snd_ctl_card_info err=" << snd_strerror(err);
}
//_stateMessage = errorText( Mixer::ERR_READ );
snd_ctl_close( ctl_handle );
return Mixer::ERR_READ;
}
const char* mixer_card_name = snd_ctl_card_info_get_name( hw_info );
//QString mixer_card_name_QString = mixer_card_name;
registerCard(mixer_card_name);
snd_ctl_close( ctl_handle );
/* open mixer device */
if ( ( err = snd_mixer_open ( &_handle, 0 ) ) < 0 ) {
if (Mixer_ALSA::warnOnce) {
Mixer_ALSA::warnOnce = false;
kDebug() << probeMessage << "not found: snd_mixer_open err=" << snd_strerror(err);
}
_handle = 0;
return Mixer::ERR_OPEN; // if we cannot open the mixer, we have no devices
}
if ( ( err = snd_mixer_attach ( _handle, devName.toAscii().data() ) ) < 0 ) {
if (Mixer_ALSA::warnOnce) {
Mixer_ALSA::warnOnce = false;
kDebug() << probeMessage << "not found: snd_mixer_attach err=" << snd_strerror(err);
}
return Mixer::ERR_OPEN;
}
if ( ( err = snd_mixer_selem_register ( _handle, NULL, NULL ) ) < 0 ) {
if (Mixer_ALSA::warnOnce) {
Mixer_ALSA::warnOnce = false;
kDebug() << probeMessage << "not found: snd_mixer_selem_register err=" << snd_strerror(err);
}
return Mixer::ERR_READ;
}
if ( ( err = snd_mixer_load ( _handle ) ) < 0 ) {
if (Mixer_ALSA::warnOnce) {
Mixer_ALSA::warnOnce = false;
kDebug() << probeMessage << "not found: snd_mixer_load err=" << snd_strerror(err);
}
close();
return Mixer::ERR_READ;
}
Mixer_ALSA::warnOnce = true;
kDebug() << probeMessage << "found";
return 0;
}
/* setup for select on stdin and the mixer fd */
int Mixer_ALSA::setupAlsaPolling()
{
// --- Step 1: Retrieve FD's from ALSALIB
int err;
int countNew = 0;
if ((countNew = snd_mixer_poll_descriptors_count(_handle)) < 0) {
kDebug() << "Mixer_ALSA::poll() , snd_mixer_poll_descriptors_count() err=" << countNew << "\n";
return Mixer::ERR_OPEN;
}
//if ( countNew != m_sns.size() )
if (true) {
// Redo everything if count of FD's have changed (emulating alsamixer behaviour here)
while (!m_sns.isEmpty())
delete m_sns.takeFirst();
free(m_fds);
m_fds = (struct pollfd*)calloc(countNew, sizeof(struct pollfd));
if (m_fds == NULL) {
kDebug() << "Mixer_ALSA::poll() , calloc() = null" << "\n";
return Mixer::ERR_OPEN;
}
if ((err = snd_mixer_poll_descriptors(_handle, m_fds, countNew)) < 0) {
kDebug() << "Mixer_ALSA::poll() , snd_mixer_poll_descriptors_count() err=" << err << "\n";
return Mixer::ERR_OPEN;
}
if (err != countNew) {
kDebug() << "Mixer_ALSA::poll() , snd_mixer_poll_descriptors_count() err=" << err << " m_count=" << countNew << "\n";
return Mixer::ERR_OPEN;
}
// --- Step 2: Create QSocketNotifier's for the FD's
//m_sns = new QSocketNotifier*[m_count];
for ( int i = 0; i < countNew; ++i ) {
//kDebug() << "socket " << i;
QSocketNotifier* qsn = new QSocketNotifier(m_fds[i].fd, QSocketNotifier::Read);
m_sns.append(qsn);
connect(m_sns[i], SIGNAL(activated(int)), SLOT(readSetFromHW()), Qt::QueuedConnection);
}
}
return 0;
}
void Mixer_ALSA::addEnumerated(snd_mixer_elem_t *elem, QList<QString*>& enumList)
{
// --- get Enum names START ---
int numEnumitems = snd_mixer_selem_get_enum_items(elem);
if ( numEnumitems > 0 ) {
// OK. no error
for (int iEnum = 0; iEnum<numEnumitems; iEnum++ ) {
char buffer[100];
int ret = snd_mixer_selem_get_enum_item_name(elem, iEnum, 99, buffer);
buffer[99] = 0; // protect from overflow
if ( ret == 0 ) {
QString* enumName = new QString(buffer); // these QString* items are deleted above (search fo "clear temporary list")
enumList.append( enumName);
} // enumName could be read successfully
} // for all enum items of this device
} else { // no error in reading enum list
// 0 items or Error code => ignore this entry
}
}
Volume* Mixer_ALSA::addVolume(snd_mixer_elem_t *elem, bool capture)
{
Volume* vol = 0;
long maxVolume = 0, minVolume = 0;
// Add volumes
if ( !capture && snd_mixer_selem_has_playback_volume(elem) ) {
snd_mixer_selem_get_playback_volume_range( elem, &minVolume, &maxVolume );
} else if ( capture && snd_mixer_selem_has_capture_volume(elem) ) {
snd_mixer_selem_get_capture_volume_range( elem, &minVolume, &maxVolume );
}
// Check if this control has at least one volume control
bool hasVolume = snd_mixer_selem_has_playback_volume(elem) || snd_mixer_selem_has_capture_volume(elem);
// Check if a appropriate switch is present (appropriate means, based o nthe "capture" parameer)
bool hasCommonSwitch = snd_mixer_selem_has_common_switch ( elem );
bool hasSwitch = hasCommonSwitch |
(capture
? snd_mixer_selem_has_capture_switch ( elem )
: snd_mixer_selem_has_playback_switch ( elem ));
if ( hasVolume || hasSwitch ) {
//kDebug() << "Add somthing with chn=" << chn << ", capture=" << capture;
vol = new Volume( maxVolume, minVolume, hasSwitch, capture);
// Add volumes
if ( !capture && snd_mixer_selem_has_playback_volume(elem) ) {
if ( snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_FRONT_LEFT )) vol->addVolumeChannel(VolumeChannel(Volume::LEFT));
if ( snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_FRONT_RIGHT )) vol->addVolumeChannel(VolumeChannel(Volume::RIGHT));
if ( snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_FRONT_CENTER)) vol->addVolumeChannel(VolumeChannel(Volume::CENTER));
if ( snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_REAR_LEFT )) vol->addVolumeChannel(VolumeChannel(Volume::SURROUNDLEFT));
if ( snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_REAR_RIGHT )) vol->addVolumeChannel(VolumeChannel(Volume::SURROUNDRIGHT));
if ( snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_REAR_CENTER )) vol->addVolumeChannel(VolumeChannel(Volume::REARCENTER));
if ( snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_WOOFER )) vol->addVolumeChannel(VolumeChannel(Volume::WOOFER));
if ( snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_SIDE_LEFT )) vol->addVolumeChannel(VolumeChannel(Volume::REARSIDELEFT));
if ( snd_mixer_selem_has_playback_channel(elem,SND_MIXER_SCHN_SIDE_RIGHT )) vol->addVolumeChannel(VolumeChannel(Volume::REARSIDERIGHT));
} else if ( capture && snd_mixer_selem_has_capture_volume(elem) ) {
if ( snd_mixer_selem_has_capture_channel(elem,SND_MIXER_SCHN_FRONT_LEFT )) vol->addVolumeChannel(VolumeChannel(Volume::LEFT));
if ( snd_mixer_selem_has_capture_channel(elem,SND_MIXER_SCHN_FRONT_RIGHT )) vol->addVolumeChannel(VolumeChannel(Volume::RIGHT));
if ( snd_mixer_selem_has_capture_channel(elem,SND_MIXER_SCHN_FRONT_CENTER)) vol->addVolumeChannel(VolumeChannel(Volume::CENTER));
if ( snd_mixer_selem_has_capture_channel(elem,SND_MIXER_SCHN_REAR_LEFT )) vol->addVolumeChannel(VolumeChannel(Volume::SURROUNDLEFT));
if ( snd_mixer_selem_has_capture_channel(elem,SND_MIXER_SCHN_REAR_RIGHT )) vol->addVolumeChannel(VolumeChannel(Volume::SURROUNDRIGHT));
if ( snd_mixer_selem_has_capture_channel(elem,SND_MIXER_SCHN_REAR_CENTER )) vol->addVolumeChannel(VolumeChannel(Volume::REARCENTER));
if ( snd_mixer_selem_has_capture_channel(elem,SND_MIXER_SCHN_WOOFER )) vol->addVolumeChannel(VolumeChannel(Volume::WOOFER));
if ( snd_mixer_selem_has_capture_channel(elem,SND_MIXER_SCHN_SIDE_LEFT )) vol->addVolumeChannel(VolumeChannel(Volume::REARSIDELEFT));
if ( snd_mixer_selem_has_capture_channel(elem,SND_MIXER_SCHN_SIDE_RIGHT )) vol->addVolumeChannel(VolumeChannel(Volume::REARSIDERIGHT));
}
}
return vol;
}
void Mixer_ALSA::deinitAlsaPolling()
{
if ( m_fds )
free( m_fds );
m_fds = 0;
while (!m_sns.isEmpty())
delete m_sns.takeFirst();
}
int
Mixer_ALSA::close()
{
// kDebug() << "close " << this;
int ret=0;
m_isOpen = false;
if ( ctl_handle != 0) {
//snd_ctl_close( ctl_handle );
ctl_handle = 0;
}
if ( _handle != 0 ) {
//kDebug() << "IN Mixer_ALSA::close()";
snd_mixer_free ( _handle );
if ( ( ret = snd_mixer_detach ( _handle, devName.toAscii().data() ) ) < 0 ) {
kDebug() << "snd_mixer_detach err=" << snd_strerror(ret);
}
int ret2 = 0;
if ( ( ret2 = snd_mixer_close ( _handle ) ) < 0 ) {
kDebug() << "snd_mixer_close err=" << snd_strerror(ret2);
if ( ret == 0 )
ret = ret2; // no error before => use current error code
}
_handle = 0;
//kDebug() << "OUT Mixer_ALSA::close()";
}
mixer_elem_list.clear();
mixer_sid_list.clear();
m_id2numHash.clear();
deinitAlsaPolling();
closeCommon();
return ret;
}
/**
* Resolve index to a control (snd_mixer_elem_t*)
* @par idx Index to query. For any invalid index (including -1) returns a 0 control.
*/
snd_mixer_elem_t* Mixer_ALSA::getMixerElem(int idx) {
snd_mixer_elem_t* elem = 0;
if ( ! m_isOpen )
return elem; // unplugging guard
if ( idx == -1 ) {
return elem;
}
if ( int( mixer_sid_list.count() ) > idx ) {
snd_mixer_selem_id_t * sid = mixer_sid_list[ idx ];
// The next line (hopefully) only finds selem's, not elem's.
elem = snd_mixer_find_selem(_handle, sid);
if ( elem == 0 ) {
// !! Check, whether the warning should be omitted. Probably
// Route controls are non-simple elements.
kDebug() << "Error finding mixer element " << idx;
}
}
return elem;
/*
I would have liked to use the following trivial implementation instead of the
code above. But it will also return elem's. which are not selem's. As there is
no way to check an elem's type (e.g. elem->type == SND_MIXER_ELEM_SIMPLE), callers
of getMixerElem() cannot check the type. :-(
snd_mixer_elem_t* elem = mixer_elem_list[ devnum ];
return elem;
*/
}
int Mixer_ALSA::id2num(const QString& id) {
//kDebug() << "id2num() id=" << id;
int num = -1;
if ( m_id2numHash.contains(id) ) {
num = m_id2numHash[id];
}
//kDebug() << "id2num() num=" << num;
return num;
}
bool Mixer_ALSA::prepareUpdateFromHW() {
if ( !m_fds || !m_isOpen )
return false;
setupAlsaPolling();
// Poll on fds with 10ms timeout
// Hint: alsamixer has an infinite timeout, but we cannot do this because we would block
// the X11 event handling (Qt event loop) with this.
int finished = poll(m_fds, m_sns.size(), 10);
bool updated = false;
if (finished > 0) {
//kDebug() << "Mixer_ALSA::prepareUpdate() 5\n";
unsigned short revents;
if (snd_mixer_poll_descriptors_revents(_handle, m_fds, m_sns.size(), &revents) >= 0) {
//kDebug() << "Mixer_ALSA::prepareUpdate() 6\n";
if (revents & POLLNVAL) {
/* Bug 127294 shows, that we receive POLLNVAL when the user
unplugs an USB soundcard. Lets close the card. */
kDebug() << "Mixer_ALSA::poll() , Error: poll() returns POLLNVAL\n";
close(); // Card was unplugged (unplug, driver unloaded)
return false;
}
if (revents & POLLERR) {
kDebug() << "Mixer_ALSA::poll() , Error: poll() returns POLLERR\n";
return false;
}
if (revents & POLLIN) {
//kDebug() << "Mixer_ALSA::prepareUpdate() 7\n";
snd_mixer_handle_events(_handle);
updated = true;
}
}
}
//kDebug() << "Mixer_ALSA::prepareUpdate() 8\n";
return updated;
}
bool Mixer_ALSA::isRecsrcHW( const QString& id )
{
int devnum = id2num(id);
bool isCurrentlyRecSrc = false;
snd_mixer_elem_t *elem = getMixerElem( devnum );
if ( !elem ) {
return false;
}
if ( snd_mixer_selem_has_capture_switch( elem ) ) {
// Has a on-off switch
// Yes, this element can be record source. But the user can switch it off, so lets see if it is switched on.
int swLeft;
int ret = snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &swLeft );
if ( ret != 0 ) kDebug() << "snd_mixer_selem_get_capture_switch() failed 1\n";
if (snd_mixer_selem_has_capture_switch_joined( elem ) ) {
isCurrentlyRecSrc = (swLeft != 0);
#ifdef ALSA_SWITCH_DEBUG
kDebug() << "has_switch joined: #" << devnum << " >>> " << swLeft << " : " << isCurrentlyRecSrc;
#endif
} else {
int swRight;
snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_RIGHT, &swRight );
isCurrentlyRecSrc = ( (swLeft != 0) || (swRight != 0) );
#ifdef ALSA_SWITCH_DEBUG
kDebug() << "has_switch non-joined, state " << isCurrentlyRecSrc;
#endif
}
} else {
// Has no on-off switch
if ( snd_mixer_selem_has_capture_volume( elem ) ) {
// Has a volume, but has no OnOffSwitch => We assume that this is a fixed record source (always on). (esken)
isCurrentlyRecSrc = true;
#ifdef ALSA_SWITCH_DEBUG
kDebug() << "has_no_switch, state " << isCurrentlyRecSrc;
#endif
}
}
return isCurrentlyRecSrc;
}
/**
* Sets the ID of the currently selected Enum entry.
* Warning: ALSA supports to have different enums selected on each channel
* of the SAME snd_mixer_elem_t. KMix does NOT support that and
* always sets both channels (0 and 1).
*/
void Mixer_ALSA::setEnumIdHW(const QString& id, unsigned int idx) {
//kDebug() << "Mixer_ALSA::setEnumIdHW() id=" << id << " , idx=" << idx << ") 1\n";
int devnum = id2num(id);
snd_mixer_elem_t *elem = getMixerElem( devnum );
for (int i = 0; i <= SND_MIXER_SCHN_LAST; ++i) {
int ret = snd_mixer_selem_set_enum_item(elem, (snd_mixer_selem_channel_id_t)i,idx);
if (ret < 0 && i == 0) {
// Log errors only for one channel. This should be enough, and another reason is that I also do not check which channels are supported at all.
kError() << "Mixer_ALSA::setEnumIdHW(" << devnum << "), errno=" << ret << "\n";
}
}
return;
}
/**
* Return the ID of the currently selected Enum entry.
* Warning: ALSA supports to have different enums selected on each channel
* of the SAME snd_mixer_elem_t. KMix does NOT support that and
* always shows the value of the first channel.
*/
unsigned int Mixer_ALSA::enumIdHW(const QString& id) {
int devnum = id2num(id);
snd_mixer_elem_t *elem = getMixerElem( devnum );
unsigned int idx = 0;
if ( elem != 0 && snd_mixer_selem_is_enumerated(elem) ) {
int ret = snd_mixer_selem_get_enum_item(elem,SND_MIXER_SCHN_FRONT_LEFT,&idx);
if (ret < 0) {
idx = 0;
kError() << "Mixer_ALSA::enumIdHW(" << devnum << "), errno=" << ret << "\n";
}
}
return idx;
}
int Mixer_ALSA::readVolumeFromHW( const QString& id, std::shared_ptr<MixDevice> md )
{
Volume& volumePlayback = md->playbackVolume();
Volume& volumeCapture = md->captureVolume();
int devnum = id2num(id);
int elem_sw;
long vol;
snd_mixer_elem_t *elem = getMixerElem( devnum );
if ( !elem ) {
return Mixer::OK_UNCHANGED;
}
vol = Volume::MNONE;
// --- playback volume
if ( snd_mixer_selem_has_playback_volume( elem ) ) {
if ( md->isVirtuallyMuted() ) {
// Special code path for controls w/o physical mute switch. Doing it in all backends is not perfect,
// but it saves a lot of code and removes a lot of complexity in the Volume and MixDevice classes.
// Don't feed back the actual 0 volume back from the device to KMix. Just do nothing!
} else {
foreach (VolumeChannel vc, volumePlayback.getVolumes() ) {
int ret = 0;
switch(vc.chid) {
case Volume::LEFT : ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_FRONT_LEFT , &vol); break;
case Volume::RIGHT : ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_FRONT_RIGHT , &vol); break;
case Volume::CENTER : ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_FRONT_CENTER, &vol); break;
case Volume::SURROUNDLEFT : ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_REAR_LEFT , &vol); break;
case Volume::SURROUNDRIGHT: ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_REAR_RIGHT , &vol); break;
case Volume::REARCENTER : ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_REAR_CENTER , &vol); break;
case Volume::WOOFER : ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_WOOFER , &vol); break;
case Volume::REARSIDELEFT : ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_SIDE_LEFT , &vol); break;
case Volume::REARSIDERIGHT: ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_SIDE_RIGHT , &vol); break;
default: kDebug() << "FATAL: Unknown channel type for playback << " << vc.chid << " ... please report this"; break;
}
if ( ret != 0 ) {
kDebug() << "readVolumeFromHW(" << devnum << ") [get_playback_volume] failed, errno=" << ret;
} else {
volumePlayback.setVolume( vc.chid, vol);
//if (id== "Master:0" || id== "PCM:0" ) { kDebug() << "volumePlayback control=" << id << ", chid=" << i << ", vol=" << vol; }
}
}
}
} // has playback volume
// --- playback switch
// TODO: What about has_common_switch()
if ( snd_mixer_selem_has_playback_switch( elem ) ) {
snd_mixer_selem_get_playback_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &elem_sw );
md->setMuted( elem_sw == 0 );
}
vol = Volume::MNONE;
// --- capture volume
if ( snd_mixer_selem_has_capture_volume ( elem ) ) {
foreach (VolumeChannel vc, volumeCapture.getVolumes() ) {
int ret = 0;
switch(vc.chid) {
case Volume::LEFT : ret = snd_mixer_selem_get_capture_volume( elem, SND_MIXER_SCHN_FRONT_LEFT , &vol); break;
case Volume::RIGHT : ret = snd_mixer_selem_get_capture_volume( elem, SND_MIXER_SCHN_FRONT_RIGHT , &vol); break;
case Volume::CENTER : ret = snd_mixer_selem_get_capture_volume( elem, SND_MIXER_SCHN_FRONT_CENTER, &vol); break;
case Volume::SURROUNDLEFT : ret = snd_mixer_selem_get_capture_volume( elem, SND_MIXER_SCHN_REAR_LEFT , &vol); break;
case Volume::SURROUNDRIGHT: ret = snd_mixer_selem_get_capture_volume( elem, SND_MIXER_SCHN_REAR_RIGHT , &vol); break;
case Volume::REARCENTER : ret = snd_mixer_selem_get_capture_volume( elem, SND_MIXER_SCHN_REAR_CENTER , &vol); break;
case Volume::WOOFER : ret = snd_mixer_selem_get_capture_volume( elem, SND_MIXER_SCHN_WOOFER , &vol); break;
case Volume::REARSIDELEFT : ret = snd_mixer_selem_get_capture_volume( elem, SND_MIXER_SCHN_SIDE_LEFT , &vol); break;
case Volume::REARSIDERIGHT: ret = snd_mixer_selem_get_capture_volume( elem, SND_MIXER_SCHN_SIDE_RIGHT , &vol); break;
default: kDebug() << "FATAL: Unknown channel type for capture << " << vc.chid << " ... please report this"; break;
}
if ( ret != 0 ) {
kDebug() << "readVolumeFromHW(" << devnum << ") [get_capture_volume] failed, errno=" << ret;
} else {
volumeCapture.setVolume( vc.chid, vol);
}
}
} // has capture volume
// --- capture switch
// TODO: What about has_common_switch()
if ( snd_mixer_selem_has_capture_switch( elem ) ) {
snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &elem_sw );
md->setRecSource( elem_sw == 1 );
// Refresh the capture switch information of *all* controls of this card.
// Doing it for all is necessary, because enabling one record source often
// automatically disables another record source (due to the hardware design)
foreach ( std::shared_ptr<MixDevice> md, m_mixDevices ) {
bool isRecsrc = isRecsrcHW( md->id() );
// kDebug() << "Mixer::setRecordSource(): isRecsrcHW(" << md->id() << ") =" << isRecsrc;
md->setRecSource( isRecsrc );
}
}
// The state Mixer::OK_UNCHANGED is not implemented. It is not strictly required for
// non-pollling backends.
return Mixer::OK;
}
int Mixer_ALSA::writeVolumeToHW( const QString& id, std::shared_ptr<MixDevice> md )
{
Volume& volumePlayback = md->playbackVolume();
Volume& volumeCapture = md->captureVolume();
int devnum = id2num(id);
snd_mixer_elem_t *elem = getMixerElem( devnum );
if ( !elem ) {
return 0;
}
// --- playback switch
bool hasPlaybackSwitch = snd_mixer_selem_has_playback_switch( elem ) || snd_mixer_selem_has_common_switch ( elem );
if (hasPlaybackSwitch) {
int sw = 0;
if (!md->isMuted())
sw = !sw; // invert all bits
snd_mixer_selem_set_playback_switch_all(elem, sw);
}
// --- playback volume
if ( snd_mixer_selem_has_playback_volume( elem ) ) {
// kDebug() << "phys=" << md->hasPhysicalMuteSwitch() << ", muted=" << md->isMuted();
if ( md->isVirtuallyMuted() ) {
// Special code path for controls w/o physical mute switch. Doing it in all backends is not perfect,
// but it saves a lot of code and removes a lot of complexity in the Volume and MixDevice classes.
int ret = snd_mixer_selem_set_playback_volume_all( elem, (long)0);
if ( ret != 0 )
kDebug() << "writeVolumeToHW(" << devnum << ") [set_playback_volume] failed, errno=" << ret;
} else {
foreach (VolumeChannel vc, volumePlayback.getVolumes() ) {
int ret = 0;
switch(vc.chid) {
case Volume::LEFT : ret = snd_mixer_selem_set_playback_volume( elem, SND_MIXER_SCHN_FRONT_LEFT , vc.volume); break;
case Volume::RIGHT : ret = snd_mixer_selem_set_playback_volume( elem, SND_MIXER_SCHN_FRONT_RIGHT , vc.volume); break;
case Volume::CENTER : ret = snd_mixer_selem_set_playback_volume( elem, SND_MIXER_SCHN_FRONT_CENTER, vc.volume); break;
case Volume::SURROUNDLEFT : ret = snd_mixer_selem_set_playback_volume( elem, SND_MIXER_SCHN_REAR_LEFT , vc.volume); break;
case Volume::SURROUNDRIGHT: ret = snd_mixer_selem_set_playback_volume( elem, SND_MIXER_SCHN_REAR_RIGHT , vc.volume); break;
case Volume::REARCENTER : ret = snd_mixer_selem_set_playback_volume( elem, SND_MIXER_SCHN_REAR_CENTER , vc.volume); break;
case Volume::WOOFER : ret = snd_mixer_selem_set_playback_volume( elem, SND_MIXER_SCHN_WOOFER , vc.volume); break;
case Volume::REARSIDELEFT : ret = snd_mixer_selem_set_playback_volume( elem, SND_MIXER_SCHN_SIDE_LEFT , vc.volume); break;
case Volume::REARSIDERIGHT: ret = snd_mixer_selem_set_playback_volume( elem, SND_MIXER_SCHN_SIDE_RIGHT , vc.volume); break;
default: kDebug() << "FATAL: Unknown channel type for playback << " << vc.chid << " ... please report this"; break;
}
if ( ret != 0 )
kDebug() << "writeVolumeToHW(" << devnum << ") [set_playback_volume] failed, errno=" << ret;
//if (id== "Master:0" || id== "PCM:0" ) { kDebug() << "volumePlayback control=" << id << ", chid=" << vc.chid << ", vol=" << vc.volume; }
}
}
} // has playback volume
// --- capture volume
if ( snd_mixer_selem_has_capture_volume ( elem ) ) {
foreach (VolumeChannel vc, volumeCapture.getVolumes() ) {
int ret = 0;
switch(vc.chid) {
case Volume::LEFT : ret = snd_mixer_selem_set_capture_volume( elem, SND_MIXER_SCHN_FRONT_LEFT , vc.volume); break;
case Volume::RIGHT : ret = snd_mixer_selem_set_capture_volume( elem, SND_MIXER_SCHN_FRONT_RIGHT , vc.volume); break;
case Volume::CENTER : ret = snd_mixer_selem_set_capture_volume( elem, SND_MIXER_SCHN_FRONT_CENTER, vc.volume); break;
case Volume::SURROUNDLEFT : ret = snd_mixer_selem_set_capture_volume( elem, SND_MIXER_SCHN_REAR_LEFT , vc.volume); break;
case Volume::SURROUNDRIGHT: ret = snd_mixer_selem_set_capture_volume( elem, SND_MIXER_SCHN_REAR_RIGHT , vc.volume); break;
case Volume::REARCENTER : ret = snd_mixer_selem_set_capture_volume( elem, SND_MIXER_SCHN_REAR_CENTER , vc.volume); break;
case Volume::WOOFER : ret = snd_mixer_selem_set_capture_volume( elem, SND_MIXER_SCHN_WOOFER , vc.volume); break;
case Volume::REARSIDELEFT : ret = snd_mixer_selem_set_capture_volume( elem, SND_MIXER_SCHN_SIDE_LEFT , vc.volume); break;
case Volume::REARSIDERIGHT: ret = snd_mixer_selem_set_capture_volume( elem, SND_MIXER_SCHN_SIDE_RIGHT , vc.volume); break;
default: kDebug() << "FATAL: Unknown channel type for capture << " << vc.chid << " ... please report this"; break;
}
if ( ret != 0 )
kDebug() << "writeVolumeToHW(" << devnum << ") [set_capture_volume] failed, errno=" << ret;
//if (id== "Master:0" || id== "PCM:0" ) { kDebug() << "volumecapture control=" << id << ", chid=" << i << ", vol=" << vc.volume; }
}
} // has capture volume
// --- capture switch
if ( snd_mixer_selem_has_capture_switch( elem ) ) {
// Hint: snd_mixer_selem_has_common_switch() is already covered in the playback .
// switch. This is probably enough. It would be helpful, if the ALSA project would
// write documentation. Until then, I need to continue guessing semantics.
int sw = 0;
if ( md->isRecSource())
sw = !sw; // invert all bits
snd_mixer_selem_set_capture_switch_all( elem, sw );
}
return 0;
}
QString Mixer_ALSA::errorText( int mixer_error )
{
QString l_s_errmsg;
switch ( mixer_error ) {
case Mixer::ERR_PERM: {
l_s_errmsg = i18n("You do not have permission to access the alsa mixer device.\n" \
"Please verify if all alsa devices are properly created.");
break;
}
case Mixer::ERR_OPEN: {
l_s_errmsg = i18n("Alsa mixer cannot be found.\n" \
"Please check that the soundcard is installed and the\n" \
"soundcard driver is loaded.\n" );
break;
}
default: {
l_s_errmsg = Mixer_Backend::errorText( mixer_error );
break;
}
}
return l_s_errmsg;
}
QString ALSA_getDriverName()
{
return "ALSA";
}
QString Mixer_ALSA::getDriverName()
{
return "ALSA";
}

View file

@ -1,330 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 2006-2007 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mixer_backend.h"
#include <klocale.h>
// for the "ERR_" declartions, #include mixer.h
#include "core/mixer.h"
#include "core/ControlManager.h"
#include <QTimer>
#define POLL_RATE_SLOW 1500
#define POLL_RATE_FAST 50
#include "mixer_backend_i18n.cpp"
Mixer_Backend::Mixer_Backend(Mixer *mixer, int device) :
m_devnum (device) , m_isOpen(false), m_recommendedMaster(), _mixer(mixer), _pollingTimer(0), _cardInstance(1)
{
// In all cases create a QTimer. We will use it once as a singleShot(), even if something smart
// like ::select() is possible (as in ALSA). And force to do an update.
_readSetFromHWforceUpdate = true;
_pollingTimer = new QTimer(); // will be started on open() and stopped on close()
connect( _pollingTimer, SIGNAL(timeout()), this, SLOT(readSetFromHW()), Qt::QueuedConnection);
}
void Mixer_Backend::closeCommon()
{
freeMixDevices();
}
int Mixer_Backend::close()
{
kDebug() << "Implicit close on " << this << ". Please instead call closeCommon() and close() explicitly (in concrete Backend destructor)";
// ^^^ Background. before the destructor runs, the C++ runtime changes the virtual pointers to point back
// to the common base class. So what actually runs is not run Mixer_ALSA::close(), but this method.
//
// Comment: IMO this is totally stupid and insane behavior of C++, because you cannot simply cannot call
// the overwritten (cleanup) methods in the destructor.
return 0;
}
Mixer_Backend::~Mixer_Backend()
{
unregisterCard(this->getName());
if (!m_mixDevices.isEmpty())
{
kDebug() << "Implicit close on " << this << ". Please instead call closeCommon() and close() explicitly (in concrete Backend destructor)";
}
delete _pollingTimer;
}
void Mixer_Backend::freeMixDevices()
{
foreach (std::shared_ptr<MixDevice> md, m_mixDevices)
md->close();
m_mixDevices.clear();
}
bool Mixer_Backend::openIfValid()
{
int ret = open();
if (ret == 0 && (m_mixDevices.count() > 0 || _mixer->isDynamic()))
{
// Hint: _id is probably not yet perfectly set, as it requires the value from open() and an external
// counter. Thus we start the Timer while _id is not properly set. But it will be done immediately
// by the caller of this method.
// Future directions: Do the counter calculation in the backend. It really belongs there, as it is part of
// the PK calculation. Probably provide a standard implementation in Mixer_Backend itself. Also the
// key should be an own class, like: MixerKey(QString backend, QString baseId, int cardInstance)
if (needsPolling())
{
_pollingTimer->start(POLL_RATE_FAST);
}
else
{
// The initial state must be read manually
QTimer::singleShot( POLL_RATE_FAST, this, SLOT(readSetFromHW()));
}
return true; // could be opened
}
else
{
//shutdown();
return false; // could not open
}
}
bool Mixer_Backend::isOpen() {
return m_isOpen;
}
/**
* Queries the backend driver whether there are new changes in any of the controls.
* If you cannot find out for a backend, return "true" - this is also the default implementation.
* @return true, if there are changes. Otherwise false is returned.
*/
bool Mixer_Backend::prepareUpdateFromHW() {
return true;
}
/**
* The name of the Mixer this backend represents.
* Often it is just a name/id for the kernel. so name and id are usually identical. Virtual/abstracting backends are
* different, as they represent some distinct function like "Application streams" or "Capture Devices". Also backends
* that do not have names might can to set ID and name different like i18n("SUN Audio") and "SUNAudio".
*/
QString Mixer_Backend::getName() const
{
return m_mixerName;
}
/**
* The id of the Mixer this backend represents. The default implementation simply returns the name.
* Often it is just a name/id for the kernel. so name and id are usually identical. See also #Mixer_Backend::getName().
* You must override this method if you want to set ID different from name.
*/
QString Mixer_Backend::getId() const
{
return m_mixerName; // Backwards compatibility.
}
/**
* After calling this, readSetFromHW() will do a complete update. This will
* trigger emitting the appropriate signals like controlChanged().
*
* This method is useful, if you need to get a "refresh signal" - used at:
* 1) Start of KMix - so that we can be sure an initial signal is emitted
* 2) When reconstructing any MixerWidget (e.g. DockIcon after applying preferences)
*/
void Mixer_Backend::readSetFromHWforceUpdate() const {
_readSetFromHWforceUpdate = true;
}
/**
* You can call this to retrieve the freshest information from the mixer HW.
* This method is also called regularly by the mixer timer.
*/
void Mixer_Backend::readSetFromHW()
{
bool updated = prepareUpdateFromHW();
if ( (! updated) && (! _readSetFromHWforceUpdate) ) {
// Some drivers (ALSA) are smart. We don't need to run the following
// time-consuming update loop if there was no change
kDebug(67100) << "Mixer::readSetFromHW(): smart-update-tick";
return;
}
_readSetFromHWforceUpdate = false;
int ret = Mixer::OK_UNCHANGED;
foreach (std::shared_ptr<MixDevice> md, m_mixDevices )
{
//bool debugMe = (md->id() == "PCM:0" );
bool debugMe = false;
if (debugMe) kDebug() << "Old PCM:0 playback state" << md->isMuted()
<< ", vol=" << md->playbackVolume().getAvgVolumePercent(Volume::MALL);
int retLoop = readVolumeFromHW( md->id(), md );
if (debugMe) kDebug() << "New PCM:0 playback state" << md->isMuted()
<< ", vol=" << md->playbackVolume().getAvgVolumePercent(Volume::MALL);
if (md->isEnum() )
{
/*
* This could be reworked:
* Plan: Read everything (incuding enum's) in readVolumeFromHW().
* readVolumeFromHW() should then be renamed to readHW().
*/
md->setEnumId( enumIdHW(md->id()) );
}
// Transition the outer return value with the value from this loop iteration
if ( retLoop == Mixer::OK && ret == Mixer::OK_UNCHANGED )
{
// Unchanged => OK (Changed)
ret = Mixer::OK;
}
else if ( retLoop != Mixer::OK && retLoop != Mixer::OK_UNCHANGED )
{
// If current ret from loop in not OK, then transition to that: ret (Something) => retLoop (Error)
ret = retLoop;
}
}
if ( ret == Mixer::OK )
{
// We explicitly exclude Mixer::OK_UNCHANGED and Mixer::ERROR_READ
if ( needsPolling() )
{
// Upgrade polling frequency temporarily to be more smoooooth
_pollingTimer->setInterval(POLL_RATE_FAST);
QTime fastPollingEndsAt = QTime::currentTime ();
fastPollingEndsAt = fastPollingEndsAt.addSecs(5);
_fastPollingEndsAt = fastPollingEndsAt;
//_fastPollingEndsAt = fastPollingEndsAt;
kDebug() << "Start fast polling from " << QTime::currentTime() <<"until " << _fastPollingEndsAt;
}
ControlManager::instance().announce(_mixer->id(), ControlChangeType::Volume, QString("Mixer.fromHW"));
}
else
{
// This code path is entered on Mixer::OK_UNCHANGED and ERROR
bool fastPollingEndsNow = (!_fastPollingEndsAt.isNull()) && _fastPollingEndsAt < QTime::currentTime ();
if ( fastPollingEndsNow )
{
kDebug() << "End fast polling";
_fastPollingEndsAt = QTime(); // NULL time
_pollingTimer->setInterval(POLL_RATE_SLOW);
}
}
}
/**
* Return the MixDevice, that would qualify best as MasterDevice. The default is to return the
* first device in the device list. Backends can override this (i.e. the ALSA Backend does so).
* The users preference is NOT returned by this method - see the Mixer class for that.
*/
std::shared_ptr<MixDevice> Mixer_Backend::recommendedMaster()
{
if ( m_recommendedMaster )
{
// Backend has set a recommended master. Thats fine. Using it.
return m_recommendedMaster;
}
else if ( ! m_mixDevices.isEmpty() )
{
// Backend has NOT set a recommended master. Evil backend
// => lets help out, using the first device (if exists)
return m_mixDevices.at(0);
}
else
{
if ( !_mixer->isDynamic())
// This should never ever happen, as KMix does NOT accept soundcards without controls
kError(67100) << "Mixer_Backend::recommendedMaster(): returning invalid master. This is a bug in KMix. Please file a bug report stating how you produced this.";
}
// If we reach this code path, then obiously m_recommendedMaster == 0 (see above)
return m_recommendedMaster;
}
/**
* Sets the ID of the currently selected Enum entry.
* This is a dummy implementation - if the Mixer backend
* wants to support it, it must implement the driver specific
* code in its subclass (see Mixer_ALSA.cpp for an example).
*/
void Mixer_Backend::setEnumIdHW(const QString& , unsigned int) {
return;
}
/**
* Return the ID of the currently selected Enum entry.
* This is a dummy implementation - if the Mixer backend
* wants to support it, it must implement the driver specific
* code in its subclass (see Mixer_ALSA.cpp for an example).
*/
unsigned int Mixer_Backend::enumIdHW(const QString& ) {
return 0;
}
/**
* Move the stream to a new destination
*/
bool Mixer_Backend::moveStream( const QString& id, const QString& destId ) {
Q_UNUSED(id);
Q_UNUSED(destId);
return false;
}
QString Mixer_Backend::errorText(int mixer_error)
{
QString l_s_errmsg;
switch (mixer_error)
{
case Mixer::ERR_PERM:
l_s_errmsg = i18n("kmix:You do not have permission to access the mixer device.\n" \
"Please check your operating systems manual to allow the access.");
break;
case Mixer::ERR_WRITE:
l_s_errmsg = i18n("kmix: Could not write to mixer.");
break;
case Mixer::ERR_READ:
l_s_errmsg = i18n("kmix: Could not read from mixer.");
break;
case Mixer::ERR_OPEN:
l_s_errmsg = i18n("kmix: Mixer cannot be found.\n" \
"Please check that the soundcard is installed and that\n" \
"the soundcard driver is loaded.\n");
break;
default:
l_s_errmsg = i18n("kmix: Unknown error. Please report how you produced this error.");
break;
}
return l_s_errmsg;
}
#include "moc_mixer_backend.cpp"

View file

@ -1,234 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 2006-2007 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MIXER_BACKEND_H
#define MIXER_BACKEND_H
#include <QString>
#include <QtCore/qdatetime.h>
#include <QTimer>
//#include "core/mixer.h"
#include "core/mixdevice.h"
#include "core/mixset.h"
class Mixer;
class Mixer_Backend : public QObject
{
Q_OBJECT
friend class Mixer;
// The Mixer Backend's may only be accessed from the Mixer class.
protected:
Mixer_Backend(Mixer *mixer, int devnum);
virtual ~Mixer_Backend();
/**
* Derived classes MUST implement this to open the mixer.
*
* @return a KMix error code (O=OK).
*/
virtual int open() = 0;
/**
* Derived classes MUST implement this to close the mixer. Do not call this directly, but use shutdown() instead.
* The method cannot be made pure virtual, as we use close() in the destructor, and C++ does not allow this.
* http://stackoverflow.com/questions/99552/where-do-pure-virtual-function-call-crashes-come-from?lq=1
*
* @return a KMix error code (O=OK).
*/
virtual int close(); // Not pure virtual. See comment!
/**
* Shutdown deinitializes this MixerBackend, freeing resources
*/
void closeCommon();
/**
* Returns the driver name, e.g. "ALSA" or "OSS". This virtual method is for looking up the
* driver name on instanciated objects.
*
* Please note, that there is also a static implementation of the driverName
* (Because there is no "virtual static" in C++, I need the method twice).
* The static implementation is for the Mixer Factory (who needs it *before* instanciating an object).
* While it is not a member function, its implementation can still be found in the corresponding
* Backend implementation. For example in mixer_oss.cpp there is a global function called OSS_getDriverName().
*/
virtual QString getDriverName() = 0;
/**
* Opens the mixer, if it constitures a valid Device. You should return "false", when
* the Mixer with the devnum given in the constructor is not supported by the Backend. The two
* typical cases are:
* (1) No such hardware installed
* (2) The hardware exists, but has no mixer support (e.g. external soundcard with only mechanical volume knobs)
* The implementation calls open(), checks the return code and whether the number of
* supported channels is > 0. The device remains opened if it is valid, otherwise a close() is done.
*/
bool openIfValid();
/** @return true, if the Mixer is open (and thus can be operated) */
bool isOpen();
virtual bool prepareUpdateFromHW();
void readSetFromHWforceUpdate() const;
/// Volume Read
virtual int readVolumeFromHW( const QString& id, std::shared_ptr<MixDevice> ) = 0;
/// Volume Write
virtual int writeVolumeToHW( const QString& id, std::shared_ptr<MixDevice> ) = 0;
/// Enums
virtual void setEnumIdHW(const QString& id, unsigned int);
virtual unsigned int enumIdHW(const QString& id);
virtual bool moveStream( const QString& id, const QString& destId );
// Future directions: Move media*() methods to MediaController class
virtual int mediaPlay(QString ) { return 0; }; // implement in the backend if it supports it
virtual int mediaPrev(QString ) { return 0; }; // implement in the backend if it supports it
virtual int mediaNext(QString ) { return 0;}; // implement in the backend if it supports it
/// Overwrite in the backend if the backend can see changes without polling
virtual bool needsPolling() { return true; }
std::shared_ptr<MixDevice> recommendedMaster();
/**
* Return a translated error text for the given error number.
* Subclasses can override this method to produce platform
* specific error descriptions.
*/
virtual QString errorText(int mixer_error);
/// Returns translated WhatsThis messages for a control.Translates from
virtual QString translateKernelToWhatsthis(const QString &kernelName);
// Return an Universal Device Identification (suitable for the OS, especially for Hotplug and Unplug events)
virtual QString& udi() { return _udi; };
int m_devnum;
/**
* User friendly name of the Mixer (e.g. "USB 7.1 Surround System"). If your mixer API gives you a usable name, use that name.
*/
virtual QString getName() const;
virtual QString getId() const;
virtual int getCardInstance() const { return _cardInstance; }
// All controls of this card
MixSet m_mixDevices;
/******************************************************************************************
* Please don't access the next vars from the Mixer class (even though Mixer is a friend).
* There are proper access methods for them.
******************************************************************************************/
bool m_isOpen;
// The MixDevice that would qualify best as MasterDevice (according to the taste of the Backend developer)
std::shared_ptr<MixDevice> m_recommendedMaster;
// The Mixer is stored her only for one reason: The backend creates the MixDevice's, and it has shown
// that it is helpful if the MixDevice's know their corresponding Mixer. KMix lived 10 years without that,
// but just believe me. It's *really* better, for example, you can put controls of different soundcards in
// one View. That is very cool! Also the MDW doesn't need to store the Mixer any longer (MDW is a GUI element,
// so that was 'wrong' anyhow
Mixer* _mixer;
QTimer* _pollingTimer;
QString _udi; // Universal Device Identification
mutable bool _readSetFromHWforceUpdate;
signals:
void controlChanged( void ); // TODO remove?
protected:
void freeMixDevices();
QMap<QString,int> s_mixerNums;
/**
* Registers the card for this Backend and sets the card discriminator for the given card name.
* The discriminator should always be 1, unless a second card with
* the same name of a registered card was already registered. Default implementation will return 2, 3 and so on
* for more cards. Subclasses can override this and return arbitrary ID's, but any ID that is not 1 will be
* displayed to the user everywhere where a mixer name is shown, like in the tab name.
*
* For the background please see BKO-327471 and read the following info:
* "Count mixer nums for every mixer name to identify mixers with equal names.
* This is for creating persistent (reusable) primary keys, which can safely
* be referenced (especially for config file access, so it is meant to be persistent!)."
*
*
*
* @param cardBaseName
*/
void registerCard(QString cardBaseName)
{
m_mixerName = cardBaseName;
int cardDiscriminator = 1 + s_mixerNums[cardBaseName];
kDebug() << "cardBaseName=" << cardBaseName << ", cardDiscriminator=" << cardDiscriminator;
_cardInstance = cardDiscriminator;
// return cardDiscriminator;
}
/**
* Unregisters the card of this Backend. The cardDiscriminator counter for this card name is reduced by 1.
* See #registerCard() for more info.
*
* TODO This is not entirely correct. Example: If the first card (cardDiscrimiator == 1) is unpluggged, then
* s_mixerNums["cardName"] is changed from 2 to 1. The next plug of registerCard("cardName") will use
* cardDiscriminator == 2, but the card with taht discrimniator was not unplugged => BANG!!!
*
* @param cardBaseName
*/
void unregisterCard(QString cardBaseName)
{
QMap<QString,int>::const_iterator it = s_mixerNums.constFind(cardBaseName);
if (it != s_mixerNums.constEnd())
{
int beforeValue = it.value();
int afterValue = beforeValue-1;
if (beforeValue > 0)
s_mixerNums[cardBaseName] = afterValue;
kDebug() << "beforeValue=" << beforeValue << ", afterValue" << afterValue;
}
}
int _cardInstance;
protected slots:
virtual void readSetFromHW();
private:
QTime _fastPollingEndsAt;
QString m_mixerName;
};
typedef Mixer_Backend *getMixerFunc( Mixer* mixer, int device );
typedef QString getDriverNameFunc( );
struct MixerFactory
{
getMixerFunc *getMixer;
getDriverNameFunc *getDriverName;
};
#endif

View file

@ -1,29 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 2006-2009 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
QString Mixer_Backend::translateKernelToWhatsthis(const QString &kernelName)
{
if (kernelName == "Mic:0") return i18n("Recording level of the microphone input.");
else if (kernelName == "Master:0") return i18n("Controls the volume of the front speakers or all speakers (depending on your soundcard model). If you use a digital output, you might need to also use other controls like ADC or DAC. For headphones, soundcards often supply a Headphone control.");
else if (kernelName == "PCM:0") return i18n("Most media, such as MP3s or Videos, are played back using the PCM channel. As such, the playback volume of such media is controlled by both this and the Master or Headphone channels.");
else if (kernelName == "Headphone:0") return i18n("Controls the headphone volume. Some soundcards include a switch that must be manually activated to enable the headphone output.");
else return i18n("---");
}

View file

@ -1,648 +0,0 @@
/**
* KMix -- MPRIS2 backend
*
* Copyright (C) 2011 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "mixer_mpris2.h"
#include "core/mixer.h"
#include "core/ControlManager.h"
#include "core/GlobalConfig.h"
#include <QStringList>
#include <QDBusReply>
#include <QString>
#include <qvariant.h>
#include <KDebug>
#include <KLocale>
// Set the QDBUS_DEBUG env variable for debugging Qt DBUS calls.
Mixer_Backend* MPRIS2_getMixer(Mixer *mixer, int device )
{
return new Mixer_MPRIS2(mixer, device );
}
Mixer_MPRIS2::Mixer_MPRIS2(Mixer *mixer, int device) : Mixer_Backend(mixer, device )
{
}
int Mixer_MPRIS2::open()
{
if ( m_devnum != 0 )
return Mixer::ERR_OPEN;
registerCard(i18n("Playback Streams"));
_id = "Playback Streams";
_mixer->setDynamic();
return addAllRunningPlayersAndInitHotplug();
}
int Mixer_MPRIS2::close()
{
m_isOpen = false;
closeCommon();
qDeleteAll(controls);
controls.clear();
return 0;
}
int Mixer_MPRIS2::mediaPlay(QString id)
{
return mediaControl(id, "PlayPause");
}
int Mixer_MPRIS2::mediaPrev(QString id)
{
return mediaControl(id, "Previous");
}
int Mixer_MPRIS2::mediaNext(QString id)
{
return mediaControl(id, "Next");
}
/**
* Sends a media control command to the given application.
* @param applicationId The MPRIS applicationId
* @returns Always 0. Hint: Currently nobody uses the return code
*/
int Mixer_MPRIS2::mediaControl(QString applicationId, QString commandName)
{
MPrisControl* mad = controls.value(applicationId);
if ( mad == 0 )
return 0; // Might have disconnected recently => simply ignore command
kDebug() << "Send " << commandName << " to id=" << applicationId;
QDBusPendingReply<> repl2 = mad->playerIfc->asyncCall(commandName);
QDBusPendingCallWatcher* watchMediaControlReply = new QDBusPendingCallWatcher(repl2, mad);
connect(watchMediaControlReply, SIGNAL(finished(QDBusPendingCallWatcher *)), this, SLOT(watcherMediaControl(QDBusPendingCallWatcher *)));
return 0; // Presume everything went well. Can't do more for ASYNC calls
}
void Mixer_MPRIS2::watcherMediaControl(QDBusPendingCallWatcher* watcher)
{
MPrisControl* mprisCtl = watcherHelperGetMPrisControl(watcher);
if (mprisCtl == 0) {
return; // Reply for unknown media player. Probably "unplugged" (or not yet plugged)
}
// Actually the code below in this method is more or less just debugging
const QDBusMessage& msg = watcher->reply();
QString id = mprisCtl->getId();
QString busDestination = mprisCtl->getBusDestination();
kDebug() << "Media control for id=" << id << ", path=" << msg.path() << ", interface=" << msg.interface() << ", busDestination" << busDestination;
}
/**
* readVolumeFromHW() should be used only for hotplug (and even that should go away). Everything should operate via
* the slot volumeChanged in the future.
*/
int Mixer_MPRIS2::readVolumeFromHW( const QString& /*id*/, std::shared_ptr<MixDevice> /*md*/)
{
// Everything is done by notifications => no code neccessary
return Mixer::OK_UNCHANGED;
}
/**
* A slot that processes data from the MPrisControl that emit the signal.
*
* @param The emitting MPrisControl
* @param newVolume The new volume
*/
void Mixer_MPRIS2::playbackStateChanged(MPrisControl* mad, MediaController::PlayState playState)
{
std::shared_ptr<MixDevice> md = m_mixDevices.get(mad->getId());
md->getMediaController()->setPlayState(playState);
QMetaObject::invokeMethod(this, "announceGUI", Qt::QueuedConnection);
// ControlManager::instance().announce(_mixer->id(), ControlChangeType::GUI, QString("MixerMPRIS2.playbackStateChanged"));
}
/**
* A slot that processes data from the MPrisControl that emit the signal.
*
* @param The emitting MPrisControl
* @param newVolume The new volume
*/
void Mixer_MPRIS2::volumeChanged(MPrisControl* mad, double newVolume)
{
std::shared_ptr<MixDevice> md = m_mixDevices.get(mad->getId());
int volInt = newVolume *100;
if (GlobalConfig::instance().data.debugVolume)
kDebug() << "changed" << volInt;
volumeChangedInternal(md, volInt);
}
void Mixer_MPRIS2::volumeChangedInternal(std::shared_ptr<MixDevice> md, int volumePercentage)
{
if ( md->isVirtuallyMuted() && volumePercentage == 0) {
// Special code path for virtual mute switches. Don't write back the volume if it is muted in the KMix GUI
return;
}
Volume& vol = md->playbackVolume();
vol.setVolume( Volume::LEFT, volumePercentage);
md->setMuted(volumePercentage == 0);
QMetaObject::invokeMethod(this, "announceVolume", Qt::QueuedConnection);
// ControlManager::instance().announce(_mixer->id(), ControlChangeType::Volume, QString("MixerMPRIS2.volumeChanged"));
}
// The following is an example message for an incoming volume change:
/*
signal sender=:1.125 -> dest=(null destination) serial=503 path=/org/mpris/MediaPlayer2; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged
string "org.mpris.MediaPlayer2.Player"
array [
dict entry(
string "Volume"
variant double 0.81
)
]
array [
]
*/
/**
* @overload
*
* @param id
* @param md
* @return
*/
int Mixer_MPRIS2::writeVolumeToHW( const QString& id, std::shared_ptr<MixDevice> md )
{
Volume& vol = md->playbackVolume();
double volFloat = 0;
if ( ! md->isMuted() ) {
int volInt = vol.getVolume(Volume::LEFT);
volFloat = volInt/100.0;
}
QList<QVariant> arg;
arg.append(QString("org.mpris.MediaPlayer2.Player"));
arg.append(QString("Volume"));
arg << QVariant::fromValue(QDBusVariant(volFloat));
MPrisControl* mad = controls.value(id);
QVariant v1 = QVariant(QString("org.mpris.MediaPlayer2.Player"));
QVariant v2 = QVariant(QString("Volume"));
QVariant v3 = QVariant::fromValue(QDBusVariant(volFloat));
// QVariant v3 = QVariant(volFloat);
// I don't care too much for the reply, as I won't receive a result. Thus fire-and-forget here.
mad->propertyIfc->asyncCall("Set", v1, v2, v3);
return 0;
}
void Mixer_MPRIS2::setEnumIdHW(const QString&, unsigned int)
{
// no enums in MPRIS
}
unsigned int Mixer_MPRIS2::enumIdHW(const QString&)
{
// no enums in MPRIS
return 0;
}
bool Mixer_MPRIS2::moveStream( const QString&, const QString& )
{
// not supported in MPRIS
return false;
}
/**
* Adds all currently running players and then starts listening
* for changes (new players, and disappearing players).<br>
*
* @return int
**/
int Mixer_MPRIS2::addAllRunningPlayersAndInitHotplug()
{
QDBusConnection dbusConn = QDBusConnection::sessionBus();
if (! dbusConn.isConnected() ) {
kError(67100) << "Cannot connect to the D-Bus session bus.\n"
<< "To start it, run:\n"
<<"\teval `dbus-launch --auto-syntax`\n";
return Mixer::ERR_OPEN;
}
// Start listening for new Mediaplayers
bool connected = dbusConn.connect("", QString("/org/freedesktop/DBus"), "org.freedesktop.DBus", "NameOwnerChanged", this, SLOT(newMediaPlayer(QString,QString,QString)) );
if (!connected) {
kWarning() << "MPRIS2 hotplug init failure. New Media Players will not be detected.";
}
/* Here is a small concurrency issue.
* If new players appear between registeredServiceNames() below and the connect() above these players *might* show up doubled in KMix.
* There is no simple solution (reversing could have the problem of not-adding), so we live for now with it.
*/
/*
* Bug 311189: Introspecting via "dbusConn.interface()->registeredServiceNames()" does not work too well.
* Comment: I am not so sure that registeredServiceNames() is really an issue. It is more likely
* in a later step, when talking to the probed apps. Still, I now do a hand crafted 3-line version of
* registeredServiceNames() via "ListNames", so I can later more easily change to async.
*/
QDBusInterface dbusIfc("org.freedesktop.DBus", "/org/freedesktop/DBus",
"org.freedesktop.DBus", dbusConn);
QDBusPendingReply<QStringList> repl = dbusIfc.asyncCall("ListNames");
repl.waitForFinished();
if (! repl.isValid() ) {
kError() << "Invalid reply while listing Media Players. MPRIS2 players will not be available." << repl.error();
return 1;
}
foreach (QString busDestination , repl.value() ) {
if ( busDestination.startsWith("org.mpris.MediaPlayer2") ){
addMprisControlAsync(busDestination);
kDebug() << "MPRIS2: Attached media player on busDestination=" << busDestination;
}
}
return 0;
}
QString Mixer_MPRIS2::busDestinationToControlId(const QString& busDestination)
{
const QString prefix = "org.mpris.MediaPlayer2.";
if (! busDestination.startsWith(prefix)) {
kWarning() << "Ignoring unsupported control, busDestination=" << busDestination;
return QString();
}
return busDestination.mid(prefix.length());
}
/**
* Asynchronously add the MPRIS control designated by the DBUS busDestination.
* to the internal apps list.
*
* @param conn An open connection to the DBUS Session Bus
* @param busDestination The DBUS busDestination, e.g. "org.mpris.MediaPlayer2.amarok"
*/
void Mixer_MPRIS2::addMprisControlAsync(QString busDestination)
{
// -1- Create a MPrisControl. Its fields will be filled partially here, partially via ASYNC DUBUS replies
QString id = busDestinationToControlId(busDestination);
kDebug() << "Get control of busDestination=" << busDestination << "id=" << id;
QDBusConnection conn = QDBusConnection::sessionBus();
QDBusInterface *qdbiProps = new QDBusInterface(QString(busDestination), QString("/org/mpris/MediaPlayer2"), "org.freedesktop.DBus.Properties", conn, this);
QDBusInterface *qdbiPlayer = new QDBusInterface(QString(busDestination), QString("/org/mpris/MediaPlayer2"), "org.mpris.MediaPlayer2.Player", conn, this);
// -2- Add the control to our official control list
MPrisControl* mad = new MPrisControl(id, busDestination);
mad->propertyIfc = qdbiProps;
mad->playerIfc = qdbiPlayer;
controls.insert(id, mad);
/*
* WTF: - asyncCall("Get", arg) : returns an error message (see below)
* - asyncCallWithArgumentList("Get", arg) : returns an error message (see below)
* - callWithArgumentList(QDBus::Block, "Get", arg) : works
* - syncCall("Get", v1, v2) : works
*
* kmix(13543) Mixer_MPRIS2::addMPrisControl: (marok), msg2= QDBusMessage(type=Error, service=":1.44", error name="org.freedesktop.DBus.Error.UnknownMethod", error message="No such method 'Get' in interface 'org.freedesktop.DBus.Properties' at object path '/org/mpris/MediaPlayer2' (signature 'av')", signature="s", contents=("No such method 'Get' in interface 'org.freedesktop.DBus.Properties' at object path '/org/mpris/MediaPlayer2' (signature 'av')") ) , isValid= false , isFinished= true , isError= true
*
* This behavior is total counter-intuitive :-(((
*/
// Create ASYNC DBUS queries for the new control. This effectively starts a chain of async DBUS commands.
QVariant v1 = QVariant(QString("org.mpris.MediaPlayer2"));
QVariant v2 = QVariant(QString("Identity"));
QDBusPendingReply<QVariant > repl2 = mad->propertyIfc->asyncCall("Get", v1, v2);
QDBusPendingCallWatcher* watchIdentity = new QDBusPendingCallWatcher(repl2, mad);
connect(watchIdentity, SIGNAL(finished(QDBusPendingCallWatcher *)), this, SLOT(watcherPlugControlId(QDBusPendingCallWatcher *)));
}
MixDevice::ChannelType Mixer_MPRIS2::getChannelTypeFromPlayerId(const QString& id)
{
// TODO This hardcoded application list is a quick hack. It should be generalized.
MixDevice::ChannelType ct = MixDevice::APPLICATION_STREAM;
if (id.startsWith("amarok")) {
ct = MixDevice::APPLICATION_AMAROK;
} else if (id.startsWith("banshee")) {
ct = MixDevice::APPLICATION_BANSHEE;
} else if (id.startsWith("vlc")) {
ct = MixDevice::APPLICATION_VLC;
} else if (id.startsWith("xmms")) {
ct = MixDevice::APPLICATION_XMM2;
} else if (id.startsWith("tomahawk")) {
ct = MixDevice::APPLICATION_TOMAHAWK;
} else if (id.startsWith("clementine")) {
ct = MixDevice::APPLICATION_CLEMENTINE;
}
return ct;
}
void Mixer_MPRIS2::watcherInitialVolume(QDBusPendingCallWatcher* watcher)
{
MPrisControl* mprisCtl = watcherHelperGetMPrisControl(watcher);
if (mprisCtl == 0)
return; // Reply for unknown media player. Probably "unplugged" (or not yet plugged)
const QDBusMessage& msg = watcher->reply();
QList<QVariant> repl = msg.arguments();
if ( ! repl.isEmpty() ) {
QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(repl.at(0));
QVariant result2 = dbusVariant.variant();
double volume = result2.toDouble();
volumeChanged(mprisCtl, volume);
}
watcher->deleteLater();
}
void Mixer_MPRIS2::watcherInitialPlayState(QDBusPendingCallWatcher* watcher)
{
MPrisControl* mprisCtl = watcherHelperGetMPrisControl(watcher);
if (mprisCtl == 0)
return; // Reply for unknown media player. Probably "unplugged" (or not yet plugged)
const QDBusMessage& msg = watcher->reply();
QList<QVariant> repl = msg.arguments();
if ( ! repl.isEmpty() ) {
QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(repl.at(0));
QVariant result2 = dbusVariant.variant();
QString playbackStateString = result2.toString();
MediaController::PlayState playState = Mixer_MPRIS2::mprisPlayStateString2PlayState(playbackStateString);
playbackStateChanged(mprisCtl, playState);
}
watcher->deleteLater();
}
/**
* Convenience method for the watcher*() methods.
* Returns the MPrisControl that is parent of the given watcher, if the reply is valid. In this case you can
* use the result and call watcher->deleteLater() after processing the result.
*
* Otherwise 0 is returned, and watcher->deleteLater() is called. <b>Important</b> You must call watcher->deleteLater()
* yourself for the other (normal/good) case.
*
* @param watcher
* @return
*/
MPrisControl* Mixer_MPRIS2::watcherHelperGetMPrisControl(QDBusPendingCallWatcher* watcher)
{
const QDBusMessage& msg = watcher->reply();
if ( msg.type() == QDBusMessage::ReplyMessage ) {
QObject* obj = watcher->parent();
MPrisControl* mad = qobject_cast<MPrisControl*>(obj);
if (mad != 0) {
return mad;
}
kWarning() << "Ignoring unexpected Control Id. object=" << obj;
} else if ( msg.type() == QDBusMessage::ErrorMessage ) {
kError() << "ERROR in Media control operation, path=" << msg.path() << ", msg=" << msg;
}
watcher->deleteLater();
return 0;
}
void Mixer_MPRIS2::watcherPlugControlId(QDBusPendingCallWatcher* watcher)
{
MPrisControl* mprisCtl = watcherHelperGetMPrisControl(watcher);
if (mprisCtl == 0) {
return; // Reply for unknown media player. Probably "unplugged" (or not yet plugged)
}
const QDBusMessage& msg = watcher->reply();
QString id = mprisCtl->getId();
QString busDestination = mprisCtl->getBusDestination();
QString readableName = id; // Start with ID, but replace with reply (if exists)
kDebug() << "Plugging id=" << id << ", busDestination" << busDestination << ", name= " << readableName;
QList<QVariant> repl = msg.arguments();
if ( ! repl.isEmpty() ) {
// We have to do some very ugly casting from QVariant to QDBusVariant to QVariant. This API totally sucks.
QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(repl.at(0));
QVariant result2 = dbusVariant.variant();
readableName = result2.toString();
// kDebug() << "REPLY " << result2.type() << ": " << readableName;
MixDevice::ChannelType ct = getChannelTypeFromPlayerId(id);
MixDevice* mdNew = new MixDevice(_mixer, id, readableName, ct);
// MPRIS2 doesn't support an actual mute switch. Mute is defined as volume = 0.0
// Thus we won't add the playback switch
Volume* vol = new Volume( 100, 0, false, false);
vol->addVolumeChannel(VolumeChannel(Volume::LEFT)); // MPRIS is only one control ("Mono")
MediaController* mediaContoller = mdNew->getMediaController();
mediaContoller->addMediaPlayControl();
mediaContoller->addMediaNextControl();
mediaContoller->addMediaPrevControl();
mdNew->setApplicationStream(true);
mdNew->addPlaybackVolume(*vol);
m_mixDevices.append( mdNew->addToPool() );
delete vol; // vol is only temporary. mdNew has its own volume object. => delete
QDBusConnection sessionBus = QDBusConnection::sessionBus();
sessionBus.connect(busDestination, QString("/org/mpris/MediaPlayer2"), "org.freedesktop.DBus.Properties", "PropertiesChanged", mprisCtl, SLOT(onPropertyChange(QString,QVariantMap,QStringList)) );
connect(mprisCtl, SIGNAL(volumeChanged(MPrisControl*,double)), this, SLOT(volumeChanged(MPrisControl*,double)) );
connect(mprisCtl, SIGNAL(playbackStateChanged(MPrisControl*,MediaController::PlayState)), SLOT (playbackStateChanged(MPrisControl*,MediaController::PlayState)) );
sessionBus.connect(busDestination, QString("/Player"), "org.freedesktop.MediaPlayer", "TrackChange", mprisCtl, SLOT(trackChangedIncoming(QVariantMap)) );
// The following line is evil: mad->playerIfc->property("Volume") is in fact a synchronous call, and
// sync calls are strictly forbidden, see bug 317926
// volumeChanged(mad, mad->playerIfc->property("Volume").toDouble());
// --- Query initial state --------------------------------------------------------------------------------
QVariant v1 = QVariant(QString("org.mpris.MediaPlayer2.Player"));
QVariant v2 = QVariant(QString("Volume"));
QDBusPendingReply<QVariant > repl2 = mprisCtl->propertyIfc->asyncCall("Get", v1, v2);
QDBusPendingCallWatcher* watcherOutgoing = new QDBusPendingCallWatcher(repl2, mprisCtl);
connect(watcherOutgoing, SIGNAL(finished(QDBusPendingCallWatcher *)), this, SLOT(watcherInitialVolume(QDBusPendingCallWatcher *)));
v2 = QVariant(QString("PlaybackStatus"));
repl2 = mprisCtl->propertyIfc->asyncCall("Get", v1, v2);
watcherOutgoing = new QDBusPendingCallWatcher(repl2, mprisCtl);
connect(watcherOutgoing, SIGNAL(finished(QDBusPendingCallWatcher *)), this, SLOT(watcherInitialPlayState(QDBusPendingCallWatcher *)));
// Push notifyToReconfigureControls to stack, so it will not be executed synchronously
announceControlListAsync(id);
}
watcher->deleteLater();
}
// -----------------------------------------------------------------------------------------------------------
// ASYNC announce slots, including convenience wrappers
// -----------------------------------------------------------------------------------------------------------
/**
* Convenience wrapper to do the ASYNC call to #announceControlList()
* @param
*/
void Mixer_MPRIS2::announceControlListAsync(QString /*streamId*/)
{
// currently we do not use the streamId
QMetaObject::invokeMethod(this, "announceControlList", Qt::QueuedConnection);
}
void Mixer_MPRIS2::announceControlList()
{
ControlManager::instance().announce(_mixer->id(), ControlChangeType::ControlList, getDriverName());
}
void Mixer_MPRIS2::announceGUI()
{
ControlManager::instance().announce(_mixer->id(), ControlChangeType::GUI, getDriverName());
}
void Mixer_MPRIS2::announceVolume()
{
ControlManager::instance().announce(_mixer->id(), ControlChangeType::Volume, getDriverName());
}
// -----------------------------------------------------------------------------------------------------------
/**
* Handles the hotplug of new MPRIS2 enabled Media Players
*/
void Mixer_MPRIS2::newMediaPlayer(QString name, QString oldOwner, QString newOwner)
{
if ( name.startsWith("org.mpris.MediaPlayer2") ) {
if ( oldOwner.isEmpty() && !newOwner.isEmpty()) {
kDebug() << "Mediaplayer registers: " << name;
addMprisControlAsync(name);
} else if ( !oldOwner.isEmpty() && newOwner.isEmpty()) {
QString id = busDestinationToControlId(name);
kDebug() << "Mediaplayer unregisters: " << name << " , id=" << id;
// -1- Remove Mediaplayer connection
if (controls.contains(id)) {
const MPrisControl *control = controls.value(id);
QObject::disconnect(control,0,0,0);
controls.remove(id);
}
// -2- Remove MixDevice from internal list
std::shared_ptr<MixDevice> md = m_mixDevices.get(id);
if (md) {
// We know about the player that is unregistering => remove internally
md->close();
m_mixDevices.removeById(id);
announceControlListAsync(id);
kDebug() << "MixDevice 4 useCount=" << md.use_count();
}
} else {
kWarning() << "Mediaplayer has registered under a new name. This is currently not supported by KMix";
}
}
}
/**
* This slot is a simple proxy that enriches the DBUS signal with our data, which especially contains the id of the MixDevice.
*/
void MPrisControl::trackChangedIncoming(QVariantMap /*msg*/)
{
kDebug() << "Track changed";
}
MediaController::PlayState Mixer_MPRIS2::mprisPlayStateString2PlayState(const QString& playbackStatus)
{
MediaController::PlayState playState = MediaController::PlayUnknown;
if (playbackStatus == "Playing") {
playState = MediaController::PlayPlaying;
} else if (playbackStatus == "Stopped") {
playState = MediaController::PlayStopped;
} else if (playbackStatus == "Paused") {
playState = MediaController::PlayPaused;
}
return playState;
}
/**
* This slot is a simple proxy that enriches the DBUS signal with our data, which especially contains the id of the MixDevice.
*/
void MPrisControl::onPropertyChange(QString /*ifc*/,QVariantMap msg ,QStringList /*sl*/)
{
QMap<QString, QVariant>::iterator v = msg.find("Volume");
if (v != msg.end() ) {
double volDouble = v.value().toDouble();
kDebug(67100) << "volumeChanged incoming: vol=" << volDouble;
emit volumeChanged( this, volDouble);
}
v = msg.find("PlaybackStatus");
if (v != msg.end() ) {
QString playbackStatus = v.value().toString();
MediaController::PlayState playState = Mixer_MPRIS2::mprisPlayStateString2PlayState(playbackStatus);
kDebug() << "PlaybackStatus is now " << playbackStatus;
emit playbackStateChanged(this, playState);
}
}
Mixer_MPRIS2::~Mixer_MPRIS2()
{
close();
}
MPrisControl::MPrisControl(QString id, QString busDestination)
: propertyIfc(0)
, playerIfc(0)
{
volume = 0;
this->id = id;
this->busDestination = busDestination;
retrievedElems = MPrisControl::NONE;
}
MPrisControl::~MPrisControl()
{
delete propertyIfc;
delete playerIfc;
}
QString Mixer_MPRIS2::getDriverName()
{
return "MPRIS2";
}
QString MPRIS2_getDriverName()
{
return "MPRIS2";
}
#include "moc_mixer_mpris2.cpp"

View file

@ -1,175 +0,0 @@
/**
* KMix -- MPRIS2 backend
*
* Copyright (C) 2011 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef Mixer_MPRIS2_H
#define Mixer_MPRIS2_H
#include <QString>
#include <QtDBus>
#include <QDBusInterface>
#include <QtDBus/qdbuspendingcall.h>
#include <QMap>
#include <QMutex>
#include <QQueue>
#include "mixer_backend.h"
class MPrisControl : public QObject
{
Q_OBJECT
public:
MPrisControl(QString id, QString busDestination);
~MPrisControl();
enum DATA_ELEM
{
NONE = 0, NAME = 1, VOLUME = 2, ALL = 3
};
QDBusInterface* propertyIfc;
QDBusInterface* playerIfc;
private:
QString id;
QString busDestination;
QString name;
int volume;
int retrievedElems;
public:
const QString& getId() const
{
return id;
}
const QString& getBusDestination() const
{
return busDestination;
}
const QString& getName() const
{
return name;
}
void setName(const QString& name)
{
retrievedElems |= MPrisControl::NAME;
this->name = name;
}
int getVolume() const
{
return volume;
}
void setVolume(int volume)
{
retrievedElems |= MPrisControl::VOLUME;
this->volume = volume;
}
bool isComplete()
{
return retrievedElems == MPrisControl::ALL;
}
public slots:
void trackChangedIncoming(QVariantMap msg);
void onPropertyChange(QString,QVariantMap,QStringList);
signals:
void volumeChanged(MPrisControl* mad, double);
void playbackStateChanged(MPrisControl* mad, MediaController::PlayState);
};
class Mixer_MPRIS2 : public Mixer_Backend
{
Q_OBJECT
// friend class Mixer_MPRIS2_Thread;
public:
Mixer_MPRIS2(Mixer *mixer, int device);
virtual ~Mixer_MPRIS2();
QString getDriverName();
virtual QString getId() const { return _id; };
virtual int open();
virtual int close();
virtual int readVolumeFromHW( const QString& id, std::shared_ptr<MixDevice> );
virtual int writeVolumeToHW( const QString& id, std::shared_ptr<MixDevice> );
virtual void setEnumIdHW(const QString& id, unsigned int);
virtual unsigned int enumIdHW(const QString& id);
virtual bool moveStream( const QString& id, const QString& destId );
virtual bool needsPolling() { return false; }
virtual int mediaPlay(QString id);
virtual int mediaPrev(QString id);
virtual int mediaNext(QString id);
virtual int mediaControl(QString id, QString command);
static MediaController::PlayState mprisPlayStateString2PlayState(const QString& playbackStatus);
public slots:
void volumeChanged(MPrisControl *mad, double);
void playbackStateChanged(MPrisControl* mad, MediaController::PlayState);
void newMediaPlayer(QString name, QString oldOwner, QString newOwner);
void addMprisControlAsync(QString arg1);
void announceControlListAsync(QString streamId);
private slots:
// asynchronous announce call slots
void announceControlList();
void announceGUI();
void announceVolume();
// Async QDBusPendingCallWatcher's
void watcherMediaControl(QDBusPendingCallWatcher* watcher);
void watcherPlugControlId(QDBusPendingCallWatcher* watcher);
void watcherInitialVolume(QDBusPendingCallWatcher* watcher);
void watcherInitialPlayState(QDBusPendingCallWatcher* watcher);
private:
// Helpers for the watchers
MPrisControl* watcherHelperGetMPrisControl(QDBusPendingCallWatcher* watcher);
private:
// void asyncAddMprisControl(QString busDestination);
// void messageQueueThreadLoop();
int addAllRunningPlayersAndInitHotplug();
void volumeChangedInternal(std::shared_ptr<MixDevice> md, int volumePercentage);
QString busDestinationToControlId(const QString& busDestination);
MixDevice::ChannelType getChannelTypeFromPlayerId(const QString& id);
QMap<QString,MPrisControl*> controls;
QString _id;
};
#endif

View file

@ -1,476 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright (C) 1996-2000 Christian Esken
* esken@kde.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mixer_oss.h"
#include "core/mixer.h"
#include "core/kmixdevicemanager.h"
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
// Since we're guaranteed an OSS setup here, let's make life easier
#if !defined(Q_OS_NETBSD) && !defined(Q_OS_OPENBSD)
#include <sys/soundcard.h>
#else
#include <soundcard.h>
#endif
#include <klocale.h>
#include <QTimer>
/*
I am using a fixed MAX_MIXDEVS #define here.
People might argue, that I should rather use the SOUND_MIXER_NRDEVICES
#define used by OSS. But using this #define is not good, because it is
evaluated during compile time. Compiling on one platform and running
on another with another version of OSS with a different value of
SOUND_MIXER_NRDEVICES is very bad. Because of this, usage of
SOUND_MIXER_NRDEVICES should be discouraged.
The #define below is only there for internal reasons.
In other words: Don't play around with this value
*/
#define MAX_MIXDEVS 32
const char* MixerDevNames[32]={
I18N_NOOP("Volume"), I18N_NOOP("Bass"), I18N_NOOP("Treble"),
I18N_NOOP("Synth"), I18N_NOOP("Pcm"), I18N_NOOP("Speaker"),
I18N_NOOP("Line"), I18N_NOOP("Microphone"), I18N_NOOP("CD"),
I18N_NOOP("Mix"), I18N_NOOP("Pcm2"), I18N_NOOP("RecMon"),
I18N_NOOP("IGain"), I18N_NOOP("OGain"), I18N_NOOP("Line1"),
I18N_NOOP("Line2"), I18N_NOOP("Line3"), I18N_NOOP("Digital1"),
I18N_NOOP("Digital2"), I18N_NOOP("Digital3"), I18N_NOOP("PhoneIn"),
I18N_NOOP("PhoneOut"), I18N_NOOP("Video"), I18N_NOOP("Radio"),
I18N_NOOP("Monitor"), I18N_NOOP("3D-depth"), I18N_NOOP("3D-center"),
I18N_NOOP("unknown"), I18N_NOOP("unknown"), I18N_NOOP("unknown"),
I18N_NOOP("unknown") , I18N_NOOP("unused")
};
const MixDevice::ChannelType MixerChannelTypes[32] = {
MixDevice::VOLUME, MixDevice::BASS, MixDevice::TREBLE,
MixDevice::MIDI, MixDevice::AUDIO, MixDevice::SPEAKER,
MixDevice::EXTERNAL, MixDevice::MICROPHONE, MixDevice::CD,
MixDevice::VOLUME, MixDevice::AUDIO, MixDevice::RECMONITOR,
MixDevice::VOLUME, MixDevice::RECMONITOR, MixDevice::EXTERNAL,
MixDevice::EXTERNAL, MixDevice::EXTERNAL, MixDevice::DIGITAL,
MixDevice::DIGITAL, MixDevice::DIGITAL, MixDevice::EXTERNAL,
MixDevice::EXTERNAL, MixDevice::VIDEO, MixDevice::EXTERNAL,
MixDevice::EXTERNAL, MixDevice::VOLUME, MixDevice::VOLUME,
MixDevice::UNKNOWN, MixDevice::UNKNOWN, MixDevice::UNKNOWN,
MixDevice::UNKNOWN, MixDevice::UNKNOWN
};
Mixer_Backend* OSS_getMixer( Mixer* mixer, int device )
{
Mixer_Backend *l_mixer;
l_mixer = new Mixer_OSS( mixer, device );
return l_mixer;
}
Mixer_OSS::Mixer_OSS(Mixer* mixer, int device)
: Mixer_Backend(mixer, device)
{
if (device == -1) {
m_devnum = 0;
}
m_fd = -1; // point to an invalid FD
}
Mixer_OSS::~Mixer_OSS()
{
close();
}
int Mixer_OSS::open()
{
QString finalDeviceName;
finalDeviceName = deviceName( m_devnum );
kDebug() << "OSS open() " << finalDeviceName;
if ((m_fd= ::open( finalDeviceName.toAscii().data(), O_RDWR)) < 0) {
if ( errno == EACCES ) {
return Mixer::ERR_PERM;
} else {
finalDeviceName = deviceNameDevfs( m_devnum );
if ((m_fd= ::open( finalDeviceName.toAscii().data(), O_RDWR)) < 0) {
if ( errno == EACCES ) {
return Mixer::ERR_PERM;
} else {
return Mixer::ERR_OPEN;
}
}
}
}
_udi = KMixDeviceManager::instance()->getUDI_OSS(finalDeviceName);
if ( _udi.isEmpty() ) {
QString msg("No UDI found for '");
msg += finalDeviceName;
msg += "'. Hotplugging not possible";
kDebug(67100) << msg;
}
int devmask, recmask, i_recsrc, stereodevs;
// Mixer is open. Now define properties
if (ioctl(m_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
return Mixer::ERR_READ;
}
if (ioctl(m_fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) {
return Mixer::ERR_READ;
}
if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1) {
return Mixer::ERR_READ;
}
if (ioctl(m_fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1) {
return Mixer::ERR_READ;
}
int idx = 0;
while( devmask && idx < MAX_MIXDEVS ) {
if( devmask & ( 1 << idx ) ) { // device active?
Volume playbackVol( 100, 1, true, false );
playbackVol.addVolumeChannel(VolumeChannel(Volume::LEFT));
if ( stereodevs & ( 1 << idx ) ) {
playbackVol.addVolumeChannel(VolumeChannel(Volume::RIGHT));
}
QString id;
id.setNum(idx);
MixDevice* md = new MixDevice(
_mixer,
id,
i18n(MixerDevNames[idx]),
MixerChannelTypes[idx]);
md->addPlaybackVolume(playbackVol);
// Tutorial: Howto add a simple capture switch
if ( recmask & ( 1 << idx ) ) {
// can be captured => add capture volume, with no capture volume
Volume captureVol( 100, 1, true, true );
md->addCaptureVolume(captureVol);
}
m_mixDevices.append( md->addToPool() );
}
idx++;
}
#if defined(SOUND_MIXER_INFO)
struct mixer_info l_mix_info;
if (ioctl(m_fd, SOUND_MIXER_INFO, &l_mix_info) != -1) {
registerCard(l_mix_info.name);
} else
#endif
{
registerCard("OSS Audio Mixer");
}
m_isOpen = true;
return 0;
}
int Mixer_OSS::close()
{
_pollingTimer->stop();
m_isOpen = false;
int l_i_ret = ::close(m_fd);
closeCommon();
return l_i_ret;
}
QString Mixer_OSS::deviceName(int devnum)
{
switch (devnum) {
case 0: {
return QString("/dev/mixer");
break;
}
default: {
QString devname("/dev/mixer%1");
return devname.arg(devnum);
}
}
}
QString Mixer_OSS::deviceNameDevfs(int devnum)
{
switch (devnum) {
case 0: {
return QString("/dev/sound/mixer");
break;
}
default: {
QString devname("/dev/sound/mixer");
devname += ('0'+devnum);
return devname;
}
}
}
QString Mixer_OSS::errorText(int mixer_error)
{
QString l_s_errmsg;
switch (mixer_error) {
case Mixer::ERR_PERM: {
l_s_errmsg = i18n("kmix: You do not have permission to access the mixer device.\n" \
"Login as root and do a 'chmod a+rw /dev/mixer*' to allow the access.");
break;
}
case Mixer::ERR_OPEN: {
l_s_errmsg = i18n("kmix: Mixer cannot be found.\n" \
"Please check that the soundcard is installed and the\n" \
"soundcard driver is loaded.\n" \
"On Linux you might need to use 'insmod' to load the driver.\n" \
"Use 'soundon' when using commercial OSS.");
break;
}
default: {
l_s_errmsg = Mixer_Backend::errorText(mixer_error);
break;
}
}
return l_s_errmsg;
}
void print_recsrc(int recsrc)
{
int i;
QString msg;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
if ((1 << i) & recsrc) {
msg += '+';
} else {
msg += '.';
}
}
kDebug() << msg;
}
int Mixer_OSS::setRecsrcToOSS( const QString& id, bool on )
{
int i_recsrc; //, oldrecsrc;
int devnum = id2num(id);
if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1) {
errormsg(Mixer::ERR_READ);
return Mixer::ERR_READ;
}
// oldrecsrc = i_recsrc = on ?
// (i_recsrc | (1 << devnum )) :
// (i_recsrc & ~(1 << devnum ));
// Change status of record source(s)
if (ioctl(m_fd, SOUND_MIXER_WRITE_RECSRC, &i_recsrc) == -1) {
errormsg (Mixer::ERR_WRITE);
// don't return here. It is much better to re-read the capture switch states.
}
/* The following if {} patch was submitted by Tim McCormick <tim@pcbsd.org>. */
/* Comment (cesken): This patch fixes an issue with mutual exclusive recording sources.
Actually the kernel soundcard driver *could* "do the right thing" by examining the change
(old-recsrc XOR new-recsrc), and knowing which sources are mutual exclusive.
The OSS v3 API docs indicate that the behaviour is undefined for this case, and it is not
clearly documented how and whether SOUND_MIXER_CAP_EXCL_INPUT is evaluated in the OSS driver.
Evaluating that in the application (KMix) could help, but the patch will work independent
on whether SOUND_MIXER_CAP_EXCL_INPUT is set or not.
In any case this patch is a superb workaround for a shortcoming of the OSS v3 API.
*/
// If the record source is supposed to be on, but wasn't set, explicitly
// set the record source. Not all cards support multiple record sources.
// As a result, we also need to do the read & write again.
if (((i_recsrc & ( 1<<devnum)) == 0) && on) {
// Setting the new device failed => Try to enable it *exclusively*
// oldrecsrc = i_recsrc = 1 << devnum;
if (ioctl(m_fd, SOUND_MIXER_WRITE_RECSRC, &i_recsrc) == -1) {
errormsg (Mixer::ERR_WRITE);
}
if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1) {
errormsg(Mixer::ERR_READ);
}
}
// Re-read status of record source(s). Just in case the hardware/driver has
// some limitaton (like exclusive switches)
int recsrcMask;
if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &recsrcMask) == -1) {
errormsg(Mixer::ERR_READ);
} else {
for(int i=0; i< m_mixDevices.count() ; i++ ) {
std::shared_ptr<MixDevice> md = m_mixDevices[i];
bool isRecsrc = ( (recsrcMask & ( 1<<devnum)) != 0 );
md->setRecSource(isRecsrc);
} // for all controls
} // reading newrecsrcmask is OK
return Mixer::OK;
}
int Mixer_OSS::id2num(const QString& id)
{
return id.toInt();
}
/**
* Prints out a translated error text for the given error number on stderr
*/
void Mixer_OSS::errormsg(int mixer_error)
{
QString l_s_errText;
l_s_errText = errorText(mixer_error);
kError() << l_s_errText << "\n";
}
int Mixer_OSS::readVolumeFromHW( const QString& id, std::shared_ptr<MixDevice> md )
{
int ret = 0;
// --- VOLUME ---
Volume& vol = md->playbackVolume();
int devnum = id2num(id);
bool controlChanged = false;
if ( vol.hasVolume() ) {
int volume;
if (ioctl(m_fd, MIXER_READ( devnum ), &volume) == -1) {
/* Oops, can't read mixer */
errormsg(Mixer::ERR_READ);
ret = Mixer::ERR_READ;
} else {
int volLeft = (volume & 0x7f);
int volRight = ((volume>>8) & 0x7f);
// if ( md->id() == "0" )
// kDebug() << md->id() << ": " << "volLeft=" << volLeft << ", volRight" << volRight;
bool isMuted = volLeft==0 && ( vol.count() < 2 || volRight==0 ); // muted is "left and right muted" or "left muted when mono"
md->setMuted( isMuted );
if ( ! isMuted ) {
// Muted is represented in OSS by value 0. We don't want to write the value 0 as a volume,
// but instead we only mark it muted (see setMuted() above).
foreach (VolumeChannel vc, vol.getVolumes() ) {
long volOld = 0;
long volNew = 0;
switch(vc.chid) {
case Volume::LEFT: {
volOld = vol.getVolume(Volume::LEFT);
volNew = volLeft;
vol.setVolume( Volume::LEFT, volNew );
break;
}
case Volume::RIGHT: {
volOld = vol.getVolume(Volume::RIGHT);
volNew = volRight;
vol.setVolume( Volume::RIGHT, volNew );
break;
}
default: {
// not supported by OSSv3
break;
}
}
if ( volOld != volNew ) {
controlChanged = true;
//if ( md->id() == "0" ) kDebug() << "changed";
}
} // foreach
} // muted
}
}
// --- RECORD SWITCH ---
//Volume& captureVol = md->captureVolume();
int recsrcMask;
if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &recsrcMask) == -1) {
ret = Mixer::ERR_READ;
} else {
bool isRecsrcOld = md->isRecSource();
// test if device bit is set in record bit mask
bool isRecsrc = ( (recsrcMask & ( 1<<devnum)) != 0 );
md->setRecSource(isRecsrc);
if ( isRecsrcOld != isRecsrc ) {
controlChanged = true;
}
}
if ( ret== 0) {
if ( controlChanged ) {
//kDebug() << "FINE! " << ret;
return Mixer::OK;
} else {
return Mixer::OK_UNCHANGED;
}
} else {
//kDebug() << "SHIT! " << ret;
return ret;
}
}
int Mixer_OSS::writeVolumeToHW( const QString& id, std::shared_ptr<MixDevice> md)
{
int volume;
int devnum = id2num(id);
Volume& vol = md->playbackVolume();
if( md->isMuted() ) {
volume = 0;
} else {
if ( vol.getVolumes().count() > 1 ) {
volume = (vol.getVolume(Volume::LEFT) + (vol.getVolume(Volume::RIGHT)<<8));
} else {
volume = vol.getVolume(Volume::LEFT);
}
}
if (ioctl(m_fd, MIXER_WRITE( devnum ), &volume) == -1) {
return Mixer::ERR_WRITE;
}
setRecsrcToOSS( id, md->isRecSource() );
return 0;
}
QString OSS_getDriverName()
{
return "OSS";
}
QString Mixer_OSS::getDriverName()
{
return "OSS";
}

View file

@ -1,58 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MIXER_OSS_H
#define MIXER_OSS_H
#include <QString>
#include "mixer_backend.h"
class Mixer_OSS : public Mixer_Backend
{
public:
Mixer_OSS(Mixer *mixer, int device);
virtual ~Mixer_OSS();
virtual QString errorText(int mixer_error);
virtual int readVolumeFromHW( const QString& id, std::shared_ptr<MixDevice> );
virtual int writeVolumeToHW ( const QString& id, std::shared_ptr<MixDevice> );
virtual QString getDriverName();
protected:
virtual int open();
virtual int close();
virtual QString deviceName( int );
virtual QString deviceNameDevfs( int );
private:
int m_fd;
QString m_deviceName;
int setRecsrcToOSS( const QString& id, bool on );
void errormsg(int mixer_error);
int id2num(const QString& id);
};
#endif

View file

@ -1,799 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright (C) 1996-2000 Christian Esken
* esken@kde.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//OSS4 mixer backend for KMix by Yoper Team released under GPL v2 or later
/* We're getting soundcard.h via mixer_oss4.h */
#include "mixer_oss4.h"
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <klocale.h>
#include <QList>
#include <QRegExp>
Mixer_Backend* OSS4_getMixer(Mixer *mixer, int device)
{
Mixer_Backend *l_mixer;
l_mixer = new Mixer_OSS4(mixer, device);
return l_mixer;
}
Mixer_OSS4::Mixer_OSS4(Mixer *mixer, int device) : Mixer_Backend(mixer, device)
{
if ( device == -1 ) m_devnum = 0;
m_numExtensions = 0;
m_fd = -1;
m_ossVersion = 0;
m_modifyCounter = -1;
}
bool Mixer_OSS4::CheckCapture(oss_mixext *ext)
{
QString name = ext->extname;
if ( ext->flags & MIXF_RECVOL || name.split('.').contains("in") )
{
return true;
}
return false;
}
Mixer_OSS4::~Mixer_OSS4()
{
close();
}
//classifies mixexts according to their name, last classification wins
MixDevice::ChannelType Mixer_OSS4::classifyAndRename(QString &name, int flags)
{
MixDevice::ChannelType cType = MixDevice::UNKNOWN;
QStringList classes = name.split (QRegExp( QLatin1String( "[-,.]" ) ));
if ( flags & MIXF_PCMVOL ||
flags & MIXF_MONVOL ||
flags & MIXF_MAINVOL )
{
cType = MixDevice::VOLUME;
}
for ( QStringList::Iterator it = classes.begin(); it != classes.end(); ++it )
{
if ( *it == "line" )
{
*it = "Line";
cType = MixDevice::EXTERNAL;
} else
if ( *it == "mic" )
{
*it = "Microphone";
cType = MixDevice::MICROPHONE;
} else
if ( *it == "vol" )
{
*it = "Volume";
cType = MixDevice::VOLUME;
} else
if ( *it == "surr" )
{
*it = "Surround";
cType = MixDevice::SURROUND;
} else
if ( *it == "bass" )
{
*it = "Bass";
cType = MixDevice::BASS;
} else
if ( *it == "treble" )
{
*it = "Treble";
cType = MixDevice::TREBLE;
} else
if ( (*it).startsWith ( "pcm" ) )
{
(*it).replace ( "pcm","PCM" );
cType = MixDevice::AUDIO;
} else
if ( *it == "src" )
{
*it = "Source";
} else
if ( *it == "rec" )
{
*it = "Recording";
} else
if ( *it == "cd" )
{
*it = (*it).toUpper();
cType = MixDevice::CD;
}
if ( (*it).startsWith("vmix") )
{
(*it).replace("vmix","Virtual Mixer");
cType = MixDevice::VOLUME;
} else
if ( (*it).endsWith("vol") )
{
QCharRef ref = (*it)[0];
ref = ref.toUpper();
cType = MixDevice::VOLUME;
} else
if ( (*it).contains("speaker") )
{
QCharRef ref = (*it)[0];
ref = ref.toUpper();
cType = MixDevice::SPEAKER;
} else
if ( (*it).contains("center") && (*it).contains("lfe"))
{
QCharRef ref = (*it)[0];
ref = ref.toUpper();
cType = MixDevice::SURROUND_LFE;
} else
if ( (*it).contains("rear") )
{
QCharRef ref = (*it)[0];
ref = ref.toUpper();
cType = MixDevice::SURROUND_CENTERBACK;
} else
if ( (*it).contains("front") )
{
QCharRef ref = (*it)[0];
ref = ref.toUpper();
cType = MixDevice::SURROUND_CENTERFRONT;
} else
if ( (*it).contains("headphone") )
{
QCharRef ref = (*it)[0];
ref = ref.toUpper();
cType = MixDevice::HEADPHONE;
}
else
{
QCharRef ref = (*it)[0];
ref = ref.toUpper();
}
}
name = classes.join( QLatin1String( " " ));
return cType;
}
int Mixer_OSS4::open()
{
if ( (m_fd= ::open("/dev/mixer", O_RDWR)) < 0 )
{
if ( errno == EACCES )
return Mixer::ERR_PERM;
else
return Mixer::ERR_OPEN;
}
/*
* Intentionally not wrapped - some systems may not support this ioctl, and therefore
* aren't OSSv4. No need to throw needless error messages at the user in that case.
*/
if( ::ioctl (m_fd, OSS_GETVERSION, &m_ossVersion) < 0)
{
return Mixer::ERR_OPEN;
}
if (m_ossVersion < 0x040000)
{
return Mixer::ERR_OPEN;
}
wrapIoctl( ioctl (m_fd, SNDCTL_MIX_NRMIX, &m_numMixers) );
if ( m_mixDevices.isEmpty() )
{
if ( m_devnum >= 0 && m_devnum < m_numMixers )
{
m_numExtensions = m_devnum;
bool masterChosen = false;
bool masterHeuristicAvailable = false;
bool saveAsMasterHeuristc = false;
std::shared_ptr<MixDevice> masterHeuristic;
oss_mixext ext;
ext.dev = m_devnum;
oss_mixerinfo mi;
mi.dev = m_devnum;
if ( wrapIoctl( ioctl (m_fd, SNDCTL_MIXERINFO, &mi) ) < 0 )
{
return Mixer::ERR_READ;
}
/* Mixer is disabled - this can happen, e.g. disconnected USB device */
if (!mi.enabled)
{
return Mixer::ERR_READ;
}
::close(m_fd);
if ( (m_fd= ::open(mi.devnode, O_RDWR)) < 0 )
{
return Mixer::ERR_OPEN;
}
if ( wrapIoctl( ioctl (m_fd, SNDCTL_MIX_NREXT, &m_numExtensions) ) < 0 )
{
//TO DO: more specific error handling here
return Mixer::ERR_READ;
}
if( m_numExtensions == 0 )
{
return Mixer::ERR_OPEN;
}
ext.ctrl = 0;
//read MIXT_DEVROOT, return Mixer::NODEV on error
if ( wrapIoctl ( ioctl( m_fd, SNDCTL_MIX_EXTINFO, &ext) ) < 0 )
{
return Mixer::ERR_OPEN;
}
oss_mixext_root *root = (oss_mixext_root *) ext.data;
registerCard(root->name);
for ( int i = 1; i < m_numExtensions; i++ )
{
bool isCapture = false;
ext.dev = m_devnum;
ext.ctrl = i;
//wrapIoctl handles reinitialization, cancel loading on EIDRM
if ( wrapIoctl( ioctl( m_fd, SNDCTL_MIX_EXTINFO, &ext) ) == EIDRM )
{
return 0;
}
QString name = ext.extname;
//skip unreadable controls
if ( ext.flags & MIXF_READABLE
#ifndef MIXT_MUTE
&& (name.contains("mute"))
#endif
#ifdef MIXT_MUTE
&& (name.contains("mute") || ext.flags == MIXT_MUTE)
#endif
)
{
continue;
}
//skip all vmix controls with the exception of outvol
else if ( name.contains("vmix") && ! name.contains("enable") )
{
//some heuristic in case we got no exported main volume control
if( name.contains("outvol") && ext.type != MIXT_ONOFF )
{
saveAsMasterHeuristc = true;
}
else
{
continue;
}
}
//fix for old legacy names, according to Hannu's suggestions
if ( name.contains('_') )
{
name = name.section('_',1,1).toLower();
}
isCapture = CheckCapture (&ext);
Volume::ChannelMask chMask = Volume::MNONE;
MixDevice::ChannelType cType = classifyAndRename(name, ext.flags);
if ( (ext.type == MIXT_STEREOSLIDER16 ||
ext.type == MIXT_STEREOSLIDER ||
ext.type == MIXT_MONOSLIDER16 ||
ext.type == MIXT_MONOSLIDER ||
ext.type == MIXT_SLIDER
) )
{
if ( ext.type == MIXT_STEREOSLIDER16 ||
ext.type == MIXT_STEREOSLIDER
)
{
chMask = Volume::ChannelMask(Volume::MLEFT|Volume::MRIGHT);
}
else
{
chMask = Volume::MLEFT;
}
Volume vol (ext.maxvalue, ext.minvalue, false, isCapture);
vol.addVolumeChannels(chMask);
MixDevice* md_ptr = new MixDevice(_mixer,
QString::number(i),
name,
cType);
std::shared_ptr<MixDevice> md = md_ptr->addToPool();
m_mixDevices.append(md);
if(isCapture)
{
md->addCaptureVolume(vol);
}
else
{
md->addPlaybackVolume(vol);
}
if( saveAsMasterHeuristc && ! masterHeuristicAvailable )
{
masterHeuristic = md;
masterHeuristicAvailable = true;
}
if ( !masterChosen && ext.flags & MIXF_MAINVOL )
{
m_recommendedMaster = md;
masterChosen = true;
}
}
else if ( ext.type == MIXT_HEXVALUE )
{
chMask = Volume::ChannelMask(Volume::MLEFT);
Volume vol (ext.maxvalue, ext.minvalue, false, isCapture);
vol.addVolumeChannels(chMask);
MixDevice* md_ptr = new MixDevice(_mixer,
QString::number(i),
name,
cType);
std::shared_ptr<MixDevice> md = md_ptr->addToPool();
m_mixDevices.append(md);
if(isCapture)
{
md->addCaptureVolume(vol);
}
else
{
md->addPlaybackVolume(vol);
}
if ( !masterChosen && ext.flags & MIXF_MAINVOL )
{
m_recommendedMaster = md;
masterChosen = true;
}
}
else if ( ext.type == MIXT_ONOFF
#ifdef MIXT_MUTE
|| ext.type == MIXT_MUTE
#endif
)
{
Volume vol(1, 0, true, isCapture);
if (isCapture)
vol.setSwitchType (Volume::CaptureSwitch);
else if (ext.type == MIXT_ONOFF)
{
vol.setSwitchType (Volume::SpecialSwitch);
}
MixDevice* md_ptr = new MixDevice(_mixer,
QString::number(i),
name,
cType);
std::shared_ptr<MixDevice> md = md_ptr->addToPool();
m_mixDevices.append(md);
if(isCapture)
{
md->addCaptureVolume(vol);
}
else
{
md->addPlaybackVolume(vol);
}
}
else if ( ext.type == MIXT_ENUM )
{
oss_mixer_enuminfo ei;
ei.dev = m_devnum;
ei.ctrl = i;
if ( wrapIoctl( ioctl (m_fd, SNDCTL_MIX_ENUMINFO, &ei) ) != -1 )
{
Volume vol(ext.maxvalue, ext.minvalue,
false, isCapture);
vol.addVolumeChannel(VolumeChannel(Volume::LEFT));
MixDevice* md_ptr = new MixDevice (_mixer,
QString::number(i),
name,
cType);
QList<QString*> enumValuesRef;
QString thisElement;
for ( int j = 0; j < ei.nvalues; j++ )
{
thisElement = &ei.strings[ ei.strindex[j] ];
if ( thisElement.isEmpty() )
{
thisElement = QString::number(j);
}
enumValuesRef.append( new QString(thisElement) );
}
md_ptr->addEnums(enumValuesRef);
std::shared_ptr<MixDevice> md = md_ptr->addToPool();
m_mixDevices.append(md);
}
}
if ( ! masterChosen && masterHeuristicAvailable )
{
m_recommendedMaster = masterHeuristic;
}
}
}
else
{
return -1;
}
}
m_isOpen = true;
return 0;
}
int Mixer_OSS4::close()
{
m_isOpen = false;
int l_i_ret = ::close(m_fd);
m_recommendedMaster.reset();
closeCommon();
return l_i_ret;
}
QString Mixer_OSS4::errorText(int mixer_error)
{
QString l_s_errmsg;
switch( mixer_error )
{
case Mixer::ERR_PERM:
l_s_errmsg = i18n("kmix: You do not have permission to access the mixer device.\n" \
"Login as root and do a 'chmod a+rw /dev/mixer*' to allow the access.");
break;
case Mixer::ERR_OPEN:
l_s_errmsg = i18n("kmix: Mixer cannot be found.\n" \
"Please check that the soundcard is installed and the\n" \
"soundcard driver is loaded.\n" \
"On Linux you might need to use 'insmod' to load the driver.\n" \
"Use 'soundon' when using OSS4 from 4front.");
break;
default:
l_s_errmsg = Mixer_Backend::errorText(mixer_error);
}
return l_s_errmsg;
}
int Mixer_OSS4::id2num(const QString& id)
{
return id.toInt();
}
bool Mixer_OSS4::prepareUpdateFromHW()
{
oss_mixerinfo minfo;
minfo.dev = -1;
if ( wrapIoctl( ioctl(m_fd, SNDCTL_MIXERINFO, &minfo) ) < 0 )
{
kDebug(67100) << "Can't get mixerinfo from card!";
return false;
}
if (!minfo.enabled)
{
// Mixer is disabled. Probably disconnected USB device or card is unavailable;
kDebug(67100) << "Mixer for card is disabled!";
close();
return false;
}
if (minfo.modify_counter == m_modifyCounter) return false;
else m_modifyCounter = minfo.modify_counter;
return true;
}
int Mixer_OSS4::readVolumeFromHW(const QString& id, std::shared_ptr<MixDevice> md)
{
oss_mixext extinfo;
oss_mixer_value mv;
extinfo.dev = m_devnum;
extinfo.ctrl = id2num(id);
if ( wrapIoctl( ioctl(m_fd, SNDCTL_MIX_EXTINFO, &extinfo) ) < 0 )
{
//TO DO: more specific error handling
return Mixer::ERR_READ;
}
Volume &vol = (CheckCapture (&extinfo)) ? md->captureVolume() : md->playbackVolume();
mv.dev = extinfo.dev;
mv.ctrl = extinfo.ctrl;
mv.timestamp = extinfo.timestamp;
if ( wrapIoctl ( ioctl (m_fd, SNDCTL_MIX_READ, &mv) ) < 0 )
{
/* Oops, can't read mixer */
return Mixer::ERR_READ;
}
else
{
if ( md->isMuted() && extinfo.type != MIXT_ONOFF )
{
return 0;
}
switch ( extinfo.type )
{
#ifdef MIXT_MUTE
case MIXT_MUTE:
#endif
case MIXT_ONOFF:
md->setMuted(mv.value != extinfo.minvalue);
break;
case MIXT_MONOSLIDER:
vol.setVolume(Volume::LEFT, mv.value & 0xff);
break;
case MIXT_STEREOSLIDER:
vol.setVolume(Volume::LEFT, mv.value & 0xff);
vol.setVolume(Volume::RIGHT, ( mv.value >> 8 ) & 0xff);
break;
case MIXT_SLIDER:
vol.setVolume(Volume::LEFT, mv.value);
break;
case MIXT_MONOSLIDER16:
vol.setVolume(Volume::LEFT, mv.value & 0xffff);
break;
case MIXT_STEREOSLIDER16:
vol.setVolume(Volume::LEFT, mv.value & 0xffff);
vol.setVolume(Volume::RIGHT, ( mv.value >> 16 ) & 0xffff);
break;
}
}
return 0;
}
int Mixer_OSS4::writeVolumeToHW(const QString& id, std::shared_ptr<MixDevice> md)
{
int volume = 0;
oss_mixext extinfo;
oss_mixer_value mv;
extinfo.dev = m_devnum;
extinfo.ctrl = id2num(id);
if ( wrapIoctl( ioctl(m_fd, SNDCTL_MIX_EXTINFO, &extinfo) ) < 0 )
{
//TO DO: more specific error handling
kDebug ( 67100 ) << "failed to read info for control " << id2num(id);
return Mixer::ERR_READ;
}
Volume &vol = (CheckCapture (&extinfo)) ? md->captureVolume() : md->playbackVolume();
switch ( extinfo.type )
{
#ifdef MIXT_MUTE
case MIXT_MUTE:
#endif
case MIXT_ONOFF:
volume = (md->isMuted()) ? (extinfo.maxvalue) : (extinfo.minvalue);
break;
case MIXT_MONOSLIDER:
volume = vol.getVolume(Volume::LEFT);
break;
case MIXT_STEREOSLIDER:
volume = vol.getVolume(Volume::LEFT) | ( vol.getVolume(Volume::RIGHT) << 8 );
break;
case MIXT_SLIDER:
volume = vol.getVolume(Volume::LEFT);
break;
case MIXT_MONOSLIDER16:
volume = vol.getVolume(Volume::LEFT);
break;
case MIXT_STEREOSLIDER16:
volume = vol.getVolume(Volume::LEFT) | ( vol.getVolume(Volume::RIGHT) << 16 );
break;
default:
return -1;
}
mv.dev = extinfo.dev;
mv.ctrl = extinfo.ctrl;
mv.timestamp = extinfo.timestamp;
mv.value = volume - extinfo.minvalue;
if ( wrapIoctl ( ioctl (m_fd, SNDCTL_MIX_WRITE, &mv) ) < 0 )
{
kDebug ( 67100 ) << "error writing to control" << extinfo.extname;
return Mixer::ERR_WRITE;
}
return 0;
}
void Mixer_OSS4::setEnumIdHW(const QString& id, unsigned int idx)
{
oss_mixext extinfo;
oss_mixer_value mv;
extinfo.dev = m_devnum;
extinfo.ctrl = id2num(id);
if ( wrapIoctl ( ioctl(m_fd, SNDCTL_MIX_EXTINFO, &extinfo) ) < 0 )
{
//TO DO: more specific error handling
kDebug ( 67100 ) << "failed to read info for control " << id2num(id);
return;
}
if ( extinfo.type != MIXT_ENUM )
{
return;
}
//according to oss docs maxVal < minVal could be true - strange...
unsigned int maxVal = (unsigned int) extinfo.maxvalue;
unsigned int minVal = (unsigned int) extinfo.minvalue;
if ( maxVal < minVal )
{
int temp;
temp = maxVal;
maxVal = minVal;
minVal = temp;
}
if ( idx > maxVal || idx < minVal )
idx = minVal;
mv.dev = extinfo.dev;
mv.ctrl = extinfo.ctrl;
mv.timestamp = extinfo.timestamp;
mv.value = idx;
if ( wrapIoctl ( ioctl (m_fd, SNDCTL_MIX_WRITE, &mv) ) < 0 )
{
/* Oops, can't write to mixer */
kDebug ( 67100 ) << "error writing to control" << extinfo.extname;
}
}
unsigned int Mixer_OSS4::enumIdHW(const QString& id)
{
oss_mixext extinfo;
oss_mixer_value mv;
extinfo.dev = m_devnum;
extinfo.ctrl = id2num(id);
if ( wrapIoctl ( ioctl(m_fd, SNDCTL_MIX_EXTINFO, &extinfo) ) < 0 )
{
//TO DO: more specific error handling
//TO DO: check whether those return values are actually possible
return Mixer::ERR_READ;
}
if ( extinfo.type != MIXT_ENUM )
{
return Mixer::ERR_READ;
}
mv.dev = extinfo.dev;
mv.ctrl = extinfo.ctrl;
mv.timestamp = extinfo.timestamp;
if ( wrapIoctl ( ioctl (m_fd, SNDCTL_MIX_READ, &mv) ) < 0 )
{
/* Oops, can't read mixer */
return Mixer::ERR_READ;
}
return mv.value;
}
int Mixer_OSS4::wrapIoctl(int ioctlRet)
{
switch( ioctlRet )
{
case EIO:
{
kDebug ( 67100 ) << "A hardware level error occurred";
break;
}
case EINVAL:
{
kDebug ( 67100 ) << "Operation caused an EINVAL. You may have found a bug in kmix OSS4 module or in OSS4 itself";
break;
}
case ENXIO:
{
kDebug ( 67100 ) << "Operation index out of bounds or requested device does not exist - you likely found a bug in the kmix OSS4 module";
break;
}
case EPERM:
case EACCES:
{
kDebug ( 67100 ) << errorText ( Mixer::ERR_PERM );
break;
}
case ENODEV:
{
kDebug ( 67100 ) << "kmix received an ENODEV error - are the OSS4 drivers loaded ?";
break;
}
case EPIPE:
case EIDRM:
{
reinitialize();
}
}
return ioctlRet;
}
QString Mixer_OSS4::getDriverName()
{
return "OSS4";
}
QString OSS4_getDriverName()
{
return "OSS4";
}

View file

@ -1,44 +0,0 @@
//-*-C++-*-
#ifndef MIXER_OSS4_H
#define MIXER_OSS4_H
#include "mixer_backend.h"
#include <sys/soundcard.h>
class Mixer_OSS4 : public Mixer_Backend
{
public:
Mixer_OSS4(Mixer* mixer, int device);
virtual ~Mixer_OSS4();
virtual QString errorText(int mixer_error);
virtual QString getDriverName();
virtual bool CheckCapture(oss_mixext *ext);
virtual bool prepareUpdateFromHW();
virtual int readVolumeFromHW(const QString& id, std::shared_ptr<MixDevice> md);
virtual int writeVolumeToHW(const QString& id, std::shared_ptr<MixDevice> md );
virtual void setEnumIdHW(const QString& id, unsigned int idx);
virtual unsigned int enumIdHW(const QString& id);
protected:
MixDevice::ChannelType classifyAndRename(QString &name, int flags);
int wrapIoctl(int ioctlRet);
void reinitialize() { open(); close(); };
virtual int open();
virtual int close();
int m_ossVersion;
int m_fd;
int m_numMixers;
int m_numExtensions;
int m_modifyCounter;
QString m_deviceName;
private:
int id2num(const QString& id);
};
#endif

View file

@ -1,495 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2000 Christian Esken <esken@kde.org>
* 2000 Brian Hanson <bhanson@hotmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mixer_sun.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/audioio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include "core/mixer.h"
#include <sys/soundcard.h>
#include <QTimer>
//======================================================================
// CONSTANT/ENUM DEFINITIONS
//======================================================================
//
// Mixer Device Numbers
//
// Note: We can't just use the Sun port #defines because :
// 1) Some logical devices don't correspond to ports (master&recmon)
// 2) The play and record port definitions reuse the same values
//
enum MixerDevs
{
MIXERDEV_MASTER_VOLUME,
MIXERDEV_INTERNAL_SPEAKER,
MIXERDEV_HEADPHONE,
MIXERDEV_LINE_OUT,
MIXERDEV_RECORD_MONITOR,
MIXERDEV_MICROPHONE,
MIXERDEV_LINE_IN,
MIXERDEV_CD,
// Insert new devices before this marker
MIXERDEV_END_MARKER
};
const int numDevs = MIXERDEV_END_MARKER;
//
// Device name strings
//
const char* MixerDevNames[] =
{
I18N_NOOP("Master Volume"),
I18N_NOOP("Internal Speaker"),
I18N_NOOP("Headphone"),
I18N_NOOP("Line Out"),
I18N_NOOP("Record Monitor"),
I18N_NOOP("Microphone"),
I18N_NOOP("Line In"),
I18N_NOOP("CD")
};
//
// Channel types (this specifies which icon to display)
//
const MixDevice::ChannelType MixerChannelTypes[] =
{
MixDevice::VOLUME, // MASTER_VOLUME
MixDevice::AUDIO, // INTERNAL_SPEAKER
MixDevice::EXTERNAL, // HEADPHONE (we really need an icon for this)
MixDevice::EXTERNAL, // LINE_OUT
MixDevice::RECMONITOR, // RECORD_MONITOR
MixDevice::MICROPHONE, // MICROPHONE
MixDevice::EXTERNAL, // LINE_IN
MixDevice::CD // CD
};
//
// Mapping from device numbers to Sun port mask values
//
const uint_t MixerSunPortMasks[] =
{
0, // MASTER_VOLUME - no associated port
AUDIO_SPEAKER,
AUDIO_HEADPHONE,
AUDIO_LINE_OUT,
0, // RECORD_MONITOR - no associated port
AUDIO_MICROPHONE,
AUDIO_LINE_IN,
AUDIO_CD
};
//======================================================================
// FUNCTION/METHOD DEFINITIONS
//======================================================================
//======================================================================
// FUNCTION : SUN_getMixer
// DESCRIPTION : Creates and returns a new mixer object.
//======================================================================
Mixer_Backend* SUN_getMixer( Mixer *mixer, int devnum )
{
Mixer_Backend *l_mixer;
l_mixer = new Mixer_SUN( mixer, devnum );
return l_mixer;
}
//======================================================================
// FUNCTION : Mixer::Mixer
// DESCRIPTION : Class constructor.
//======================================================================
Mixer_SUN::Mixer_SUN(Mixer *mixer, int devnum) : Mixer_Backend(mixer, devnum)
{
if ( devnum == -1 )
m_devnum = 0;
}
//======================================================================
// FUNCTION : Mixer::Mixer
// DESCRIPTION : Class destructor.
//======================================================================
Mixer_SUN::~Mixer_SUN()
{
close();
}
//======================================================================
// FUNCTION : Mixer::open
// DESCRIPTION : Initialize the mixer and open the hardware driver.
//======================================================================
int Mixer_SUN::open()
{
//
// We don't support multiple devices
//
if ( m_devnum !=0 )
return Mixer::ERR_OPEN;
//
// Open the mixer hardware driver
//
QString audiodev(getenv("AUDIODEV"));
if(audiodev.isNull())
audiodev = "/dev/audio";
audiodev += "ctl";
_udi = audiodev; // use device name as UDI. Doesn't matter as we only use it for hotplugging/unplugging.
if ( ( fd = ::open( audiodev.toAscii().data(), O_RDWR ) ) < 0 )
{
if ( errno == EACCES )
return Mixer::ERR_PERM;
else
return Mixer::ERR_OPEN;
}
else
{
//
// Mixer is open. Now define all of the mix devices.
//
int devmask, recmask, i_recsrc, stereodevs;
// Mixer is open. Now define properties
if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
return Mixer::ERR_READ;
if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
return Mixer::ERR_READ;
if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1)
return Mixer::ERR_READ;
if (ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1)
return Mixer::ERR_READ;
for ( int idx = 0; idx < numDevs; idx++ )
{
Volume::ChannelMask chnmask = Volume::MLEFT;
if ( stereodevs & ( 1 << idx ) ) chnmask = (Volume::ChannelMask)(chnmask|Volume::MRIGHT);
Volume playbackVol( 100, 1, true, false );
QString id;
id.setNum(idx);
MixDevice* md = new MixDevice( _mixer, id,
QString(MixerDevNames[idx]), MixerChannelTypes[idx]);
md->addPlaybackVolume(playbackVol);
// Tutorial: Howto add a simple capture switch
if ( recmask & ( 1 << idx ) ) {
// can be captured => add capture volume, with no capture volume
chnmask = Volume::MNONE;
Volume captureVol( 100, 1, true, true );
md->addCaptureVolume(captureVol);
}
m_mixDevices.append( md->addToPool() );
}
registerCard("SUN Audio Mixer");
m_isOpen = true;
return 0;
}
}
//======================================================================
// FUNCTION : Mixer::close
// DESCRIPTION : Close the hardware driver.
//======================================================================
int Mixer_SUN::close()
{
_pollingTimer->stop();
m_isOpen = false;
int l_i_ret = ::close( fd );
closeCommon();
return l_i_ret;
}
//======================================================================
// FUNCTION : Mixer::errorText
// DESCRIPTION : Convert an error code enum to a text string.
//======================================================================
QString Mixer_SUN::errorText( int mixer_error )
{
QString errmsg;
switch (mixer_error)
{
case Mixer::ERR_PERM:
errmsg = i18n(
"kmix: You do not have permission to access the mixer device.\n"
"Ask your system administrator to fix /dev/audioctl to allow access."
);
break;
default:
errmsg = Mixer_Backend::errorText( mixer_error );
}
return errmsg;
}
//======================================================================
// FUNCTION : Mixer::readVolumeFromHW
// DESCRIPTION : Read the audio information from the driver.
//======================================================================
int Mixer_SUN::readVolumeFromHW( const QString& id, std::shared_ptr<MixDevice> md )
{
audio_info_t audioinfo;
int devnum = id2num(id);
uint_t devMask = MixerSunPortMasks[devnum];
Volume& volume = md->playbackVolume();
//
// Read the current audio information from the driver
//
if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 )
{
return( Mixer::ERR_READ );
}
else
{
//
// Extract the appropriate fields based on the requested device
//
switch ( devnum )
{
case MIXERDEV_MASTER_VOLUME :
//volume.setSwitchActivated( audioinfo.output_muted );
GainBalanceToVolume( audioinfo.play.gain,
audioinfo.play.balance,
volume );
break;
case MIXERDEV_RECORD_MONITOR :
md->setMuted(false);
volume.setAllVolumes( audioinfo.monitor_gain );
break;
case MIXERDEV_INTERNAL_SPEAKER :
case MIXERDEV_HEADPHONE :
case MIXERDEV_LINE_OUT :
md->setMuted( (audioinfo.play.port & devMask) ? false : true );
GainBalanceToVolume( audioinfo.play.gain,
audioinfo.play.balance,
volume );
break;
case MIXERDEV_MICROPHONE :
case MIXERDEV_LINE_IN :
case MIXERDEV_CD :
md->setMuted( (audioinfo.record.port & devMask) ? false : true );
GainBalanceToVolume( audioinfo.record.gain,
audioinfo.record.balance,
volume );
break;
default :
return Mixer::ERR_READ;
}
return 0;
}
}
//======================================================================
// FUNCTION : Mixer::writeVolumeToHW
// DESCRIPTION : Write the specified audio settings to the hardware.
//======================================================================
int Mixer_SUN::writeVolumeToHW( const QString& id, std::shared_ptr<MixDevice> md )
{
uint_t gain;
uchar_t balance;
uchar_t mute;
Volume& volume = md->playbackVolume();
int devnum = id2num(id);
//
// Convert the Volume(left vol, right vol) to the Gain/Balance Sun uses
//
VolumeToGainBalance( volume, gain, balance );
mute = md->isMuted() ? 1 : 0;
//
// Read the current audio settings from the hardware
//
audio_info_t audioinfo;
if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 )
{
return( Mixer::ERR_READ );
}
//
// Now, based on the devnum that we are writing to, update the appropriate
// volume field and twiddle the appropriate bitmask to enable/mute the
// device as necessary.
//
switch ( devnum )
{
case MIXERDEV_MASTER_VOLUME :
audioinfo.play.gain = gain;
audioinfo.play.balance = balance;
audioinfo.output_muted = mute;
break;
case MIXERDEV_RECORD_MONITOR :
audioinfo.monitor_gain = gain;
// no mute or balance for record monitor
break;
case MIXERDEV_INTERNAL_SPEAKER :
case MIXERDEV_HEADPHONE :
case MIXERDEV_LINE_OUT :
audioinfo.play.gain = gain;
audioinfo.play.balance = balance;
if ( mute )
audioinfo.play.port &= ~MixerSunPortMasks[devnum];
else
audioinfo.play.port |= MixerSunPortMasks[devnum];
break;
case MIXERDEV_MICROPHONE :
case MIXERDEV_LINE_IN :
case MIXERDEV_CD :
audioinfo.record.gain = gain;
audioinfo.record.balance = balance;
if ( mute )
audioinfo.record.port &= ~MixerSunPortMasks[devnum];
else
audioinfo.record.port |= MixerSunPortMasks[devnum];
break;
default :
return Mixer::ERR_READ;
}
//
// Now that we've updated the audioinfo struct, write it back to the hardware
//
if ( ioctl( fd, AUDIO_SETINFO, &audioinfo ) < 0 )
{
return( Mixer::ERR_WRITE );
}
else
{
return 0;
}
}
//======================================================================
// FUNCTION : Mixer::isRecsrcHW
// DESCRIPTION : Returns true if the specified device is a record source.
//======================================================================
// isRecsrcHW() is not supported any longer. You must set the state in the MixDevice in readVolumeFromHW() or writeVolumeFromHW() appropriately
//bool Mixer_SUN::isRecsrcHW( isRecsrcHW(const QString& id )
//{
// int devnum = id2num(id);
// switch ( devnum )
// {
// case MIXERDEV_MICROPHONE :
// case MIXERDEV_LINE_IN :
// case MIXERDEV_CD :
// return true;
//
// default :
// return false;
// }
//}
//======================================================================
// FUNCTION : Mixer::VolumeToGainBalance
// DESCRIPTION : Converts a Volume(left vol + right vol) into the
// Gain/Balance values used by Sun.
//======================================================================
void Mixer_SUN::VolumeToGainBalance( Volume& volume, uint_t& gain, uchar_t& balance )
{
if ( ( volume.count() == 1 ) ||
( volume.getVolume(Volume::LEFT) == volume.getVolume(Volume::RIGHT) ) )
{
gain = volume.getVolume(Volume::LEFT);
balance = AUDIO_MID_BALANCE;
}
else
{
if ( volume.getVolume(Volume::LEFT) > volume.getVolume(Volume::RIGHT) )
{
gain = volume.getVolume(Volume::LEFT);
balance = AUDIO_LEFT_BALANCE +
( AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE ) *
volume.getVolume(Volume::RIGHT) / volume.getVolume(Volume::LEFT);
}
else
{
gain = volume.getVolume(Volume::RIGHT);
balance = AUDIO_RIGHT_BALANCE -
( AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE ) *
volume.getVolume(Volume::LEFT) / volume.getVolume(Volume::RIGHT);
}
}
}
//======================================================================
// FUNCTION : Mixer::GainBalanceToVolume
// DESCRIPTION : Converts Gain/Balance returned by Sun driver to the
// Volume(left vol + right vol) format used by kmix.
//======================================================================
void Mixer_SUN::GainBalanceToVolume( uint_t& gain, uchar_t& balance, Volume& volume )
{
if ( volume.count() == 1 )
{
volume.setVolume( Volume::LEFT, gain );
}
else
{
if ( balance <= AUDIO_MID_BALANCE )
{
volume.setVolume( Volume::LEFT, gain );
volume.setVolume( Volume::RIGHT, gain *
( balance - AUDIO_LEFT_BALANCE ) /
( AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE ) );
}
else
{
volume.setVolume( Volume::RIGHT, gain );
volume.setVolume( Volume::LEFT, gain *
( AUDIO_RIGHT_BALANCE - balance ) /
( AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE ) );
}
}
}
int Mixer_SUN::id2num(const QString& id)
{
return id.toInt();
}
QString SUN_getDriverName() {
return "SUNAudio";
}
QString Mixer_SUN::getDriverName()
{
return "SUNAudio";
}

View file

@ -1,55 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2000 Christian Esken <esken@kde.org>
* 2000 Brian Hanson <bhanson@hotmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MIXER_SUN_H
#define MIXER_SUN_H
#include <QString>
#include "mixer_backend.h"
class Mixer_SUN : public Mixer_Backend
{
public:
Mixer_SUN(Mixer *mixer, int devnum);
virtual ~Mixer_SUN();
virtual QString errorText(int mixer_error);
virtual int readVolumeFromHW( const QString& id, std::shared_ptr<MixDevice> );
virtual int writeVolumeToHW ( const QString& id, std::shared_ptr<MixDevice> );
virtual QString getDriverName();
protected:
virtual int open();
virtual int close();
void VolumeToGainBalance( Volume& volume, uint_t& gain, uchar_t& balance );
void GainBalanceToVolume( uint_t& gain, uchar_t& balance, Volume& volume );
int fd;
private:
int id2num(const QString& id);
};
#endif

View file

@ -1,271 +0,0 @@
<ui version="4.0" stdsetdef="1" >
<author>Stefan Schimanski &lt;1Stein@gmx.de></author>
<class>ColorWidget</class>
<widget class="QWidget" name="ColorWidget" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>272</width>
<height>305</height>
</rect>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="customColors" >
<property name="text" >
<string>&amp;Use custom colors</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="activeColors" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="title" >
<string>Active</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<number>0</number>
</property>
<item row="2" column="1" >
<widget class="KColorButton" name="activeBack" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="text" >
<string/>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="TextLabel3" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="text" >
<string>&amp;Silent:</string>
</property>
<property name="buddy" stdset="0" >
<cstring>activeLow</cstring>
</property>
<property name="wordWrap" >
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="KColorButton" name="activeLow" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="text" >
<string/>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="KColorButton" name="activeHigh" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>1</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string/>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="labelLoad" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="text" >
<string>&amp;Loud:</string>
</property>
<property name="buddy" stdset="0" >
<cstring>activeHigh</cstring>
</property>
<property name="wordWrap" >
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="TextLabel4" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="text" >
<string>&amp;Background:</string>
</property>
<property name="buddy" stdset="0" >
<cstring>activeBack</cstring>
</property>
<property name="wordWrap" >
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="mutedColors" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="title" >
<string>Muted</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<number>0</number>
</property>
<item row="0" column="0" >
<widget class="QLabel" name="TextLabel6" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="text" >
<string>Lou&amp;d:</string>
</property>
<property name="buddy" stdset="0" >
<cstring>mutedHigh</cstring>
</property>
<property name="wordWrap" >
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="TextLabel8" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="text" >
<string>Backgrou&amp;nd:</string>
</property>
<property name="buddy" stdset="0" >
<cstring>mutedBack</cstring>
</property>
<property name="wordWrap" >
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="TextLabel7" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="text" >
<string>Silen&amp;t:</string>
</property>
<property name="buddy" stdset="0" >
<cstring>mutedLow</cstring>
</property>
<property name="wordWrap" >
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="KColorButton" name="mutedHigh" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>1</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string/>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="KColorButton" name="mutedLow" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="text" >
<string/>
</property>
</widget>
</item>
<item row="2" column="1" >
<widget class="KColorButton" name="mutedBack" >
<property name="enabled" >
<bool>true</bool>
</property>
<property name="text" >
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="spacer5" >
<property name="sizeHint" >
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Vertical</enum>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>
<tabstop>customColors</tabstop>
<tabstop>activeHigh</tabstop>
<tabstop>activeLow</tabstop>
<tabstop>activeBack</tabstop>
<tabstop>mutedHigh</tabstop>
<tabstop>mutedLow</tabstop>
<tabstop>mutedBack</tabstop>
</tabstops>
<includes>
<include location="global" >klocale.h</include>
<include location="global" >kseparator.h</include>
</includes>
<connections>
<connection>
<sender>customColors</sender>
<signal>toggled(bool)</signal>
<receiver>activeColors</receiver>
<slot>setEnabled(bool)</slot>
</connection>
<connection>
<sender>customColors</sender>
<signal>toggled(bool)</signal>
<receiver>mutedColors</receiver>
<slot>setEnabled(bool)</slot>
</connection>
</connections>
</ui>

View file

@ -1,197 +0,0 @@
/*
KMix -- KDE's full featured mini mixer
Copyright (C) 2012 Christian Esken <esken@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "ControlManager.h"
#include "core/GlobalConfig.h"
#include <QtCore/qlist.h>
#include <KDebug>
ControlManager ControlManager::instanceSingleton;
ControlManager& ControlManager::instance()
{
return instanceSingleton;
}
ControlManager::ControlManager()
{
listenersChanged = false;
}
/**
* Announce a change for one or all mixers.
*
* @param mixerId The mixerId. Use an empty QString() to announce a change for all mixers
* @param changeType A bit array of ControlChangeType flags
* @param sourceId Only for logging
*
*/
void ControlManager::announce(QString mixerId, ControlChangeType::Type changeType, QString sourceId)
{
bool listenersModified = false;
QSet<Listener*> processedListeners;
do
{
listenersModified = false;
QList<Listener>::iterator it;
for (it = listeners.begin(); it != listeners.end(); ++it)
{
Listener& listener = *it;
bool mixerIsOfInterest = listener.getMixerId().isEmpty() || mixerId.isEmpty()
|| listener.getMixerId() == mixerId;
bool listenerAlreadyProcesed = processedListeners.contains(&listener);
if ( listenerAlreadyProcesed )
{
if (GlobalConfig::instance().data.debugControlManager)
kDebug() << "Skipping already processed listener";
continue;
}
if (mixerIsOfInterest && listener.getChangeType() == changeType)
{
bool success = QMetaObject::invokeMethod(listener.getTarget(), "controlsChange", Qt::DirectConnection,
Q_ARG(int, changeType));
if (GlobalConfig::instance().data.debugControlManager)
{
kDebug() << "Listener " << listener.getSourceId() <<" is interested in " << mixerId
<< ", " << ControlChangeType::toString(changeType);
}
if (!success)
{
kError() << "Listener Failed to send to " << listener.getTarget()->metaObject()->className();
}
processedListeners.insert(&listener);
if (listenersChanged)
{
// The invokeMethod() above has changed the listeners => my Iterator is invalid => restart loop
if (GlobalConfig::instance().data.debugControlManager)
kDebug() << "Listeners modified => restart loop";
listenersChanged = false;
listenersModified = true;
break; // break inner loop => restart via outer loop
}
}
}
}
while ( listenersModified);
if (GlobalConfig::instance().data.debugControlManager)
{
kDebug()
<< "Announcing " << ControlChangeType::toString(changeType) << " for "
<< (mixerId.isEmpty() ? "all cards" : mixerId) << " by " << sourceId;
}
}
/**
* Adds a listener for the given mixerId and changeType.
* Listeners are informed about all corresponding changes via a signal.
* Listeners are not informed about changes that originate from oneself (according to sourceId).
*
* @param mixerId The id of the Mixer you are interested in
* @param changetType The changeType of interest
* @param target The QObject, where the notification signal is sent to. It must implement the SLOT controlChanged(QString mixerId, ControlChangeType::Type changeType).
* @param sourceId Only for logging
*/
void ControlManager::addListener(QString mixerId, ControlChangeType::Type changeType, QObject* target, QString sourceId)
{
if (GlobalConfig::instance().data.debugControlManager)
{
kDebug()
<< "Listening to " << ControlChangeType::toString(changeType) << " for "
<< (mixerId.isEmpty() ? "all cards" : mixerId) << " by " << sourceId << ". Announcements are sent to "
<< target;
}
for ( ControlChangeType::Type ct = ControlChangeType::TypeFirst; ct != ControlChangeType::TypeLast; ct = (ControlChangeType::Type)(ct << 1))
{
if ( changeType & ct )
{
// Add all listeners.
Listener listener = Listener(mixerId, ct, target, sourceId);
listeners.append(listener);
listenersChanged = true;
}
}
if (GlobalConfig::instance().data.debugControlManager)
{
kDebug()
<< "We now have" << listeners.size() << "listeners";
}
}
/**
* Removes all listeners of the given target.
* @param target The QObject that was used to register via addListener()
*/
void ControlManager::removeListener(QObject* target)
{
ControlManager::instance().removeListener(target, target->metaObject()->className());
}
/**
* Removes all listeners of the given target.
* @param target The QObject that was used to register via addListener()
* @param sourceId Optional: Only for logging
*/
void ControlManager::removeListener(QObject* target, QString sourceId)
{
QMutableListIterator<Listener> it(listeners);
while ( it.hasNext())
{
Listener& listener = it.next();
if (listener.getTarget() == target)
{
if (GlobalConfig::instance().data.debugControlManager)
kDebug()
<< "Stop Listening of " << listener.getSourceId() << " requested by " << sourceId << " from " << target;
it.remove();
// Hint: As we have actual objects no explicit delete is needed
listenersChanged = true;
}
}
}
void ControlManager::warnUnexpectedChangeType(ControlChangeType::Type type, QObject *obj)
{
kWarning() << "Unexpected type " << type << " received by " << obj->metaObject()->className();
}
void ControlManager::shutdownNow()
{
if (GlobalConfig::instance().data.debugControlManager)
kDebug() << "Shutting down ControlManager";
QList<Listener>::iterator it;
for (it = listeners.begin(); it != listeners.end(); ++it)
{
Listener& listener = *it;
if (GlobalConfig::instance().data.debugControlManager)
kDebug()
<< "Listener still connected. Closing it. source=" << listener.getSourceId() << "listener="
<< listener.getTarget()->metaObject()->className();
}
}
#include "moc_ControlManager.cpp"

View file

@ -1,149 +0,0 @@
/*
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 2012 Christian Esken <esken@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef CONTROLMANAGER_H
#define CONTROLMANAGER_H
#include <QObject>
#include <QString>
// typedef int ControlChangeType;
// enum ControlChangeType {
// Volume, // Volume or Switch change (Mute or Capture Switch, or Enum)
// ControlList, // Control added or deleted
// GUI // Visual changes, like "split channel" OR "show labels"
// };
class ControlChangeType: QObject
{
Q_OBJECT
public:
enum Type
{
None = 0, //
TypeFirst = 1,
Volume = 1, // Volume or Switch change (Mute or Capture Switch, or Enum)
ControlList = 2, // Control added or deleted
GUI = 4, // Visual changes, like "split channel" OR "show labels"
MasterChanged = 8 // Master (global or local) has changed
,
TypeLast = 16
};
static QString toString(Type changeType)
{
QString ret;
bool needsSeparator = false;
for (ControlChangeType::Type ct = ControlChangeType::TypeFirst; ct != ControlChangeType::TypeLast; ct =
(ControlChangeType::Type) (ct << 1))
{
if (changeType & ct)
{
if (needsSeparator)
ret.append('|');
switch (ct)
{
case Volume:
ret.append("Volume");
break;
case ControlList:
ret.append("ControlList");
break;
case GUI:
ret.append("GUI");
break;
case MasterChanged:
ret.append("MasterChange");
break;
default:
ret.append("Invalid");
break;
}
needsSeparator = true;
}
}
return ret;
};
static ControlChangeType::Type fromInt(int type)
{
switch ( type )
{
case 1: return Volume;
case 2: return ControlList;
case 4: return GUI;
case 8: return MasterChanged;
default: return None;
}
};
};
class Listener
{
public:
Listener(const QString mixerId, ControlChangeType::Type changeType, QObject* target, QString& sourceId)
{
this->mixerId = mixerId;
this->controlChangeType = changeType;
// target is bit dangerous, as it might get deleted.
this->target = target;
this->sourceId = sourceId;
}
const QString& getMixerId() { return mixerId; };
ControlChangeType::Type& getChangeType() { return controlChangeType; };
QObject* getTarget() { return target; };
const QString& getSourceId() { return sourceId; };
private:
QString mixerId;
ControlChangeType::Type controlChangeType;
QObject* target;
QString sourceId;
};
class ControlManager
{
public:
static ControlManager& instance();
void announce(QString mixerId, ControlChangeType::Type changeType, QString sourceId);
void addListener(QString mixerId, ControlChangeType::Type changeType, QObject* target, QString sourceId);
void removeListener(QObject* target);
void removeListener(QObject* target, QString sourceId);
static void warnUnexpectedChangeType(ControlChangeType::Type type, QObject *obj);
void shutdownNow();
private:
ControlManager();
static ControlManager instanceSingleton;
QList<Listener> listeners;
bool listenersChanged;
};
#endif // CONTROLMANAGER_H

View file

@ -1,83 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (c) The KMix Authors
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "ControlPool.h"
#include <QString>
#include <QMap>
std::shared_ptr<MixDevice> ControlPool::TheEmptyDevice; // = std::shared_ptr<MixDevice>(ControlPool::TheEmptyDevicePtr);
ControlPool::ControlPool()
{
pool = new QMap<QString, std::shared_ptr<MixDevice> >();
}
ControlPool* ControlPool::_instance = 0;
ControlPool* ControlPool::instance()
{
if ( _instance == 0 )
ControlPool::_instance = new ControlPool();
return ControlPool::_instance;
}
/**
* Adds a Control to the pool, and returns it wrapped in QSharedPointer.
* if the Control was already in the Pool, the existing Control is returned
*
* @param key A key, unique over all controls of all cards, e.g. "Master:0@ALSA::Creative_XFI:0"
* @param mixDevice
* @return
*/
std::shared_ptr<MixDevice> ControlPool::add(const QString& key, MixDevice* md)
{
std::shared_ptr<MixDevice> controlFromPool(get(key));
if ( controlFromPool.get() != 0)
{
kDebug() << "----ControlPool already cached key=" << key;
return controlFromPool;
}
// else: Add the control to the pool
kDebug() << "----ControlPool add key=" << key;
std::shared_ptr<MixDevice> mdShared(md);
pool->insert(key, mdShared);
return mdShared;
}
/**
* Retrieves a Control from the pool as QSharedPointer. If the Control is not
* in the pool, a QSharedPointer that points to null (0) is returned.
*
* @param key
* @return The Control wrapped in QSharedPointer. If not found, a QSharedPointer that points to null.
*/
std::shared_ptr<MixDevice> ControlPool::get(const QString& key)
{
std::shared_ptr<MixDevice> mixDeviceShared = pool->value(key, TheEmptyDevice);
return mixDeviceShared;
}

View file

@ -1,48 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (c) The KMix Authors
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef CONTROL_POOL_H
#define CONTROL_POOL_H
#include <memory>
#include "core/mixdevice.h"
class ControlPool
{
public:
static ControlPool* instance();
std::shared_ptr<MixDevice> add(const QString& key, MixDevice* mixDevice);
std::shared_ptr<MixDevice> get(const QString& key);
private:
ControlPool();
virtual ~ControlPool() {};
QMap<QString, std::shared_ptr<MixDevice> > *pool;
static ControlPool* _instance;
static std::shared_ptr<MixDevice> TheEmptyDevice;
};
#endif

View file

@ -1,107 +0,0 @@
/*
KMix -- KDE's full featured mini mixer
Copyright (C) 2012 Christian Esken <esken@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "GlobalConfig.h"
// instanceObj must be created "late", so we can refer to the correct application config file kmixrc instead of kderc.
GlobalConfig* GlobalConfig::instanceObj;
GlobalConfig::GlobalConfig() :
KConfigSkeleton()
{
setCurrentGroup("Global");
// General
addItemBool("Tickmarks", data.showTicks, true);
addItemBool("Labels", data.showLabels, true);
ItemString* is = addItemString("Orientation", data.orientationMainGUIString, "Vertical");
kDebug() << is->name() << is->value();
addItemString("Orientation.TrayPopup", data.orientationTrayPopupString, QLatin1String("Vertical"));
// Sound Menu
addItemBool("showOSD", data.showOSD, true);
addItemBool("AllowDocking", data.showDockWidget, true);
// addItemBool("TrayVolumeControl", data.trayVolumePopupEnabled, true); // removed support in KDE4.13. Always active!
// Startup
addItemBool("AutoStart", data.allowAutostart, true);
addItemBool("startkdeRestore", data.startkdeRestore, true);
// Debug options: Not in dialog
addItemBool("Debug.ControlManager", data.debugControlManager, false);
addItemBool("Debug.GUI", data.debugGUI, false);
addItemBool("Debug.Volume", data.debugVolume, false);
readConfig();
}
// --- Special READ/WRITE ----------------------------------------------------------------------------------------
void GlobalConfig::usrReadConfig()
{
// kDebug() << "or=" << data.orientationMainGUIString;
// Convert orientation strings to Qt::Orientation
data.convertOrientation();
}
//void GlobalConfig::usrWriteConfig()
//{
// // TODO: Is this any good? When is usrWriteConfig() called? Hopefully BEFORE actually writing. Otherwise
// // I must move this code to #setToplevelOrientation() and #setTraypopupOrientation().
//}
Qt::Orientation GlobalConfigData::getToplevelOrientation()
{
return toplevelOrientation;
}
Qt::Orientation GlobalConfigData::getTraypopupOrientation()
{
return traypopupOrientation;
}
/**
* Converts the orientation strings to Qt::Orientation
*/
void GlobalConfigData::convertOrientation()
{
toplevelOrientation = stringToOrientation(orientationMainGUIString);
traypopupOrientation = stringToOrientation(orientationTrayPopupString);
}
void GlobalConfigData::setToplevelOrientation(Qt::Orientation orientation)
{
toplevelOrientation = orientation;
orientationMainGUIString = orientationToString(toplevelOrientation);
}
void GlobalConfigData::setTraypopupOrientation(Qt::Orientation orientation)
{
traypopupOrientation = orientation;
orientationTrayPopupString = orientationToString(traypopupOrientation);
}
Qt::Orientation GlobalConfigData::stringToOrientation(QString& orientationString)
{
return orientationString == "Horizontal" ? Qt::Horizontal : Qt::Vertical;
}
QString GlobalConfigData::orientationToString(Qt::Orientation orientation)
{
return orientation == Qt::Horizontal ? "Horizontal" : "Vertical";
}

View file

@ -1,121 +0,0 @@
/*
KMix -- KDE's full featured mini mixer
Copyright (C) 2012 Christian Esken <esken@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef GLOBALCONFIG_H
#define GLOBALCONFIG_H
#include <QtCore/qnamespace.h>
#include <QSet>
#include <KConfigSkeleton>
#include <KDebug>
class GlobalConfigData
{
friend class GlobalConfig;
public:
// Hint: We are using the standard 1-arg constructor as copy constructor
bool showTicks;
bool showLabels;
bool showOSD;
// Startup
bool allowAutostart;
bool showDockWidget;
bool startkdeRestore;
// Debug options
bool debugControlManager;
bool debugGUI;
bool debugVolume;
Qt::Orientation getToplevelOrientation();
Qt::Orientation getTraypopupOrientation();
void setToplevelOrientation(Qt::Orientation orientation);
void setTraypopupOrientation(Qt::Orientation orientation);
private:
QString orientationMainGUIString;
QString orientationTrayPopupString;
// The following two values are only converted/cached date from the former fields.
Qt::Orientation toplevelOrientation;
Qt::Orientation traypopupOrientation;
void convertOrientation();
Qt::Orientation stringToOrientation(QString& orientationString);
QString orientationToString(Qt::Orientation orientation);
};
class GlobalConfig: public KConfigSkeleton
{
private:
static GlobalConfig* instanceObj;
public:
static GlobalConfig& instance()
{
return *instanceObj;
}
;
/**
* Call this init method when your app core is properly initialized.
* It is very important that KGlobal is initialized then. Otherwise KGlobal::config() could return a reference to
* the "kderc" config instead of the actual application config "kmixrc" or "kmixctrlrc".
*
*/
static void init()
{
instanceObj = new GlobalConfig();
}
;
GlobalConfigData data;
void setMixersForSoundmenu(QSet<QString> mixersForSoundmenu)
{
this->mixersForSoundmenu = mixersForSoundmenu;
}
;
QSet<QString> getMixersForSoundmenu()
{
return mixersForSoundmenu;
}
;
protected:
QSet<QString> mixersForSoundmenu;
private:
GlobalConfig();
/**
* @Override
*/
virtual void usrReadConfig();
/**
* @Override
*/
// virtual void usrWriteConfig();
};
#endif // GLOBALCONFIG_H

View file

@ -1,41 +0,0 @@
/*
* MasterControl.cpp
*
* Created on: 02.01.2011
* Author: kde
*/
#include "MasterControl.h"
MasterControl::MasterControl()
{
}
MasterControl::~MasterControl()
{
}
QString MasterControl::getCard() const
{
return card;
}
QString MasterControl::getControl() const
{
return control;
}
void MasterControl::set(QString card, QString control)
{
this->card = card;
this->control = control;
}
bool MasterControl::isValid()
{
if ( control.isEmpty() || card.isEmpty() )
return false;
return true;
}

View file

@ -1,30 +0,0 @@
/*
* MasterControl.h
*
* Created on: 02.01.2011
* Author: kde
*/
#ifndef MASTERCONTROL_H_
#define MASTERCONTROL_H_
#include <QString>
class MasterControl
{
public:
MasterControl();
virtual ~MasterControl();
QString getCard() const;
QString getControl() const;
void set(QString card, QString control);
bool isValid();
private:
QString card;
QString control;
};
#endif /* MASTERCONTROL_H_ */

View file

@ -1,63 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 1996-2014 The KMix authors. Maintainer: Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
* MediaController.cpp
*
* Created on: 17.12.2013
* Author: chris
*/
#include "core/MediaController.h"
#include <KDebug>
MediaController::MediaController(QString controlId) :
id(controlId), playState(PlayUnknown)
{
mediaPlayControl = false;
mediaNextControl = false;
mediaPrevControl = false;
}
MediaController::~MediaController()
{
}
/**
* Returns whether this device has at least one media player control.
* @return
*/
bool MediaController::hasControls()
{
return mediaPlayControl | mediaNextControl | mediaPrevControl;
}
MediaController::PlayState MediaController::getPlayState()
{
return playState;
}
void MediaController::setPlayState(PlayState playState)
{
this->playState = playState;
}

View file

@ -1,69 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 1996-2014 The KMix authors. Maintainer: Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
* MediaController.h
*
* Created on: 17.12.2013
* Author: chris
*/
#ifndef MEDIACONTROLLER_H_
#define MEDIACONTROLLER_H_
#include <QString>
/**
* A MediaController controls exactly one Media Player. You can think of it as a single control, like PCM.
*/
class MediaController
{
public:
enum PlayState { PlayPaused, PlayPlaying, PlayStopped, PlayUnknown };
MediaController(QString);
virtual ~MediaController();
void addMediaPlayControl() { mediaPlayControl = true; };
void addMediaNextControl() { mediaNextControl = true; };
void addMediaPrevControl() { mediaPrevControl = true; };
bool hasMediaPlayControl() { return mediaPlayControl; };
bool hasMediaNextControl() { return mediaNextControl; };
bool hasMediaPrevControl() { return mediaPrevControl; };
bool hasControls();
MediaController::PlayState getPlayState();
void setPlayState(PlayState playState);
bool canSkipNext();
bool canSkipPrevious();
private:
QString id;
PlayState playState;
bool mediaPlayControl;
bool mediaNextControl;
bool mediaPrevControl;
};
#endif /* MEDIACONTROLLER_H_ */

View file

@ -1,204 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 2006-2007 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "core/kmixdevicemanager.h"
#include <kdebug.h>
#include <iostream>
#include <QRegExp>
#include <QString>
#include <solid/device.h>
#include <solid/devicenotifier.h>
#include <solid/audiointerface.h>
KMixDeviceManager* KMixDeviceManager::s_KMixDeviceManager = 0;
KMixDeviceManager::KMixDeviceManager()
{
}
KMixDeviceManager::~KMixDeviceManager()
{
}
KMixDeviceManager* KMixDeviceManager::instance()
{
if ( s_KMixDeviceManager == 0 ) {
s_KMixDeviceManager = new KMixDeviceManager();
}
return s_KMixDeviceManager;
}
void KMixDeviceManager::initHotplug()
{
connect (Solid::DeviceNotifier::instance(), SIGNAL(deviceAdded(QString)), SLOT(pluggedSlot(QString)) );
connect (Solid::DeviceNotifier::instance(), SIGNAL(deviceRemoved(QString)), SLOT(unpluggedSlot(QString)) );
}
QString KMixDeviceManager::getUDI_ALSA(int num)
{
QList<Solid::Device> dl = Solid::Device::listFromType(Solid::DeviceInterface::AudioInterface);
QString numString;
numString.setNum(num);
bool found = false;
QString udi;
QString devHandle;
foreach ( const Solid::Device &device, dl )
{
// std::cout << "Coldplug udi = '" << device.udi().toUtf8().data() << "'\n";
// LEAK audiohw leaks, but Solid does not document whether it is its own or "my" Object.
const Solid::AudioInterface *audiohw = device.as<Solid::AudioInterface>();
if (audiohw != 0)
{
if (audiohw->deviceType() & ( Solid::AudioInterface::AudioControl))
{
switch (audiohw->driver())
{
case Solid::AudioInterface::Alsa:
devHandle = audiohw->driverHandle().toList().first().toString();
if ( numString == devHandle )
{
found = true;
udi = device.udi();
}
break;
default:
break;
} // driver type
} // is an audio control
// If I delete audiohw, kmix crashes. If I do not, there is a definite leak according to valgrind:
// ==24561== 4,958 (1,200 direct, 3,758 indirect) bytes in 25 blocks are definitely lost in loss record 1,882 of 1,918
// ==24561== at 0x4C27D49: operator new(unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
// ==24561== by 0x5665462: ??? (in /usr/lib64/libsolid.so.4.11.3)
// ==24561== by 0x565EC57: ??? (in /usr/lib64/libsolid.so.4.11.3)
// ==24561== by 0x563610B: Solid::Device::asDeviceInterface(Solid::DeviceInterface::Type const&) const (in /usr/lib64/libsolid.so.4.11.3)
// ==24561== by 0x4EB7DA6: Solid::AudioInterface const* Solid::Device::as<Solid::AudioInterface>() const (device.h:254)
// ==24561== by 0x4EB6F6F: KMixDeviceManager::getUDI_ALSA(int) (kmixdevicemanager.cpp:70)
// ==24561== by 0x4E6C809: Mixer_ALSA::open() (mixer_alsa9.cpp:139)
// ==24561== by 0x4E6334E: Mixer_Backend::openIfValid() (mixer_backend.cpp:84)
// ==24561== by 0x4EBD4F9: Mixer::openIfValid(int) (mixer.cpp:268)
// ==24561== by 0x4EB5F10: MixerToolBox::possiblyAddMixer(Mixer*) (mixertoolbox.cpp:310)
// ==24561== by 0x4EB57AE: MixerToolBox::initMixerInternal(MixerToolBox::MultiDriverMode, QList<QString>, QString&) (mixertoolbox.cpp:165)
// ==24561== by 0x4EB52B6: MixerToolBox::initMixer(MixerToolBox::MultiDriverMode, QList<QString>, QString&) (mixertoolbox.cpp:85)
// ==24561== by 0x4EB5267: MixerToolBox::initMixer(bool, QList<QString>, QString&) (mixertoolbox.cpp:80)
// ==24561== by 0x4E7ED75: KMixWindow::KMixWindow(bool) (kmix.cpp:96)
// ==24561== by 0x4E8A7CF: KMixApp::newInstance() (KMixApp.cpp:112)
// ==24561== by 0x5B30A7E: KUniqueApplication::Private::_k_newInstanceNoFork() (in /usr/lib64/libkdeui.so.5.11.3)
// ==24561== by 0x774A11D: QObject::event(QEvent*) (in /usr/lib64/libQtCore.so.4.8.5)
// ==24561== by 0x61338A2: QApplication::event(QEvent*) (in /usr/lib64/libQtGui.so.4.8.5)
// ==24561== by 0x612E8AB: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /usr/lib64/libQtGui.so.4.8.5)
// ==24561== by 0x6134E6F: QApplication::notify(QObject*, QEvent*) (in /usr/lib64/libQtGui.so.4.8.5)
// The data seems to be "static" device info from Solid, so I will leave it alone.
// delete audiohw;
}
if (found)
{
break;
}
} // foreach
return udi;
}
QString KMixDeviceManager::getUDI_OSS(const QString& devname)
{
QList<Solid::Device> dl = Solid::Device::listFromType(Solid::DeviceInterface::AudioInterface);
bool found = false;
QString udi;
QString devHandle;
foreach ( const Solid::Device &device, dl )
{
// std::cout << "Coldplug udi = '" << device.udi().toUtf8().data() << "'\n";
const Solid::AudioInterface *audiohw = device.as<Solid::AudioInterface>();
if (audiohw && (audiohw->deviceType() & ( Solid::AudioInterface::AudioControl))) {
switch (audiohw->driver()) {
case Solid::AudioInterface::OpenSoundSystem:
devHandle = audiohw->driverHandle().toString();
// std::cout << ">>> Coldplugged OSS ='" << devHandle.toUtf8().data() << "'\n";
if ( devname == devHandle ) {
found = true;
// std::cout << ">>> Match!!! Coldplugged OSS ='" << devHandle.toUtf8().data() << "'\n";
udi = device.udi();
}
break;
default:
break;
} // driver type
} // is an audio control
if ( found) break;
} // foreach
return udi;
}
void KMixDeviceManager::pluggedSlot(const QString& udi) {
// std::cout << "Plugged udi='" << udi.toUtf8().data() << "'\n";
Solid::Device device(udi);
Solid::AudioInterface *audiohw = device.as<Solid::AudioInterface>();
if (audiohw && (audiohw->deviceType() & ( Solid::AudioInterface::AudioControl))) {
QString dev;
QRegExp devExpr( QLatin1String( "^\\D+(\\d+)$" ));
switch (audiohw->driver()) {
case Solid::AudioInterface::Alsa:
if ( _hotpluggingBackend == "ALSA" || _hotpluggingBackend == "*" ) {
dev = audiohw->driverHandle().toList().first().toString();
emit plugged("ALSA", udi, dev);
}
break;
case Solid::AudioInterface::OpenSoundSystem:
if ( _hotpluggingBackend == "OSS" || _hotpluggingBackend == "*" ) {
dev = audiohw->driverHandle().toString();
if ( devExpr.indexIn(dev) > -1 ) {
dev = devExpr.cap(1); // Get device number from device name (e.g "/dev/mixer1" or "/dev/sound/mixer2")
}
else {
dev = '0'; // "/dev/mixer" or "/dev/sound/mixer"
}
emit plugged("OSS", udi, dev);
}
break;
default:
kError(67100) << "Plugged UNKNOWN Audio device (ignored)";
break;
}
}
}
void KMixDeviceManager::unpluggedSlot(const QString& udi) {
// std::cout << "Unplugged udi='" << udi.toUtf8().data() << "'\n";
// Solid::Device device(udi);
// At this point the device has already been unplugged by the user. Solid doesn't know anything about the
// device except the UDI (not even device.as<Solid::AudioInterface>() is possible). Thus I'll forward any
// unplugging action (could e.g. also be HID or mass storage). The receiver of the signal has to deal with it,
// but a simple UDI matching is enough.
emit unplugged(udi);
}
#include "moc_kmixdevicemanager.cpp"

View file

@ -1,55 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 2006-2007 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef kmixdevicemanager_h
#define kmixdevicemanager_h
#include <QObject>
class KMixDeviceManager : public QObject
{
Q_OBJECT
public:
static KMixDeviceManager* instance();
void initHotplug();
void setHotpluggingBackends(const QString& backendName) { _hotpluggingBackend = backendName; } ;
QString getUDI_ALSA(int num);
QString getUDI_OSS(const QString& devname);
signals:
void plugged( const char* driverName, const QString& udi, QString& dev);
void unplugged( const QString& udi);
private:
KMixDeviceManager();
~KMixDeviceManager();
QString _hotpluggingBackend;
private slots:
void pluggedSlot(const QString&);
void unpluggedSlot(const QString&);
private:
static KMixDeviceManager* s_KMixDeviceManager;
};
#endif

View file

@ -1,471 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "core/mixdevice.h"
#include <qregexp.h>
#include <kdebug.h>
#include <klocale.h>
#include "core/ControlPool.h"
#include "core/mixer.h"
#include "dbus/dbuscontrolwrapper.h"
#include "gui/guiprofile.h"
#include "core/volume.h"
static const QString channelTypeToIconName( MixDevice::ChannelType type )
{
switch (type) {
case MixDevice::AUDIO:
return "mixer-pcm";
case MixDevice::BASS:
case MixDevice::SURROUND_LFE: // "LFE" SHOULD have an own icon
return "mixer-lfe";
case MixDevice::CD:
return "mixer-cd";
case MixDevice::EXTERNAL:
return "mixer-line";
case MixDevice::MICROPHONE:
return "mixer-microphone";
case MixDevice::MIDI:
return "mixer-midi";
case MixDevice::RECMONITOR:
return "mixer-capture";
case MixDevice::TREBLE:
return "mixer-pcm-default";
case MixDevice::UNKNOWN:
return "mixer-front";
case MixDevice::VOLUME:
return "mixer-master";
case MixDevice::VIDEO:
return "mixer-video";
case MixDevice::SURROUND:
case MixDevice::SURROUND_BACK:
return "mixer-surround";
case MixDevice::SURROUND_CENTERFRONT:
case MixDevice::SURROUND_CENTERBACK:
return "mixer-surround-center";
case MixDevice::HEADPHONE:
return "mixer-headset";
case MixDevice::DIGITAL:
return "mixer-digital";
case MixDevice::AC97:
return "mixer-ac97";
case MixDevice::SPEAKER:
return "mixer-pc-speaker";
case MixDevice::MICROPHONE_BOOST:
return "mixer-microphone-boost";
case MixDevice::MICROPHONE_FRONT_BOOST:
return "mixer-microphone-front-boost";
case MixDevice::MICROPHONE_FRONT:
return "mixer-microphone-front";
case MixDevice::KMIX_COMPOSITE:
return "mixer-line";
case MixDevice::APPLICATION_AMAROK:
return "amarok";
case MixDevice::APPLICATION_BANSHEE:
return "media-player-banshee";
case MixDevice::APPLICATION_XMM2:
return "xmms";
case MixDevice::APPLICATION_TOMAHAWK:
return "tomahawk";
case MixDevice::APPLICATION_CLEMENTINE:
return "application-x-clementine";
case MixDevice::APPLICATION_VLC:
return "vlc";
case MixDevice::APPLICATION_STREAM:
return "mixer-pcm";
}
return "mixer-front";
}
/**
* Constructs a MixDevice. A MixDevice represents one channel or control of
* the mixer hardware. A MixDevice has a type (e.g. PCM), a descriptive name
* (for example "Master" or "Headphone" or "IEC 958 Output"),
* can have a volume level (2 when stereo), can be recordable and muted.
* The ChannelType tells which kind of control the MixDevice is.
*/
MixDevice::MixDevice( Mixer* mixer, const QString& id, const QString& name, ChannelType type )
{
init(mixer, id, name, channelTypeToIconName(type), (MixSet*)0);
}
MixDevice::MixDevice( Mixer* mixer, const QString& id, const QString& name, const QString& iconName, MixSet* moveDestinationMixSet )
{
init(mixer, id, name, iconName, moveDestinationMixSet);
}
void MixDevice::init( Mixer* mixer, const QString& id, const QString& name, const QString& iconName, MixSet* moveDestinationMixSet )
{
_artificial = false;
_applicationStream = false;
_dbusControlWrapper = 0; // will be set in addToPool()
_mixer = mixer;
_id = id;
_enumCurrentId = 0;
mediaController = new MediaController(_id);
if( name.isEmpty() )
_name = i18n("unknown");
else
_name = name;
if ( iconName.isEmpty() )
_iconName = "mixer-front";
else
_iconName = iconName;
_moveDestinationMixSet = moveDestinationMixSet;
if ( _id.contains(' ') ) {
// The key is used in the config file. IdbusControlWrappert MUST NOT contain spaces
kError(67100) << "MixDevice::setId(\"" << id << "\") . Invalid key - it must not contain spaces";
_id.replace(' ', '_');
}
// kDebug(67100) << "MixDevice::init() _id=" << _id;
}
/*
* When a MixDevice shall be finally discarded, you must use this method to free its resources.
* You must not use this MixDevice after calling close().
* <br>
* The necessity stems from a memory leak due to object cycle (MixDevice<->DBusControlWrapper), so the reference
* counting std::shared_ptr has no chance to clean up. See Bug 309464 for background information.
*/
void MixDevice::close()
{
delete _dbusControlWrapper;
_dbusControlWrapper = 0;
}
MediaController* MixDevice::getMediaController()
{
return mediaController;
}
std::shared_ptr<MixDevice> MixDevice::addToPool()
{
// kDebug() << "id=" << _mixer->id() << ":" << _id;
std::shared_ptr<MixDevice> thisSharedPtr(this);
// std::shared_ptr<MixDevice> thisSharedPtr = ControlPool::instance()->add(fullyQualifiedId, this);
_dbusControlWrapper = new DBusControlWrapper( thisSharedPtr, dbusPath() );
return thisSharedPtr;
}
/**
* Changes the internal state of this MixDevice.
* It does not commit the change to the hardware.
*
* You might want to call something like m_mixdevice->mixer()->commitVolumeChange(m_mixdevice); after calling this method.
*/
void MixDevice::increaseOrDecreaseVolume(bool decrease, Volume::VolumeTypeFlag volumeType)
{
bool debugme = false;
// bool debugme = id() == "PCM:0" ;
if (volumeType & Volume::Playback)
{
Volume& volP = playbackVolume();
long inc = volP.volumeStep(decrease);
if (debugme)
kDebug() << ( decrease ? "decrease by " : "increase by " ) << inc ;
if (!decrease && isMuted())
{
// increasing from muted state: unmute and start with a low volume level
if (debugme)
kDebug() << "set all to " << inc << "muted old=" << isMuted();
setMuted(false);
volP.setAllVolumes(inc);
}
else
{
volP.changeAllVolumes(inc);
if (debugme)
kDebug() << (decrease ? "decrease by " : "increase by ") << inc;
}
}
if (volumeType & Volume::Capture)
{
if (debugme)
kDebug() << "VolumeType=" << volumeType << " c";
Volume& volC = captureVolume();
long inc = volC.volumeStep(decrease);
volC.changeAllVolumes(inc);
}
}
/**
* Returns the name of the config group
* @param Prefix of the group, e.g. "View_ALSA_USB_01"
* @returns The config group name in the format "prefix.mixerId,controlId"
*/
QString MixDevice::configGroupName(QString prefix)
{
QString devgrp = QString("%1.%2.%3").arg(prefix).arg(mixer()->id()).arg(id());
return devgrp;
}
QString MixDevice::getFullyQualifiedId()
{
QString fqId = QString("%1@%2").arg(_id).arg(_mixer->id());
return fqId;
}
void MixDevice::addPlaybackVolume(Volume &playbackVol)
{
// Hint: "_playbackVolume" gets COPIED from "playbackVol", because the copy-constructor actually copies the volume levels.
_playbackVolume = playbackVol;
_playbackVolume.setSwitchType(Volume::PlaybackSwitch);
}
void MixDevice::addCaptureVolume (Volume &captureVol)
{
_captureVolume = captureVol;
_captureVolume.setSwitchType(Volume::CaptureSwitch);
}
void MixDevice::addEnums(QList<QString*>& ref_enumList)
{
if ( ref_enumList.count() > 0 ) {
int maxEnumId = ref_enumList.count();
for (int i=0; i<maxEnumId; i++ ) {
// we have an enum. Lets set the names of the enum items in the MixDevice
// the enum names are assumed to be static!
_enumValues.append( *(ref_enumList.at(i)) );
}
}
_enumCurrentId = 0; // default is first entry (used if we don't get a value via backend or volume restore)
}
MixDevice::~MixDevice()
{
_enumValues.clear(); // The QString's inside will be auto-deleted, as they get unref'ed
delete _dbusControlWrapper;
}
Volume& MixDevice::playbackVolume()
{
return _playbackVolume;
}
Volume& MixDevice::captureVolume()
{
return _captureVolume;
}
void MixDevice::setEnumId(int enumId)
{
if ( enumId < _enumValues.count() ) {
_enumCurrentId = enumId;
}
}
unsigned int MixDevice::enumId()
{
return _enumCurrentId;
}
QList<QString>& MixDevice::enumValues() {
return _enumValues;
}
const QString& MixDevice::id() const {
return _id;
}
const QString MixDevice::dbusPath() {
QString controlPath = _id;
controlPath.replace(QRegExp("[^a-zA-Z0-9_]"), "_");
controlPath.replace(QLatin1String("//"), QLatin1String("/"));
if ( controlPath.endsWith( '/' ) )
{
controlPath.chop(1);
}
return _mixer->dbusPath() + '/' + controlPath;
}
bool MixDevice::isMuted() { return ! _playbackVolume.isSwitchActivated(); }
/**
* Returns whether this MixDevice is virtually muted. Only MixDevice objects w/o a physical switch can be muted virtually.
*/
bool MixDevice::isVirtuallyMuted()
{
return !hasPhysicalMuteSwitch() && isMuted();
}
void MixDevice::setMuted(bool mute) { _playbackVolume.setSwitch(!mute); }
void MixDevice::toggleMute() { setMuted( _playbackVolume.isSwitchActivated()); }
bool MixDevice::hasMuteSwitch() { return playbackVolume().hasVolume() || playbackVolume().hasSwitch(); }
bool MixDevice::hasPhysicalMuteSwitch() { return playbackVolume().hasSwitch(); }
bool MixDevice::isRecSource() { return ( _captureVolume.hasSwitch() && _captureVolume.isSwitchActivated() ); }
bool MixDevice::isNotRecSource() { return ( _captureVolume.hasSwitch() && !_captureVolume.isSwitchActivated() ); }
void MixDevice::setRecSource(bool value) { _captureVolume.setSwitch( value ); }
bool MixDevice::isEnum() { return ( ! _enumValues.empty() ); }
int MixDevice::mediaPlay() { return mixer()->mediaPlay(_id); }
int MixDevice::mediaPrev() { return mixer()->mediaPrev(_id); }
int MixDevice::mediaNext() { return mixer()->mediaNext(_id); }
bool MixDevice::operator==(const MixDevice& other) const
{
return ( _id == other._id );
}
void MixDevice::setControlProfile(ProfControl* control)
{
_profControl = control;
}
ProfControl* MixDevice::controlProfile() {
return _profControl;
}
/**
* This method is currently only called on "kmixctrl --restore"
*
* Normally we have a working _volume object already, which is very important,
* because we need to read the minimum and maximum volume levels.
* (Another solution would be to "equip" volFromConfig with maxInt and minInt values).
*/
bool MixDevice::read( KConfig *config, const QString& grp )
{
if ( _mixer->isDynamic() || isArtificial() ) {
kDebug(67100) << "MixDevice::read(): This MixDevice does not permit volume restoration (i.e. because it is handled lower down in the audio stack). Ignoring.";
return false;
}
QString devgrp = QString("%1.Dev%2").arg(grp).arg(_id);
KConfigGroup cg = config->group( devgrp );
//kDebug(67100) << "MixDevice::read() of group devgrp=" << devgrp;
readPlaybackOrCapture(cg, false);
readPlaybackOrCapture(cg, true );
bool mute = cg.readEntry("is_muted", false);
setMuted( mute );
bool recsrc = cg.readEntry("is_recsrc", false);
setRecSource( recsrc );
int enumId = cg.readEntry("enum_id", -1);
if ( enumId != -1 ) {
setEnumId( enumId );
}
return true;
}
void MixDevice::readPlaybackOrCapture(const KConfigGroup& config, bool capture)
{
Volume& volume = capture ? captureVolume() : playbackVolume();
for ( Volume::ChannelID chid=Volume::CHIDMIN; chid<= Volume::CHIDMAX; )
{
QString volstr = getVolString(chid,capture);
if ( config.hasKey(volstr) ) {
volume.setVolume(chid, config.readEntry(volstr, 0));
} // if saved channel exists
chid = (Volume::ChannelID)( 1 + (int)chid); // ugly
} // for all channels
}
/**
* called on "kmixctrl --save" and from the GUI's (currently only on exit)
*/
bool MixDevice::write( KConfig *config, const QString& grp )
{
if (_mixer->isDynamic() || isArtificial()) {
kDebug(67100) << "MixDevice::write(): This MixDevice does not permit volume saving (i.e. because it is handled lower down in the audio stack). Ignoring.";
return false;
}
QString devgrp = QString("%1.Dev%2").arg(grp).arg(_id);
KConfigGroup cg = config->group(devgrp);
// kDebug(67100) << "MixDevice::write() of group devgrp=" << devgrp;
writePlaybackOrCapture(cg, false);
writePlaybackOrCapture(cg, true );
cg.writeEntry("is_muted" , isMuted() );
cg.writeEntry("is_recsrc", isRecSource() );
cg.writeEntry("name", _name);
if ( isEnum() ) {
cg.writeEntry("enum_id", enumId() );
}
return true;
}
void MixDevice::writePlaybackOrCapture(KConfigGroup& config, bool capture)
{
Volume& volume = capture ? captureVolume() : playbackVolume();
foreach (VolumeChannel vc, volume.getVolumes() )
{
config.writeEntry(getVolString(vc.chid,capture) , (int)vc.volume);
} // for all channels
}
QString MixDevice::getVolString(Volume::ChannelID chid, bool capture)
{
QString volstr (Volume::ChannelNameForPersistence[chid]);
if ( capture ) volstr += "Capture";
return volstr;
}
/**
* Returns the playback volume level in percent. If the volume is muted, 0 is returned.
* If the given MixDevice contains no playback volume, the capture volume isd used
* instead, and 0 is returned if capturing is disabled for the given MixDevice.
*
* @returns The volume level in percent
*/
int MixDevice::getUserfriendlyVolumeLevel()
{
MixDevice* md = this;
bool usePlayback = md->playbackVolume().hasVolume();
Volume& vol = usePlayback ? md->playbackVolume() : md->captureVolume();
bool isActive = usePlayback ? !md->isMuted() : md->isRecSource();
int val = isActive ? vol.getAvgVolumePercent(Volume::MALL) : 0;
return val;
}
#include "moc_mixdevice.cpp"

View file

@ -1,254 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MixDevice_h
#define MixDevice_h
#include <memory>
//KMix
#include "core/MediaController.h"
#include "core/volume.h"
class Mixer;
class MixSet;
class ProfControl;
class DBusControlWrapper;
// KDE
#include <kconfig.h>
#include <kconfiggroup.h>
// Qt
#include <QList>
#include <QObject>
#include <QString>
/**
* This is the abstraction of a single control of a sound card, e.g. the PCM control. A control
* can contain the 5 following subcontrols: playback-volume, capture-volume, playback-switch,
* capture-switch and enumeration.
The class is called MixDevice for historical reasons. Today it is just the Synonym for "Control".
Design hint: In the past I (esken) considered merging the MixDevice and Volume classes.
I finally decided against it, as it seems better to have the MixDevice being the container
for the embedded subcontrol(s). These could be either Volume, Enum or some virtual MixDevice.
*/
class MixDevice : public QObject
{
Q_OBJECT
public:
// For each ChannelType a special icon exists
enum ChannelType { AUDIO = 1,
BASS,
CD,
EXTERNAL,
MICROPHONE,
MIDI,
RECMONITOR,
TREBLE,
UNKNOWN,
VOLUME,
VIDEO,
SURROUND,
HEADPHONE,
DIGITAL,
AC97,
SURROUND_BACK,
SURROUND_LFE,
SURROUND_CENTERFRONT,
SURROUND_CENTERBACK,
SPEAKER,
MICROPHONE_BOOST,
MICROPHONE_FRONT_BOOST,
MICROPHONE_FRONT,
KMIX_COMPOSITE,
APPLICATION_STREAM,
// Some specific applications
APPLICATION_AMAROK,
APPLICATION_BANSHEE,
APPLICATION_XMM2,
APPLICATION_TOMAHAWK,
APPLICATION_CLEMENTINE,
// Hint: VLC still has compatibility problems:
// 2.0 is not detected
// 2.2-nightly has volume issues (total overdrive)
APPLICATION_VLC,
};
enum SwitchType { OnOff, Mute, Capture, Activator };
/**
* Constructor for a MixDevice.
* After having constructed a MixDevice, you <b>must</b> add it to the ControlPool
* by calling addToPool(). You may then <b>not</b> delete this object.
*
* @par mixer The mixer this control belongs to
* @par id Defines the ID, e.g. used in looking up the keys in kmixrc. Also it is used heavily inside KMix as unique key.
* It is advised to set a nice name, like 'PCM:2', which would mean
* "2nd PCM device of the sound card". The ID's may NOT contain whitespace.
* The Creator (normally the backend) MUST pass distinct ID's for each MixDevices of one card.
*
* Virtual Controls (controls not created by a backend) are prefixed with "KMix::", e.g.
* "KMix::RecSelector:0"
* @par name is the readable name. This one is presented to the user in the GUI
* @par type The control type. It is only used to find an appropriate icon
*/
MixDevice( Mixer* mixer, const QString& id, const QString& name, ChannelType type );
MixDevice( Mixer* mixer, const QString& id, const QString& name, const QString& iconName = "", MixSet* moveDestinationMixSet = 0 );
~MixDevice();
void close();
std::shared_ptr<MixDevice> addToPool();
const QString& iconName() const { return _iconName; }
void addPlaybackVolume(Volume &playbackVol);
void addCaptureVolume (Volume &captureVol);
void addEnums (QList<QString*>& ref_enumList);
// Media controls. New for KMix 4.0
MediaController* getMediaController();
// TODO move all media player controls to the MediaController class
int mediaPlay();
int mediaPrev();
int mediaNext();
// Returns a user readable name of the control.
QString readableName() { return _name; }
// Sets a user readable name for the control.
void setReadableName(QString& name) { _name = name; }
QString configGroupName(QString prefix);
/**
* Returns an ID of this MixDevice, as passed in the constructor. The Creator (normally the backend)
* MUST ensure that all MixDevices's of one card have unique ID's.
* The ID is used through the whole KMix application (including the config file) for identifying controls.
*/
const QString& id() const;
QString getFullyQualifiedId();
/**
* Returns the DBus path for this MixDevice
*/
const QString dbusPath();
// Returns the associated mixer
Mixer* mixer() { return _mixer; }
// operator==() is used currently only for duplicate detection with QList's contains() method
bool operator==(const MixDevice& other) const;
// Methods for handling the switches. This methods are useful, because the Switch in the Volume object
// is an abstract concept. It places no interpretation on the meaning of the switch (e.g. does "switch set" mean
// "mute on", or does it mean "playback on", or "Capture active", or ...
virtual bool isMuted();
virtual bool isVirtuallyMuted();
virtual void setMuted(bool value);
virtual bool hasMuteSwitch();
virtual void toggleMute();
virtual bool isRecSource();
virtual bool isNotRecSource();
virtual void setRecSource(bool value);
virtual bool isEnum();
/**
* Returns whether this is an application stream.
*/
virtual bool isApplicationStream() const { return _applicationStream; };
/**
* Mark this MixDevice as application stream
*/
void setApplicationStream(bool applicationStream) { _applicationStream = applicationStream; }
bool isMovable() const
{
return (0 != _moveDestinationMixSet);
}
MixSet *getMoveDestinationMixSet() const
{
return _moveDestinationMixSet;
}
bool isArtificial() const
{
return _artificial;
}
void setArtificial(bool artificial)
{
_artificial = artificial;
}
void setControlProfile(ProfControl* control);
ProfControl* controlProfile();
virtual Volume& playbackVolume();
virtual Volume& captureVolume();
void setEnumId(int);
unsigned int enumId();
QList<QString>& enumValues();
bool hasPhysicalMuteSwitch();
bool read( KConfig *config, const QString& grp );
bool write( KConfig *config, const QString& grp );
int getUserfriendlyVolumeLevel();
void increaseOrDecreaseVolume(bool decrease, Volume::VolumeTypeFlag volumeType);
protected:
void init( Mixer* mixer, const QString& id, const QString& name, const QString& iconName, MixSet* moveDestinationMixSet );
private:
QString getVolString(Volume::ChannelID chid, bool capture);
Mixer *_mixer;
Volume _playbackVolume;
Volume _captureVolume;
int _enumCurrentId;
QList<QString> _enumValues; // A MixDevice, that is an ENUM, has these _enumValues
DBusControlWrapper *_dbusControlWrapper;
MediaController* mediaController;
// A virtual control. It will not be saved/restored and/or doesn't get shortcuts
// Actually we discriminate those "virtual" controls in artificial controls and dynamic controls:
// Type Shortcut Restore
// Artificial: yes no Virtual::GlobalMaster or Virtual::CaptureGroup_3 (controls that are constructed artificially from other controls)
// Dynamic : no no Controls that come and go, like Pulse Stream controls
bool _artificial;
MixSet *_moveDestinationMixSet;
QString _iconName;
bool _applicationStream;
QString _name; // Channel name
QString _id; // Primary key, used as part in config file keys
ProfControl *_profControl;
void readPlaybackOrCapture(const KConfigGroup& config, bool capture);
void writePlaybackOrCapture(KConfigGroup& config, bool capture);
};
#endif

View file

@ -1,159 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2010 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "core/mixdevicecomposite.h"
const long MixDeviceComposite::VolMax = 10000;
MixDeviceComposite::MixDeviceComposite( Mixer* mixer, const QString& id, QList<std::shared_ptr<MixDevice> >& mds, const QString& name, ChannelType type ) :
MixDevice( mixer, id, name, type ) // this will use doNotRestore == true
{
setArtificial(true);
_compositePlaybackVolume = new Volume( MixDeviceComposite::VolMax, 0, true, false);
_compositePlaybackVolume->addVolumeChannel(Volume::LEFT);
_compositePlaybackVolume->addVolumeChannel(Volume::RIGHT);
QListIterator<std::shared_ptr<MixDevice> > it(mds);
while ( it.hasNext()) {
std::shared_ptr<MixDevice> md = it.next();
_mds.append(md);
}
}
MixDeviceComposite::~MixDeviceComposite()
{
while ( ! _mds.empty() ) {
_mds.removeAt(0);
}
delete _compositePlaybackVolume;
// delete _compositeCaptureVolume;
}
Volume& MixDeviceComposite::playbackVolume()
{
return *_compositePlaybackVolume;
}
// Volume& MixDeviceComposite::captureVolume()
// {
// return *_compositeCaptureVolume;
// }
void MixDeviceComposite::update()
{
long volAvg;
volAvg = calculateVolume( Volume::PlaybackVT );
_compositePlaybackVolume->setAllVolumes(volAvg);
volAvg = calculateVolume( Volume::CaptureVT );
// _compositeCaptureVolume->setAllVolumes(volAvg);
}
long MixDeviceComposite::calculateVolume(Volume::VolumeType vt)
{
QListIterator<std::shared_ptr<MixDevice> > it(_mds);
long volSum = 0;
int volCount = 0;
while ( it.hasNext())
{
std::shared_ptr<MixDevice> md = it.next();
Volume& vol = ( vt == Volume::CaptureVT ) ? md->captureVolume() : md->playbackVolume();
if (vol.hasVolume() && (vol.maxVolume() != 0) ) {
qreal normalizedVolume =
( vol.getAvgVolumePercent(Volume::MALL) * MixDeviceComposite::VolMax )
/ vol.maxVolume();
volSum += normalizedVolume;
++volCount;
}
}
if ( volCount == 0 )
return 0;
else
return (volSum/volCount);
}
bool MixDeviceComposite::isMuted()
{
bool isMuted = false;
QListIterator<std::shared_ptr<MixDevice> > it(_mds);
while ( it.hasNext()) {
std::shared_ptr<MixDevice> md = it.next();
isMuted |= md->isMuted();
if ( isMuted ) break; // Enough. It can't get more true :-)
}
return isMuted;
}
void MixDeviceComposite::setMuted(bool value)
{
QListIterator<std::shared_ptr<MixDevice> > it(_mds);
while ( it.hasNext()) {
std::shared_ptr<MixDevice> md = it.next();
md->setMuted(value);
}
}
bool MixDeviceComposite::isRecSource()
{
bool isRecSource = false;
QListIterator<std::shared_ptr<MixDevice> > it(_mds);
while ( it.hasNext()) {
std::shared_ptr<MixDevice> md = it.next();
isRecSource |= md->isRecSource();
if ( isRecSource ) break; // Enough. It can't get more true :-)
}
return isRecSource;
}
void MixDeviceComposite::setRecSource(bool value)
{
QListIterator<std::shared_ptr<MixDevice> > it(_mds);
while ( it.hasNext()) {
std::shared_ptr<MixDevice> md = it.next();
md->setRecSource(value);
}
}
bool MixDeviceComposite::isEnum()
{
bool isEnum = true;
QListIterator<std::shared_ptr<MixDevice> > it(_mds);
while ( it.hasNext()) {
std::shared_ptr<MixDevice> md = it.next();
isEnum &= md->isEnum();
if ( ! isEnum ) break; // Enough. It can't get more false :-)
}
return isEnum;
}
#include "moc_mixdevicecomposite.cpp"

View file

@ -1,109 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MixDeviceComposite_h
#define MixDeviceComposite_h
//KMix
class Mixer;
class MixSet;
#include "core/mixdevice.h"
#include "core/volume.h"
// KDE
#include <kconfig.h>
#include <kconfiggroup.h>
// Qt
#include <QList>
#include <QObject>
#include <QString>
// !!! This SHOULD be subclassed (MixDeviceVolume, MixDeviceEnum).
// The isEnum() works out OK as a workaround, but it is insane
// in the long run.
// Additionally there might be Implementations for virtual MixDevice's, e.g.
// MixDeviceRecselector, MixDeviceCrossfader.
// I am not sure if a MixDeviceBalancing would work out.
/**
* This is the abstraction of a single control of a sound card, e.g. the PCM control. A control
* can contain the 5 following subcontrols: playback-volume, capture-volume, playback-switch,
* capture-switch and enumeration.
The class is called MixDevice for historical reasons. Today it is just the Synonym for "Control".
Design hint: In the past I (esken) considered merging the MixDevice and Volume classes.
I finally decided against it, as it seems better to have the MixDevice being the container
for the embedded subcontrol(s). These could be either Volume, Enum or some virtual MixDevice.
*/
class MixDeviceComposite : public MixDevice
{
Q_OBJECT
public:
/**
* Constructor:
* @par mixer The mixer this control belongs to
* @par id Defines the ID, e.g. used in looking up the keys in kmixrc. Also it is used heavily inside KMix as unique key.
* It is advised to set a nice name, like 'PCM:2', which would mean
* "2nd PCM device of the sound card". The ID's may NOT contain whitespace.
* The Creator (normally the backend) MUST pass distinct ID's for each MixDevices of one card.
*
* Virtual Controls (controls not created by a backend) are prefixed with "KMix::", e.g.
* "KMix::RecSelector:0"
* @par name is the readable name. This one is presented to the user in the GUI
* @par type The control type. It is only used to find an appropriate icon
*/
MixDeviceComposite( Mixer* mixer, const QString& id, QList<std::shared_ptr<MixDevice> >& mds, const QString& name, ChannelType type );
// MixDevice( Mixer* mixer, const QString& id, const QString& name, const QString& iconName = "", bool doNotRestore = false, MixSet* moveDestinationMixSet = 0 );
~MixDeviceComposite();
// Methods for handling the switches. This methods are useful, because the Sswitch in the Volume object
// is an abstract concept. It places no interpration on the meaning of the switch (e.g. does "switch set" mean
// "mute on", or does it mean "playback on".
virtual bool isMuted();
virtual void setMuted(bool value);
virtual bool isRecSource();
virtual void setRecSource(bool value);
virtual bool isEnum();
// Refresh the composite from its components
void update();
virtual Volume& playbackVolume();
//virtual Volume& captureVolume();
private:
long calculateVolume(Volume::VolumeType vt);
QList<std::shared_ptr<MixDevice> > _mds;
static const long VolMax;
Volume* _compositePlaybackVolume;
// Volume* _compositeCaptureVolume;
};
#endif

View file

@ -1,704 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2004 Christian Esken - esken@kde.org
* 2002 Helio Chissini de Castro - helio@conectiva.com.br
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "core/mixer.h"
#include <klocale.h>
#include <kconfig.h>
#include <kglobal.h>
#include <kdebug.h>
#include "backends/mixer_backend.h"
#include "backends/kmix-backends.cpp"
#include "core/ControlManager.h"
#include "core/GlobalConfig.h"
#include "core/volume.h"
/**
* Some general design hints. Hierachy is Mixer->MixDevice->Volume
*/
QList<Mixer *> Mixer::s_mixers;
MasterControl Mixer::_globalMasterCurrent;
MasterControl Mixer::_globalMasterPreferred;
int Mixer::numDrivers()
{
MixerFactory *factory = g_mixerFactories;
int num = 0;
while( factory->getMixer!=0 )
{
num++;
factory++;
}
return num;
}
/*
* Returns a reference of the current mixer list.
*/
QList<Mixer *>& Mixer::mixers()
{
return s_mixers;
}
/**
* Returns whether there is at least one dynamic mixer active.
* @returns true, if at least one dynamic mixer is active
*/
bool Mixer::dynamicBackendsPresent()
{
foreach ( Mixer* mixer, Mixer::mixers() )
{
if ( mixer->isDynamic() )
return true;
}
return false;
}
Mixer::Mixer( QString& ref_driverName, int device )
: m_balance(0), _mixerBackend(0L), m_dynamic(false)
{
_mixerBackend = 0;
int driverCount = numDrivers();
for (int driver=0; driver<driverCount; driver++ ) {
QString driverName = Mixer::driverName(driver);
if ( driverName == ref_driverName ) {
// driver found => retrieve Mixer factory for that driver
getMixerFunc *f = g_mixerFactories[driver].getMixer;
if( f!=0 ) {
_mixerBackend = f( this, device );
readSetFromHWforceUpdate(); // enforce an initial update on first readSetFromHW()
}
break;
}
}
}
Mixer::~Mixer() {
// Close the mixer. This might also free memory, depending on the called backend method
close();
delete _mixerBackend;
}
/*
* Find a Mixer. If there is no mixer with the given id, 0 is returned
*/
Mixer* Mixer::findMixer( const QString& mixer_id)
{
Mixer *mixer = 0;
int mixerCount = Mixer::mixers().count();
for ( int i=0; i<mixerCount; ++i)
{
if ( ((Mixer::mixers())[i])->id() == mixer_id )
{
mixer = (Mixer::mixers())[i];
break;
}
}
return mixer;
}
///**
// * Set the card instance. Usually this will be 1, but if there is
// * more than one card with the same name install, then you need
// * to use 2, 3, ...
// */
//void Mixer::setCardInstance(int cardInstance)
//{
// _cardInstance = cardInstance;
// recreateId();
// // DBusMixerWrapper must be called after recreateId(), as it uses the id
// new DBusMixerWrapper(this, dbusPath());
//}
/**
* Set the final ID of this Mixer.
* <br>Warning: This method is VERY fragile, because it is requires information that we have very late,
* especially the _cardInstance. We only know the _cardInstance, when we know the ID of the _mixerBackend->getId().
* OTOH, the Mixer backend needs the _cardInstance during construction of its MixDevice instances.
*
* This means, we need the _cardInstance during construction of the Mixer, but we only know it after its constructed.
* Actually its a design error. The _cardInstance MUST be set and managed by the backend.
*
* The current solution works but is very hacky - cardInstance is a parameter of openIfValid().
*
*/
void Mixer::recreateId()
{
/* As we use "::" and ":" as separators, the parts %1,%2 and %3 may not
* contain it.
* %1, the driver name is from the KMix backends, it does not contain colons.
* %2, the mixer name, is typically coming from an OS driver. It could contain colons.
* %3, the mixer number, is a number: it does not contain colons.
*/
QString mixerName = _mixerBackend->getId();
mixerName.replace(':','_');
QString primaryKeyOfMixer = QString("%1::%2:%3")
.arg(getDriverName())
.arg(mixerName)
.arg(getCardInstance());
// The following 3 replaces are for not messing up the config file
primaryKeyOfMixer.replace(']','_');
primaryKeyOfMixer.replace('[','_'); // not strictly necessary, but lets play safe
primaryKeyOfMixer.replace(' ','_');
primaryKeyOfMixer.replace('=','_');
_id = primaryKeyOfMixer;
kDebug() << "Early _id=" << _id;
}
const QString Mixer::dbusPath()
{
// _id needs to be fixed from the very beginning, as the MixDevice construction uses MixDevice::dbusPath().
// So once the first MixDevice is created, this must return the correct value
if (_id.isEmpty())
{
// Bug 308014: This a rather dirty hack, but it will guarantee that _id is definitely set.
// Even the _cardInstance is set at default value during construction of the MixDevice instances
recreateId();
}
// kDebug() << "Late _id=" << _id;
// kDebug() << "handMade=" << QString("/Mixers/" + getDriverName() + "." + _mixerBackend->getId()).replace(" ", "x").replace(".", "_");
// mixerName may contain arbitrary characters, so replace all that are not allowed to be be part of a DBUS path
QString cardPath = _id;
cardPath.replace(QRegExp("[^a-zA-Z0-9_]"), "_");
cardPath.replace(QLatin1String("//"), QLatin1String("/"));
return QString("/Mixers/" + cardPath);
}
void Mixer::volumeSave( KConfig *config )
{
// kDebug(67100) << "Mixer::volumeSave()";
_mixerBackend->readSetFromHW();
QString grp("Mixer");
grp.append(id());
_mixerBackend->m_mixDevices.write( config, grp );
}
void Mixer::volumeLoad( KConfig *config )
{
QString grp("Mixer");
grp.append(id());
if ( ! config->hasGroup(grp) ) {
// no such group. Volumes (of this mixer) were never saved beforehand.
// Thus don't restore anything (also see Bug #69320 for understanding the real reason)
return; // make sure to bail out immediately
}
// else restore the volumes
if ( ! _mixerBackend->m_mixDevices.read( config, grp ) ) {
// Some mixer backends don't support reading the volume into config
// files, so bail out early if that's the case.
return;
}
// set new settings
for(int i=0; i<_mixerBackend->m_mixDevices.count() ; i++ )
{
std::shared_ptr<MixDevice> md = _mixerBackend->m_mixDevices[i];
if ( md.get() == 0 )
continue;
_mixerBackend->writeVolumeToHW( md->id(), md );
if ( md->isEnum() )
_mixerBackend->setEnumIdHW( md->id(), md->enumId() );
}
}
/**
* Opens the mixer.
* Also, starts the polling timer, for polling the Volumes from the Mixer.
*
* @param cardId The cardId Usually this will be 1, but if there is
* more than one card with the same name install, then you need
* to use 2, 3, ...
*
* @return true, if Mixer could be opened.
*/
bool Mixer::openIfValid()
{
if (_mixerBackend == 0 )
{
// if we did not instantiate a suitable Backend, then Mixer is invalid
return false;
}
bool ok = _mixerBackend->openIfValid();
if ( ok )
{
recreateId();
std::shared_ptr<MixDevice> recommendedMaster = _mixerBackend->recommendedMaster();
if ( recommendedMaster.get() != 0 )
{
QString recommendedMasterStr = recommendedMaster->id();
setLocalMasterMD( recommendedMasterStr );
kDebug() << "Mixer::open() detected master: " << recommendedMaster->id();
}
else
{
if ( !m_dynamic )
kError(67100) << "Mixer::open() no master detected.";
QString noMaster = "---no-master-detected---";
setLocalMasterMD(noMaster); // no master
}
// cesken: The following connect() looks mighty strange. I removed it on 2013-12-18
//connect( _mixerBackend, SIGNAL(controlChanged()), SIGNAL(controlChanged()) );
new DBusMixerWrapper(this, dbusPath());
}
return ok;
}
/**
* Closes the mixer.
*/
void Mixer::close()
{
if ( _mixerBackend != 0)
_mixerBackend->closeCommon();
}
/* ------- WRAPPER METHODS. START ------------------------------ */
unsigned int Mixer::size() const
{
return _mixerBackend->m_mixDevices.count();
}
std::shared_ptr<MixDevice> Mixer::operator[](int num)
{
std::shared_ptr<MixDevice> md = _mixerBackend->m_mixDevices.at( num );
return md;
}
MixSet& Mixer::getMixSet()
{
return _mixerBackend->m_mixDevices;
}
/**
* Returns the driver name, that handles this Mixer.
*/
QString Mixer::getDriverName()
{
QString driverName = _mixerBackend->getDriverName();
// kDebug(67100) << "Mixer::getDriverName() = " << driverName << "\n";
return driverName;
}
bool Mixer::isOpen() const {
if ( _mixerBackend == 0 )
return false;
else
return _mixerBackend->isOpen();
}
void Mixer::readSetFromHWforceUpdate() const {
_mixerBackend->readSetFromHWforceUpdate();
}
/// Returns translated WhatsThis messages for a control.Translates from
QString Mixer::translateKernelToWhatsthis(const QString &kernelName)
{
return _mixerBackend->translateKernelToWhatsthis(kernelName);
}
/* ------- WRAPPER METHODS. END -------------------------------- */
int Mixer::balance() const {
return m_balance;
}
void Mixer::setBalance(int balance)
{
if( balance == m_balance ) {
// balance unchanged => return
return;
}
m_balance = balance;
std::shared_ptr<MixDevice> master = getLocalMasterMD();
if ( master.get() == 0 )
{
// no master device available => return
return;
}
Volume& volP = master->playbackVolume();
setBalanceInternal(volP);
Volume& volC = master->captureVolume();
setBalanceInternal(volC);
_mixerBackend->writeVolumeToHW( master->id(), master );
emit newBalance( volP );
}
void Mixer::setBalanceInternal(Volume& vol)
{
//_mixerBackend->readVolumeFromHW( master->id(), master );
int left = vol.getVolume(Volume::LEFT);
int right = vol.getVolume( Volume::RIGHT );
int refvol = left > right ? left : right;
if( m_balance < 0 ) // balance left
{
vol.setVolume( Volume::LEFT, refvol);
vol.setVolume( Volume::RIGHT, (m_balance * refvol) / 100 + refvol );
}
else
{
vol.setVolume( Volume::LEFT, -(m_balance * refvol) / 100 + refvol );
vol.setVolume( Volume::RIGHT, refvol);
}
}
/**
* Returns a name suitable for a human user to read (on a label, ...)
*/
QString Mixer::readableName()
{
return readableName(false);
}
/**
* Returns a name suitable for a human user to read, possibly with quoted ampersand. The latter is required by
* some GUI elements like QRadioButton or when used as a Tab label, as '&' introduces an accelerator there.
*
* @param ampersandQuoted
* @return
*/
QString Mixer::readableName(bool ampersandQuoted)
{
QString finalName = _mixerBackend->getName();
if (ampersandQuoted)
finalName.replace('&', "&&");
if ( getCardInstance() > 1)
finalName = finalName.append(" %1").arg(getCardInstance());
// kDebug() << "name=" << _mixerBackend->getName() << "instance=" << getCardInstance() << ", finalName" << finalName;
return finalName;
}
QString Mixer::getBaseName()
{
return _mixerBackend->getName();
}
/**
* Queries the Driver Factory for a driver.
* @par driver Index number. 0 <= driver < numDrivers()
*/
QString Mixer::driverName( int driver )
{
getDriverNameFunc *f = g_mixerFactories[driver].getDriverName;
if( f!=0 )
return f();
else
return "unknown";
}
/* obsoleted by setInstance()
void Mixer::setID(QString& ref_id)
{
_id = ref_id;
}
*/
QString& Mixer::id()
{
return _id;
}
QString& Mixer::udi(){
return _mixerBackend->udi();
}
/**
* Set the global master, which is shown in the dock area and which is accessible via the
* DBUS masterVolume() method.
*
* The parameters are taken over as-is, this means without checking for validity.
* This allows the User to define a master card that is not always available
* (e.g. it is an USB hotplugging device). Also you can set the master at any time you
* like, e.g. after reading the KMix configuration file and before actually constructing
* the Mixer instances (hint: this method is static!).
*
* @param ref_card The card id
* @param ref_control The control id. The corresponding control must be present in the card.
* @param preferred Whether this is the preferred master (auto-selected on coldplug and hotplug).
*/
void Mixer::setGlobalMaster(QString ref_card, QString ref_control, bool preferred)
{
kDebug() << "ref_card=" << ref_card << ", ref_control=" << ref_control << ", preferred=" << preferred;
_globalMasterCurrent.set(ref_card, ref_control);
if ( preferred )
_globalMasterPreferred.set(ref_card, ref_control);
kDebug() << "Mixer::setGlobalMaster() card=" <<ref_card<< " control=" << ref_control;
}
Mixer* Mixer::getGlobalMasterMixerNoFalback()
{
foreach ( Mixer* mixer, Mixer::mixers())
{
if ( mixer != 0 && mixer->id() == _globalMasterCurrent.getCard() )
return mixer;
}
return 0;
}
Mixer* Mixer::getGlobalMasterMixer()
{
Mixer *mixer = getGlobalMasterMixerNoFalback();
if ( mixer == 0 && Mixer::mixers().count() > 0 ) {
mixer = Mixer::mixers()[0]; // produce fallback
}
//kDebug() << "Mixer::masterCard() returns " << mixer->id();
return mixer;
}
/**
* Return the preferred global master.
* If there is no preferred global master, returns the current master instead.
*/
MasterControl& Mixer::getGlobalMasterPreferred()
{
if ( _globalMasterPreferred.isValid() ) {
kDebug() << "Returning preferred master";
return _globalMasterPreferred;
}
else {
kDebug() << "Returning current master";
return _globalMasterCurrent;
}
}
std::shared_ptr<MixDevice> Mixer::getGlobalMasterMD()
{
return getGlobalMasterMD(true);
}
std::shared_ptr<MixDevice> Mixer::getGlobalMasterMD(bool fallbackAllowed)
{
std::shared_ptr<MixDevice> mdRet;
std::shared_ptr<MixDevice> firstDevice;
Mixer *mixer = fallbackAllowed ?
Mixer::getGlobalMasterMixer() : Mixer::getGlobalMasterMixerNoFalback();
if ( mixer == 0 )
return mdRet;
foreach (std::shared_ptr<MixDevice> md, mixer->_mixerBackend->m_mixDevices )
{
if ( md.get() == 0 )
continue; // invalid
firstDevice=md;
if ( md->id() == _globalMasterCurrent.getControl() )
{
mdRet = md;
break; // found
}
}
if ( mdRet.get() == 0 )
{
//For some sound cards when using pulseaudio the mixer id is not proper hence returning the first device as master channel device
//This solves the bug id:290177 and problems stated in review #105422
kDebug() << "Mixer::masterCardDevice() returns 0 (no globalMaster), returning the first device";
mdRet=firstDevice;
}
return mdRet;
}
std::shared_ptr<MixDevice> Mixer::getLocalMasterMD()
{
return find( _masterDevicePK );
}
void Mixer::setLocalMasterMD(QString &devPK)
{
_masterDevicePK = devPK;
}
std::shared_ptr<MixDevice> Mixer::find(const QString& mixdeviceID)
{
std::shared_ptr<MixDevice> mdRet;
foreach (std::shared_ptr<MixDevice> md, _mixerBackend->m_mixDevices )
{
if ( md.get() == 0 )
continue; // invalid
if ( md->id() == mixdeviceID )
{
mdRet = md;
break; // found
}
}
return mdRet;
}
std::shared_ptr<MixDevice> Mixer::getMixdeviceById( const QString& mixdeviceID )
{
kDebug() << "id=" << mixdeviceID << "md=" << _mixerBackend->m_mixDevices.get(mixdeviceID).get()->id();
return _mixerBackend->m_mixDevices.get(mixdeviceID);
// std::shared_ptr<MixDevice> md;
// int num = _mixerBackend->id2num(mixdeviceID);
// if ( num!=-1 && num < (int)size() )
// {
// md = (*this)[num];
// }
// return md;
}
/**
Call this if you have a *reference* to a Volume object and have modified that locally.
Pass the MixDevice associated to that Volume to this method for writing back
the changed value to the mixer.
Hint: Why do we do it this way?
- It is fast (no copying of Volume objects required)
- It is easy to understand ( read - modify - commit )
*/
void Mixer::commitVolumeChange(std::shared_ptr<MixDevice> md)
{
_mixerBackend->writeVolumeToHW(md->id(), md);
if (md->isEnum())
{
_mixerBackend->setEnumIdHW(md->id(), md->enumId());
}
if (md->captureVolume().hasSwitch())
{
// Make sure to re-read the hardware, because setting capture might have failed.
// This is due to exclusive capture groups.
// If we wouldn't do this, KMix might show a Capture Switch disabled, but
// in reality the capture switch is still on.
//
// We also cannot rely on a notification from the driver (SocketNotifier), because
// nothing has changed, and so there s nothing to notify.
_mixerBackend->readSetFromHWforceUpdate();
if (GlobalConfig::instance().data.debugControlManager)
kDebug()
<< "committing a control with capture volume, that might announce: " << md->id();
_mixerBackend->readSetFromHW();
}
if (GlobalConfig::instance().data.debugControlManager)
kDebug()
<< "committing announces the change of: " << md->id();
// We announce the change we did, so all other parts of KMix can pick up the change
ControlManager::instance().announce(md->mixer()->id(), ControlChangeType::Volume,
QString("Mixer.commitVolumeChange()"));
}
// @dbus, used also in kmix app
void Mixer::increaseVolume( const QString& mixdeviceID )
{
increaseOrDecreaseVolume(mixdeviceID, false);
}
// @dbus
void Mixer::decreaseVolume( const QString& mixdeviceID )
{
increaseOrDecreaseVolume(mixdeviceID, true);
}
/**
* Increase or decrease all playback and capture channels of the given control.
* This method is very similar to MDWSlider::increaseOrDecreaseVolume(), but it will
* NOT auto-unmute.
*
* @param mixdeviceID The control name
* @param decrease true for decrease. false for increase
*/
void Mixer::increaseOrDecreaseVolume( const QString& mixdeviceID, bool decrease )
{
std::shared_ptr<MixDevice> md= getMixdeviceById( mixdeviceID );
if (md.get() != 0)
{
Volume& volP=md->playbackVolume();
if ( volP.hasVolume() )
{
volP.changeAllVolumes(volP.volumeStep(decrease));
}
Volume& volC=md->captureVolume();
if ( volC.hasVolume() )
{
volC.changeAllVolumes(volC.volumeStep(decrease));
}
_mixerBackend->writeVolumeToHW(mixdeviceID, md);
}
ControlManager::instance().announce(md->mixer()->id(), ControlChangeType::Volume, QString("Mixer.increaseOrDecreaseVolume()"));
/************************************************************
It is important, not to implement this method like this:
int vol=volume(mixdeviceID);
setVolume(mixdeviceID, vol-5);
It creates too big rounding errors. If you don't believe me, then
do a decreaseVolume() and increaseVolume() with "vol.maxVolume() == 31".
***********************************************************/
}
void Mixer::setDynamic ( bool dynamic )
{
m_dynamic = dynamic;
}
bool Mixer::isDynamic()
{
return m_dynamic;
}
bool Mixer::moveStream( const QString id, const QString& destId )
{
// We should really check that id is within our md's....
bool ret = _mixerBackend->moveStream( id, destId );
ControlManager::instance().announce(QString(), ControlChangeType::ControlList, QString("Mixer.moveStream()"));
return ret;
}
#include "moc_mixer.cpp"

View file

@ -1,207 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
* 1996-2000 Christian Esken <esken@kde.org>
* Sven Fischer <herpes@kawo2.rwth-aachen.de>
* 2002 - Helio Chissini de Castro <helio@conectiva.com.br>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef RANDOMPREFIX_MIXER_H
#define RANDOMPREFIX_MIXER_H
#include <QList>
#include <QObject>
#include <QString>
#include "core/volume.h"
#include "backends/mixer_backend.h"
#include "core/MasterControl.h"
#include "mixset.h"
#include "core/mixdevice.h"
#include "dbus/dbusmixerwrapper.h"
class Volume;
class KConfig;
class Mixer : public QObject
{
Q_OBJECT
public:
/**
* Status for Mixer operations.
*
* OK_UNCHANGED is a apecial variant of OK. It must be implemented by
* backends that use needsPolling() == true. See Mixer_OSS.cpp for an
* example. Rationale is that we need a proper change check: Otherwise
* the DBUS Session Bus is massively spammed. Also quite likely the Mixer
* GUI might get updated all the time.
*
*/
enum MixerError { OK=0, ERR_PERM=1, ERR_WRITE, ERR_READ,
ERR_OPEN, OK_UNCHANGED };
Mixer( QString& ref_driverName, int device );
virtual ~Mixer();
static int numDrivers();
QString getDriverName();
std::shared_ptr<MixDevice> find(const QString& devPK);
static Mixer* findMixer( const QString& mixer_id);
void volumeSave( KConfig *config );
void volumeLoad( KConfig *config );
/// Tells the number of the mixing devices
unsigned int size() const;
/// Returns a pointer to the mix device with the given number
// TODO remove this method. Only used by ViewDockAreaPopup: dockMD = (*mixer)[0];
std::shared_ptr<MixDevice> operator[](int val_i_num);
/// Returns a pointer to the mix device whose type matches the value
/// given by the parameter and the array MixerDevNames given in
/// mixer_oss.cpp (0 is Volume, 4 is PCM, etc.)
std::shared_ptr<MixDevice> getMixdeviceById( const QString& deviceID );
/// Open/grab the mixer for further intraction
bool openIfValid();
/// Returns whether the card is open/operational
bool isOpen() const;
/// Close/release the mixer
virtual void close();
/// Reads balance
int balance() const;
/// Returns a detailed state message after errors. Only for diagnostic purposes, no i18n.
QString& stateMessage() const;
/**
* Returns the name of the card/chip/hardware, as given by the driver. The name is NOT instance specific,
* so if you install two identical soundcards, two of them will deliver the same mixerName().
* Use this method if you need an instance-UNspecific name, e.g. for finding an appropriate
* mixer layout for this card, or as a prefix for constructing instance specific ID's like in id().
*/
virtual QString getBaseName();
/// Wrapper to Mixer_Backend
QString translateKernelToWhatsthis(const QString &kernelName);
/// Return the name of the card/chip/hardware, which is suitable for humans
QString readableName();
QString readableName(bool ampersandQuoted);
// Returns the name of the driver, e.g. "OSS" or "ALSA0.9"
static QString driverName(int num);
/**
* Returns an unique ID of the Mixer. It currently looks like "<soundcard_descr>::<hw_number>:<driver>"
*/
QString& id();
int getCardInstance() const { return _mixerBackend->getCardInstance(); }
/// Returns an Universal Device Identifaction of the Mixer. This is an ID that relates to the underlying operating system.
// For OSS and ALSA this is taken from Solid (actually HAL). For Solaris this is just the device name.
// Examples:
// ALSA: /org/freedesktop/Hal/devices/usb_device_d8c_1_noserial_if0_sound_card_0_2_alsa_control__1
// OSS: /org/freedesktop/Hal/devices/usb_device_d8c_1_noserial_if0_sound_card_0_2_oss_mixer__1
// Solaris: /dev/audio
QString& udi();
// Returns a DBus path for this mixer
// Used also by MixDevice to bind to this path
const QString dbusPath();
static QList<Mixer*> & mixers();
/******************************************
The KMix GLOBAL master card. Please note that KMix and KMixPanelApplet can have a
different MasterCard's at the moment (but actually KMixPanelApplet does not read/save this yet).
At the moment it is only used for selecting the Mixer to use in KMix's DockIcon.
******************************************/
static void setGlobalMaster(QString ref_card, QString ref_control, bool preferred);
static std::shared_ptr<MixDevice> getGlobalMasterMD();
static std::shared_ptr<MixDevice> getGlobalMasterMD(bool fallbackAllowed);
static Mixer* getGlobalMasterMixer();
static Mixer* getGlobalMasterMixerNoFalback();
static MasterControl& getGlobalMasterPreferred();
/******************************************
The recommended master of this Mixer.
******************************************/
std::shared_ptr<MixDevice> getLocalMasterMD();
void setLocalMasterMD(QString&);
/// get the actual MixSet
MixSet& getMixSet();
/// DBUS oriented methods
virtual void increaseVolume( const QString& mixdeviceID );
virtual void decreaseVolume( const QString& mixdeviceID );
/// Says if we are dynamic (e.g. widgets can come and go)
virtual void setDynamic( bool dynamic = true );
virtual bool isDynamic();
static bool dynamicBackendsPresent();
virtual bool moveStream( const QString id, const QString& destId );
virtual int mediaPlay(QString id) { return _mixerBackend->mediaPlay(id); };
virtual int mediaPrev(QString id) { return _mixerBackend->mediaPrev(id); };
virtual int mediaNext(QString id) { return _mixerBackend->mediaNext(id); };
void commitVolumeChange( std::shared_ptr<MixDevice> md );
public slots:
void readSetFromHWforceUpdate() const;
virtual void setBalance(int balance); // sets the m_balance (see there)
signals:
void newBalance(Volume& );
void controlChanged(void); // TODO remove?
protected:
int m_balance; // from -100 (just left) to 100 (just right)
static QList<Mixer*> s_mixers;
private:
void setBalanceInternal(Volume& vol);
void recreateId();
void increaseOrDecreaseVolume( const QString& mixdeviceID, bool decrease );
Mixer_Backend *_mixerBackend;
QString _id;
QString _masterDevicePK;
static MasterControl _globalMasterCurrent;
static MasterControl _globalMasterPreferred;
bool m_dynamic;
};
#endif

View file

@ -1,389 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "core/mixer.h"
#include <QDir>
#include <QWidget>
#include <QString>
//#include <kdebug.h>
#include <klocale.h>
#include <kstandarddirs.h>
#include "core/kmixdevicemanager.h"
#include "core/mixdevice.h"
#include "core/mixertoolbox.h"
MixerToolBox* MixerToolBox::s_instance = 0;
QRegExp MixerToolBox::s_ignoreMixerExpression( QLatin1String( "Modem" ));
//KLocale* MixerToolBox::s_whatsthisLocale = 0;
/***********************************************************************************
Attention:
This MixerToolBox is linked to the KMix Main Program, the KMix Applet and kmixctrl.
As we do not want to link in more than necessary to kmixctrl, you are asked
not to put any GUI classes in here.
In the case where it is unavoidable, please put them in KMixToolBox.
***********************************************************************************/
MixerToolBox* MixerToolBox::instance()
{
if ( s_instance == 0 ) {
s_instance = new MixerToolBox();
// if ( s_ignoreMixerExpression.isEmpty() )
// s_ignoreMixerExpression.setPattern("Modem");
}
return s_instance;
}
/**
* Scan for Mixers in the System. This is the method that implicitely fills the
* list of Mixer's, which is accessible via the static Mixer::mixer() method.
*
* This is run only once during the initialization phase of KMix. It has the following tasks:
* 1) Coldplug scan, to fill the initial mixer list
* 2) Rember UDI's, to match them when unplugging a device
* 3) Find out, which Backend to use (plugin events of other Backends are ignored).
*
* @deprecated TODO this method has to go away. Migrate to MultiDriverMode enum
*
* @par multiDriverMode Whether the Mixer scan should try more all backendends.
* 'true' means to scan all backends. 'false' means: After scanning the
* current backend the next backend is only scanned if no Mixers were found yet.
* @par backendList Activated backends (typically a value from the kmixrc or a default)
* @par ref_hwInfoString Here a descripitive text of the scan is returned (Hardware Information)
*/
void MixerToolBox::initMixer(bool multiDriverModeBool, QList<QString> backendList, QString& ref_hwInfoString)
{
MultiDriverMode multiDriverMode = multiDriverModeBool ? MULTI : SINGLE_PLUS_MPRIS2;
initMixer(multiDriverMode, backendList, ref_hwInfoString);
}
void MixerToolBox::initMixer(MultiDriverMode multiDriverMode, QList<QString> backendList, QString& ref_hwInfoString)
{
initMixerInternal(multiDriverMode, backendList, ref_hwInfoString);
if ( Mixer::mixers().isEmpty() )
initMixerInternal(multiDriverMode, QList<QString>(), ref_hwInfoString); // try again without filter
}
/**
*
*/
void MixerToolBox::initMixerInternal(MultiDriverMode multiDriverMode, QList<QString> backendList, QString& ref_hwInfoString)
{
bool useBackendFilter = ( ! backendList.isEmpty() );
bool backendMprisFound = false; // only for SINGLE_PLUS_MPRIS2
bool regularBackendFound = false; // only for SINGLE_PLUS_MPRIS2
kDebug() << "multiDriverMode=" << multiDriverMode << ", backendList=" << backendList;
// Find all mixers and initialize them
int drvNum = Mixer::numDrivers();
int driverWithMixer = -1;
bool multipleDriversActive = false;
QString driverInfo = "";
QString driverInfoUsed = "";
for( int drv1=0; drv1<drvNum; drv1++ )
{
QString driverName = Mixer::driverName(drv1);
if ( driverInfo.length() > 0 ) {
driverInfo += " + ";
}
driverInfo += driverName;
}
/* Run a loop over all drivers. The loop will terminate after the first driver which
has mixers. And here is the reason:
- If you run ALSA with ALSA-OSS-Emulation enabled, mixers will show up twice: once
as native ALSA mixer, once as OSS mixer (emulated by ALSA). This is bad and WILL
confuse users. So it is a design decision that we can compile in multiple drivers
but we can run only one driver.
- For special usage scenarios, people will still want to run both drivers at the
same time. We allow them to hack their Config-File, where they can enable a
multi-driver mode.
- Another remark: For KMix3.0 or so, we should allow multiple-driver, for allowing
addition of special-use drivers, e.g. an Jack-Mixer-Backend, or a CD-Rom volume Backend.
*/
bool autodetectionFinished = false;
for( int drv=0; drv<drvNum; drv++ )
{
if ( autodetectionFinished )
{
// inner loop indicates that we are finished => sane exit from outer loop
break;
}
QString driverName = Mixer::driverName(drv);
kDebug(67100) << "Looking for mixers with the : " << driverName << " driver";
if ( useBackendFilter && ! backendList.contains(driverName) )
{
kDebug() << "Skipping " << driverName << " (filtered)";
continue;
}
bool regularBackend = driverName != "MPRIS2";
if (regularBackend && regularBackendFound)
{
// Only accept one regular backend => skip this one
continue;
}
bool drvInfoAppended = false;
// The "19" below is just a "silly" number:
// (Old: The loop will break as soon as an error is detected - e.g. on 3rd loop when 2 soundcards are installed)
// New: We don't try be that clever anymore. We now blindly scan 20 cards, as the clever
// approach doesn't work for the one or other user (e.g. hotplugging might create holes in the list of soundcards).
int devNumMax = 19;
for( int dev=-1; dev<=devNumMax; dev++ )
{
Mixer *mixer = new Mixer( driverName, dev );
bool mixerAccepted = possiblyAddMixer(mixer);
/* Lets decide if the autoprobing shall end.
* If the user has configured a backend filter, we will use that as a plain list to obey. It overrides the
* multiDriverMode.
*/
if ( ! useBackendFilter )
{
bool foundSomethingAndLastControlReached = dev == devNumMax && ! Mixer::mixers().isEmpty();
switch ( multiDriverMode )
{
case SINGLE:
// In Single-Driver-mode we only need to check after we reached devNumMax
if ( foundSomethingAndLastControlReached )
autodetectionFinished = true; // highest device number of driver and a Mixer => finished
break;
case MULTI:
// In multiDriver mode we scan all devices, so we will simply continue
break;
case SINGLE_PLUS_MPRIS2:
if ( driverName == "MPRIS2" )
{
backendMprisFound = true;
}
else
{
// same check as in SINGLE
if ( foundSomethingAndLastControlReached )
regularBackendFound = true;
}
if ( backendMprisFound && regularBackendFound )
autodetectionFinished = true; // highest device number of driver and a Mixer => finished
break;
}
}
else
{
// Using backend filter. This is a plain list to obey.
// Simply continue (and filter at the start of the loop).
}
if ( mixerAccepted )
{
kDebug(67100) << "Success! Found a mixer with the : " << driverName << " driver";
// append driverName (used drivers)
if ( !drvInfoAppended )
{
drvInfoAppended = true;
if ( Mixer::mixers().count() > 1)
driverInfoUsed += " + ";
driverInfoUsed += driverName;
}
// Check whether there are mixers in different drivers, so that the user can be warned
if ( !multipleDriversActive )
{
if ( driverWithMixer == -1 )
{
// Aha, this is the very first detected device
driverWithMixer = drv;
}
else if ( driverWithMixer != drv )
{
// Got him: There are mixers in different drivers
multipleDriversActive = true;
}
} // !multipleDriversActive
} // mixerAccepted
} // loop over sound card devices of current driver
if (autodetectionFinished) {
break;
}
} // loop over soundcard drivers
// Add a master device (if we haven't defined one yet)
if ( !Mixer::getGlobalMasterMD(false) ) {
// We have no master card yet. This actually only happens when there was
// not one defined in the kmixrc.
// So lets just set the first card as master card.
if ( Mixer::mixers().count() > 0 ) {
std::shared_ptr<MixDevice> master = Mixer::mixers().first()->getLocalMasterMD();
if ( master ) {
QString controlId = master->id();
Mixer::setGlobalMaster( Mixer::mixers().first()->id(), controlId, true);
}
}
}
else {
// setGlobalMaster was already set after reading the configuration.
// So we must make the local master consistent
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
QString mdID = md->id();
md->mixer()->setLocalMasterMD(mdID);
}
if ( Mixer::mixers().count() == 0 )
{
// If there was no mixer found, we assume, that hotplugging will take place
// on the preferred driver (this is always the first in the backend list).
driverInfoUsed = Mixer::driverName(0);
}
ref_hwInfoString = i18n("Sound drivers supported:");
ref_hwInfoString.append(" ").append( driverInfo ).append( "\n").append(i18n("Sound drivers used:")) .append(" ").append(driverInfoUsed);
if ( multipleDriversActive )
{
// this will only be possible by hacking the config-file, as it will not be officially supported
ref_hwInfoString.append("\n").append(i18n("Experimental multiple-Driver mode activated"));
QString allDrivermatch("*");
KMixDeviceManager::instance()->setHotpluggingBackends(allDrivermatch);
}
else {
KMixDeviceManager::instance()->setHotpluggingBackends(driverInfoUsed);
}
kDebug(67100) << ref_hwInfoString << "\nTotal number of detected Mixers: " << Mixer::mixers().count();
//kDebug(67100) << "OUT MixerToolBox::initMixer()";
}
/**
* Opens and adds a mixer to the KMix wide Mixer array, if the given Mixer is valid.
* Otherwise the Mixer is deleted.
* This method can be used for adding "static" devices (at program start) and also for hotplugging.
*
* @arg mixer
* @returns true if the Mixer was added
*/
bool MixerToolBox::possiblyAddMixer(Mixer *mixer)
{
if ( mixer->openIfValid() )
{
if ( (!s_ignoreMixerExpression.isEmpty()) && mixer->id().contains(s_ignoreMixerExpression) )
{
// This Mixer should be ignored (default expression is "Modem").
// next 3 lines are duplicated code
delete mixer;
mixer = 0;
return false;
}
else
{
Mixer::mixers().append( mixer );
kDebug(67100) << "Added card " << mixer->id();
emit mixerAdded(mixer->id()); // TODO should we still use this, as we now have our publish/subscribe notification system?
return true;
}
} // valid
else
{
delete mixer;
mixer = 0;
return false;
} // invalid
}
/* This allows to set an expression form Mixers that should be ignored.
The default is "Modem", because most people don't want to control the modem volume. */
void MixerToolBox::setMixerIgnoreExpression(const QString& ignoreExpr)
{
s_ignoreMixerExpression.setPattern(ignoreExpr);
}
QString MixerToolBox::mixerIgnoreExpression() const
{
return s_ignoreMixerExpression.pattern( );
}
void MixerToolBox::removeMixer(Mixer *par_mixer)
{
for (int i=0; i<Mixer::mixers().count(); ++i) {
Mixer *mixer = (Mixer::mixers())[i];
if ( mixer == par_mixer ) {
kDebug(67100) << "Removing card " << mixer->id();
Mixer::mixers().removeAt(i);
delete mixer;
}
}
}
/*
* Clean up and free all resources of all found Mixers, which were found in the initMixer() call
*/
void MixerToolBox::deinitMixer()
{
//kDebug(67100) << "IN MixerToolBox::deinitMixer()";
int mixerCount = Mixer::mixers().count();
for ( int i=0; i<mixerCount; ++i)
{
Mixer* mixer = (Mixer::mixers())[i];
//kDebug(67100) << "MixerToolBox::deinitMixer() Remove Mixer";
mixer->close();
delete mixer;
}
Mixer::mixers().clear();
// kDebug(67100) << "OUT MixerToolBox::deinitMixer()";
}
/*
KLocale* MixerToolBox::whatsthisControlLocale()
{
if ( s_whatsthisLocale == 0 ) {
s_whatsthisLocale = new KLocale("kmix-controls");
}
return s_whatsthisLocale;
}
*/
#include "moc_mixertoolbox.cpp"

View file

@ -1,67 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MIXERTOOLBOX_H
#define MIXERTOOLBOX_H
#include <qobject.h>
#include <qlist.h>
#include <QMap>
#include <QRegExp>
#include <QString>
class Mixer;
/**
* This toolbox contains various static methods that are shared throughout KMix.
* It only contains no-GUI code. The shared with-GUI code is in KMixToolBox
* The reason, why it is not put in a common base class is, that the classes are
* very different and cannot be changed (e.g. KPanelApplet) without major headache.
*/
class MixerToolBox : public QObject
{
Q_OBJECT
enum MultiDriverMode { SINGLE, SINGLE_PLUS_MPRIS2, MULTI };
public:
static MixerToolBox* instance();
void initMixer(bool, QList<QString> backendList, QString&);
void initMixer(MultiDriverMode, QList<QString> backendList, QString&);
void initMixerInternal(MultiDriverMode, QList<QString> backendList, QString&);
void deinitMixer();
bool possiblyAddMixer(Mixer *mixer);
void removeMixer(Mixer *mixer);
void setMixerIgnoreExpression(const QString& ignoreExpr);
QString mixerIgnoreExpression() const;
//static KLocale* whatsthisControlLocale();
signals:
void mixerAdded(QString mixerID);
private:
static MixerToolBox* s_instance;
static QRegExp s_ignoreMixerExpression;
//static KLocale* s_whatsthisLocale;
};
#endif

View file

@ -1,107 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//KMix
#include "mixset.h"
#include "core/mixdevice.h"
// KDE
#include <kdebug.h>
#include <kconfig.h>
#include <kconfiggroup.h>
// Qt
#include <QString>
MixSet::~MixSet()
{
clear();
}
bool MixSet::read( KConfig *config, const QString& grp )
{
kDebug(67100) << "MixSet::read() of group " << grp;
KConfigGroup group = config->group(grp);
m_name = group.readEntry( "name", m_name );
bool have_success = false, have_fail = false;
foreach ( std::shared_ptr<MixDevice> md, *this)
{
if ( md->read( config, grp ) )
have_success = true;
else
have_fail = true;
}
return have_success && !have_fail;
}
bool MixSet::write( KConfig *config, const QString& grp )
{
kDebug(67100) << "MixSet::write() of group " << grp;
KConfigGroup conf = config->group(grp);
conf.writeEntry( "name", m_name );
bool have_success = false, have_fail = false;
foreach ( std::shared_ptr<MixDevice> md, *this)
{
if ( md->write( config, grp ) )
have_success = true;
else
have_fail = true;
}
return have_success && !have_fail;
}
void MixSet::setName( const QString &name )
{
m_name = name;
}
std::shared_ptr<MixDevice> MixSet::get(QString id)
{
std::shared_ptr<MixDevice> mdRet;
foreach ( std::shared_ptr<MixDevice> md, *this)
{
if ( md->id() == id )
{
mdRet = md;
break;
}
}
return mdRet;
}
void MixSet::removeById(QString id)
{
for (int i=0; i < count() ; i++ )
{
std::shared_ptr<MixDevice> md = operator[](i);
if ( md->id() == id )
{
removeAt(i);
break;
}
}
}

View file

@ -1,47 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MixSet_h
#define MixSet_h
#include <QList>
#include "core/mixdevice.h"
class MixSet : public QList <std::shared_ptr<MixDevice> >
{
public:
~MixSet();
bool read( KConfig *config, const QString& grp );
bool write( KConfig *config, const QString& grp );
QString name() { return m_name; }
void setName( const QString &name );
std::shared_ptr<MixDevice> get(QString id);
void removeById(QString id);
private:
QString m_name;
};
#endif

View file

@ -1,25 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef APP_VERSION
#define APP_VERSION "4.5"
#define KMIX_CONFIG_VERSION 3
#endif // APP_VERSION

View file

@ -1,382 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "core/volume.h"
// for operator<<()
#include <iostream>
#include <kdebug.h>
#include <klocalizedstring.h>
#include <qmath.h>
float Volume::VOLUME_STEP_DIVISOR = 20;
float Volume::VOLUME_PAGESTEP_DIVISOR = 10;
int Volume::_channelMaskEnum[9] =
{ MLEFT, MRIGHT, MCENTER,
MWOOFER,
MSURROUNDLEFT, MSURROUNDRIGHT,
MREARSIDELEFT, MREARSIDERIGHT,
MREARCENTER
};
QString Volume::ChannelNameReadable[9] =
{
// "Left", "Right",
// "Center", "Subwoofer",
// "Surround Left", "Surround Right",
// "Side Left", "Side Right",
// "Rear Center"
i18nc("Channel name", "Left"), i18nc("Channel name", "Right"),
i18nc("Channel name", "Center"), i18nc("Channel name", "Subwoofer"),
i18nc("Channel name", "Surround Left"), i18nc("Channel name", "Surround Right"),
i18nc("Channel name", "Side Left"), i18nc("Channel name", "Side Right"),
i18nc("Channel name", "Rear Center")
};
char Volume::ChannelNameForPersistence[9][30] = {
"volumeL", "volumeR",
"volumeCenter", "volumeWoofer",
"volumeSurroundL", "volumeSurroundR",
"volumeSideL", "volumeSideR",
"volumeRearCenter"
};
// Forbidden/private. Only here because if there is no CaptureVolume we need the values initialized
// And also QMap requires it.
Volume::Volume()
{
_minVolume = 0;
_maxVolume = 0;
_hasSwitch = false;
_switchActivated = false;
_switchType = None;
_isCapture = false;
_chmask = MNONE;
}
/**
* Do not use. Only implicitely required for QMap.
*
* @deprecated Do not use
*/
VolumeChannel::VolumeChannel()
{
volume = 0;
chid = Volume::NOCHANNEL;
}
VolumeChannel::VolumeChannel(Volume::ChannelID chid)
{
volume = 0;
this->chid = chid;
}
Volume::Volume(long maxVolume, long minVolume, bool hasSwitch, bool isCapture )
{
init((ChannelMask)0, maxVolume, minVolume, hasSwitch, isCapture );
}
/**
* @deprecated
*/
void Volume::addVolumeChannels(ChannelMask chmask)
{
for ( Volume::ChannelID chid=Volume::CHIDMIN; chid<= Volume::CHIDMAX; )
{
if ( chmask & Volume::_channelMaskEnum[chid] )
{
addVolumeChannel(VolumeChannel(chid));
}
chid = (Volume::ChannelID)( 1 + (int)chid); // ugly
} // for all channels
}
void Volume::addVolumeChannel(VolumeChannel vc)
{
_volumesL.insert(vc.chid, vc);
// Add the correpsonnding "muted version" of the chnnel.
// VolumeChannel* zeroChannel = new VolumeChannel(vc.chid);
// zeroChannel->volume = 0;
// _volumesMuted.insert(zeroChannel->chid, *zeroChannel); // TODO remove _volumesMuted
}
void Volume::init( ChannelMask chmask, long maxVolume, long minVolume, bool hasSwitch, bool isCapture)
{
_chmask = chmask;
_maxVolume = maxVolume;
_minVolume = minVolume;
_hasSwitch = hasSwitch;
_isCapture = isCapture;
//_muted = false;
// Presume that the switch is active. This will always work:
// a) Physical switches will be updated after start from the hardware.
// b) Emulated virtual/switches will not receive updates from the hardware, so they shouldn't disable the channels.
_switchActivated = true;
}
QMap<Volume::ChannelID, VolumeChannel> Volume::getVolumesWhenActive() const
{
return _volumesL;
}
QMap<Volume::ChannelID, VolumeChannel> Volume::getVolumes() const
{
return _volumesL;
}
/**
* Returns the absolute change to do one "step" for this volume. This is similar to a page step in a slider,
* namely a fixed percentage of the range.
* One step is the percentage given by 100/VOLUME_STEP_DIVISOR. The
* default VOLUME_STEP_DIVISOR is 20, so default change is 5% of the volumeSpan().
*
* This method guarantees a minimum absolute change of 1, zero is never returned.
*
* It is NOT verified, that such a volume change would actually be possible. You might hit the upper or lower bounds
* of the volume range.
*
*
* @param decrease true, if you want a volume step that decreases the volume by one page step
* @return The volume step. It will be negative if you have used decrease==true
*
*/
long Volume::volumeStep(bool decrease)
{
long inc = volumeSpan() / Volume::VOLUME_STEP_DIVISOR;
if ( inc == 0 ) inc = 1;
if ( decrease ) inc *= -1;
return inc;
}
// @ compatibility
void Volume::setAllVolumes(long vol)
{
long int finalVol = volrange(vol);
QMap<Volume::ChannelID, VolumeChannel>::iterator it = _volumesL.begin();
while (it != _volumesL.end())
{
it.value().volume = finalVol;
//it.value().unmutedVolume= finalVol;
++it;
}
}
void Volume::changeAllVolumes( long step )
{
QMap<Volume::ChannelID, VolumeChannel>::iterator it = _volumesL.begin();
while (it != _volumesL.end())
{
long int finalVol = volrange(it.value().volume + step);
it.value().volume = finalVol;
// it.value().unmutedVolume= finalVol;
++it;
}
}
/**
* Sets the volume for the given Channel
* @ compatibility
*/
void Volume::setVolume( ChannelID chid, long vol)
{
QMap<Volume::ChannelID, VolumeChannel>::iterator it = _volumesL.find(chid);
if ( it != _volumesL.end())
{
it.value().volume = vol;
//it.value().unmutedVolume = vol;
}
}
/**
* Copy the volume elements contained in v to this Volume object.
*/
// void Volume::setVolume(const Volume &v)
// {
// foreach (VolumeChannel vc, _volumesL )
// {
// ChannelID chid = vc.chid;
// v.getVolumes()[chid].volume = vc.volume;
// //v.getVolumes()[chid].unmutedVolume = vc.volume;
// }
// }
void Volume::setSwitch( bool active )
{
_switchActivated = active;
// if ( isCapture() )
// return;
//
// for playback volumes we will not only do the switch, but also set the volume to 0
// QMap<Volume::ChannelID, VolumeChannel>::iterator it = _volumesL.begin();
// if ( active )
// {
// while (it != _volumesL.end())
// {
// VolumeChannel& vc = it.value();
// vc.volume = vc.unmutedVolume;
// ++it;
// }
// }
// else
// {
// while (it != _volumesL.end())
// {
// VolumeChannel& vc = it.value();
// vc.unmutedVolume = vc.volume;
// vc.volume = 0;
// ++it;
// }
// }
}
long Volume::maxVolume() {
return _maxVolume;
}
long Volume::minVolume() {
return _minVolume;
}
long Volume::volumeSpan() {
return _maxVolume - _minVolume + 1;
}
/**
* Returns the volume of the given channel.
*/
long Volume::getVolume(ChannelID chid)
{
return _volumesL.value(chid).volume;
}
/**
* Returns the volume of the given channel. If this Volume is inactive (switched off), 0 is returned.
*/
long Volume::getVolumeForGUI(ChannelID chid)
{
if (! isSwitchActivated() )
return 0;
return _volumesL.value(chid).volume;
}
qreal Volume::getAvgVolume(ChannelMask chmask)
{
int avgVolumeCounter = 0;
long long sumOfActiveVolumes = 0;
foreach (VolumeChannel vc, _volumesL )
{
if (Volume::_channelMaskEnum[vc.chid] & chmask )
{
sumOfActiveVolumes += vc.volume;
++avgVolumeCounter;
}
}
if (avgVolumeCounter != 0) {
qreal sumOfActiveVolumesQreal = sumOfActiveVolumes;
sumOfActiveVolumesQreal /= avgVolumeCounter;
return sumOfActiveVolumesQreal;
}
else
return 0;
}
int Volume::getAvgVolumePercent(ChannelMask chmask)
{
qreal volume = getAvgVolume(chmask);
// min=-100, max=200 => volSpan = 301
// volume = -50 => volShiftedToZero = -50+min = 50
qreal volSpan = volumeSpan();
qreal volShiftedToZero = volume - _minVolume;
qreal percentReal = ( volSpan == 0 ) ? 0 : ( 100 * volShiftedToZero ) / ( volSpan - 1);
int percent = qRound(percentReal);
//kDebug() << "volSpan=" << volSpan << ", volume=" << volume << ", volShiftedToPositive=" << volShiftedToZero << ", percent=" << percent;
return percent;
}
int Volume::count() {
return getVolumes().count();
}
/**
* returns a "sane" volume level. This means, it is a volume level inside the
* valid bounds
*/
long Volume::volrange( long vol )
{
if ( vol < _minVolume ) {
return _minVolume;
}
else if ( vol < _maxVolume ) {
return vol;
}
else {
return _maxVolume;
}
}
std::ostream& operator<<(std::ostream& os, const Volume& vol) {
os << "(";
bool first = true;
foreach ( const VolumeChannel vc, vol.getVolumes() )
{
if ( !first ) os << ",";
else first = false;
os << vc.volume;
} // all channels
os << ")";
os << " [" << vol._minVolume << "-" << vol._maxVolume;
if ( vol._switchActivated ) { os << " : switch active ]"; } else { os << " : switch inactive ]"; }
return os;
}
QDebug operator<<(QDebug os, const Volume& vol) {
os << "(";
bool first = true;
foreach ( VolumeChannel vc, vol.getVolumes() )
{
if ( !first ) os << ",";
else first = false;
os << vc.volume;
} // all channels
os << ")";
os << " [" << vol._minVolume << "-" << vol._maxVolume;
if ( vol._switchActivated ) { os << " : switch active ]"; } else { os << " : switch inactive ]"; }
return os;
}

View file

@ -1,204 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef RADOMPREFIX_VOLUME_H
#define RADOMPREFIX_VOLUME_H
#include <fstream>
#include <kdebug.h>
#include <QList>
#include <QMap>
class VolumeChannel;
class Volume
{
friend class MixDevice;
public:
// Channel definition:
// For example a 2.0 system just has MLEFT and MRIGHT.
// A 5.1 system adds MCENTER, MWOOFER, MSURROUNDLEFT and MSURROUNDRIGHT.
// A 7.1 system furthermore adds MREARLEFT and MREARRIGHT.
enum ChannelMask { MNONE = 0,
MLEFT = 1, MRIGHT = 2, MCENTER = 4,
MMAIN = 3, MFRONT = 7,
MWOOFER = 8,
// SURROUND (4.0 or 4.1 or in higher - like 5.1)
MSURROUNDLEFT = 0x10, MSURROUNDRIGHT = 0x20,
// MSURROUND
MSURROUND = 0x30,
// REARSIDE (Usually only in 7.1)
MREARSIDELEFT = 0x40, MREARSIDERIGHT = 0x80,
// REARCENTER (Usually only in 6.1)
MREARCENTER = 0x100,
// MREAR
MREAR = 0x1C0,
MALL=0xFFFF };
enum ChannelID { NOCHANNEL =-1, CHIDMIN = 0,
LEFT = 0, RIGHT = 1, CENTER = 2,
WOOFER = 3,
SURROUNDLEFT = 4, SURROUNDRIGHT = 5,
REARSIDELEFT = 6, REARSIDERIGHT = 7,
REARCENTER = 8,
CHIDMAX = 8 };
static char ChannelNameForPersistence[9][30];
static QString ChannelNameReadable[9];
enum VolumeType { PlaybackVT = 0 , CaptureVT = 1 };
enum VolumeTypeFlag { Playback = 1, Capture = 2, Both = 3 };
// regular constructor (old, deprecsted)
//Volume( ChannelMask chmask, long maxVolume, long minVolume, bool hasSwitch, bool isCapture );
// regular constructor
Volume(long maxVolume, long minVolume, bool hasSwitch, bool isCapture );
void addVolumeChannel(VolumeChannel ch);
/// @Deprecated
void addVolumeChannels(ChannelMask chmask);
// Set all volumes as given by vol
void setAllVolumes(long vol);
// Set all volumes to the ones given in vol
// void setVolume(const Volume &vol );
// Set volumes as specified by the channel mask
//void setVolume( const Volume &vol, ChannelMask chmask);
void setVolume( ChannelID chid, long volume);
// Increase or decrease all volumes by step
void changeAllVolumes( long step );
long getVolume(ChannelID chid);
long getVolumeForGUI(ChannelID chid);
qreal getAvgVolume(ChannelMask chmask);
int getAvgVolumePercent(ChannelMask chmask);
//long operator[](int);
long maxVolume();
long minVolume();
/**
* The number of valid volume levels, mathematically: maxVolume - minVolume + 1
*/
long volumeSpan();
int count();
bool hasSwitch() const
{
return _hasSwitch;
};
bool hasVolume() const { return (_maxVolume != _minVolume); }
/**
* Returns whether this is a playback or capture volume.
*
* @return true, if it is a capture volume
*/
bool isCapture() const
{
return _isCapture;
}
// Some playback switches control playback, and some are special.
// ALSA doesn't differentiate between playback, OnOff and special, so users can add this information in the profile.
// It is only used for GUI things, like showing a "Mute" text or tooltip
// Capture is not really used, and has only been added for completeness and future extensibility.
enum SwitchType { None, PlaybackSwitch, CaptureSwitch, OnSwitch, OffSwitch, SpecialSwitch };
void setSwitchType(SwitchType type) { _switchType = type; }
Volume::SwitchType switchType() { return _switchType; }
friend std::ostream& operator<<(std::ostream& os, const Volume& vol);
friend QDebug operator<<(QDebug os, const Volume& vol);
// _channelMaskEnum[] and the following elements moved to public seection. operator<<() could not
// access it, when private. Strange, as operator<<() is declared friend.
static int _channelMaskEnum[9];
QMap<Volume::ChannelID, VolumeChannel> getVolumes() const;
QMap<Volume::ChannelID, VolumeChannel> getVolumesWhenActive() const;
long volumeStep(bool decrease);
static float VOLUME_STEP_DIVISOR; // The divisor for defining volume control steps (for mouse-wheel, DBUS and Normal step for Sliders )
static float VOLUME_PAGESTEP_DIVISOR; // The divisor for defining volume control steps (page-step for sliders)
protected:
long _chmask;
QMap<Volume::ChannelID, VolumeChannel> _volumesL;
// QMap<Volume::ChannelID, VolumeChannel> _volumesMuted;
long _minVolume;
long _maxVolume;
// setSwitch() and isSwitchActivated() are tricky. No regular class (incuding the Backends) shall use
// these functions. Our friend class MixDevice will handle that gracefully for us.
void setSwitch( bool active );
bool isSwitchActivated() const // TODO rename to isActive()
{
return _switchActivated;
};
private:
// constructor for dummy volumes
Volume();
void init( ChannelMask chmask, long maxVolume, long minVolume, bool hasSwitch, bool isCapture);
long volrange( long vol );
bool _hasSwitch;
bool _switchActivated;
SwitchType _switchType;
bool _isCapture;
};
class VolumeChannel
{
public:
VolumeChannel();
/**
* Construct a channel for the given channel id.
*
* @param chid
*/
VolumeChannel(Volume::ChannelID chid);
long volume;
Volume::ChannelID chid;
};
std::ostream& operator<<(std::ostream& os, const Volume& vol);
QDebug operator<<(QDebug os, const Volume& vol);
#endif // RADOMPREFIX_VOLUME

View file

@ -1,145 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 1996-2004 Christian Esken <esken@kde.org>
* Copyright 2011 Igor Poboiko <igor.poboiko@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "dbuscontrolwrapper.h"
#include "controladaptor.h"
#include "core/mixer.h"
#include "core/volume.h"
DBusControlWrapper::DBusControlWrapper(std::shared_ptr<MixDevice> parent, const QString& path)
: QObject(0)
{
// kDebug() << "QDBusConnection for control created" << path;
m_md = parent;
new ControlAdaptor( this );
QDBusConnection::sessionBus().registerObject( path, this );
}
DBusControlWrapper::~DBusControlWrapper()
{
}
QString DBusControlWrapper::id()
{
return m_md->id();
}
QString DBusControlWrapper::readableName()
{
return m_md->readableName();
}
QString DBusControlWrapper::iconName()
{
return m_md->iconName();
}
void DBusControlWrapper::setVolume(int percentage)
{
Volume& volP = m_md->playbackVolume();
Volume& volC = m_md->captureVolume();
volP.setAllVolumes( volP.minVolume() + ((percentage * volP.volumeSpan()) / 100) );
volC.setAllVolumes( volC.minVolume() + ((percentage * volC.volumeSpan()) / 100) );
m_md->mixer()->commitVolumeChange( m_md );
}
int DBusControlWrapper::volume()
{
Volume &useVolume = (m_md->playbackVolume().count() != 0) ? m_md->playbackVolume() : m_md->captureVolume();
return useVolume.getAvgVolumePercent(Volume::MALL);
}
void DBusControlWrapper::increaseVolume()
{
m_md->mixer()->increaseVolume(m_md->id());
}
void DBusControlWrapper::decreaseVolume()
{
m_md->mixer()->decreaseVolume(m_md->id());
}
long DBusControlWrapper::absoluteVolumeMin()
{
Volume &useVolume = (m_md->playbackVolume().count() != 0) ? m_md->playbackVolume() : m_md->captureVolume();
return useVolume.minVolume();
}
long DBusControlWrapper::absoluteVolumeMax()
{
Volume &useVolume = (m_md->playbackVolume().count() != 0) ? m_md->playbackVolume() : m_md->captureVolume();
return useVolume.maxVolume();
}
void DBusControlWrapper::setAbsoluteVolume(long absoluteVolume)
{
m_md->playbackVolume().setAllVolumes( absoluteVolume );
m_md->captureVolume().setAllVolumes( absoluteVolume );
m_md->mixer()->commitVolumeChange( m_md );
}
long DBusControlWrapper::absoluteVolume()
{
Volume &useVolume = (m_md->playbackVolume().count() != 0) ? m_md->playbackVolume() : m_md->captureVolume();
qreal avgVol= useVolume.getAvgVolume( Volume::MALL );
long avgVolRounded = avgVol <0 ? avgVol-.5 : avgVol+.5;
return avgVolRounded;
}
void DBusControlWrapper::setMute(bool muted)
{
m_md->setMuted( muted );
m_md->mixer()->commitVolumeChange( m_md );
}
void DBusControlWrapper::toggleMute()
{
m_md->toggleMute();
m_md->mixer()->commitVolumeChange( m_md );
}
bool DBusControlWrapper::canMute()
{
return m_md->hasMuteSwitch();
}
bool DBusControlWrapper::isMuted()
{
return m_md->isMuted();
}
bool DBusControlWrapper::isRecordSource()
{
return m_md->isRecSource();
}
void DBusControlWrapper::setRecordSource(bool on)
{
m_md->setRecSource(on);
m_md->mixer()->commitVolumeChange( m_md );
}
bool DBusControlWrapper::hasCaptureSwitch()
{
return m_md->captureVolume().hasSwitch();
}
#include "moc_dbuscontrolwrapper.cpp"

View file

@ -1,74 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 1996-2004 Christian Esken <esken@kde.org>
* Copyright 2011 Igor Poboiko <igor.poboiko@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DBUS_CONTROL_WRAPPER_H
#define DBUS_CONTROL_WRAPPER_H
#include <QObject>
#include "core/mixdevice.h"
class DBusControlWrapper : public QObject
{
Q_OBJECT
Q_PROPERTY(QString id READ id)
Q_PROPERTY(QString readableName READ readableName)
Q_PROPERTY(QString iconName READ iconName)
Q_PROPERTY(int volume READ volume WRITE setVolume)
Q_PROPERTY(long absoluteVolume READ absoluteVolume WRITE setAbsoluteVolume)
Q_PROPERTY(long absoluteVolumeMin READ absoluteVolumeMin)
Q_PROPERTY(long absoluteVolumeMax READ absoluteVolumeMax)
Q_PROPERTY(bool mute READ isMuted WRITE setMute)
Q_PROPERTY(bool recordSource READ isRecordSource WRITE setRecordSource)
Q_PROPERTY(bool canMute READ canMute)
Q_PROPERTY(bool hasCaptureSwitch READ hasCaptureSwitch)
public:
DBusControlWrapper(std::shared_ptr<MixDevice> parent, const QString& path);
~DBusControlWrapper();
void increaseVolume();
void decreaseVolume();
void toggleMute();
private:
std::shared_ptr<MixDevice> m_md;
QString id();
QString readableName();
QString iconName();
void setVolume(int percentage);
int volume();
void setAbsoluteVolume(long absoluteVolume);
long absoluteVolumeMin();
long absoluteVolumeMax();
long absoluteVolume();
bool canMute();
void setMute(bool muted);
bool isMuted();
bool hasCaptureSwitch();
void setRecordSource(bool on);
bool isRecordSource();
};
#endif /* DBUS_MIXER_WRAPPER_H */

View file

@ -1,145 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 1996-2004 Christian Esken <esken@kde.org>
* Copyright 2011 Igor Poboiko <igor.poboiko@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "dbusmixerwrapper.h"
#include <QStringList>
#include "core/ControlManager.h"
#include "core/mixdevice.h"
#include "core/volume.h"
#include "dbus/dbusmixsetwrapper.h"
#include "mixeradaptor.h"
DBusMixerWrapper::DBusMixerWrapper(Mixer* parent, const QString& path)
: QObject(parent)
, m_dbusPath(path)
{
m_mixer = parent;
new MixerAdaptor( this );
kDebug() << "Create QDBusConnection for object " << path;
QDBusConnection::sessionBus().registerObject( path, this );
ControlManager::instance().addListener(
m_mixer->id(),
(ControlChangeType::Type)(ControlChangeType::ControlList | ControlChangeType::Volume),
this,
QString("DBusMixerWrapper.%1").arg(m_mixer->id())
);
if (DBusMixSetWrapper::instance())
DBusMixSetWrapper::instance()->signalMixersChanged();
}
DBusMixerWrapper::~DBusMixerWrapper()
{
ControlManager::instance().removeListener(this);
kDebug() << "Remove QDBusConnection for object " << m_dbusPath;
if (DBusMixSetWrapper::instance())
DBusMixSetWrapper::instance()->signalMixersChanged();
}
void DBusMixerWrapper::controlsChange(int changeType)
{
ControlChangeType::Type type = ControlChangeType::fromInt(changeType);
switch (type )
{
case ControlChangeType::ControlList:
createDeviceWidgets();
break;
case ControlChangeType::Volume:
refreshVolumeLevels();
break;
default:
ControlManager::warnUnexpectedChangeType(type, this);
break;
}
}
QString DBusMixerWrapper::driverName()
{
return m_mixer->getDriverName();
}
QStringList DBusMixerWrapper::controls()
{
QStringList result;
foreach ( std::shared_ptr<MixDevice> md, m_mixer->getMixSet() )
{
result.append( md->dbusPath() );
}
return result;
}
QString DBusMixerWrapper::masterControl()
{
std::shared_ptr<MixDevice> md = m_mixer->getLocalMasterMD();
// XXX: Since empty object path is invalid, using "/"
return md ? md->dbusPath() : QString("/");
}
bool DBusMixerWrapper::isOpened()
{
return m_mixer->isOpen();
}
int DBusMixerWrapper::balance()
{
return m_mixer->balance();
}
void DBusMixerWrapper::setBalance(int balance)
{
m_mixer->setBalance(balance);
}
QString DBusMixerWrapper::readableName()
{
return m_mixer->readableName();
}
QString DBusMixerWrapper::id()
{
return m_mixer->id();
}
QString DBusMixerWrapper::udi()
{
return m_mixer->udi();
}
void DBusMixerWrapper::refreshVolumeLevels()
{
QDBusMessage signal = QDBusMessage::createSignal( m_dbusPath,
"org.kde.KMix.Mixer", "controlChanged" );
QDBusConnection::sessionBus().send( signal );
}
void DBusMixerWrapper::createDeviceWidgets()
{
QDBusMessage signal = QDBusMessage::createSignal( m_dbusPath,
"org.kde.KMix.Mixer", "changed" );
QDBusConnection::sessionBus().send( signal );
}
#include "moc_dbusmixerwrapper.cpp"

View file

@ -1,66 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 1996-2004 Christian Esken <esken@kde.org>
* Copyright 2011 Igor Poboiko <igor.poboiko@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DBUSMIXERWRAPPER_H
#define DBUSMIXERWRAPPER_H
#include <QObject>
#include <QStringList>
#include "core/mixer.h"
class DBusMixerWrapper : public QObject
{
Q_OBJECT
Q_PROPERTY(QString driverName READ driverName)
Q_PROPERTY(QString masterControl READ masterControl)
Q_PROPERTY(QString readableName READ readableName)
Q_PROPERTY(bool opened READ isOpened)
Q_PROPERTY(QString id READ id)
Q_PROPERTY(QString udi READ udi)
Q_PROPERTY(int balance READ balance WRITE setBalance)
Q_PROPERTY(QStringList controls READ controls)
public:
DBusMixerWrapper(Mixer* parent, const QString& path);
~DBusMixerWrapper();
QString driverName();
QStringList controls();
QString masterControl();
bool isOpened();
QString readableName();
QString id();
QString udi();
int balance();
void setBalance(int balance);
public slots:
void controlsChange(int changeType);
private:
void createDeviceWidgets();
void refreshVolumeLevels();
Mixer *m_mixer;
QString m_dbusPath;
};
#endif /* DBUSMIXERWRAPPER_H */

View file

@ -1,126 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 2011 Igor Poboiko <igor.poboiko@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "dbusmixsetwrapper.h"
#include "core/mixdevice.h"
#include "core/ControlManager.h"
#include "mixsetadaptor.h"
DBusMixSetWrapper* DBusMixSetWrapper::instanceSingleton;
void DBusMixSetWrapper::initialize(QObject* parent, const QString& path)
{
/* This should not happen! */
if (instanceSingleton)
delete instanceSingleton;
instanceSingleton = new DBusMixSetWrapper(parent, path);
}
DBusMixSetWrapper* DBusMixSetWrapper::instance() {
return instanceSingleton;
}
DBusMixSetWrapper::DBusMixSetWrapper(QObject* parent, const QString& path)
: QObject(parent)
, m_dbusPath( path )
{
new MixSetAdaptor( this );
QDBusConnection::sessionBus().registerObject( m_dbusPath, this );
ControlManager::instance().addListener(
QString(),
ControlChangeType::MasterChanged,
this,
QString("DBusMixSetWrapper"));
}
DBusMixSetWrapper::~DBusMixSetWrapper()
{
}
void DBusMixSetWrapper::controlsChange(int changeType)
{
ControlChangeType::Type type = ControlChangeType::fromInt(changeType);
switch (type)
{
case ControlChangeType::MasterChanged:
signalMasterChanged();
break;
default:
ControlManager::warnUnexpectedChangeType(type, this);
}
}
QStringList DBusMixSetWrapper::mixers() const
{
QStringList result;
Q_FOREACH(Mixer* mixer, Mixer::mixers())
result.append( mixer->dbusPath() );
return result;
}
QString DBusMixSetWrapper::currentMasterMixer() const
{
Mixer* masterMixer = Mixer::getGlobalMasterMixer();
return masterMixer ? masterMixer->id() : QString();
}
QString DBusMixSetWrapper::currentMasterControl() const
{
std::shared_ptr<MixDevice> masterControl = Mixer::getGlobalMasterMD();
return masterControl ? masterControl->id() : QString();
}
QString DBusMixSetWrapper::preferredMasterMixer() const
{
return Mixer::getGlobalMasterPreferred().getCard();
}
QString DBusMixSetWrapper::preferredMasterControl() const
{
return Mixer::getGlobalMasterPreferred().getControl();
}
void DBusMixSetWrapper::setCurrentMaster(const QString &mixer, const QString &control)
{
Mixer::setGlobalMaster(mixer, control, false);
}
void DBusMixSetWrapper::setPreferredMaster(const QString &mixer, const QString &control)
{
Mixer::setGlobalMaster(mixer, control, true);
}
void DBusMixSetWrapper::signalMixersChanged()
{
QDBusMessage signal = QDBusMessage::createSignal( m_dbusPath,
"org.kde.KMix.MixSet", "mixersChanged" );
QDBusConnection::sessionBus().send( signal );
}
void DBusMixSetWrapper::signalMasterChanged()
{
QDBusMessage signal = QDBusMessage::createSignal( m_dbusPath,
"org.kde.KMix.MixSet", "masterChanged" );
QDBusConnection::sessionBus().send( signal );
}
#include "moc_dbusmixsetwrapper.cpp"

View file

@ -1,60 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 2011 Igor Poboiko <igor.poboiko@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DBUSMIXSETWRAPPER_H
#define DBUSMIXSETWRAPPER_H
#include <QStringList>
#include "core/mixer.h"
class DBusMixSetWrapper : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList mixers READ mixers)
Q_PROPERTY(QString currentMasterMixer READ currentMasterMixer)
Q_PROPERTY(QString currentMasterControl READ currentMasterControl)
Q_PROPERTY(QString preferredMasterMixer READ preferredMasterMixer)
Q_PROPERTY(QString preferredMasterControl READ preferredMasterControl)
public:
static void initialize(QObject* parent, const QString& path);
static DBusMixSetWrapper* instance();
DBusMixSetWrapper(QObject* parent, const QString& path);
~DBusMixSetWrapper();
void signalMixersChanged();
void signalMasterChanged();
public slots:
QStringList mixers() const;
QString currentMasterMixer() const;
QString currentMasterControl() const;
QString preferredMasterMixer() const;
QString preferredMasterControl() const;
void setCurrentMaster(const QString &mixer, const QString &control);
void setPreferredMaster(const QString &mixer, const QString &control);
void controlsChange(int changeType);
private:
static DBusMixSetWrapper* instanceSingleton;
QString m_dbusPath;
};
#endif /* DBUSMIXSETWRAPPER_H */

View file

@ -1,20 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.KMix.Control">
<property access="read" name="id" type="s"/>
<property access="read" name="readableName" type="s"/>
<property access="read" name="iconName" type="s"/>
<property access="readwrite" name="mute" type="b"/>
<property access="read" name="canMute" type="b"/>
<property access="readwrite" name="recordSource" type="b"/>
<property access="read" name="hasCaptureSwitch" type="b"/>
<property access="readwrite" name="volume" type="i"/>
<property access="readwrite" name="absoluteVolume" type="i"/>
<property access="read" name="absoluteVolumeMin" type="i"/>
<property access="read" name="absoluteVolumeMax" type="i"/>
<method name="increaseVolume"/>
<method name="decreaseVolume"/>
<method name="toggleMute"/>
</interface>
</node>

View file

@ -1,16 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.KMix.Mixer">
<property access="read" type="s" name="driverName"/>
<property access="read" type="s" name="masterControl"/>
<property access="read" type="b" name="opened"/>
<property access="read" type="s" name="readableName"/>
<property access="read" type="s" name="id"/>
<property access="read" type="s" name="udi"/>
<property access="readwrite" type="i" name="balance"/>
<property access="read" type="as" name="controls"/>
<signal name="controlChanged"/>
<signal name="changed"/>
</interface>
</node>

View file

@ -1,21 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.KMix.MixSet">
<property access="read" type="as" name="mixers"/>
<property access="read" type="s" name="currentMasterMixer"/>
<property access="read" type="s" name="currentMasterControl"/>
<property access="read" type="s" name="preferredMasterMixer"/>
<property access="read" type="s" name="preferredMasterControl"/>
<method name="setCurrentMaster">
<arg name="mixer" type="s" direction="in"/>
<arg name="control" type="s" direction="in"/>
</method>
<method name="setPreferredMaster">
<arg name="mixer" type="s" direction="in"/>
<arg name="control" type="s" direction="in"/>
</method>
<signal name="mixersChanged"/>
<signal name="masterChanged"/>
</interface>
</node>

View file

@ -1,243 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gui/dialogaddview.h"
#include <qbuttongroup.h>
#include <QLabel>
#include <qradiobutton.h>
#include <qscrollarea.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <kcombobox.h>
#include <kdebug.h>
#include <klocale.h>
#include "core/mixdevice.h"
#include "core/mixer.h"
QStringList DialogAddView::viewNames;
QStringList DialogAddView::viewIds;
DialogAddView::DialogAddView(QWidget* parent, Mixer *mixer )
: KDialog( parent )
{
// TODO 000 Adding View for MPRIS2 is broken. We need at least a dummy XML GUI Profile. Also the
// fixed list below is plain wrong. Actually we should get the Profile list from either the XML files or
// from the backend. The latter is probably easier for now.
if ( viewNames.isEmpty() )
{
// initialize static list. Later this list could be generated from the actually installed profiles
viewNames.append(i18n("All controls"));
viewNames.append(i18n("Only playback controls"));
viewNames.append(i18n("Only capture controls"));
viewIds.append("default");
viewIds.append("playback");
viewIds.append("capture");
}
setCaption( i18n( "Add View" ) );
if ( Mixer::mixers().count() > 0 )
setButtons( Ok|Cancel );
else {
setButtons( Cancel );
}
setDefaultButton( Ok );
_layout = 0;
m_vboxForScrollView = 0;
m_scrollableChannelSelector = 0;
m_buttonGroupForScrollView = 0;
createWidgets(mixer); // Open with Mixer Hardware #0
}
DialogAddView::~DialogAddView()
{
delete _layout;
delete m_vboxForScrollView;
}
/**
* Create basic widgets of the Dialog.
*/
void DialogAddView::createWidgets(Mixer *ptr_mixer)
{
m_mainFrame = new QFrame( this );
setMainWidget( m_mainFrame );
_layout = new QVBoxLayout(m_mainFrame);
_layout->setMargin(0);
if ( Mixer::mixers().count() > 1 ) {
// More than one Mixer => show Combo-Box to select Mixer
// Mixer widget line
QHBoxLayout* mixerNameLayout = new QHBoxLayout();
_layout->addItem( mixerNameLayout );
mixerNameLayout->setSpacing(KDialog::spacingHint());
QLabel *qlbl = new QLabel( i18n("Select mixer:"), m_mainFrame );
mixerNameLayout->addWidget(qlbl);
qlbl->setFixedHeight(qlbl->sizeHint().height());
m_cMixer = new KComboBox( false, m_mainFrame);
m_cMixer->setObjectName( QLatin1String( "mixerCombo" ) );
m_cMixer->setFixedHeight(m_cMixer->sizeHint().height());
connect( m_cMixer, SIGNAL(activated(int)), this, SLOT(createPageByID(int)) );
for( int i =0; i<Mixer::mixers().count(); i++ )
{
Mixer *mixer = (Mixer::mixers())[i];
m_cMixer->addItem( mixer->readableName() );
} // end for all_Mixers
// Make the current Mixer the current item in the ComboBox
int findIndex = m_cMixer->findText( ptr_mixer->readableName() );
if ( findIndex != -1 ) m_cMixer->setCurrentIndex( findIndex );
m_cMixer->setToolTip( i18n("Current mixer" ) );
mixerNameLayout->addWidget(m_cMixer);
} // end if (more_than_1_Mixer)
if ( Mixer::mixers().count() > 0 ) {
QLabel *qlbl = new QLabel( i18n("Select the design for the new view:"), m_mainFrame );
_layout->addWidget(qlbl);
createPage();
connect( this, SIGNAL(okClicked()) , this, SLOT(apply()) );
}
else {
QLabel *qlbl = new QLabel( i18n("No sound card is installed or currently plugged in."), m_mainFrame );
_layout->addWidget(qlbl);
}
}
/**
* Create RadioButton's for the Mixer with number 'mixerId'.
* @par mixerId The Mixer, for which the RadioButton's should be created.
*/
void DialogAddView::createPageByID(int mixerId)
{
//kDebug(67100) << "DialogAddView::createPage()";
QString selectedMixerName = m_cMixer->itemText(mixerId);
for( int i =0; i<Mixer::mixers().count(); i++ )
{
Mixer *mixer = (Mixer::mixers())[i];
if ( mixer->readableName() == selectedMixerName ) {
createPage();
break;
}
} // for
}
/**
* Create RadioButton's for the Mixer with number 'mixerId'.
* @par mixerId The Mixer, for which the RadioButton's should be created.
* TODO: The mixer's backend MUST be inspected to find out the supported profiles.
*/
void DialogAddView::createPage()
{
/** --- Reset page -----------------------------------------------
* In case the user selected a new Mixer via m_cMixer, we need
* to remove the stuff created on the last call.
*/
// delete the VBox. This should automatically remove all contained QRadioButton's.
delete m_vboxForScrollView;
delete m_scrollableChannelSelector;
delete m_buttonGroupForScrollView;
enableButton(Ok, false);
/** Reset page end -------------------------------------------------- */
m_buttonGroupForScrollView = new QButtonGroup(this); // invisible QButtonGroup
m_scrollableChannelSelector = new QScrollArea(m_mainFrame);
_layout->addWidget(m_scrollableChannelSelector);
m_vboxForScrollView = new KVBox();
for( int i=0; i<viewNames.size(); ++i )
{
// Create a RadioButton for each view type
QString name = viewNames.at(i);
name.replace('&', "&&"); // Quoting the '&' needed, to prevent QRadioButton creating an accelerator
QRadioButton* qrb = new QRadioButton( name, m_vboxForScrollView);
connect( qrb, SIGNAL(toggled(bool)), this, SLOT(profileRbtoggled(bool)) );
qrb->setObjectName(viewIds.at(i)); // The object name is used as ID here: see apply()
m_buttonGroupForScrollView->addButton(qrb);
}
m_scrollableChannelSelector->setWidget(m_vboxForScrollView);
m_vboxForScrollView->show(); // show() is necessary starting with the second call to createPage()
}
void DialogAddView::profileRbtoggled(bool selected)
{
if ( selected)
enableButton(Ok, true);
}
void DialogAddView::apply()
{
Mixer *mixer = 0;
if ( Mixer::mixers().count() == 1 ) {
// only one mixer => no combo box => take first entry
mixer = (Mixer::mixers())[0];
}
else if ( Mixer::mixers().count() > 1 ) {
// find mixer that is currently active in the ComboBox
QString selectedMixerName = m_cMixer->itemText(m_cMixer->currentIndex());
for( int i =0; i<Mixer::mixers().count(); i++ )
{
mixer = (Mixer::mixers())[i];
if ( mixer->readableName() == selectedMixerName ) {
mixer = (Mixer::mixers())[i];
break;
}
} // for
}
QAbstractButton* button = m_buttonGroupForScrollView->checkedButton();
if ( button != 0 ) {
QString viewName = button->objectName();
if ( mixer == 0 ) {
kError(67100) << "DialogAddView::createPage(): Invalid Mixer (mixer=0)";
return; // can not happen
}
else {
kDebug() << "We should now create a new view " << viewName << " for mixer " << mixer->id();
resultMixerId = mixer->id();
resultViewName = viewName;
}
}
}
#include "moc_dialogaddview.cpp"

View file

@ -1,69 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DIALOGADDVIEW_H
#define DIALOGADDVIEW_H
#include <QButtonGroup>
#include <QStringList>
class KComboBox;
#include <qradiobutton.h>
#include <QScrollArea>
#include <kvbox.h>
#include <QVBoxLayout>
#include <kdialog.h>
class Mixer;
class DialogAddView : public KDialog
{
Q_OBJECT
public:
DialogAddView(QWidget* parent, Mixer*);
~DialogAddView();
QString getresultViewName() { return resultViewName; }
QString getresultMixerId() { return resultMixerId; }
public slots:
void apply();
private:
void createWidgets(Mixer*);
void createPage();
QVBoxLayout* _layout;
KComboBox* m_cMixer;
QScrollArea* m_scrollableChannelSelector;
KVBox *m_vboxForScrollView;
QButtonGroup *m_buttonGroupForScrollView;
QFrame *m_mainFrame;
static QStringList viewNames;
static QStringList viewIds;
QString resultViewName;
QString resultMixerId;
private slots:
void createPageByID(int mixerId);
void profileRbtoggled(bool selected);
};
#endif

View file

@ -1,161 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gui/dialogchoosebackends.h"
#include <qbuttongroup.h>
#include <QCheckBox>
#include <QLabel>
#include <QSet>
#include <qscrollarea.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <kcombobox.h>
#include <kdebug.h>
#include <klocale.h>
#include "core/ControlManager.h"
#include "core/GlobalConfig.h"
#include "core/mixdevice.h"
#include "core/mixer.h"
/**
* Creates a dialog to choose mixers from. All currently known mixers will be shown, and the given mixerID's
* will be preselected.
*
* @param mixerIds A set of preselected mixer ID's
* @param noButtons is a migration option. When DialogChooseBackends has been integrated as a Tab, it will be removed.
*/
DialogChooseBackends::DialogChooseBackends(QWidget* parent, const QSet<QString>& mixerIds)
: QWidget(parent), modified(false)
{
// setCaption( i18n( "Select Mixers" ) );
// setButtons( None );
_layout = 0;
m_vboxForScrollView = 0;
m_scrollableChannelSelector = 0;
m_buttonGroupForScrollView = 0;
createWidgets(mixerIds);
}
DialogChooseBackends::~DialogChooseBackends()
{
delete _layout;
delete m_vboxForScrollView;
}
/**
* Create basic widgets of the Dialog.
*/
void DialogChooseBackends::createWidgets(const QSet<QString>& mixerIds)
{
m_mainFrame = this;
// m_mainFrame = new QFrame( this );
// setMainWidget( m_mainFrame );
_layout = new QVBoxLayout(m_mainFrame);
_layout->setMargin(0);
if ( !Mixer::mixers().isEmpty() )
{
QLabel *qlbl = new QLabel( i18n("Select the Mixers to display in the sound menu"), m_mainFrame );
_layout->addWidget(qlbl);
createPage(mixerIds);
}
else
{
QLabel *qlbl = new QLabel( i18n("No sound card is installed or currently plugged in."), m_mainFrame );
_layout->addWidget(qlbl);
}
}
/**
* Create RadioButton's for the Mixer with number 'mixerId'.
* @par mixerId The Mixer, for which the RadioButton's should be created.
*/
void DialogChooseBackends::createPage(const QSet<QString>& mixerIds)
{
m_buttonGroupForScrollView = new QButtonGroup(this); // invisible QButtonGroup
m_scrollableChannelSelector = new QScrollArea(m_mainFrame);
_layout->addWidget(m_scrollableChannelSelector);
m_vboxForScrollView = new KVBox();
bool hasMixerFilter = !mixerIds.isEmpty();
kDebug() << "MixerIds=" << mixerIds;
foreach ( Mixer* mixer, Mixer::mixers())
{
QCheckBox* qrb = new QCheckBox(mixer->readableName(true), m_vboxForScrollView);
qrb->setObjectName(mixer->id());// The object name is used as ID here: see getChosenBackends()
connect(qrb, SIGNAL(stateChanged(int)), SLOT(backendsModifiedSlot()));
checkboxes.append(qrb);
bool mixerShouldBeShown = !hasMixerFilter || mixerIds.contains(mixer->id());
qrb->setChecked(mixerShouldBeShown);
}
m_scrollableChannelSelector->setWidget(m_vboxForScrollView);
m_vboxForScrollView->show(); // show() is necessary starting with the second call to createPage()
}
QSet<QString> DialogChooseBackends::getChosenBackends()
{
QSet<QString> newMixerList;
foreach ( QCheckBox* qcb, checkboxes)
{
if (qcb->isChecked())
{
newMixerList.insert(qcb->objectName());
kDebug() << "apply found " << qcb->objectName();
}
}
kDebug() << "New list is " << newMixerList;
return newMixerList;
}
/**
* Returns whether there were any modifications (activation/deactivation) and resets the flag.
* @return
*/
bool DialogChooseBackends::getAndResetModifyFlag()
{
bool modifiedOld = modified;
modified = false;
return modifiedOld;
}
bool DialogChooseBackends::getModifyFlag()
{
return modified;
}
void DialogChooseBackends::backendsModifiedSlot()
{
modified = true;
emit backendsModified();
}
#include "moc_dialogchoosebackends.cpp"

View file

@ -1,66 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DIALOGCHOOSEBACKENDS_H
#define DIALOGCHOOSEBACKENDS_H
#include <QButtonGroup>
#include <qcheckbox.h>
#include <QList>
#include <QScrollArea>
#include <QVBoxLayout>
class KComboBox;
#include <kdialog.h>
#include <kvbox.h>
class Mixer;
class DialogChooseBackends: public QWidget
{
Q_OBJECT
public:
DialogChooseBackends(QWidget* parent, const QSet<QString>& backends);
~DialogChooseBackends();
QSet<QString> getChosenBackends();
bool getAndResetModifyFlag();
bool getModifyFlag();
signals:
void backendsModified();
private:
void createWidgets(const QSet<QString>& backends);
void createPage(const QSet<QString>& backends);
QVBoxLayout* _layout;
QScrollArea* m_scrollableChannelSelector;
KVBox *m_vboxForScrollView;
QButtonGroup *m_buttonGroupForScrollView;
QList<QCheckBox*> checkboxes;
QWidget *m_mainFrame;
bool modified;
private slots:
void backendsModifiedSlot();
};
#endif

View file

@ -1,214 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gui/dialogselectmaster.h"
#include <qbuttongroup.h>
#include <QLabel>
#include <qradiobutton.h>
#include <qscrollarea.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <kcombobox.h>
#include <kdebug.h>
#include <klocale.h>
#include "core/ControlManager.h"
#include "core/mixdevice.h"
#include "core/mixer.h"
DialogSelectMaster::DialogSelectMaster( Mixer *mixer, QWidget *parent )
: KDialog( parent )
{
setCaption( i18n( "Select Master Channel" ) );
if ( Mixer::mixers().count() > 0 )
setButtons( Ok|Cancel );
else {
setButtons( Cancel );
}
setDefaultButton( Ok );
_layout = 0;
m_vboxForScrollView = 0;
m_scrollableChannelSelector = 0;
m_buttonGroupForScrollView = 0;
createWidgets(mixer); // Open with Mixer Hardware #0
}
DialogSelectMaster::~DialogSelectMaster()
{
delete _layout;
delete m_vboxForScrollView;
}
/**
* Create basic widgets of the Dialog.
*/
void DialogSelectMaster::createWidgets(Mixer *ptr_mixer)
{
m_mainFrame = new QFrame( this );
setMainWidget( m_mainFrame );
_layout = new QVBoxLayout(m_mainFrame);
_layout->setMargin(0);
if ( Mixer::mixers().count() > 1 ) {
// More than one Mixer => show Combo-Box to select Mixer
// Mixer widget line
QHBoxLayout* mixerNameLayout = new QHBoxLayout();
_layout->addItem( mixerNameLayout );
mixerNameLayout->setSpacing(KDialog::spacingHint());
QLabel *qlbl = new QLabel( i18n("Current mixer:"), m_mainFrame );
mixerNameLayout->addWidget(qlbl);
qlbl->setFixedHeight(qlbl->sizeHint().height());
m_cMixer = new KComboBox( false, m_mainFrame);
m_cMixer->setObjectName( QLatin1String( "mixerCombo" ) );
m_cMixer->setFixedHeight(m_cMixer->sizeHint().height());
connect( m_cMixer, SIGNAL(activated(int)), this, SLOT(createPageByID(int)) );
for( int i =0; i<Mixer::mixers().count(); i++ )
{
Mixer *mixer = (Mixer::mixers())[i];
m_cMixer->addItem( mixer->readableName(), mixer->id() );
} // end for all_Mixers
// Make the current Mixer the current item in the ComboBox
int findIndex = m_cMixer->findData( ptr_mixer->id() );
if ( findIndex != -1 ) m_cMixer->setCurrentIndex( findIndex );
m_cMixer->setToolTip( i18n("Current mixer" ) );
mixerNameLayout->addWidget(m_cMixer);
} // end if (more_than_1_Mixer)
if ( Mixer::mixers().count() > 0 ) {
QLabel *qlbl = new QLabel( i18n("Select the channel representing the master volume:"), m_mainFrame );
_layout->addWidget(qlbl);
createPage(ptr_mixer);
connect( this, SIGNAL(okClicked()) , this, SLOT(apply()) );
}
else {
QLabel *qlbl = new QLabel( i18n("No sound card is installed or currently plugged in."), m_mainFrame );
_layout->addWidget(qlbl);
}
}
/**
* Create RadioButton's for the Mixer with number 'mixerId'.
* @par mixerId The Mixer, for which the RadioButton's should be created.
*/
void DialogSelectMaster::createPageByID(int mixerId)
{
QString mixer_id = m_cMixer->itemData(mixerId).toString();
Mixer * mixer = Mixer::findMixer(mixer_id);
if ( mixer != NULL )
createPage(mixer);
}
/**
* Create RadioButton's for the Mixer with number 'mixerId'.
* @par mixerId The Mixer, for which the RadioButton's should be created.
*/
void DialogSelectMaster::createPage(Mixer* mixer)
{
/** --- Reset page -----------------------------------------------
* In case the user selected a new Mixer via m_cMixer, we need
* to remove the stuff created on the last call.
*/
// delete the VBox. This should automatically remove all contained QRadioButton's.
delete m_vboxForScrollView;
delete m_scrollableChannelSelector;
delete m_buttonGroupForScrollView;
/** Reset page end -------------------------------------------------- */
m_buttonGroupForScrollView = new QButtonGroup(this); // invisible QButtonGroup
//m_buttonGroupForScrollView->hide();
m_scrollableChannelSelector = new QScrollArea(m_mainFrame);
// m_scrollableChannelSelector->viewport()->setBackgroundRole(QPalette::Background);
_layout->addWidget(m_scrollableChannelSelector);
m_vboxForScrollView = new KVBox(); //m_scrollableChannelSelector->viewport()
std::shared_ptr<MixDevice> master = mixer->getLocalMasterMD();
QString masterKey = ( master.get() != 0 ) ? master->id() : "----noMaster---"; // Use non-matching name as default
const MixSet& mixset = mixer->getMixSet();
MixSet& mset = const_cast<MixSet&>(mixset);
for( int i=0; i< mset.count(); ++i )
{
std::shared_ptr<MixDevice> md = mset[i];
// Create a RadioButton for each MixDevice (excluding Enum's)
if ( md->playbackVolume().hasVolume() )
{
// kDebug(67100) << "DialogSelectMaster::createPage() mset append qrb";
QString mdName = md->readableName();
mdName.replace('&', "&&"); // Quoting the '&' needed, to prevent QRadioButton creating an accelerator
QRadioButton* qrb = new QRadioButton(mdName, m_vboxForScrollView);
qrb->setObjectName(md->id()); // The object name is used as ID here: see apply()
m_buttonGroupForScrollView->addButton(qrb); //(qrb, md->num());
qrb->setChecked(md->id() == masterKey); // preselect the current master
}
}
m_scrollableChannelSelector->setWidget(m_vboxForScrollView);
m_vboxForScrollView->show(); // show() is necessary starting with the second call to createPage()
}
void DialogSelectMaster::apply()
{
Mixer *mixer = 0;
if ( Mixer::mixers().count() == 1 ) {
// only one mxier => no combo box => take first entry
mixer = (Mixer::mixers())[0];
}
else if ( Mixer::mixers().count() > 1 ) {
// find mixer that is currently active in the ComboBox
int idx = m_cMixer->currentIndex();
QString mixer_id = m_cMixer->itemData(idx).toString();
mixer = Mixer::findMixer(mixer_id);
}
if ( mixer == 0 )
return; // User must have unplugged everything
QAbstractButton* button = m_buttonGroupForScrollView->checkedButton();
if ( button != 0 )
{
QString control_id = button->objectName();
mixer->setLocalMasterMD( control_id );
Mixer::setGlobalMaster(mixer->id(), control_id, true);
ControlManager::instance().announce(mixer->id(), ControlChangeType::MasterChanged, QString("Select Master Dialog"));
}
}
#include "moc_dialogselectmaster.cpp"

View file

@ -1,60 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DIALOGSELECTMASTER_H
#define DIALOGSELECTMASTER_H
#include <QButtonGroup>
class KComboBox;
#include <qradiobutton.h>
#include <QScrollArea>
#include <kvbox.h>
#include <QVBoxLayout>
#include <kdialog.h>
class Mixer;
class DialogSelectMaster : public KDialog
{
Q_OBJECT
public:
DialogSelectMaster(Mixer * = 0, QWidget *parent = 0);
~DialogSelectMaster();
public slots:
void apply();
private:
void createWidgets(Mixer*);
void createPage(Mixer*);
QVBoxLayout* _layout;
KComboBox* m_cMixer;
QScrollArea* m_scrollableChannelSelector;
KVBox *m_vboxForScrollView;
QButtonGroup *m_buttonGroupForScrollView;
//QStringList m_mixerPKs;
QFrame *m_mainFrame;
private slots:
void createPageByID(int mixerId);
};
#endif

View file

@ -1,453 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "dialogviewconfiguration.h"
#include <algorithm>
#include <QCheckBox>
#include <QPushButton>
#include <QLabel>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QGridLayout>
#include <kdebug.h>
#include <kdialog.h>
#include <klocale.h>
#include <kvbox.h>
#include "gui/guiprofile.h"
#include "gui/mixdevicewidget.h"
#include "core/ControlManager.h"
#include "core/mixdevice.h"
#include "core/mixer.h"
DialogViewConfigurationItem::DialogViewConfigurationItem(QListWidget *parent) :
QListWidgetItem(parent)
{
kDebug() << "DialogViewConfigurationItem() default constructor";
refreshItem();
}
DialogViewConfigurationItem::DialogViewConfigurationItem(QListWidget *parent, QString id, bool shown, QString name, int splitted, const QString& iconName) :
QListWidgetItem(parent), _id(id), _shown(shown), _name(name), _splitted(splitted), _iconName(iconName)
{
refreshItem();
}
void DialogViewConfigurationItem::refreshItem()
{
setFlags((flags() | Qt::ItemIsDragEnabled) & ~Qt::ItemIsDropEnabled);
setText(_name);
setIcon(KIconLoader::global()->loadIcon( _iconName, KIconLoader::Small, KIconLoader::SizeSmallMedium ) );
setData(Qt::ToolTipRole, _id); // a hack. I am giving up to do it right
setData(Qt::DisplayRole, _name);
}
/**
* Serializer. Used for DnD.
*/
static QDataStream & operator<< ( QDataStream & s, const DialogViewConfigurationItem & item ) {
s << item._id;
s << item._shown;
s << item._name;
s << item._splitted;
s << item._iconName;
//kDebug() << "<< serialize << " << s;
return s;
}
/**
* Deserializer. Used for DnD.
*/
static QDataStream & operator>> ( QDataStream & s, DialogViewConfigurationItem & item ) {
QString id;
s >> id;
item._id = id;
bool shown;
s >> shown;
item._shown = shown;
QString name;
s >> name;
item._name = name;
int splitted;
s >> splitted;
item._splitted = splitted;
QString iconName;
s >> iconName;
item._iconName = iconName;
//kDebug() << ">> deserialize >> " << id << name << iconName;
return s;
}
DialogViewConfigurationWidget::DialogViewConfigurationWidget(QWidget *parent)
: QListWidget(parent),
m_activeList(true)
{
setDragDropMode(QAbstractItemView::DragDrop);
setDropIndicatorShown(true);
setAcceptDrops(true);
setSelectionMode(QAbstractItemView::SingleSelection);
setDragEnabled(true);
viewport()->setAcceptDrops(true);
setAlternatingRowColors(true);
}
QMimeData* DialogViewConfigurationWidget::mimeData(const QList<QListWidgetItem*> items) const
{
if (items.isEmpty())
return 0;
QMimeData* mimedata = new QMimeData();
DialogViewConfigurationItem* item = 0;
QByteArray data;
{
QDataStream stream(&data, QIODevice::WriteOnly);
// we only support single selection
item = static_cast<DialogViewConfigurationItem *>(items.first());
stream << *item;
}
bool active = isActiveList();
mimedata->setData("application/x-kde-action-list", data);
mimedata->setData("application/x-kde-source-treewidget", active ? "active" : "inactive");
return mimedata;
}
bool DialogViewConfigurationWidget::dropMimeData(int index, const QMimeData * mimeData, Qt::DropAction /*action*/)
{
const QByteArray data = mimeData->data("application/x-kde-action-list");
if (data.isEmpty())
return false;
QDataStream stream(data);
const bool sourceIsActiveList = mimeData->data("application/x-kde-source-treewidget") == "active";
DialogViewConfigurationItem* item = new DialogViewConfigurationItem(0); // needs parent, use this temporarily
stream >> *item;
item->refreshItem();
emit dropped(this, index, item, sourceIsActiveList);
return true;
}
DialogViewConfiguration::DialogViewConfiguration( QWidget*, ViewBase& view)
: KDialog( 0),
_view(view)
{
setCaption( i18n( "Configure Channels" ) );
setButtons( Ok|Cancel );
setDefaultButton( Ok );
frame = new QWidget( this );
frame->setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding);
setMainWidget( frame );
// The _layout will hold two items: The title and the Drag-n-Drop area
_layout = new QVBoxLayout(frame );
_layout->setMargin( 0 );
_layout->setSpacing(KDialog::spacingHint());
// --- HEADER ---
qlb = new QLabel( i18n("Configuration of the channels. Drag icon to update."), frame );
_layout->addWidget(qlb);
_glayout = new QGridLayout();
_layout->addLayout(_glayout);
_qlw = 0;
_qlwInactive = 0;
createPage();
}
/**
* Drop an item from one list to the other
*/
void DialogViewConfiguration::slotDropped ( DialogViewConfigurationWidget* list, int index, DialogViewConfigurationItem* item, bool sourceIsActiveList )
{
//kDebug() << "dropped item (index" << index << "): " << item->_id << item->_shown << item->_name << item->_splitted << item->_iconName;
if ( list == _qlw ) {
//DialogViewConfigurationItem* after = index > 0 ? static_cast<DialogViewConfigurationItem *>(list->item(index-1)) : 0;
//kDebug() << "after" << after->text() << after->internalTag();
if ( sourceIsActiveList ) {
// has been dragged within the active list (moved).
_qlw->insertItem ( index, item );
//moveActive(item, after);
} else {
// dragged from the inactive list to the active list
_qlw->insertItem ( index, item );
//insertActive(item, after, true);
}
}
else if ( list == _qlwInactive ) {
// has been dragged to the inactive list -> remove from the active list.
//removeActive(item);
_qlwInactive->insertItem ( index, item );
}
}
void DialogViewConfiguration::addSpacer(int row, int col)
{
QWidget *dummy = new QWidget();
dummy->setFixedWidth(4);
_glayout->addWidget(dummy,row,col);
}
void DialogViewConfiguration::moveSelection(DialogViewConfigurationWidget* from, DialogViewConfigurationWidget* to)
{
foreach ( QListWidgetItem* item, from->selectedItems() )
{
QListWidgetItem *clonedItem = item->clone();
to->addItem ( clonedItem );
to->setCurrentItem(clonedItem);
delete item;
}
}
void DialogViewConfiguration::moveSelectionToActiveList()
{
moveSelection(_qlwInactive, _qlw);
}
void DialogViewConfiguration::moveSelectionToInactiveList()
{
moveSelection(_qlw, _qlwInactive);
}
void DialogViewConfiguration::selectionChangedActive()
{
// bool activeIsNotEmpty = _qlw->selectedItems().isEmpty();
moveRightButton->setEnabled(! _qlw->selectedItems().isEmpty());
moveLeftButton->setEnabled(false);
}
void DialogViewConfiguration::selectionChangedInactive()
{
moveLeftButton->setEnabled(! _qlwInactive->selectedItems().isEmpty());
moveRightButton->setEnabled(false);
}
/**
* Create basic widgets of the Dialog.
*/
void DialogViewConfiguration::createPage()
{
QList<QWidget *> &mdws = _view._mdws;
QLabel *l1 = new QLabel( i18n("Visible channels") );
_glayout->addWidget(l1,0,0);
QLabel *l2 = new QLabel( i18n("Available channels") );
_glayout->addWidget(l2,0,6);
_qlwInactive = new DialogViewConfigurationWidget(frame);
_qlwInactive->setDragDropMode(QAbstractItemView::DragDrop);
_qlwInactive->setActiveList(false);
_glayout->addWidget(_qlwInactive,1,6);
connect(_qlwInactive, SIGNAL(dropped(DialogViewConfigurationWidget*,int,DialogViewConfigurationItem*,bool)),
this , SLOT(slotDropped(DialogViewConfigurationWidget*,int,DialogViewConfigurationItem*,bool)));
addSpacer(1,1);
const KIcon& icon = KIcon( QLatin1String( "arrow-left" ));
moveLeftButton = new QPushButton(icon, "");
moveLeftButton->setEnabled(false);
_glayout->addWidget(moveLeftButton,1,2);
connect(moveLeftButton, SIGNAL(clicked(bool)), SLOT(moveSelectionToActiveList()));
addSpacer(1,3);
const KIcon& icon2 = KIcon( QLatin1String( "arrow-right" ));
moveRightButton = new QPushButton(icon2, "");
moveRightButton->setEnabled(false);
_glayout->addWidget(moveRightButton,1,4);
connect(moveRightButton, SIGNAL(clicked(bool)), SLOT(moveSelectionToInactiveList()));
addSpacer(1,5);
_qlw = new DialogViewConfigurationWidget(frame);
_glayout->addWidget(_qlw,1,0);
connect(_qlw , SIGNAL(dropped(DialogViewConfigurationWidget*,int,DialogViewConfigurationItem*,bool)),
this , SLOT(slotDropped(DialogViewConfigurationWidget*,int,DialogViewConfigurationItem*,bool)));
// --- CONTROLS IN THE GRID ------------------------------------
//QPalette::ColorRole bgRole;
for ( int i=0; i<mdws.count(); ++i )
{
//if ( i%2 == 0) bgRole = QPalette::Base; else bgRole = QPalette::AlternateBase;
QWidget *qw = mdws[i];
if ( qw->inherits("MixDeviceWidget") ) {
MixDeviceWidget *mdw = static_cast<MixDeviceWidget*>(qw);
std::shared_ptr<MixDevice> md = mdw->mixDevice();
QString mdName = md->readableName();
int splitted = -1;
if ( ! md->isEnum() ) {
splitted = ( md->playbackVolume().count() > 1) || ( md->captureVolume().count() > 1 ) ;
}
//kDebug() << "add DialogViewConfigurationItem: " << mdName << " visible=" << mdw->isVisible() << "splitted=" << splitted;
if ( mdw->isVisible() ) {
new DialogViewConfigurationItem(_qlw, md->id(), mdw->isVisible(), mdName, splitted, mdw->mixDevice()->iconName());
}
else {
new DialogViewConfigurationItem(_qlwInactive, md->id(), mdw->isVisible(), mdName, splitted, mdw->mixDevice()->iconName());
}
/*
if ( ! md->isEnum() && ( ( md->playbackVolume().count() > 1) || ( md->captureVolume().count() > 1) ) ) {
cb = new QCheckBox( "", vboxForScrollView ); // split
cb->setBackgroundRole(bgRole);
cb->setAutoFillBackground(true);
_qSplitCB.append(cb);
cb->setChecked( ! mdw->isStereoLinked() );
grid->addWidget(cb,1+i,1);
}
else {
_qSplitCB.append(0);
}
*/
/*
if ( ! md->isEnum() && ( md->playbackVolume().count() + md->captureVolume().count() >0 ) ) {
cb = new QCheckBox( "", vboxForScrollView ); // limit
cb->setBackgroundRole(bgRole);
cb->setAutoFillBackground(true);
_qLimitCB.append(cb);
grid->addWidget(cb,1+i,2);
}
else {
*/
//_qLimitCB.append(0);
/*}*/
} // is not enum
} // for all MDW's
connect(_qlwInactive, SIGNAL(itemSelectionChanged()),
this , SLOT(selectionChangedInactive()));
connect(_qlw, SIGNAL(itemSelectionChanged()),
this , SLOT(selectionChangedActive()));
// scrollArea->updateGeometry();
updateGeometry();
connect( this, SIGNAL(okClicked()) , this, SLOT(apply()) );
}
DialogViewConfiguration::~DialogViewConfiguration()
{
}
void DialogViewConfiguration::apply()
{
// --- We have a 3-Step Apply of the Changes -------------------------------
// -1- Update view and profile *****************************************
GUIProfile* prof = _view.guiProfile();
GUIProfile::ControlSet& oldControlset = prof->getControls();
GUIProfile::ControlSet newControlset;
QAbstractItemModel* model;
model = _qlw->model();
prepareControls(model, true, oldControlset, newControlset);
model = _qlwInactive->model();
prepareControls(model, false, oldControlset, newControlset);
// -2- Copy all mandatory "catch-all" controls form the old to the new ControlSet *******
foreach ( ProfControl* pctl, oldControlset)
{
if ( pctl->isMandatory() ) {
ProfControl* newCtl = new ProfControl(*pctl);
newCtl->show = "full"; // The user has selected controls => mandatory controls are now only necessary in extended or full mode
newControlset.push_back(newCtl);
}
}
prof->setControls(newControlset);
prof->finalizeProfile();
prof->setDirty();
// --- Step 3: Tell the view, that it has changed (probably it needs some "polishing" ---
if ( _view.getMixers().size() == 1 )
ControlManager::instance().announce(_view.getMixers().first()->id(), ControlChangeType::ControlList, QString("View Configuration Dialog"));
else
ControlManager::instance().announce(QString(), ControlChangeType::ControlList, QString("View Configuration Dialog"));
}
void DialogViewConfiguration::prepareControls(QAbstractItemModel* model, bool isActiveView, GUIProfile::ControlSet& oldCtlSet, GUIProfile::ControlSet& newCtlSet)
{
int numRows = model->rowCount();
for (int row = 0; row < numRows; ++row) {
// -1- Extract the value from the model ***************************
QModelIndex index = model->index(row, 0);
QVariant vdci;
vdci = model->data(index, Qt::ToolTipRole); // TooltipRole stores the ID (well, thats not really clean design, but it works)
QString ctlId = vdci.toString();
// -2- Find the mdw, und update it **************************
foreach ( QWidget *qw, _view._mdws )
{
MixDeviceWidget *mdw = qobject_cast<MixDeviceWidget*>(qw);
if ( !mdw ) {
continue;
}
if ( mdw->mixDevice()->id() == ctlId ) {
mdw->setVisible(isActiveView);
break;
} // mdw was found
} // find mdw
// -3- Insert it in the new ControlSet **************************
// kDebug() << "Should add to new ControlSet: " << ctlId;
foreach ( ProfControl* control, oldCtlSet)
{
//kDebug() << " checking " << control->id;
QRegExp idRegexp(control->id);
if ( ctlId.contains(idRegexp) ) {
// found. Create a copy
ProfControl* newCtl = new ProfControl(*control);
newCtl->id = '^' + ctlId + '$'; // Replace the (possible generic) regexp by the actual ID
// We have made this an an actual control. As it is derived (from e.g. ".*") it is NOT mandatory.
newCtl->setMandatory(false);
newCtl->setVisible(isActiveView);
newCtlSet.push_back(newCtl);
// kDebug() << "Added to new ControlSet (done): " << newCtl->id;
break;
}
}
}
}
#include "moc_dialogviewconfiguration.cpp"

View file

@ -1,145 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DIALOGVIEWCONFIGURATION_H
#define DIALOGVIEWCONFIGURATION_H
// QT
#include <QCheckBox>
#include <QLabel>
#include <QGridLayout>
#include <QHBoxLayout>
#include <qlist.h>
#include <QListWidget>
#include <QScrollArea>
#include <QVBoxLayout>
// QT DND
#include <QtGui/qevent.h>
#include <QMimeData>
// KDE
#include <kdebug.h>
#include <kdialog.h>
// KMix
#include "gui/guiprofile.h"
#include "viewbase.h"
class DialogViewConfigurationItem : public QListWidgetItem
{
friend class QDataStream;
public:
DialogViewConfigurationItem( QListWidget *parent);
DialogViewConfigurationItem( QListWidget *parent, QString id, bool shown, QString name, int splitted, const QString& iconName );
void refreshItem();
public:
QString _id;
bool _shown;
QString _name;
int _splitted;
QString _iconName;
};
class DialogViewConfigurationWidget : public QListWidget
{
Q_OBJECT
public:
DialogViewConfigurationWidget(QWidget *parent=0);
void setActiveList(bool isActiveList) {
m_activeList = isActiveList;
}
bool isActiveList() const { return m_activeList; };
Q_SIGNALS:
void dropped(DialogViewConfigurationWidget* list, int index, DialogViewConfigurationItem* item, bool sourceIsActiveList);
protected:
virtual QMimeData* mimeData(const QList<QListWidgetItem*> items) const;
virtual bool dropMimeData(int index, const QMimeData * mimeData, Qt::DropAction action);
virtual Qt::DropActions supportedDropActions() const
{
//kDebug() << "supportedDropActions!";
return Qt::MoveAction;
}
virtual QStringList mimeTypes() const
{
//kDebug() << "mimeTypes!";
return QStringList() << "application/x-kde-action-list";
}
// Skip internal dnd handling in QListWidget ---- how is one supposed to figure this out
// without reading the QListWidget code !?
virtual void dropEvent(QDropEvent* ev) {
QAbstractItemView::dropEvent(ev);
}
private:
bool m_activeList;
};
class DialogViewConfiguration : public KDialog
{
Q_OBJECT
public:
DialogViewConfiguration(QWidget* parent, ViewBase& view);
~DialogViewConfiguration();
// QSize sizeHint() const;
public slots:
void apply();
private slots:
void slotDropped(DialogViewConfigurationWidget* list, int index, DialogViewConfigurationItem* item, bool sourceIsActiveList );
void moveSelectionToActiveList();
void moveSelectionToInactiveList();
void selectionChangedActive();
void selectionChangedInactive();
private:
//void dragEnterEvent(QDragEnterEvent *event);
void prepareControls(QAbstractItemModel* model, bool isActiveView, GUIProfile::ControlSet& oldCtlSet, GUIProfile::ControlSet& newCtlSet);
void createPage();
void addSpacer(int row, int col);
void moveSelection(DialogViewConfigurationWidget* from, DialogViewConfigurationWidget* to);
QVBoxLayout* _layout;
ViewBase& _view;
QWidget * frame;
QGridLayout *_glayout;
QLabel* qlb;
QPushButton* moveLeftButton;
QPushButton* moveRightButton;
DialogViewConfigurationWidget *_qlw;
DialogViewConfigurationWidget *_qlwInactive;
};
#endif

View file

@ -1,919 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 2006-2007 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gui/guiprofile.h"
// Qt
#include <QDir>
#include <qxml.h>
#include <QString>
// System
#include <iostream>
#include <utility>
// KDE
#include <kdebug.h>
#include <kstandarddirs.h>
// KMix
#include "core/mixer.h"
#include <QtCore/qstring.h>
QMap<QString, GUIProfile*> GUIProfile::s_profiles;
QString const GUIProfile::PNameSimple("simple");
QString const GUIProfile::PNameExtended("extended");
QString const GUIProfile::PNameAll("all");
QString const GUIProfile::PNameCustom("custom");
bool SortedStringComparator::operator()(const std::string& s1, const std::string& s2) const {
return ( s1 < s2 );
}
/**
* Product comparator for sorting:
* We want the comparator to sort ascending by Vendor. "Inside" the Vendors, we sort by Product Name.
*/
bool ProductComparator::operator()(const ProfProduct* p1, const ProfProduct* p2) const {
if ( p1->vendor < p2->vendor ) {
return ( true );
}
else if ( p1->vendor > p2->vendor ) {
return ( false );
}
else if ( p1->productName < p2->productName ) {
return ( true );
}
else if ( p1->productName > p2->productName ) {
return ( false );
}
else {
/**
* We reach this point, if vendor and product name is identical.
* Actually we don't care about the order then, so we decide that "p1" comes first.
*
* (Hint: As this is a set comparator, the return value HERE doesn't matter that
* much. But if we would decide later to change this Comparator to be a Map Comparator,
* we must NOT return a "0" for identity - this would lead to non-insertion on insert())
*/
return true;
}
}
GUIProfile::GUIProfile()
{
_dirty = false;
_driverVersionMin = 0;
_driverVersionMax = 0;
_generation = 1;
}
GUIProfile::~GUIProfile()
{
kWarning() << "Thou shalt not delete any GUI profile. This message is only OK, when quitting KMix";
qDeleteAll(_controls);
qDeleteAll(_products);
}
/**
* Clears the GUIProfile cache. You must only call this
* before termination of the application, as GUIProfile instances are used in other classes, especially the views.
* There is no need to call this in non-GUI applications like kmixd and kmixctrl.
*/
void GUIProfile::clearCache()
{
qDeleteAll(s_profiles);
s_profiles.clear();
}
void GUIProfile::setId(const QString& id)
{
_id = id;
}
QString GUIProfile::getId() const
{
return _id;
}
bool GUIProfile::isDirty() const {
return _dirty;
}
void GUIProfile::setDirty() {
_dirty = true;
}
/**
* Build a profile name. Suitable to use as primary key and to build filenames.
* @arg mixer The mixer
* @arg profileName The profile name (e.g. "capture", "playback", "my-cool-profile", or "any"
* @return The profile name
*/
QString GUIProfile::buildProfileName(Mixer* mixer, QString profileName, bool ignoreCard)
{
QString fname;
fname += mixer->getDriverName();
if (!ignoreCard) {
fname += ".%1.%2";
fname = fname.arg(mixer->getBaseName()).arg(mixer->getCardInstance());
}
fname += '.' + profileName;
fname.replace(' ','_');
return fname;
}
/**
* Generate a readable profile name (for presenting to the user).
* Hint: Currently used as Tab label.
*/
QString GUIProfile::buildReadableProfileName(Mixer* mixer, QString profileName)
{
QString fname;
fname += mixer->getBaseName();
if ( mixer->getCardInstance() > 1 ) {
fname += " %1";
fname = fname.arg(mixer->getCardInstance());
}
if ( profileName != "default" ) {
fname += ' ' + profileName;
}
kDebug() << fname;
return fname;
}
/**
* Returns the GUIProfile for the given ID (= "fullyQualifiedName").
* If not found 0 is returned. There is no try to load it.
*
* @returns The loaded GUIProfile for the given ID
*/
GUIProfile* GUIProfile::find(QString id)
{
// Not thread safe (due to non-atomic contains()/get()
if ( s_profiles.contains(id) )
{
return s_profiles[id];
}
else
{
return 0;
}
}
/**
* Finds the correct profile for the given mixer.
* If already loaded from disk, returns the cached version.
* Otherwise load profile from disk: Priority: Card specific profile, Card unspecific profile
*
* @arg mixer The mixer
* @arg profileName The profile name (e.g. "ALSA.X-Fi.default", or "OSS.intel-cha51.playback")
* A special case is "", which means that a card specific name should be generated.
* @arg profileNameIsFullyQualified If true, an exact match will be searched. Otherwise it is a simple name like "playback" or "capture"
* @arg ignoreCardName If profileName not fully qualified, this is used in building the requestedProfileName
* @return GUIProfile* The loaded GUIProfile, or 0 if no profile matched. Hint: if you use allowFallback==true, this should never return 0.
*/
GUIProfile* GUIProfile::find(Mixer* mixer, QString profileName, bool profileNameIsFullyQualified, bool ignoreCardName)
{
GUIProfile* guiprof = 0;
if ( mixer == 0 || profileName.isEmpty() )
return 0;
if ( mixer->isDynamic() ) {
kDebug(67100) << "GUIProfile::find() Not loading GUIProfile for Dynamic Mixer";
return 0;
}
QString requestedProfileName;
QString fullQualifiedProfileName;
if ( profileNameIsFullyQualified ) {
requestedProfileName = profileName;
fullQualifiedProfileName = profileName;
}
else {
requestedProfileName = buildProfileName(mixer, profileName, ignoreCardName);
fullQualifiedProfileName = buildProfileName(mixer, profileName, false);
}
if ( s_profiles.contains(fullQualifiedProfileName) ) {
guiprof = s_profiles.value(fullQualifiedProfileName); // Cached
}
else {
guiprof = loadProfileFromXMLfiles(mixer, requestedProfileName); // Load from XML ###Card specific profile###
if ( guiprof != 0 ) {
guiprof->_mixerId = mixer->id();
guiprof->setId(fullQualifiedProfileName); // this one contains some soundcard id (basename + instance)
if ( guiprof->getName().isEmpty() ) {
// If the profile didn't contain a name then lets define one
guiprof->setName(buildReadableProfileName(mixer,profileName)); // The caller can rename this if he likes
guiprof->setDirty();
}
if ( requestedProfileName != fullQualifiedProfileName) {
// This is very important!
// When the final profileName (fullQualifiedProfileName) is different from
// what we have loaded (requestedProfileName, e.g. "default"), we MUST
// set the profile dirty, so it gets saved. Otherwise we would write the
// fullQualifiedProfileName in the kmixrc, and will not find it on the next
// start of KMix.
guiprof->setDirty();
}
addProfile(guiprof);
}
}
return guiprof;
}
/*
* Add the profile to the internal list of profiles (Profile caching).
*/
void GUIProfile::addProfile(GUIProfile* guiprof)
{
// Possible TODO: Delete old mapped GUIProfile, if it exists. Otherwise we might leak one GUIProfile instance
// per unplug/plug sequence. Its quite likely possible that currently no Backend leads to a
// leak: This is because they either don't hotplug cards (PulseAudio, MPRIS2), or they ship
// a XML gui profile (so the Cached version is retrieved, and addProfile() is not called).
s_profiles[guiprof->getId()] = guiprof;
kDebug() << "I have added" << guiprof->getId() << "; Number of profiles is now " << s_profiles.size() ;
}
/**
* Loads a GUI Profile from disk (xml profile file).
* It tries to load the Soundcard specific file first (a).
* If it doesn't exist, it will load the default profile corresponding to the soundcard driver (b).
*/
GUIProfile* GUIProfile::loadProfileFromXMLfiles(Mixer* mixer, QString profileName)
{
GUIProfile* guiprof = 0;
QString fileName = createNormalizedFilename(profileName);
QString fileNameFQ = KStandardDirs::locate("appdata", fileName );
if ( ! fileNameFQ.isEmpty() ) {
guiprof = new GUIProfile();
if ( guiprof->readProfile(fileNameFQ) && ( guiprof->match(mixer) > 0) ) {
// loaded
}
else {
delete guiprof; // not good (e.g. Parsing error => drop this profile silently)
guiprof = 0;
}
}
else {
kDebug() << "Ignore file " <<fileName<< " (does not exist)";
}
return guiprof;
}
/**
* Returns a fallback GUIProfile. You can call this if the backends ships no profile files.
* The returned GUIProfile is also added to the static Map of all GUIProfile instances.
*/
GUIProfile* GUIProfile::fallbackProfile(Mixer *mixer)
{
// -1- Get name
QString fullQualifiedProfileName = buildProfileName(mixer, QString("default"), false);
GUIProfile *fallback = new GUIProfile();
// -2- Fill details
ProfProduct* prd = new ProfProduct();
prd->vendor = mixer->getDriverName();
prd->productName = mixer->readableName();
prd->productRelease = "1.0";
fallback->_products.insert(prd);
static QString matchAll(".*");
static QString matchAllSctl(".*");
ProfControl* ctl = new ProfControl(matchAll, matchAllSctl);
//ctl->regexp = matchAll; // make sure id matches the regexp
ctl->setMandatory(true);
fallback->_controls.push_back(ctl);
fallback->_soundcardDriver = mixer->getDriverName();
fallback->_soundcardName = mixer->readableName();
fallback->finalizeProfile();
fallback->_mixerId = mixer->id();
fallback->setId(fullQualifiedProfileName); // this one contains some soundcard id (basename + instance)
fallback->setName(buildReadableProfileName(mixer, QString("default"))); // The caller can rename this if he likes
fallback->setDirty();
/* -3- Add the profile to the static list
* Hint: This looks like a memory leak, as we never remove profiles from memory while KMix runs.
* Especially with application streams it looks suspicious. But please be aware that this method is only
* called for soundcard hotplugs, and not on stream hotplugs. At least it is supposed to be like that.
*
* Please also see the docs at addProfile(), they also address the possible memory leakage.
*/
addProfile(fallback);
return fallback;
}
/**
* Fill the profile with the data from the given XML profile file.
* @par ref_fileName: Full qualified filename (with path).
* @return bool True, if the profile was successfully created. False if not (e.g. parsing error).
*/
bool GUIProfile::readProfile(const QString& ref_fileName)
{
QXmlSimpleReader *xmlReader = new QXmlSimpleReader();
kDebug() << "Read profile:" << ref_fileName ;
QFile xmlFile( ref_fileName );
QXmlInputSource source( &xmlFile );
GUIProfileParser* gpp = new GUIProfileParser(this);
xmlReader->setContentHandler(gpp);
bool ok = xmlReader->parse( source );
//std::cout << "Raw Profile: " << *this;
if ( ok ) {
ok = finalizeProfile();
} // Read OK
else {
// !! this error message about faulty profiles should probably be surrounded with i18n()
kError(67100) << "ERROR: The profile '" << ref_fileName<< "' contains errors, and is not used.";
}
delete gpp;
delete xmlReader;
return ok;
}
const QString GUIProfile::createNormalizedFilename(const QString& profileId)
{
QString profileIdNormalized(profileId);
profileIdNormalized.replace(':', '.');
QString fileName("profiles/");
fileName = fileName + profileIdNormalized + ".xml";
return fileName;
}
bool GUIProfile::writeProfile()
{
bool ret = false;
QString profileId = getId();
QString fileName = createNormalizedFilename(profileId);
QString fileNameFQ = KStandardDirs::locateLocal("appdata", fileName, true );
kDebug() << "Write profile:" << fileNameFQ ;
QFile f(fileNameFQ);
if ( f.open(QIODevice::WriteOnly | QFile::Truncate) )
{
QTextStream out(&f);
out << *this;
f.close();
ret = true;
}
if ( ret ) {
_dirty = false;
}
return ret;
}
/** This is now empty. It can be removed */
bool GUIProfile::finalizeProfile() const
{
bool ok = true;
return ok;
}
// -------------------------------------------------------------------------------------
void GUIProfile::setControls(ControlSet& newControlSet)
{
qDeleteAll(_controls);
_controls = newControlSet;
}
const GUIProfile::ControlSet& GUIProfile::getControls() const
{
return _controls;
}
GUIProfile::ControlSet& GUIProfile::getControls()
{
return _controls;
}
void GUIProfile::addProduct(ProfProduct* prd)
{
_products.insert(prd);
}
// -------------------------------------------------------------------------------------
/**
* Returns how good the given Mixer matches this GUIProfile.
* A value between 0 (not matching at all) and MAXLONG (perfect match) is returned.
*
* Here is the current algorithm:
*
* If the driver doesn't match, 0 is returned. (OK)
* If the card-name ... (OK)
* is "*", this is worth 1 point
* doesn't match, 0 is returned.
* matches, this is worth 500 points.
*
* If the "card type" ...
* is empty, this is worth 0 points. !!! not implemented yet
* doesn't match, 0 is returned. !!! not implemented yet
* matches , this is worth 500 points. !!! not implemented yet
*
* If the "driver version" doesn't match, 0 is returned. !!! not implemented yet
* If the "driver version" matches, this is worth ...
* 4000 unlimited <=> "*:*"
* 6000 toLower-bound-limited <=> "toLower-bound:*"
* 6000 upper-bound-limited <=> "*:upper-bound"
* 8000 upper- and toLower-bound limited <=> "toLower-bound:upper-bound"
* or 10000 points (upper-bound=toLower-bound=bound <=> "bound:bound"
*
* The Profile-Generation is added to the already achieved points. (done)
* The maximum gain is 900 points.
* Thus you can create up to 900 generations (0-899) without "overriding"
* the points gained from the "driver version" or "card-type".
*
* For example: card-name="*" (1), card-type matches (1000),
* driver version "*:*" (4000), Profile-Generation 4 (4).
* Sum: 1 + 1000 + 4000 + 4 = 5004
*
* @todo Implement "card type" match value
* @todo Implement "version" match value (must be in backends as well)
*/
unsigned long GUIProfile::match(Mixer* mixer) {
unsigned long matchValue = 0;
if ( _soundcardDriver != mixer->getDriverName() ) {
return 0;
}
if ( _soundcardName == "*" ) {
matchValue += 1;
}
else if ( _soundcardName != mixer->getBaseName() ) {
return 0; // card name does not match
}
else {
matchValue += 500; // card name matches
}
// !!! we don't check current for the driver version.
// So we assign simply 4000 points for now.
matchValue += 4000;
if ( _generation < 900 ) {
matchValue += _generation;
}
else {
matchValue += 900;
}
return matchValue;
}
QString xmlify(QString raw);
QString xmlify(QString raw)
{
// kDebug() << "Before: " << raw;
raw = raw.replace('&', "&amp;");
raw = raw.replace('<', "&lt;");
raw = raw.replace('>', "&gt;");
raw = raw.replace("'", "&apos;");
raw = raw.replace("\"", "&quot;");
// kDebug() << "After : " << raw;
return raw;
}
QTextStream& operator<<(QTextStream &os, const GUIProfile& guiprof)
{
// kDebug() << "ENTER QTextStream& operator<<";
os << "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
os << endl << endl;
os << "<soundcard driver=\"" << xmlify(guiprof._soundcardDriver).toUtf8().constData() << "\""
<< " version = \"" << guiprof._driverVersionMin << ":" << guiprof._driverVersionMax << "\"" << endl
<< " name = \"" << xmlify(guiprof._soundcardName).toUtf8().constData() << "\"" << endl
<< " type = \"" << xmlify(guiprof._soundcardType).toUtf8().constData() << "\"" << endl
<< " generation = \"" << guiprof._generation << "\"" << endl
<< ">" << endl << endl ;
os << "<profile id=\"" << xmlify(guiprof._id) << "\" name=\"" << xmlify(guiprof._name) << "\"/>" << endl;
for ( GUIProfile::ProductSet::const_iterator it = guiprof._products.begin(); it != guiprof._products.end(); ++it)
{
ProfProduct* prd = *it;
os << "<product vendor=\"" << xmlify(prd->vendor).toUtf8().constData() << "\" name=\"" << xmlify(prd->productName).toUtf8().constData() << "\"";
if ( ! prd->productRelease.isNull() ) {
os << " release=\"" << xmlify(prd->productRelease).toUtf8().constData() << "\"";
}
if ( ! prd->comment.isNull() ) {
os << " comment=\"" << xmlify(prd->comment).toUtf8().constData() << "\"";
}
os << " />" << endl;
} // for all products
os << endl;
foreach ( ProfControl* profControl, guiprof.getControls() )
{
os << "<control id=\"" << xmlify(profControl->id).toUtf8().constData() << "\"" ;
if ( !profControl->name.isNull() && profControl->name != profControl->id ) {
os << " name=\"" << xmlify(profControl->name).toUtf8().constData() << "\"" ;
}
os << " subcontrols=\"" << xmlify( profControl->renderSubcontrols().toUtf8().constData()) << "\"" ;
os << " show=\"" << xmlify(profControl->show).toUtf8().constData() << "\"" ;
if ( profControl->isMandatory() ) {
os << " mandatory=\"true\"";
}
if ( profControl->isSplit() ) {
os << " split=\"true\"";
}
os << " />" << endl;
} // for all controls
os << endl;
os << "</soundcard>" << endl;
// kDebug() << "EXIT QTextStream& operator<<";
return os;
}
std::ostream& operator<<(std::ostream& os, const GUIProfile& guiprof) {
os << "Soundcard:" << std::endl
<< " Driver=" << guiprof._soundcardDriver.toUtf8().constData() << std::endl
<< " Driver-Version min=" << guiprof._driverVersionMin
<< " max=" << guiprof._driverVersionMax << std::endl
<< " Card-Name=" << guiprof._soundcardName.toUtf8().constData() << std::endl
<< " Card-Type=" << guiprof._soundcardType.toUtf8().constData() << std::endl
<< " Profile-Generation=" << guiprof._generation
<< std::endl;
os << "Profile:" << std::endl
<< " Id=" << guiprof._id.toUtf8().constData() << std::endl
<< " Name=" << guiprof._name.toUtf8().constData() << std::endl;
for ( GUIProfile::ProductSet::const_iterator it = guiprof._products.begin(); it != guiprof._products.end(); ++it)
{
ProfProduct* prd = *it;
os << "Product:\n Vendor=" << prd->vendor.toUtf8().constData() << std::endl << " Name=" << prd->productName.toUtf8().constData() << std::endl;
if ( ! prd->productRelease.isNull() ) {
os << " Release=" << prd->productRelease.toUtf8().constData() << std::endl;
}
if ( ! prd->comment.isNull() ) {
os << " Comment = " << prd->comment.toUtf8().constData() << std::endl;
}
} // for all products
foreach ( ProfControl* profControl, guiprof.getControls() )
{
// ProfControl* profControl = *it;
os << "Control:\n ID=" << profControl->id.toUtf8().constData() << std::endl;
if ( !profControl->name.isNull() && profControl->name != profControl->id ) {
os << " Name = " << profControl->name.toUtf8().constData() << std::endl;
}
os << " Subcontrols=" << profControl->renderSubcontrols().toUtf8().constData() << std::endl;
if ( profControl->isMandatory() ) {
os << " mandatory=\"true\"" << std::endl;
}
if ( profControl->isSplit() ) {
os << " split=\"true\"" << std::endl;
}
} // for all controls
return os;
}
ProfControl::ProfControl(QString& id, QString& subcontrols ) :
_mandatory(false), _split(false) {
d = new ProfControlPrivate();
this->show = "simple";
this->id = id;
setSubcontrols(subcontrols);
}
ProfControl::ProfControl(const ProfControl &profControl) :
_mandatory(false), _split(false) {
d = new ProfControlPrivate();
id = profControl.id;
name = profControl.name;
_useSubcontrolPlayback = profControl._useSubcontrolPlayback;
_useSubcontrolCapture = profControl._useSubcontrolCapture;
_useSubcontrolPlaybackSwitch = profControl._useSubcontrolPlaybackSwitch;
_useSubcontrolCaptureSwitch = profControl._useSubcontrolCaptureSwitch;
_useSubcontrolEnum = profControl._useSubcontrolEnum;
d->subcontrols = profControl.d->subcontrols;
name = profControl.name;
show = profControl.show;
backgroundColor = profControl.backgroundColor;
switchtype = profControl.switchtype;
_mandatory = profControl._mandatory;
_split = profControl._split;
}
ProfControl::~ProfControl() {
delete d;
}
void ProfControl::setVisible(bool visible)
{
show = visible ? GUIProfile::PNameSimple : GUIProfile::PNameExtended;
}
void ProfControl::setSubcontrols(QString sctls)
{
d->subcontrols = sctls;
_useSubcontrolPlayback = false;
_useSubcontrolCapture = false;
_useSubcontrolPlaybackSwitch = false;
_useSubcontrolCaptureSwitch = false;
_useSubcontrolEnum = false;
QStringList qsl = sctls.split( ',', QString::SkipEmptyParts, Qt::CaseInsensitive);
foreach (const QString &sctl, qsl) {
//kDebug() << "setSubcontrols found: " << sctl.toLocal8Bit().constData();
if ( sctl == "pvolume" ) _useSubcontrolPlayback = true;
else if ( sctl == "cvolume" ) _useSubcontrolCapture = true;
else if ( sctl == "pswitch" ) _useSubcontrolPlaybackSwitch = true;
else if ( sctl == "cswitch" ) _useSubcontrolCaptureSwitch = true;
else if ( sctl == "enum" ) _useSubcontrolEnum = true;
else if ( sctl == "*" || sctl == ".*") {
_useSubcontrolCapture = true;
_useSubcontrolCaptureSwitch = true;
_useSubcontrolPlayback = true;
_useSubcontrolPlaybackSwitch = true;
_useSubcontrolEnum = true;
}
else kWarning() << "Ignoring unknown subcontrol type '" << sctl << "' in profile";
}
}
QString ProfControl::renderSubcontrols()
{
QString sctlString;
if ( _useSubcontrolPlayback && _useSubcontrolPlaybackSwitch && _useSubcontrolCapture && _useSubcontrolCaptureSwitch && _useSubcontrolEnum ) {
return QString("*");
}
else {
if ( _useSubcontrolPlayback ) {
sctlString += "pvolume,";
}
if ( _useSubcontrolCapture ) {
sctlString += "cvolume,";
}
if ( _useSubcontrolPlaybackSwitch ) {
sctlString += "pswitch,";
}
if ( _useSubcontrolCaptureSwitch ) {
sctlString += "cswitch,";
}
if ( _useSubcontrolEnum ) {
sctlString += "enum,";
}
if ( sctlString.length() > 0 ) {
sctlString.chop(1);
}
return sctlString;
}
}
// ### PARSER START ################################################
GUIProfileParser::GUIProfileParser(GUIProfile* ref_gp) : _guiProfile(ref_gp)
{
_scope = GUIProfileParser::NONE; // no scope yet
}
bool GUIProfileParser::startDocument()
{
_scope = GUIProfileParser::NONE; // no scope yet
return true;
}
bool GUIProfileParser::startElement( const QString& ,
const QString& ,
const QString& qName,
const QXmlAttributes& attributes )
{
switch ( _scope ) {
case GUIProfileParser::NONE:
/** we are reading the "top level" ***************************/
if ( qName.toLower() == "soundcard" ) {
_scope = GUIProfileParser::SOUNDCARD;
addSoundcard(attributes);
}
else {
// skip unknown top-level nodes
std::cerr << "Ignoring unsupported element '" << qName.toUtf8().constData() << "'" << std::endl;
}
// we are accepting <soundcard> only
break;
case GUIProfileParser::SOUNDCARD:
if ( qName.toLower() == "product" ) {
// Defines product names under which the chipset/hardware is sold
addProduct(attributes);
}
else if ( qName.toLower() == "control" ) {
addControl(attributes);
}
else if ( qName.toLower() == "profile" ) {
addProfileInfo(attributes);
}
else {
std::cerr << "Ignoring unsupported element '" << qName.toUtf8().constData() << "'" << std::endl;
}
// we are accepting <product>, <control> and <tab>
break;
} // switch()
return true;
}
bool GUIProfileParser::endElement( const QString&, const QString&, const QString& qName )
{
if ( qName == "soundcard" ) {
_scope = GUIProfileParser::NONE; // should work out OK, as we don't nest soundcard entries
}
return true;
}
void GUIProfileParser::addSoundcard(const QXmlAttributes& attributes) {
/*
std::cout << "Soundcard: ";
printAttributes(attributes);
*/
QString driver = attributes.value("driver");
QString version = attributes.value("version");
QString name = attributes.value("name");
QString type = attributes.value("type");
QString generation = attributes.value("generation");
if ( !driver.isNull() && !name.isNull() ) {
_guiProfile->_soundcardDriver = driver;
_guiProfile->_soundcardName = name;
if ( type.isNull() ) {
_guiProfile->_soundcardType = "";
}
else {
_guiProfile->_soundcardType = type;
}
if ( version.isNull() ) {
_guiProfile->_driverVersionMin = 0;
_guiProfile->_driverVersionMax = 0;
}
else {
std::pair<QString,QString> versionMinMax;
splitPair(version, versionMinMax, ':');
_guiProfile->_driverVersionMin = versionMinMax.first.toULong();
_guiProfile->_driverVersionMax = versionMinMax.second.toULong();
}
if ( type.isNull() ) { type = ""; };
if ( generation.isNull() ) {
_guiProfile->_generation = 0;
}
else {
// Hint: If the conversion fails, _generation will be assigned 0 (which is fine)
_guiProfile->_generation = generation.toUInt();
}
}
}
void GUIProfileParser::addProfileInfo(const QXmlAttributes& attributes) {
QString name = attributes.value("name");
QString id = attributes.value("id");
_guiProfile->setId(id);
_guiProfile->setName(name);
}
void GUIProfileParser::addProduct(const QXmlAttributes& attributes) {
/*
std::cout << "Product: ";
printAttributes(attributes);
*/
QString vendor = attributes.value("vendor");
QString name = attributes.value("name");
QString release = attributes.value("release");
QString comment = attributes.value("comment");
if ( !vendor.isNull() && !name.isNull() ) {
// Adding a product makes only sense if we have at least vendor and product name
ProfProduct *prd = new ProfProduct();
prd->vendor = vendor;
prd->productName = name;
prd->productRelease = release;
prd->comment = comment;
_guiProfile->addProduct(prd);
}
}
void GUIProfileParser::addControl(const QXmlAttributes& attributes) {
/*
std::cout << "Control: ";
printAttributes(attributes);
*/
QString id = attributes.value("id");
QString subcontrols = attributes.value("subcontrols");
QString name = attributes.value("name");
QString show = attributes.value("show");
QString background = attributes.value("background");
QString switchtype = attributes.value("switchtype");
QString mandatory = attributes.value("mandatory");
QString split = attributes.value("split");
bool isMandatory = false;
if ( !id.isNull() ) {
// We need at least an "id". We can set defaults for the rest, if undefined.
if ( subcontrols.isNull() || subcontrols.isEmpty() ) {
subcontrols = '*'; // for compatibility reasons, we interpret an empty string as match-all (aka "*")
}
if ( name.isNull() ) {
// ignore. isNull() will be checked by all users.
}
if ( ! mandatory.isNull() && mandatory == "true" ) {
isMandatory = true;
}
if ( !background.isNull() ) {
// ignore. isNull() will be checked by all users.
}
if ( !switchtype.isNull() ) {
// ignore. isNull() will be checked by all users.
}
ProfControl *profControl = new ProfControl(id, subcontrols);
if ( show.isNull() ) { show = '*'; }
profControl->name = name;
profControl->show = show;
profControl->setBackgroundColor( background );
profControl->setSwitchtype(switchtype);
profControl->setMandatory(isMandatory);
if ( !split.isNull() && split=="true") {
profControl->setSplit(true);
}
_guiProfile->getControls().push_back(profControl);
} // id != null
}
void GUIProfileParser::printAttributes(const QXmlAttributes& attributes) {
if ( attributes.length() > 0 ) {
for ( int i = 0 ; i < attributes.length(); i++ ) {
std::cout << attributes.qName(i).toUtf8().constData() << ":"<< attributes.value(i).toUtf8().constData() << " , ";
}
std::cout << std::endl;
}
}
void GUIProfileParser::splitPair(const QString& pairString, std::pair<QString,QString>& result, char delim)
{
int delimPos = pairString.indexOf(delim);
if ( delimPos == -1 ) {
// delimiter not found => use an empty String for "second"
result.first = pairString;
result.second = "";
}
else {
// delimiter found
result.first = pairString.mid(0,delimPos);
result.second = pairString.left(delimPos+1);
}
}

View file

@ -1,244 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright 2006-2007 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _GUIPROFILE_H_
#define _GUIPROFILE_H_
class Mixer;
#include <qxml.h>
#include <QColor>
#include <QTextStream>
#include <QString>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <ostream>
#include <KDebug>
struct SortedStringComparator
{
bool operator()(const std::string&, const std::string&) const;
};
struct ProfProduct
{
QString vendor;
QString productName;
// In case the vendor ships different products under the same productName
QString productRelease;
QString comment;
};
class ProfControlPrivate
{
public:
// List of controls, e.g: "rec:1-2,recswitch"
// THIS IS RAW DATA AS LOADED FROM THE PROFILE. DO NOT USE IT, except for debugging.
QString subcontrols;
};
class ProfControl
{
public:
ProfControl(QString& id, QString& subcontrols);
ProfControl(const ProfControl &ctl); // copy constructor
~ProfControl();
// ID as returned by the Mixer Backend, e.g. Master:0
QString id;
void setSubcontrols(QString sctls);
bool useSubcontrolPlayback() {return _useSubcontrolPlayback;};
bool useSubcontrolCapture() {return _useSubcontrolCapture;};
bool useSubcontrolPlaybackSwitch() {return _useSubcontrolPlaybackSwitch;};
bool useSubcontrolCaptureSwitch() {return _useSubcontrolCaptureSwitch;};
bool useSubcontrolEnum() {return _useSubcontrolEnum;};
QString renderSubcontrols();
QString getBackgroundColor() const { return backgroundColor; }
void setBackgroundColor(QString& backgroundColor) { this->backgroundColor = backgroundColor; }
QString getSwitchtype() const { return switchtype; }
void setSwitchtype(QString switchtype) { this->switchtype = switchtype; }
// Visible name for the User ( if name.isNull(), id will be used - And in the future a default lookup table will be consulted ).
// Because the name is visible, some kind of i18n() should be used.
QString name;
void setVisible(bool);
// show or hide (contains the GUI type: simple, extended, all)
// Future direction: Make "show" private
QString show;
bool isMandatory() const
{
return _mandatory;
}
void setMandatory(bool _mandatory)
{
this->_mandatory = _mandatory;
}
void setSplit ( bool split ) {
_split = split;
}
bool isSplit() const {
return _split;
}
private:
// The following are the deserialized values of _subcontrols
bool _useSubcontrolPlayback;
bool _useSubcontrolCapture;
bool _useSubcontrolPlaybackSwitch;
bool _useSubcontrolCaptureSwitch;
bool _useSubcontrolEnum;
// For applying custom colors
QString backgroundColor;
// For defining the switch type when it is not a standard palyback or capture switch
QString switchtype;
bool _mandatory; // A mandatory control must be included in all GUIProfile copies
ProfControlPrivate *d;
bool _split; // true if this widget is to show two sliders
};
struct ProductComparator
{
bool operator()(const ProfProduct*, const ProfProduct*) const;
};
class GUIProfile
{
public:
typedef std::set<ProfProduct*, ProductComparator> ProductSet;
typedef QList<ProfControl*> ControlSet;
static const QString PNameSimple;
static const QString PNameExtended;
static const QString PNameAll;
static const QString PNameCustom;
private:
static QMap<QString, GUIProfile*>& getProfiles() { return s_profiles; }
// Loading
static QString buildProfileName(Mixer* mixer, QString profileName, bool ignoreCard);
static QString buildReadableProfileName(Mixer* mixer, QString profileName);
static GUIProfile* loadProfileFromXMLfiles(Mixer* mixer, QString profileName);
static void addProfile(GUIProfile* guiprof);
static const QString createNormalizedFilename(const QString& profileId);
static QMap<QString, GUIProfile*> s_profiles;
public:
GUIProfile();
virtual ~GUIProfile();
static void clearCache();
bool readProfile(const QString& ref_fileNamestring);
bool finalizeProfile() const;
bool writeProfile();
bool isDirty() const;
void setDirty();
void setId(const QString& id);
QString getId() const;
QString getMixerId() const { return _mixerId; }
unsigned long match(Mixer* mixer);
friend std::ostream& operator<<(std::ostream& os, const GUIProfile& vol);
friend QTextStream& operator<<(QTextStream &outStream, const GUIProfile& guiprof);
static GUIProfile* find(Mixer* mixer, QString profileName, bool profileNameIsFullyQualified, bool ignoreCardName);
static GUIProfile* find(QString id);
static GUIProfile* selectProfileFromXMLfiles(Mixer*, QString preferredProfile);
static GUIProfile* fallbackProfile(Mixer*);
// --- Getters and setters ----------------------------------------------------------------------
const ControlSet& getControls() const;
ControlSet& getControls();
void setControls(ControlSet& newControlSet);
QString getName() const { return _name; }
void setName(QString _name) { this->_name = _name; }
void addProduct(ProfProduct*);
// --- The values from the <soundcard> tag: No getters and setters for them (yet) -----------------------------
QString _soundcardDriver;
// The driver version: 1000*1000*MAJOR + 1000*MINOR + PATCHLEVEL
unsigned long _driverVersionMin;
unsigned long _driverVersionMax;
QString _soundcardName;
QString _soundcardType;
unsigned long _generation;
private:
ControlSet _controls;
ProductSet _products;
QString _id;
QString _name;
QString _mixerId;
bool _dirty;
};
std::ostream& operator<<(std::ostream& os, const GUIProfile& vol);
QTextStream& operator<<(QTextStream &outStream, const GUIProfile& guiprof);
class GUIProfileParser : public QXmlDefaultHandler
{
public:
GUIProfileParser(GUIProfile* ref_gp);
// Enumeration for the scope
enum ProfileScope { NONE, SOUNDCARD };
bool startDocument();
bool startElement( const QString&, const QString&, const QString& , const QXmlAttributes& );
bool endElement( const QString&, const QString&, const QString& );
private:
void addControl(const QXmlAttributes& attributes);
void addProduct(const QXmlAttributes& attributes);
void addSoundcard(const QXmlAttributes& attributes);
void addProfileInfo(const QXmlAttributes& attributes);
void printAttributes(const QXmlAttributes& attributes);
void splitPair(const QString& pairString, std::pair<QString,QString>& result, char delim);
ProfileScope _scope;
GUIProfile* _guiProfile;
};
#endif //_GUIPROFILE_H_

View file

@ -1,388 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
* Copyright (C) 2001 Preston Brown <pbrown@kde.org>
* Copyright (C) 2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gui/kmixdockwidget.h"
#include <kaction.h>
#include <klocale.h>
#include <kmenu.h>
#include <kdebug.h>
#include <kwindowsystem.h>
#include <kactioncollection.h>
#include <ktoggleaction.h>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDesktopWidget>
#include <QApplication>
#include <QTextDocument>
#include "apps/kmix.h"
#include "core/ControlManager.h"
#include "core/mixer.h"
#include "core/mixertoolbox.h"
#include "gui/dialogselectmaster.h"
#include "gui/mixdevicewidget.h"
#include "gui/viewdockareapopup.h"
//#define FEATURE_UNITY_POPUP true
KMixDockWidget::KMixDockWidget(KMixWindow* parent)
: KStatusNotifierItem(parent)
, _oldToolTipValue(-1)
, _oldPixmapType('-')
, _kmixMainWindow(parent)
{
setToolTipIconByName("kmix");
setTitle(i18n( "Volume Control"));
setCategory(Hardware);
setStatus(Active);
// TODO Unity / Gnome only support one type of activation (left-click == right-click)
// So we should show here the ViewDockAreaPopup instead of the menu:
//bool onlyOneMouseButtonAction = onlyHaveOneMouseButtonAction();
createMenuActions();
connect(this, SIGNAL(scrollRequested(int,Qt::Orientation)), this, SLOT(trayWheelEvent(int,Qt::Orientation)));
connect(this, SIGNAL(secondaryActivateRequested(QPoint)), this, SLOT(dockMute()));
// For bizarre reasons, we wrap the ViewDockAreaPopup in a KMenu. Must relate to how KStatusNotifierItem works.
_dockAreaPopupMenuWrapper = new KMenu(parent);
_volWA = new QWidgetAction(_dockAreaPopupMenuWrapper);
_dockView = new ViewDockAreaPopup(_dockAreaPopupMenuWrapper, "dockArea", 0, QString("no-guiprofile-yet-in-dock"), parent);
_volWA->setDefaultWidget(_dockView);
_dockAreaPopupMenuWrapper->addAction(_volWA);
connect(contextMenu(), SIGNAL(aboutToShow()), this, SLOT(contextMenuAboutToShow()));
ControlManager::instance().addListener(
QString(), // All mixers (as the Global master Mixer might change)
(ControlChangeType::Type) (ControlChangeType::Volume | ControlChangeType::MasterChanged), this,
QString("KMixDockWidget"));
// Refresh in all cases. When there is no Golbal Master we still need
// to initialize correctly (e.g. for showin 0% or hiding it)
refreshVolumeLevels();
}
KMixDockWidget::~KMixDockWidget()
{
ControlManager::instance().removeListener(this);
// Note: deleting _volWA also deletes its associated ViewDockAreaPopup (_referenceWidget) and prevents the
// action to be left with a dangling pointer.
// cesken: I adapted the patch from https://bugs.kde.org/show_bug.cgi?id=220621#c27 to branch /branches/work/kmix
delete _volWA;
}
void KMixDockWidget::controlsChange(int changeType)
{
ControlChangeType::Type type = ControlChangeType::fromInt(changeType);
switch (type )
{
case ControlChangeType::MasterChanged:
// Notify the main window, as it might need to update the visibiliy of the dock icon.
// _kmixMainWindow->updateDocking();
// _kmixMainWindow->saveConfig();
refreshVolumeLevels();
actionCollection()->action(QLatin1String("select_master"))->setEnabled(Mixer::getGlobalMasterMixer() != 0);
break;
case ControlChangeType::Volume:
refreshVolumeLevels();
break;
default:
ControlManager::warnUnexpectedChangeType(type, this);
}
}
/**
* Updates all visual parts of the volume, namely tooltip and pixmap
*/
void KMixDockWidget::refreshVolumeLevels()
{
setVolumeTip();
updatePixmap();
}
/**
* Creates the right-click menu
*/
void KMixDockWidget::createMenuActions()
{
QMenu *menu = contextMenu();
if ( menu == 0)
return; // We do not use a menu
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
if ( md.get() != 0 && md->hasMuteSwitch() ) {
// Put "Mute" selector in context menu
KToggleAction *action = actionCollection()->add<KToggleAction>( "dock_mute" );
updateDockMuteAction(action);
action->setText( i18n( "M&ute" ) );
connect(action, SIGNAL(triggered(bool)), SLOT(dockMute()));
menu->addAction( action );
}
// Put "Select Master Channel" dialog in context menu
QAction *action = actionCollection()->addAction( "select_master" );
action->setText( i18n("Select Master Channel...") );
action->setEnabled(Mixer::getGlobalMasterMixer() != 0);
connect(action, SIGNAL(triggered(bool)), _kmixMainWindow, SLOT(slotSelectMaster()));
menu->addAction( action );
//Context menu entry to access media settings
menu->addAction(_kmixMainWindow->actionCollection()->action("launch_kdesoundsetup"));
}
void
KMixDockWidget::setVolumeTip()
{
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
QString tip;
int virtualToolTipValue = 0;
if ( md.get() == 0 )
{
tip = i18n("Mixer cannot be found"); // !! text could be reworked
virtualToolTipValue = -2;
}
else
{
// Playback volume will be used for the DockIcon if available.
// This heuristic is "good enough" for the DockIcon for now.
int val = md->getUserfriendlyVolumeLevel();
tip += "<font size=\"+1\">" + i18n( "Volume at %1%", val ) + "</font>";
if ( md->isMuted() )
tip += i18n( " (Muted)" );
tip += QString( "<br/><font size=\"-1\">%1<br/>%2</font>" )
.arg( Qt::escape(md->mixer()->readableName()) ).arg( Qt::escape(md->readableName()) );
// create a new "virtual" value. With that we see "volume changes" as well as "muted changes"
virtualToolTipValue = val;
if ( md->isMuted() )
virtualToolTipValue += 10000;
}
// The actual updating is only done when the "toolTipValue" was changed (to avoid flicker)
if ( virtualToolTipValue != _oldToolTipValue )
{
// changed (or completely new tooltip)
setToolTipTitle(tip);
}
_oldToolTipValue = virtualToolTipValue;
}
void KMixDockWidget::updatePixmap()
{
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
char newPixmapType;
if ( !md )
{
// no such control => error
newPixmapType = 'e';
}
else
{
int percentage = md->getUserfriendlyVolumeLevel();
if ( percentage <= 0 ) newPixmapType = '0'; // Hint: also muted, and also negative-values
else if ( percentage < 25 ) newPixmapType = '1';
else if ( percentage < 75 ) newPixmapType = '2';
else newPixmapType = '3';
}
if ( newPixmapType != _oldPixmapType ) {
// Pixmap must be changed => do so
switch ( newPixmapType ) {
case 'e': setIconByName( "kmixdocked_error" ); break;
case 'm':
case '0': setIconByName( "audio-volume-muted" ); break;
case '1': setIconByName( "audio-volume-low" ); break;
case '2': setIconByName( "audio-volume-medium" ); break;
case '3': setIconByName( "audio-volume-high" ); break;
}
}
_oldPixmapType = newPixmapType;
}
/**
* Called whenever the icon gets "activated". Usually when its clicked.
* @overload
* @param pos
*/
void KMixDockWidget::activate(const QPoint &pos)
{
QWidget* dockAreaPopup = _dockAreaPopupMenuWrapper; // TODO Refactor to use _referenceWidget directly
if (dockAreaPopup->isVisible())
{
dockAreaPopup->hide();
return;
}
_dockAreaPopupMenuWrapper->removeAction(_volWA);
delete _volWA;
_volWA = new QWidgetAction(_dockAreaPopupMenuWrapper);
_dockView = new ViewDockAreaPopup(_dockAreaPopupMenuWrapper, "dockArea", 0, QString("no-guiprofile-yet-in-dock"),
_kmixMainWindow);
_volWA->setDefaultWidget(_dockView);
_dockAreaPopupMenuWrapper->addAction(_volWA);
//_dockView->show(); // TODO cesken check: this should be automatic
// Showing, to hopefully get the geometry manager started. We need width and height below. Also
// vdesktop->availableGeometry(dockAreaPopup) needs to know on which screen the widget will be shown.
// dockAreaPopup->show();
_dockView->adjustSize();
dockAreaPopup->adjustSize();
int x = pos.x() - dockAreaPopup->width() / 2;
if (x < 0)
x = pos.x();
int y = pos.y() - dockAreaPopup->height() / 2;
if (y < 0)
y = pos.y();
// Now handle Multihead displays. And also make sure that the dialog is not
// moved out-of-the screen on the right (see Bug 101742).
const QDesktopWidget* vdesktop = QApplication::desktop();
const QRect& vScreenSize = vdesktop->availableGeometry(dockAreaPopup);
if ((x + dockAreaPopup->width()) > (vScreenSize.width() + vScreenSize.x()))
{
// move horizontally, so that it is completely visible
x = vScreenSize.width() + vScreenSize.x() - dockAreaPopup->width() - 1;
kDebug()
<< "Multihead: (case 1) moving to" << vScreenSize.x() << "," << vScreenSize.y();
}
else if (x < vScreenSize.x())
{
// horizontally out-of bound
x = vScreenSize.x();
kDebug() << "Multihead: (case 2) moving to" << vScreenSize.x() << "," << vScreenSize.y();
}
if ((y + dockAreaPopup->height()) > (vScreenSize.height() + vScreenSize.y()))
{
// move horizontally, so that it is completely visible
y = vScreenSize.height() + vScreenSize.y() - dockAreaPopup->height() - 1;
kDebug() << "Multihead: (case 3) moving to" << vScreenSize.x() << "," << vScreenSize.y();
}
else if (y < vScreenSize.y())
{
// horizontally out-of bound
y = vScreenSize.y();
kDebug() << "Multihead: (case 4) moving to" << vScreenSize.x() << "," << vScreenSize.y();
}
KWindowSystem::setType(dockAreaPopup->winId(), NET::Dock);
KWindowSystem::setState(dockAreaPopup->winId(), NET::KeepAbove | NET::SkipTaskbar | NET::SkipPager);
dockAreaPopup->show();
dockAreaPopup->move(x, y);
}
void
KMixDockWidget::trayWheelEvent(int delta,Qt::Orientation wheelOrientation)
{
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
if ( md.get() == 0 )
return;
Volume &vol = ( md->playbackVolume().hasVolume() ) ? md->playbackVolume() : md->captureVolume();
// bko313579 Do not use "delta", as that is setting more related to documents (Editor, Browser). KMix should
// simply always use its own VOLUME_STEP_DIVISOR as a base for percentage change.
bool decrease = delta < 0;
if (wheelOrientation == Qt::Horizontal) // Reverse horizontal scroll: bko228780
decrease = !decrease;
long cv = vol.volumeStep(decrease);
bool isInactive = vol.isCapture() ? !md->isRecSource() : md->isMuted();
kDebug() << "Operating on capture=" << vol.isCapture() << ", isInactive=" << isInactive;
if ( cv > 0 && isInactive)
{
// increasing from muted state: unmute and start with a low volume level
if ( vol.isCapture())
md->setRecSource(true);
else
md->setMuted(false);
vol.setAllVolumes(cv);
}
else
vol.changeAllVolumes(cv);
md->mixer()->commitVolumeChange(md);
refreshVolumeLevels();
}
void
KMixDockWidget::dockMute()
{
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
if ( md )
{
md->toggleMute();
md->mixer()->commitVolumeChange( md );
refreshVolumeLevels();
}
}
/**
* Returns whether the running Desktop only supports one Mouse Button
* Hint: Unity / Gnome only support one type of activation (left-click == right-click).
*/
bool KMixDockWidget::onlyHaveOneMouseButtonAction()
{
QDBusConnection connection = QDBusConnection::sessionBus();
bool unityIsRunnig = (connection.interface()->isServiceRegistered("com.canonical.Unity.Panel.Service"));
// Possibly implement other detectors, like for Gnome 3 or Gnome 2
return unityIsRunnig;
}
void KMixDockWidget::contextMenuAboutToShow()
{
// Enable/Disable "Muted" menu item
KToggleAction *dockMuteAction = static_cast<KToggleAction*>(actionCollection()->action("dock_mute"));
updateDockMuteAction(dockMuteAction);
}
void KMixDockWidget::updateDockMuteAction ( KToggleAction* dockMuteAction )
{
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
if ( md && dockMuteAction != 0 )
{
Volume& vol = md->playbackVolume().hasVolume() ? md->playbackVolume() : md->captureVolume();
bool isInactive = vol.isCapture() ? !md->isRecSource() : md->isMuted();
bool hasSwitch = vol.isCapture() ? vol.hasSwitch() : md->hasMuteSwitch();
dockMuteAction->setEnabled( hasSwitch );
dockMuteAction->setChecked( isInactive );
}
}
#include "moc_kmixdockwidget.cpp"

View file

@ -1,80 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
* Copyright (C) 2003 Sven Leiber <s.leiber@web.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KMIXDOCKWIDGET_H
#define KMIXDOCKWIDGET_H
#include <QString>
#include <QWidgetAction>
class KToggleAction;
#include <kstatusnotifieritem.h>
class KMixWindow;
class Mixer;
#include "core/mixdevice.h"
class ViewDockAreaPopup;
class Volume;
class KMixDockWidget : public KStatusNotifierItem
{
Q_OBJECT
friend class KMixWindow;
public:
explicit KMixDockWidget(KMixWindow *parent);
virtual ~KMixDockWidget();
void setErrorPixmap();
void ignoreNextEvent();
void update();
public slots:
void setVolumeTip();
void updatePixmap();
void activate(const QPoint &pos);
void controlsChange(int changeType);
protected:
void createMenuActions();
void toggleMinimizeRestore();
private:
ViewDockAreaPopup *_dockView;
KMenu *_dockAreaPopupMenuWrapper;
QWidgetAction *_volWA;
int _oldToolTipValue;
char _oldPixmapType;
KMixWindow* _kmixMainWindow;
bool onlyHaveOneMouseButtonAction();
void refreshVolumeLevels();
void updateDockMuteAction ( KToggleAction* dockMuteAction );
private slots:
void dockMute();
void trayWheelEvent(int delta,Qt::Orientation);
void contextMenuAboutToShow();
};
#endif

View file

@ -1,183 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright (C) 2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gui/kmixerwidget.h"
// Qt
#include <QLabel>
#include <qpixmap.h>
#include <QString>
#include <qtoolbutton.h>
#include <qapplication.h> // for QApplication::revsreseLayout()
#include <QVBoxLayout>
// KDE
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kdebug.h>
#include <kglobal.h>
#include <kicon.h>
#include <klocale.h>
#include <ktabwidget.h>
// KMix
#include "apps/kmix.h"
#include "gui/guiprofile.h"
#include "gui/kmixtoolbox.h"
#include "gui/mixdevicewidget.h"
#include "gui/viewsliders.h"
#include "core/GlobalConfig.h"
#include "core/mixer.h"
#include "core/mixertoolbox.h"
/**
This widget is embedded in the KMix Main window. Each Hardware Card is visualized by one KMixerWidget.
KMixerWidget contains
a TabBar with n Tabs (at least one per soundcard). These contain View's with sliders, switches and other GUI elements visualizing the Mixer)
*/
KMixerWidget::KMixerWidget( Mixer *mixer,
QWidget * parent, ViewBase::ViewFlags vflags, QString guiprofId,
KActionCollection* actionCollection )
: QWidget( parent ), _mixer(mixer),
m_topLayout(0), _guiprofId(guiprofId),
_actionCollection(actionCollection)
{
createLayout(vflags);
}
KMixerWidget::~KMixerWidget()
{
foreach (ViewBase *view, _views)
{
delete view;
}
_views.clear();
}
/**
* Creates the widgets as described in the KMixerWidget constructor
*/
void KMixerWidget::createLayout(ViewBase::ViewFlags vflags)
{
// delete old objects
delete m_topLayout;
// create main layout
m_topLayout = new QVBoxLayout( this );
m_topLayout->setSpacing( 3 );
m_topLayout->setObjectName( QLatin1String( "m_topLayout" ) );
/*******************************************************************
* Now the main GUI is created.
* 1) Select a (GUI) profile, which defines which controls to show on which Tab
* 2a) Create the Tab's and the corresponding Views
* 2b) Create device widgets
* 2c) Add Views to Tab
********************************************************************/
GUIProfile* guiprof = getGuiprof();
if ( guiprof != 0 )
{
if (GlobalConfig::instance().data.debugGUI)
kDebug() << "Add a view " << _guiprofId;
ViewSliders* view = new ViewSliders( this, guiprof->getId(), _mixer, vflags, _guiprofId, _actionCollection );
possiblyAddView(view);
}
show();
}
/**
* Add the given view, if it is valid - it must have controls or at least have the chance to gain some (dynamic views)
* @param vbase
* @return true, if the view was added
*/
bool KMixerWidget::possiblyAddView(ViewBase* vbase)
{
if ( ! vbase->isValid() ) {
delete vbase;
return false;
}
else {
m_topLayout->addWidget(vbase);
_views.push_back(vbase);
connect( vbase, SIGNAL(toggleMenuBar()), parentWidget(), SLOT(toggleMenuBar()) );
if (GlobalConfig::instance().data.debugGUI)
kDebug() << "CONNECT ViewBase count " << vbase->getMixers().size();
return true;
}
}
/**
* Returns the current View. Normally we have only one View, so we always return the first view.
* This method is only here for one reason: We can plug in an action in the main menu for the view, so that
* 99,99% of all users will be well served. Those who hack their own XML Profile to contain more than one view
must use the context menu for configuring the additional views.
*/
ViewBase* KMixerWidget::currentView()
{
return _views.empty() ? 0 : _views[0];
}
void KMixerWidget::setIcons( bool on )
{
const std::vector<ViewBase*>::const_iterator viewsEnd = _views.end();
for ( std::vector<ViewBase*>::const_iterator it = _views.begin(); it != viewsEnd; ++it) {
ViewBase* mixerWidget = *it;
mixerWidget->setIcons(on);
} // for all tabs
}
void KMixerWidget::loadConfig( KConfig *config )
{
const std::vector<ViewBase*>::const_iterator viewsEnd = _views.end();
for ( std::vector<ViewBase*>::const_iterator it = _views.begin(); it != viewsEnd; ++it) {
ViewBase* view = *it;
if (GlobalConfig::instance().data.debugVolume)
kDebug(67100) << "KMixerWidget::loadConfig()" << view->id();
view->load(config);
view->configurationUpdate();
} // for all tabs
}
void KMixerWidget::saveConfig(KConfig *config)
{
const std::vector<ViewBase*>::const_iterator viewsEnd = _views.end();
for (std::vector<ViewBase*>::const_iterator it = _views.begin(); it != viewsEnd; ++it)
{
ViewBase* view = *it;
if (GlobalConfig::instance().data.debugVolume)
kDebug(67100)
<< "KMixerWidget::saveConfig()" << view->id();
view->save(config);
} // for all tabs
}
void KMixerWidget::toggleMenuBarSlot() {
emit toggleMenuBar();
}
#include "moc_kmixerwidget.cpp"

View file

@ -1,86 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KMIXERWIDGET_H
#define KMIXERWIDGET_H
#include <vector>
#include <QWidget>
#include <QString>
#include "core/ControlManager.h"
#include "core/mixer.h"
#include "gui/mixdevicewidget.h"
// QT
#include <QVBoxLayout>
// KDE
class KActionCollection;
class KConfig;
//class KTabWidget;
// KMix
class GUIProfile;
class ProfTab;
class Mixer;
#include "viewbase.h"
// KMix experimental
class KMixerWidget : public QWidget
{
Q_OBJECT
public:
explicit KMixerWidget( Mixer *mixer,
QWidget *parent, ViewBase::ViewFlags vflags, QString guiprofId, KActionCollection* coll = 0 );
~KMixerWidget();
Mixer *mixer() { return _mixer; }
ViewBase* currentView();
GUIProfile* getGuiprof() { return GUIProfile::find(_guiprofId); };
signals:
void toggleMenuBar();
public slots:
void setIcons( bool on );
void toggleMenuBarSlot();
void saveConfig( KConfig *config );
void loadConfig( KConfig *config );
private:
Mixer *_mixer;
QVBoxLayout *m_topLayout; // contains TabWidget
QString _guiprofId;
std::vector<ViewBase*> _views;
KActionCollection* _actionCollection; // -<- applciations wide action collection
void createLayout(ViewBase::ViewFlags vflags);
bool possiblyAddView(ViewBase* vbase);
};
#endif

View file

@ -1,399 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
* Copyright (C) 2001 Preston Brown <pbrown@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gui/kmixprefdlg.h"
#include <qbuttongroup.h>
#include <QCheckBox>
#include <QLabel>
#include <qradiobutton.h>
//#include <QtGlobal>
#include <kapplication.h>
#include <KConfig>
#include <KGlobal>
#include <klocale.h>
#include <KStandardDirs>
#include "gui/kmixerwidget.h"
#include "core/GlobalConfig.h"
KMixPrefDlg* KMixPrefDlg::instance = 0;
KMixPrefDlg* KMixPrefDlg::getInstance()
{
return instance;
}
KMixPrefDlg* KMixPrefDlg::createInstance(QWidget *parent, GlobalConfig& config)
{
if (instance == 0)
{
instance = new KMixPrefDlg(parent, config);
}
return instance;
}
KMixPrefDlg::KMixPrefDlg(QWidget *parent, GlobalConfig& config) :
KConfigDialog(parent, i18n("Configure"), &config), dialogConfig(config)
{
setFaceType(KPageDialog::List);
//setCaption(i18n("Configure"));
setButtons(Ok | Cancel | Apply);
setDefaultButton(Ok);
dvc = 0;
// general buttons
m_generalTab = new QFrame(this);
m_controlsTab = new QFrame(this);
m_startupTab = new QFrame(this);
createStartupTab();
createGeneralTab();
createControlsTab();
updateWidgets(); // I thought KConfigDialog would call this, but I saw during a gdb session that it does not do so.
showButtonSeparator(true);
generalPage = addPage(m_generalTab, i18n("General"), "configure");
startupPage = addPage(m_startupTab, i18n("Start"), "preferences-system-login");
soundmenuPage = addPage(m_controlsTab, i18n("Sound Menu"), "audio-volume-high");
}
KMixPrefDlg::~KMixPrefDlg()
{
}
/**
* Switches to a specific page and shows it.
* @param page
*/
void KMixPrefDlg::switchToPage(KMixPrefPage page)
{
switch (page)
{
case PrefGeneral:
setCurrentPage(generalPage);
break;
case PrefSoundMenu:
setCurrentPage(soundmenuPage);
break;
case PrefStartup:
setCurrentPage(startupPage);
break;
default:
kWarning() << "Tried to activated unknown preferences page" << page;
break;
}
show();
}
// --- TABS --------------------------------------------------------------------------------------------------
void KMixPrefDlg::createStartupTab()
{
QBoxLayout* layoutStartupTab = new QVBoxLayout(m_startupTab);
layoutStartupTab->setMargin(0);
layoutStartupTab->setSpacing(KDialog::spacingHint());
QLabel* label = new QLabel(i18n("Startup"), m_startupTab);
layoutStartupTab->addWidget(label);
m_onLogin = new QCheckBox(i18n("Restore volumes on login"), m_startupTab);
addWidgetToLayout(m_onLogin, layoutStartupTab, 10, i18n("Restore all volume levels and switches."), "startkdeRestore");
dynamicControlsRestoreWarning = new QLabel(
i18n("Dynamic controls from MPRIS2 will not be restored."), m_startupTab);
dynamicControlsRestoreWarning->setEnabled(false);
addWidgetToLayout(dynamicControlsRestoreWarning, layoutStartupTab, 10, "", "");
allowAutostart = new QCheckBox(i18n("Autostart"), m_startupTab);
addWidgetToLayout(allowAutostart, layoutStartupTab, 10,
i18n("Enables the KMix autostart service (kmix_autostart.desktop)"), "AutoStart");
allowAutostartWarning = new QLabel(
i18n("Autostart can not be enabled, as the autostart file kmix_autostart.desktop is not installed."),
m_startupTab);
addWidgetToLayout(allowAutostartWarning, layoutStartupTab, 10, "", "");
layoutStartupTab->addStretch();
}
void KMixPrefDlg::createOrientationGroup(const QString& labelSliderOrientation, QGridLayout* orientationLayout, int row, KMixPrefDlgPrefOrientationType prefType)
{
QButtonGroup* orientationGroup = new QButtonGroup(m_generalTab);
orientationGroup->setExclusive(true);
QLabel* qlb = new QLabel(labelSliderOrientation, m_generalTab);
QRadioButton* qrbHor = new QRadioButton(i18n("&Horizontal"), m_generalTab);
QRadioButton* qrbVert = new QRadioButton(i18n("&Vertical"), m_generalTab);
if (prefType == TrayOrientation)
{
_rbTraypopupHorizontal = qrbHor;
_rbTraypopupVertical = qrbVert;
orientationGroup->setObjectName("Orientation.TrayPopup");
}
else
{
_rbHorizontal = qrbHor;
_rbVertical = qrbVert;
orientationGroup->setObjectName("Orientation");
}
// Add both buttons to button group
orientationGroup->addButton(qrbHor);
orientationGroup->addButton(qrbVert);
// Add both buttons and label to layout
orientationLayout->addWidget(qlb, row, 0);
orientationLayout->addWidget(qrbHor, row, 1);
orientationLayout->addWidget(qrbVert, row, 2);
connect(qrbHor, SIGNAL(toggled(bool)), SLOT(updateButtons()));
connect(qrbVert, SIGNAL(toggled(bool)), SLOT(updateButtons()));
connect(this, SIGNAL(applyClicked()), SLOT(kmixConfigHasChangedEmitter()));
connect(this, SIGNAL(okClicked()), SLOT(kmixConfigHasChangedEmitter()));
// connect(qrbHor, SIGNAL(toggled(bool)), SLOT(settingsChangedSlot()));
// connect(qrbVert, SIGNAL(toggled(bool)), SLOT(settingsChangedSlot()));
}
void KMixPrefDlg::createGeneralTab()
{
QBoxLayout* layout = new QVBoxLayout(m_generalTab);
layout->setMargin(0);
layout->setSpacing(KDialog::spacingHint());
// --- Visual ---------------------------------------------------------
QLabel* label2 = new QLabel(i18n("Visual"), m_generalTab);
layout->addWidget(label2);
// [CONFIG]
m_showTicks = new QCheckBox(i18n("Show &tickmarks"), m_generalTab);
addWidgetToLayout(m_showTicks, layout, 10, i18n("Enable/disable tickmark scales on the sliders"), "Tickmarks");
m_showLabels = new QCheckBox(i18n("Show &labels"), m_generalTab);
addWidgetToLayout(m_showLabels, layout, 10, i18n("Enables/disables description labels above the sliders"),
"Labels");
// [CONFIG]
m_showOSD = new QCheckBox(i18n("Show On Screen Display (&OSD)"), m_generalTab);
addWidgetToLayout(m_showOSD, layout, 10, "", "showOSD");
// [CONFIG] Slider orientation (main window)
QGridLayout* orientationGrid = new QGridLayout();
layout->addItem(orientationGrid);
createOrientationGroup(i18n("Slider orientation: "), orientationGrid, 0, KMixPrefDlg::MainOrientation);
// Slider orientation (tray popup). We use an extra setting
QBoxLayout* orientation2Layout = new QHBoxLayout();
layout->addItem(orientation2Layout);
createOrientationGroup(i18n("Slider orientation (System tray volume control):"), orientationGrid, 1, KMixPrefDlg::TrayOrientation);
// Push everything above to the top
layout->addStretch();
}
void KMixPrefDlg::createControlsTab()
{
layoutControlsTab = new QVBoxLayout(m_controlsTab);
layoutControlsTab->setMargin(0);
layoutControlsTab->setSpacing(KDialog::spacingHint());
m_dockingChk = new QCheckBox(i18n("&Dock in system tray"), m_controlsTab);
addWidgetToLayout(m_dockingChk, layoutControlsTab, 10, i18n("Docks the mixer into the KDE system tray"),
"AllowDocking");
replaceBackendsInTab();
}
// --- Helper --------------------------------------------------------------------------------------------------
/**
* Register widget with correct name for KConfigDialog, then add it to the given layout
*
* @param widget
* @param layout
* @param spacingBefore
* @param toopTipText
* @param objectName
*/
void KMixPrefDlg::addWidgetToLayout(QWidget* widget, QBoxLayout* layout, int spacingBefore, QString tooltip, QString kconfigName)
{
if (!kconfigName.isEmpty())
{
// Widget to be registered for KConfig
widget->setObjectName("kcfg_" + kconfigName);
}
if ( !tooltip.isEmpty() )
{
widget->setToolTip(tooltip);
}
QBoxLayout *l = new QHBoxLayout();
l->addSpacing(spacingBefore);
l->addWidget(widget);
layout->addItem(l);
}
// --- KConfigDialog CUSTOM WIDGET management ------------------------------------------------------------------------
/**
* Update Widgets from config.
* <p>
* Hint: this get internally called by KConfigdialog on initialization and reset.
*/
void KMixPrefDlg::updateWidgets()
{
kDebug() << "";
bool toplevelHorizontal = dialogConfig.data.getToplevelOrientation() == Qt::Horizontal;
_rbHorizontal->setChecked(toplevelHorizontal);
_rbVertical->setChecked(!toplevelHorizontal);
bool trayHorizontal = dialogConfig.data.getTraypopupOrientation() == Qt::Horizontal;
_rbTraypopupHorizontal->setChecked(trayHorizontal);
_rbTraypopupVertical->setChecked(!trayHorizontal);
}
/**
* Updates config from the widgets. And emits the signal kmixConfigHasChanged().
* <p>
* Hint: this get internally called by KConfigDialog after pressing the OK or Apply button.
*/
void KMixPrefDlg::updateSettings()
{
Qt::Orientation toplevelOrientation = _rbHorizontal->isChecked() ? Qt::Horizontal : Qt::Vertical;
kDebug() << "toplevelOrientation" << toplevelOrientation << ", _rbHorizontal->isChecked()" << _rbHorizontal->isChecked();
dialogConfig.data.setToplevelOrientation(toplevelOrientation);
Qt::Orientation trayOrientation = _rbTraypopupHorizontal->isChecked() ? Qt::Horizontal : Qt::Vertical;
kDebug() << "trayOrientation" << trayOrientation << ", _rbTraypopupHorizontal->isChecked()" << _rbTraypopupHorizontal->isChecked();
dialogConfig.data.setTraypopupOrientation(trayOrientation);
// Announcing MasterChanged, as the sound menu (aka ViewDockAreaPopup) primarily shows master volume(s).
// In any case, ViewDockAreaPopup treats MasterChanged and ControlList the same, so it is better to announce
// the "smaller" change.
bool modified = dvc->getAndResetModifyFlag();
if (modified)
{
GlobalConfig::instance().setMixersForSoundmenu(dvc->getChosenBackends());
ControlManager::instance().announce(QString(), ControlChangeType::MasterChanged, QString("Select Backends Dialog"));
}
}
void KMixPrefDlg::kmixConfigHasChangedEmitter()
{
emit(kmixConfigHasChanged());
}
/**
* Returns whether the custom widgets (orientation checkboxes) has changed.
* <p>
* Hint: this get internally called by KConfigDialog from updateButtons().
* @return
*/
bool KMixPrefDlg::hasChanged()
{
bool orientationFromConfigIsHor = dialogConfig.data.getToplevelOrientation() == Qt::Horizontal;
bool orientationFromWidgetIsHor = _rbHorizontal->isChecked();
kDebug() << "Orientation MAIN fromConfig=" << (orientationFromConfigIsHor ? "Hor" : "Vert") << ", fromWidget=" << (orientationFromWidgetIsHor ? "Hor" : "Vert");
bool changed = orientationFromConfigIsHor ^ orientationFromWidgetIsHor;
if (!changed)
{
bool orientationFromConfigIsHor = dialogConfig.data.getTraypopupOrientation() == Qt::Horizontal;
orientationFromWidgetIsHor = _rbTraypopupHorizontal->isChecked();
kDebug() << "Orientation TRAY fromConfig=" << (orientationFromConfigIsHor ? "Hor" : "Vert") << ", fromWidget=" << (orientationFromWidgetIsHor ? "Hor" : "Vert");
changed = orientationFromConfigIsHor ^ orientationFromWidgetIsHor;
}
if (!changed)
{
changed = dvc->getModifyFlag();
}
kDebug() << "hasChanged=" << changed;
return changed;
}
void KMixPrefDlg::showEvent(QShowEvent * event)
{
// -1- Replace widgets ------------------------------------------------------------
// Hotplug can change mixers or backends => recreate tab
replaceBackendsInTab();
// -2- Change visibility and enable status (of the new widgets) ----------------------
// As GUI can change, the warning will only been shown on demand
dynamicControlsRestoreWarning->setVisible(Mixer::dynamicBackendsPresent());
QString autostartConfigFilename = KGlobal::dirs()->findResource("autostart", QString("kmix_autostart.desktop"));
kDebug()
<< "autostartConfigFilename = " << autostartConfigFilename;
bool autostartFileExists = !autostartConfigFilename.isNull();
//allowAutostartWarning->setEnabled(autostartFileExists);
allowAutostartWarning->setVisible(!autostartFileExists);
allowAutostart->setEnabled(autostartFileExists);
KDialog::showEvent(event);
}
void KMixPrefDlg::replaceBackendsInTab()
{
if (dvc != 0)
{
layoutControlsTab->removeWidget(dvc);
delete dvc;
}
QSet<QString> backendsFromConfig = GlobalConfig::instance().getMixersForSoundmenu();
dvc = new DialogChooseBackends(0, backendsFromConfig);
connect(dvc, SIGNAL(backendsModified()), SLOT(updateButtons()));
dvc->show();
layoutControlsTab->addWidget(dvc);
// Push everything above to the top
layoutControlsTab->addStretch();
}
#include "moc_kmixprefdlg.cpp"

View file

@ -1,125 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KMIXPREFDLG_H
#define KMIXPREFDLG_H
#include <kconfigdialog.h>
#include <kdialog.h>
class KMixPrefWidget;
#include <QBoxLayout>
#include <QCheckBox>
#include <QFrame>
#include <QGridLayout>
#include <QLabel>
#include <QRadioButton>
#include <QShowEvent>
#include <QWidget>
#include "core/GlobalConfig.h"
#include "gui/dialogchoosebackends.h"
class KMixPrefDlg: public KConfigDialog
{
Q_OBJECT
public:
enum KMixPrefPage
{
PrefGeneral, PrefSoundMenu, PrefStartup
};
static KMixPrefDlg* createInstance(QWidget *parent, GlobalConfig& config);
static KMixPrefDlg* getInstance();
void switchToPage(KMixPrefPage page);
signals:
void kmixConfigHasChanged();
private slots:
void kmixConfigHasChangedEmitter();
protected:
void showEvent(QShowEvent * event);
/**
* Orientation is not supported by default => implement manually
* @Override
*/
void updateWidgets();
/**
* Orientation is not supported by default => implement manually
* @Override
*/
void updateSettings();
bool hasChanged();
private:
static KMixPrefDlg* instance;
KMixPrefDlg(QWidget *parent, GlobalConfig& config);
virtual ~KMixPrefDlg();
enum KMixPrefDlgPrefOrientationType
{
MainOrientation, TrayOrientation
};
GlobalConfig& dialogConfig;
void addWidgetToLayout(QWidget* widget, QBoxLayout* layout, int spacingBefore, QString tooltip, QString kconfigName);
void createStartupTab();
void replaceBackendsInTab();
void createGeneralTab();
void createControlsTab();
void createOrientationGroup(const QString& labelSliderOrientation, QGridLayout* orientationLayout, int row, KMixPrefDlgPrefOrientationType type);
QFrame *m_generalTab;
QFrame *m_startupTab;
QFrame *m_controlsTab;
QCheckBox *m_dockingChk;
QLabel *dynamicControlsRestoreWarning;
QCheckBox *m_showTicks;
QCheckBox *m_showLabels;
QCheckBox* m_showOSD;
QCheckBox *m_onLogin;
QCheckBox *allowAutostart;
QLabel *allowAutostartWarning;
QBoxLayout *layoutControlsTab;
DialogChooseBackends* dvc;
QRadioButton *_rbVertical;
QRadioButton *_rbHorizontal;
QRadioButton *_rbTraypopupVertical;
QRadioButton *_rbTraypopupHorizontal;
KPageWidgetItem* generalPage;
KPageWidgetItem* soundmenuPage;
KPageWidgetItem* startupPage;
};
#endif // KMIXPREFDLG_H

View file

@ -1,89 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gui/kmixtoolbox.h"
#include <QWidget>
#include <QString>
//#include <kdebug.h>
#include <kglobalaccel.h>
#include <klocale.h>
#include <knotification.h>
#include "gui/guiprofile.h"
#include "mdwslider.h"
#include "gui/mixdevicewidget.h"
#include "core/mixdevice.h"
#include "core/mixer.h"
#include "viewbase.h"
// TODO KMixToolbox is rather superfluous today, as there is no "KMix Applet" any more, and it was probably always bad style.
// I only have to think what to do with KMixToolBox::notification()
/***********************************************************************************
KMixToolbox contains several GUI relevant methods that are shared between the
KMix Main Program, and the KMix Applet.
kmixctrl - as not non-GUI application - does NOT link to KMixToolBox.
This means: Shared GUI stuff goes into the KMixToolBox class , non-GUI stuff goes
into the MixerToolBox class.
***********************************************************************************/
void KMixToolBox::setIcons(QList<QWidget *> &mdws, bool on ) {
for (int i=0; i < mdws.count(); ++i ){
QWidget *mdw = mdws[i];
if ( mdw->inherits("MixDeviceWidget") ) { // -<- play safe here
static_cast<MixDeviceWidget*>(mdw)->setIcons( on );
}
}
}
void KMixToolBox::setLabels(QList<QWidget *> &mdws, bool on ) {
for (int i=0; i < mdws.count(); ++i ){
QWidget *mdw = mdws[i];
if ( mdw->inherits("MixDeviceWidget") ) { // -<- play safe here
static_cast<MixDeviceWidget*>(mdw)->setLabeled( on );
}
}
}
void KMixToolBox::setTicks(QList<QWidget *> &mdws, bool on ) {
for (int i=0; i < mdws.count(); ++i ){
QWidget *mdw = mdws[i];
if ( mdw->inherits("MixDeviceWidget") ) { // -<- play safe here
static_cast<MixDeviceWidget*>(mdw)->setTicks( on );
}
}
}
void KMixToolBox::notification(const char *notificationName, const QString &text,
const QStringList &actions, QObject *receiver,
const char *actionSlot)
{
KNotification *notification = new KNotification();
notification->setEventID(notificationName);
notification->setText(text);
if (!actions.isEmpty() && receiver && actionSlot) {
notification->setActions(actions);
QObject::connect(notification, SIGNAL(activated(uint)), receiver, actionSlot);
}
notification->send();
}

View file

@ -1,44 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KMIXTOOLBOX_H
#define KMIXTOOLBOX_H
#include "qlist.h"
#include "qwidget.h"
/**
* This toolbox contains various static methods that are shared throughout KMix.
* The reason, why it is not put in a common base class is, that the classes are
* very different and cannot be changed (e.g. KPanelApplet) without major headache.
*/
class KMixToolBox {
public:
static void setIcons (QList<QWidget *> &mdws, bool on );
static void setLabels (QList<QWidget *> &mdws, bool on );
static void setTicks (QList<QWidget *> &mdws, bool on );
static void notification(const char *notificationName, const QString &text, const QStringList &actions = QStringList(), QObject *receiver = 0, const char *actionSlot = 0);
};
#endif

View file

@ -1,441 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gui/ksmallslider.h"
// For INT_MAX
#include <limits.h>
#include <kdebug.h>
#include <QWidget>
#include <qpainter.h>
#include <QColor>
#include <qbrush.h>
#include <QtGui/qevent.h>
#include <qstyle.h>
#include <QtGui/qstyleoption.h>
#include "kglobalsettings.h"
#include "core/mixer.h"
KSmallSlider::KSmallSlider( int minValue, int maxValue, int pageStep,
int value, Qt::Orientation orientation,
QWidget *parent, const char * /*name*/ )
: QAbstractSlider( parent )
{
init();
setOrientation(orientation);
setRange(minValue, maxValue);
setSingleStep(1);
setPageStep(pageStep);
setValue(value);
setTracking(true);
}
void KSmallSlider::init()
{
grayed = false;
setFocusPolicy( Qt::TabFocus );
colHigh = QColor(0,255,0);
colLow = QColor(255,0,0);
colBack = QColor(0,0,0);
grayHigh = QColor(255,255,255);
grayLow = QColor(128,128,128);
grayBack = QColor(0,0,0);
}
int KSmallSlider::positionFromValue( int v ) const
{
return positionFromValue( v, available() );
}
int KSmallSlider::valueFromPosition( int p ) const
{
if ( orientation() == Qt::Vertical ) {
// Coordiante System starts at TopLeft, but the slider values increase from Bottom to Top
// Thus "revert" the position
int avail = available();
return valueFromPosition( avail - p, avail );
}
else {
// Horizontal everything is fine. Slider values match with Coordinate System
return valueFromPosition( p, available() );
}
}
/* postionFromValue() discontinued in in Qt4 => taken from Qt3 */
int KSmallSlider::positionFromValue( int logical_val, int span ) const
{
if ( span <= 0 || logical_val < minimum() || maximum() <= minimum() )
return 0;
if ( logical_val > maximum() )
return span;
uint range = maximum() - minimum();
uint p = logical_val - minimum();
if ( range > (uint)INT_MAX/4096 ) {
const int scale = 4096*2;
return ( (p/scale) * span ) / (range/scale);
// ### the above line is probably not 100% correct
// ### but fixing it isn't worth the extreme pain...
} else if ( range > (uint)span ) {
return (2*p*span + range) / (2*range);
} else {
uint div = span / range;
uint mod = span % range;
return p*div + (2*p*mod + range) / (2*range);
}
//equiv. to (p*span)/range + 0.5
// no overflow because of this implicit assumption:
// span <= 4096
}
/* valueFromPositon() discontinued in in Qt4 => taken from Qt3 */
int KSmallSlider::valueFromPosition( int pos, int span ) const
{
if ( span <= 0 || pos <= 0 )
return minimum();
if ( pos >= span )
return maximum();
uint range = maximum() - minimum();
if ( (uint)span > range )
return minimum() + (2*pos*range + span) / (2*span);
else {
uint div = range / span;
uint mod = range % span;
return minimum() + pos*div + (2*pos*mod + span) / (2*span);
}
// equiv. to minimum() + (pos*range)/span + 0.5
// no overflow because of this implicit assumption:
// pos <= span < sqrt(INT_MAX+0.0625)+0.25 ~ sqrt(INT_MAX)
}
void KSmallSlider::resizeEvent( QResizeEvent * )
{
update();
//QWidget::resizeEvent( ev );
}
// Returns the really available space for the slider. If there is no space, 0 is returned;
int KSmallSlider::available() const
{
int available = 0;
if ( orientation() == Qt::Vertical) {
available = height();
}
else {
available = width();
}
if ( available > 1 ) {
available -= 2;
}
else {
available = 0;
}
return available;
}
namespace
{
void gradient( QPainter &p, bool hor, const QRect &rect, const QColor &ca, const QColor &cb, int /*ncols*/)
{
int rDiff, gDiff, bDiff;
int rca, gca, bca, rcb, gcb, bcb;
int x, y;
if ((rect.width()<=0) || (rect.height()<=0)) return;
rDiff = (rcb = cb.red()) - (rca = ca.red());
gDiff = (gcb = cb.green()) - (gca = ca.green());
bDiff = (bcb = cb.blue()) - (bca = ca.blue());
int rl = rca << 16;
int gl = gca << 16;
int bl = bca << 16;
int rcdelta = ((1<<16) / ((!hor) ? rect.height() : rect.width())) * rDiff;
int gcdelta = ((1<<16) / ((!hor) ? rect.height() : rect.width())) * gDiff;
int bcdelta = ((1<<16) / ((!hor) ? rect.height() : rect.width())) * bDiff;
// these for-loops could be merged, but the if's in the inner loop
// would make it slow
if (!hor)
{
for ( y = rect.top(); y <= rect.bottom(); y++ ) {
rl += rcdelta;
gl += gcdelta;
bl += bcdelta;
p.setPen(QColor(rl>>16, gl>>16, bl>>16));
p.drawLine(rect.left(), y, rect.right(), y);
}
} else
{
for( x = rect.left(); x <= rect.right(); x++) {
rl += rcdelta;
gl += gcdelta;
bl += bcdelta;
p.setPen(QColor(rl>>16, gl>>16, bl>>16));
p.drawLine(x, rect.top(), x, rect.bottom());
}
}
}
QColor interpolate( const QColor& low, const QColor& high, int percent ) {
if ( percent<=0 ) return low; else
if ( percent>=100 ) return high; else
return QColor(
low.red() + (high.red()-low.red()) * percent/100,
low.green() + (high.green()-low.green()) * percent/100,
low.blue() + (high.blue()-low.blue()) * percent/100 );
}
}
void KSmallSlider::paintEvent( QPaintEvent * )
{
// kDebug(67100) << "KSmallSlider::paintEvent: width() = " << width() << ", height() = " << height();
QPainter p( this );
int sliderPos = positionFromValue( QAbstractSlider::value() );
// ------------------------ draw 3d border ---------------------------------------------
QStyleOptionSlider option;
option.init(this);
style()->drawPrimitive ( QStyle::PE_Frame, &option, &p );
// ------------------------ draw lower/left part ----------------------------------------
if ( width()>2 && height()>2 )
{
if ( orientation() == Qt::Horizontal ) {
QRect outer = QRect( 1, 1, sliderPos, height() - 2 );
// kDebug(67100) << "KSmallSlider::paintEvent: outer = " << outer;
if ( grayed )
gradient( p, true, outer, grayLow,
interpolate( grayLow, grayHigh, 100*sliderPos/(width()-2) ),
32 );
else
gradient( p, true, outer, colLow,
interpolate( colLow, colHigh, 100*sliderPos/(width()-2) ),
32 );
}
else {
QRect outer = QRect( 1, height()-sliderPos-1, width() - 2, sliderPos-1 );
/*
kDebug(67100) << "KSmallSlider::paintEvent: sliderPos=" << sliderPos
<< "height()=" << height()
<< "width()=" << width()
<< "outer = " << outer;
*/
if ( grayed )
gradient( p, false, outer,
interpolate( grayLow, grayHigh, 100*sliderPos/(height()-2) ),
grayLow, 32 );
else
gradient( p, false, outer,
interpolate( colLow, colHigh, 100*sliderPos/(height()-2) ),
colLow, 32 );
}
// -------- draw upper/right part --------------------------------------------------
QRect inner;
if ( orientation() == Qt::Vertical ) {
inner = QRect( 1, 1, width() - 2, height() - 2 -sliderPos );
}
else {
inner = QRect( sliderPos + 1, 1, width() - 2 - sliderPos, height() - 2 );
}
if ( grayed ) {
p.setBrush( grayBack );
p.setPen( grayBack );
} else {
p.setBrush( colBack );
p.setPen( colBack );
}
p.drawRect( inner );
}
}
void KSmallSlider::mousePressEvent( QMouseEvent *e )
{
//resetState();
if ( e->button() == Qt::RightButton ) {
return;
}
int pos = goodPart( e->pos() );
moveSlider( pos );
}
void KSmallSlider::mouseMoveEvent( QMouseEvent *e )
{
int pos = goodPart( e->pos() );
moveSlider( pos );
}
void KSmallSlider::wheelEvent( QWheelEvent * qwe)
{
// kDebug(67100) << "KSmallslider::wheelEvent()";
// bko313579 Do not use "delta", as that is setting more related to documents (Editor, Browser). KMix should
// simply always use its own VOLUME_STEP_DIVISOR as a base for percentage change.
bool decrease = qwe->delta() < 0;
if (qwe->orientation() == Qt::Horizontal) // Reverse horizontal scroll: bko228780
decrease = !decrease;
int inc = ( maximum() - minimum() ) / Volume::VOLUME_STEP_DIVISOR;
if ( inc < 1)
inc = 1;
//kDebug(67100) << "KSmallslider::wheelEvent() inc=" << inc << "delta=" << e->delta();
int newVal;
if ( !decrease ) {
newVal = QAbstractSlider::value() + inc;
}
else {
newVal = QAbstractSlider::value() - inc;
}
setValue( newVal );
emit valueChanged(newVal);
qwe->accept(); // Accept the event
// Hint: Qt autmatically triggers a valueChange() when we do setValue()
}
/*
* Moves slider to a dedicated position. If the value has changed
*/
void KSmallSlider::moveSlider( int pos )
{
int a = available();
int newPos = qMin( a, qMax( 0, pos ) ); // keep it inside the available bounds of the slider
int newVal = valueFromPosition( newPos );
if ( newVal != value() ) {
setValue( newVal );
emit valueChanged(newVal);
// probably done by Qt: emit valueChanged( value() ); // Only for external use
// probably we need update() here
}
update();
}
int KSmallSlider::goodPart( const QPoint &p ) const
{
if ( orientation() == Qt::Vertical ) {
return p.y() - 1;
}
else {
return p.x() - 1;
}
}
/***************** SIZE STUFF START ***************/
QSize KSmallSlider::sizeHint() const
{
//constPolish();
const int length = 25;
const int thick = 10;
if ( orientation() == Qt::Vertical )
return QSize( thick, length );
else
return QSize( length, thick );
}
QSize KSmallSlider::minimumSizeHint() const
{
QSize s(10,10);
return s;
}
QSizePolicy KSmallSlider::sizePolicy() const
{
if ( orientation() == Qt::Vertical ) {
//kDebug(67100) << "KSmallSlider::sizePolicy() vertical value=(Fixed,MinimumExpanding)\n";
return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding );
}
else {
//kDebug(67100) << "KSmallSlider::sizePolicy() horizontal value=(MinimumExpanding,Fixed)\n";
return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
}
}
/***************** SIZE STUFF END ***************/
void KSmallSlider::setGray( bool value )
{
if ( grayed!=value )
{
grayed = value;
update();
//repaint();
}
}
bool KSmallSlider::gray() const
{
return grayed;
}
void KSmallSlider::setColors( QColor high, QColor low, QColor back )
{
colHigh = high;
colLow = low;
colBack = back;
update();
//repaint();
}
void KSmallSlider::setGrayColors( QColor high, QColor low, QColor back )
{
grayHigh = high;
grayLow = low;
grayBack = back;
update();
//repaint();
}
#include "moc_ksmallslider.cpp"

View file

@ -1,88 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KSMALLSLIDER_H
#define KSMALLSLIDER_H
#include <qabstractslider.h>
#include <qpixmap.h>
#include "core/volume.h"
#include "volumesliderextradata.h"
class KSmallSlider : public QAbstractSlider
{
Q_OBJECT
public:
KSmallSlider( int minValue, int maxValue, int pageStep, int value,
Qt::Orientation, QWidget *parent, const char *name=0 );
/* void setChid(Volume::ChannelID chid) { this->chid = chid; };
Volume::ChannelID getChid() { return chid; };*/
QSize sizeHint() const;
QSizePolicy sizePolicy() const;
QSize minimumSizeHint() const;
bool gray() const;
VolumeSliderExtraData extraData;
public slots:
void setGray( bool value );
void setColors( QColor high, QColor low, QColor back );
void setGrayColors( QColor high, QColor low, QColor back );
signals:
void valueChanged( int value );
protected:
void resizeEvent( QResizeEvent * );
void paintEvent( QPaintEvent * );
void mousePressEvent( QMouseEvent * );
void mouseMoveEvent( QMouseEvent * );
void wheelEvent( QWheelEvent * );
void valueChange();
private:
void init();
int positionFromValue( int ) const;
int valueFromPosition( int ) const;
int positionFromValue( int logical_val, int span ) const;
int valueFromPosition( int pos, int span ) const;
void moveSlider( int );
int available() const;
int goodPart( const QPoint& ) const;
bool grayed;
QColor colHigh, colLow, colBack;
QColor grayHigh, grayLow, grayBack;
// Volume::ChannelID chid;
};
#endif

View file

@ -1,199 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// KMix
#include "mdwenum.h"
#include "core/mixer.h"
#include "viewbase.h"
// KDE
#include <kaction.h>
#include <kactioncollection.h>
#include <kconfig.h>
#include <kcombobox.h>
#include <kdebug.h>
#include <kglobalaccel.h>
#include <klocale.h>
#include <kmenu.h>
#include <ktoggleaction.h>
// Qt
#include <QCursor>
#include <QLabel>
#include <QtGui/qevent.h>
#include <QObject>
#include <QBoxLayout>
/**
* Class that represents an Enum element (a select one-from-many selector)
* The orientation (horizontal, vertical) is ignored
*/
MDWEnum::MDWEnum( std::shared_ptr<MixDevice> md,
Qt::Orientation orientation,
QWidget* parent, ViewBase* view, ProfControl* par_pctl) :
MixDeviceWidget(md, false, orientation, parent, view, par_pctl),
_label(0), _enumCombo(0), _layout(0)
{
// create actions (on _mdwActions, see MixDeviceWidget)
// KStandardAction::showMenubar() is in MixDeviceWidget now
KToggleAction *action = _mdwActions->add<KToggleAction>( "hide" );
action->setText( i18n("&Hide") );
connect(action, SIGNAL(triggered(bool)), SLOT(setDisabled(bool)));
QAction *c = _mdwActions->addAction( "keys" );
c->setText( i18n("C&onfigure Shortcuts...") );
connect(c, SIGNAL(triggered(bool)), SLOT(defineKeys()));
// create widgets
createWidgets();
/* remove this for production version
QAction *a = _mdwActions->addAction( "Next Value" );
c->setText( i18n( "Next Value" ) );
connect(a, SIGNAL(triggered(bool)), SLOT(nextEnumId()));
*/
installEventFilter( this ); // filter for popup
}
MDWEnum::~MDWEnum()
{
}
void MDWEnum::createWidgets()
{
if ( _orientation == Qt::Vertical ) {
_layout = new QVBoxLayout( this );
_layout->setAlignment(Qt::AlignLeft);
}
else {
_layout = new QHBoxLayout( this );
_layout->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
}
_label = new QLabel( m_mixdevice->readableName(), this);
_layout->addWidget(_label);
_enumCombo = new KComboBox( false, this);
_enumCombo->installEventFilter(this);
// ------------ fill ComboBox start ------------
int maxEnumId= m_mixdevice->enumValues().count();
for (int i=0; i<maxEnumId; i++ ) {
_enumCombo->addItem( m_mixdevice->enumValues().at(i));
}
// ------------ fill ComboBox end --------------
_layout->addWidget(_enumCombo);
connect( _enumCombo, SIGNAL(activated(int)), this, SLOT(setEnumId(int)) );
_enumCombo->setToolTip( m_mixdevice->readableName() );
_layout->addStretch(1);
}
void MDWEnum::update()
{
if ( m_mixdevice->isEnum() ) {
//kDebug(67100) << "MDWEnum::update() enumID=" << m_mixdevice->enumId();
_enumCombo->setCurrentIndex( m_mixdevice->enumId() );
}
else {
kError(67100) << "MDWEnum::update() enumID=" << m_mixdevice->enumId() << " is no Enum ... skipped";
}
}
void MDWEnum::showContextMenu(const QPoint& pos )
{
if( m_view == 0 )
return;
KMenu *menu = m_view->getPopup();
menu->popup( pos );
}
QSizePolicy MDWEnum::sizePolicy() const
{
return QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
}
/**
This slot is called, when a user has clicked the mute button. Also it is called by any other
associated KAction like the context menu.
*/
void MDWEnum::nextEnumId() {
if( m_mixdevice->isEnum() ) {
int curEnum = enumId();
if ( curEnum < m_mixdevice->enumValues().count() ) {
// next enum value
setEnumId(curEnum+1);
}
else {
// wrap around
setEnumId(0);
}
} // isEnum
}
void MDWEnum::setEnumId(int value)
{
if ( m_mixdevice->isEnum() ) {
m_mixdevice->setEnumId( value );
m_mixdevice->mixer()->commitVolumeChange( m_mixdevice );
}
}
int MDWEnum::enumId()
{
if ( m_mixdevice->isEnum() ) {
return m_mixdevice->enumId();
}
else {
return 0;
}
}
void MDWEnum::setDisabled( bool hide )
{
emit guiVisibilityChange(this, !hide);
}
/**
* An event filter for the various QWidgets. We watch for Mouse press Events, so
* that we can popup the context menu.
*/
bool MDWEnum::eventFilter( QObject* obj, QEvent* e )
{
if (e->type() == QEvent::MouseButtonPress) {
QMouseEvent *qme = static_cast<QMouseEvent*>(e);
if (qme->button() == Qt::RightButton) {
showContextMenu();
return true;
}
} else if (e->type() == QEvent::ContextMenu) {
QPoint pos = reinterpret_cast<QWidget *>(obj)->mapToGlobal(QPoint(0, 0));
showContextMenu(pos);
return true;
}
return QWidget::eventFilter(obj,e);
}
#include "moc_mdwenum.cpp"

View file

@ -1,80 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2004 Chrisitan Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MDWENUM_H
#define MDWENUM_H
#include <QWidget>
#include "core/volume.h"
// KMix
class MixDevice;
class ViewBase;
// KDE
class KAction;
class KComboBox;
// Qt
#include <QBoxLayout>
#include <QLabel>
#include "gui/mixdevicewidget.h"
class MDWEnum : public MixDeviceWidget
{
Q_OBJECT
public:
MDWEnum( std::shared_ptr<MixDevice> md,
Qt::Orientation orientation,
QWidget* parent, ViewBase* view, ProfControl* pctl);
~MDWEnum();
void addActionToPopup( KAction *action );
QSizePolicy sizePolicy() const;
bool eventFilter( QObject* obj, QEvent* e );
public slots:
// GUI hide and show
void setDisabled(bool);
// Enum handling: next and selecting
void nextEnumId();
int enumId();
void setEnumId(int value);
void update();
virtual void showContextMenu(const QPoint& pos = QCursor::pos());
signals:
void guiVisibilityChange(MixDeviceWidget* source, bool enable);
private:
void createWidgets();
QLabel *_label;
KComboBox *_enumCombo;
QBoxLayout *_layout;
};
#endif

View file

@ -1,51 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//KMix
#include "mdwmoveaction.h"
#include "core/mixdevice.h"
// KDE
#include <KIcon>
// Qt
#include <QString>
MDWMoveAction::MDWMoveAction(std::shared_ptr<MixDevice> md, QObject *parent)
: KAction(parent), m_mixDevice(md)
{
Q_ASSERT(md);
setText(m_mixDevice->readableName());
setIcon(KIcon(m_mixDevice->iconName()));
connect(this, SIGNAL(triggered(bool)), SLOT(triggered(bool)));
}
MDWMoveAction::~MDWMoveAction()
{
}
void MDWMoveAction::triggered(bool checked)
{
Q_UNUSED(checked);
emit moveRequest(m_mixDevice->id());
}

View file

@ -1,46 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MDWMoveAction_h
#define MDWMoveAction_h
#include <KAction>
#include "core/mixdevice.h"
class MDWMoveAction : public KAction
{
Q_OBJECT
public:
MDWMoveAction(std::shared_ptr<MixDevice> md, QObject *parent);
~MDWMoveAction();
signals:
void moveRequest(QString id);
protected slots:
void triggered(bool checked);
private:
std::shared_ptr<MixDevice> m_mixDevice;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,184 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright Chrisitan Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MDWSLIDER_H
#define MDWSLIDER_H
#include <KShortcut>
#include "volumeslider.h"
#include <QCheckBox>
#include <QList>
#include <QWidget>
#include <qlist.h>
#include <qpixmap.h>
#include <QBoxLayout>
#include <QToolButton>
#include <QLabel>
class KAction;
class KMenu;
#include <kshortcut.h>
class MixDevice;
class VerticalText;
class ViewBase;
#include "gui/mixdevicewidget.h"
#include "core/volume.h"
class MDWSlider : public MixDeviceWidget
{
Q_OBJECT
public:
MDWSlider( std::shared_ptr<MixDevice> md,
bool includePlayback, bool includeCapture,
bool includeMixerName, bool small, Qt::Orientation,
QWidget* parent, ViewBase* view, ProfControl *pctl);
virtual ~MDWSlider();
enum LabelType { LT_ALL, LT_FIRST_CAPTURE, LT_NONE };
void addActionToPopup( KAction *action );
void createActions();
void createShortcutActions();
// GUI
bool isStereoLinked() const { return m_linked; }
void setStereoLinked( bool value );
void setLabeled( bool value );
void setTicks( bool ticks );
void setIcons( bool value );
// void setIcon( QString filename, QLabel** label );
void setIcon( QString filename, QWidget* label );
QToolButton* addMediaButton(QString iconName, QLayout* layout, QWidget *parent);
void updateMediaButton();
void setColors( QColor high, QColor low, QColor back );
void setMutedColors( QColor high, QColor low, QColor back );
bool eventFilter( QObject* obj, QEvent* e );
QString iconName();
// Layout
QSizePolicy sizePolicy() const;
QSize sizeHint() const;
int labelExtentHint() const;
void setLabelExtent(int extent);
bool hasMuteButton() const;
void setMuteButtonSpace(bool);
void setCaptureLEDSpace(bool);
bool hasCaptureLED() const;
static VolumeSliderExtraData DummVolumeSliderExtraData;
static bool debugMe;
public slots:
void toggleRecsrc();
void toggleMuted();
void toggleStereoLinked();
void setDisabled( bool value );
void update();
void showMoveMenu();
virtual void showContextMenu( const QPoint &pos = QCursor::pos() );
void increaseOrDecreaseVolume(bool arg1, Volume::VolumeTypeFlag volumeType);
VolumeSliderExtraData& extraData(QAbstractSlider *slider);
void addMediaControls(QBoxLayout* arg1);
signals:
void toggleMenuBar(bool value);
void guiVisibilityChange(MixDeviceWidget* source, bool enable);
private slots:
void setRecsrc( bool value );
void setMuted(bool value);
void volumeChange( int );
void sliderPressed();
void sliderReleased();
void increaseVolume();
void decreaseVolume();
void moveStreamAutomatic();
void moveStream( QString destId );
void mediaPlay(bool);
void mediaNext(bool);
void mediaPrev(bool);
private:
KShortcut dummyShortcut;
QPixmap loadIcon( QString filename );
void createWidgets( bool showMuteLED, bool showCaptureLED, bool includeMixer );
void addSliders( QBoxLayout *volLayout, char type, Volume& vol,
QList<QAbstractSlider *>& ref_sliders, QString tooltipText );
//void addDefaultLabel(QBoxLayout *layout, Qt::Orientation orientation);
// Methods that are called two times from a wrapper. Once for playabck, once for capture
void setStereoLinkedInternal( QList< QAbstractSlider* >& ref_sliders, bool showSubcontrolLabels);
void setTicksInternal( QList< QAbstractSlider* >& ref_sliders, bool ticks );
void volumeChangeInternal(Volume& vol, QList< QAbstractSlider* >& ref_sliders );
void updateInternal(Volume& vol, QList< QAbstractSlider* >& ref_sliders, bool muted);
QWidget* createLabel(QWidget* parent, QString& label, QBoxLayout *layout, bool);
QString calculatePlaybackIcon(MediaController::PlayState playState);
void guiAddSlidersAndMediacontrols(bool playSliders, bool capSliders, bool mediaControls, QBoxLayout* layout, const QString& tooltipText, const QString& captureTooltipText);
void guiAddCaptureCheckbox(bool wantsCaptureLED, const Qt::Alignment& alignmentForCapture,
QBoxLayout* layoutForCapture, const QString& captureTooltipText);
void guiAddMuteButton(bool wantsMuteButton, Qt::Alignment alignment, QBoxLayout* layoutForMuteButton, const QString& muteTooltipText);
void guiAddControlIcon(Qt::Alignment alignment, QBoxLayout* layout, const QString& tooltipText);
void addGlobalShortcut(KAction* action, const QString& label, bool dynamicControl);
bool m_linked;
QWidget *muteButtonSpacer;
QWidget *captureSpacer;
QWidget *labelSpacer;
// GUI: Top portion ( Icon + Mute)
QLabel *m_iconLabelSimple;
QToolButton* m_qcb;
QLabel* m_muteText;
QLabel *m_label; // is either QLabel or VerticalText
QToolButton *mediaButton;
QCheckBox* m_captureCheckbox;
QLabel* m_captureText;
int labelSpacing;
bool muteButtonSpacing;
bool captureLEDSpacing;
KActionCollection* _mdwMoveActions;
KMenu *m_moveMenu;
QList<QAbstractSlider *> m_slidersPlayback;
QList<QAbstractSlider *> m_slidersCapture;
bool m_sliderInWork;
int m_waitForSoundSetComplete;
QList<int> volumeValues;
};
#endif

View file

@ -1,232 +0,0 @@
///*
// * KMix -- KDE's full featured mini mixer
// *
// *
// * Copyright (C) 2004 Christian Esken <esken@kde.org>
// *
// * This program is free software; you can redistribute it and/or
// * modify it under the terms of the GNU Library General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the License, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// * Library General Public License for more details.
// *
// * You should have received a copy of the GNU Library General Public
// * License along with this program; if not, write to the Free
// * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// */
//
//#include <qcursor.h>
//#include <QLabel>
//#include <QtGui/qevent.h>
//#include <QObject>
//
//#include <klocale.h>
//#include <kconfig.h>
//#include <kaction.h>
//#include <kmenu.h>
//#include <kglobalaccel.h>
//#include <kdebug.h>
//#include <ktoggleaction.h>
//#include <kactioncollection.h>
//
//#include "mdwswitch.h"
//#include "core/mixer.h"
//#include "viewbase.h"
//#include "verticaltext.h"
//
///**
// * Class that represents a single Switch
// * The orientation (horizontal, vertical) can be configured
// */
//MDWSwitch::MDWSwitch(MixDevice* md,
// bool small, Qt::Orientation orientation,
// QWidget* parent, ViewBase* mw) :
// MixDeviceWidget(md,small,orientation,parent,mw),
// _label(0) , _labelV(0) , _switchLED(0), _layout(0)
//{
// // create actions (on _mdwActions, see MixDeviceWidget)
//
// // KStandardAction::showMenubar() is in MixDeviceWidget now
// KToggleAction *action = _mdwActions->add<KToggleAction>( "hide" );
// action->setText( i18n("&Hide") );
// connect(action, SIGNAL(triggered(bool)), SLOT(setDisabled()));
// KAction *b = _mdwActions->addAction( "keys" );
// b->setText( i18n("C&onfigure Shortcuts...") );
// connect(b, SIGNAL(triggered(bool)), SLOT(defineKeys()));
//
// // create widgets
// createWidgets();
//
// KAction *a = _mdwActions->addAction( "Toggle switch" );
// a->setText( i18n( "Toggle Switch" ) );
// connect(a, SIGNAL(triggered(bool)), SLOT(toggleSwitch()));
//
// // The accel keys are loaded in KMixerWidget::loadConfig, see kmixtoolbox.cpp
//
// installEventFilter( this ); // filter for popup
//}
//
//MDWSwitch::~MDWSwitch()
//{
//}
//
//
//void MDWSwitch::createWidgets()
//{
// if ( _orientation == Qt::Vertical ) {
// _layout = new QVBoxLayout( this );
// _layout->setAlignment(Qt::AlignHCenter);
// }
// else {
// _layout = new QHBoxLayout( this );
// _layout->setAlignment(Qt::AlignVCenter);
// }
// this->setToolTip( m_mixdevice->readableName() );
//
//
// _layout->addSpacing( 4 );
// // --- LEDS --------------------------
// if ( _orientation == Qt::Vertical ) {
// if( m_mixdevice->captureVolume().hasSwitch() )
// _switchLED = new QCheckBox( Qt::red,
// m_mixdevice->isRecSource()?KLed::On:KLed::Off,
// KLed::Sunken, KLed::Circular, this, "RecordLED" );
// else
// _switchLED = new QCheckBox( Qt::yellow, KLed::On, KLed::Sunken, KLed::Circular, this, "SwitchLED" );
// _switchLED->setFixedSize(16,16);
// _labelV = new VerticalText( this, m_mixdevice->readableName().toUtf8().data() );
//
// _layout->addWidget( _switchLED );
// _layout->addSpacing( 2 );
// _layout->addWidget( _labelV );
//
// _switchLED->installEventFilter( this );
// _labelV->installEventFilter( this );
// }
// else
// {
// if( m_mixdevice->captureVolume().hasSwitch() )
// _switchLED = new QCheckBox( Qt::red,
// m_mixdevice->isRecSource()?KLed::On:KLed::Off,
// KLed::Sunken, KLed::Circular, this, "RecordLED" );
// else
// _switchLED = new QCheckBox( Qt::yellow, KLed::On, KLed::Sunken, KLed::Circular, this, "SwitchLED" );
// _switchLED->setFixedSize(16,16);
// _label = new QLabel(m_mixdevice->readableName(), this );
// _label->setObjectName( QLatin1String("SwitchName" ));
//
// _layout->addWidget( _switchLED );
// _layout->addSpacing( 1 );
// _layout->addWidget( _label );
// _switchLED->installEventFilter( this );
// _label->installEventFilter( this );
// }
// connect( _switchLED, SIGNAL(stateChanged(bool)), this, SLOT(toggleSwitch()) );
// _layout->addSpacing( 4 );
//}
//
//void MDWSwitch::update()
//{
// if ( _switchLED != 0 ) {
// _switchLED->blockSignals( true );
// if( m_mixdevice->captureVolume().hasSwitch() )
// _switchLED->setState( m_mixdevice->isRecSource() ? KLed::On : KLed::Off );
// else
// _switchLED->setState( m_mixdevice->isMuted() ? KLed::Off : KLed::On );
//
// _switchLED->blockSignals( false );
// }
//}
//
//void MDWSwitch::setBackgroundRole(QPalette::ColorRole m)
//{
// if ( _label != 0 ){
// _label->setBackgroundRole(m);
// }
// if ( _labelV != 0 ){
// _labelV->setBackgroundRole(m);
// }
// _switchLED->setBackgroundRole(m);
// MixDeviceWidget::setBackgroundRole(m);
//}
//
//void MDWSwitch::showContextMenu()
//{
// if( m_view == 0 )
// return;
//
// KMenu *menu = m_view->getPopup();
//
// QPoint pos = QCursor::pos();
// menu->popup( pos );
//}
//
//
//QSizePolicy MDWSwitch::sizePolicy() const
//{
// if ( _orientation == Qt::Vertical ) {
// return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::MinimumExpanding );
// }
// else {
// return QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
// }
//}
//
///**
// This slot is called, when a user has clicked the mute button. Also it is called by any other
// associated KAction like the context menu.
//*/
//void MDWSwitch::toggleSwitch() {
// if( m_mixdevice->captureVolume().hasSwitch() )
// setSwitch( !m_mixdevice->isRecSource() );
// else
// setSwitch( !m_mixdevice->isMuted() );
//}
//
//void MDWSwitch::setSwitch(bool value)
//{
// if ( m_mixdevice->playbackVolume().hasSwitch() ) {
// if ( m_mixdevice->captureVolume().hasSwitch() ) {
// m_mixdevice->mixer()->setRecordSource( m_mixdevice->id(), value );
// }
// else {
// m_mixdevice->setMuted( value );
// m_mixdevice->mixer()->commitVolumeChange( m_mixdevice );
// }
// }
//}
//
//void MDWSwitch::setDisabled()
//{
// setDisabled( true );
//}
//
//void MDWSwitch::setDisabled( bool value ) {
// if ( m_disabled!=value)
// {
// value ? hide() : show();
// m_disabled = value;
// }
//}
//
///**
// * An event filter for the various QWidgets. We watch for Mouse press Events, so
// * that we can popup the context menu.
// */
//bool MDWSwitch::eventFilter( QObject* obj, QEvent* e )
//{
// if (e->type() == QEvent::MouseButtonPress) {
// QMouseEvent *qme = static_cast<QMouseEvent*>(e);
// if (qme->button() == Qt::RightButton) {
// showContextMenu();
// return true;
// }
// }
// return QWidget::eventFilter(obj,e);
//}
//
//#include "mdwswitch.moc"

View file

@ -1,79 +0,0 @@
////-*-C++-*-
///*
// * KMix -- KDE's full featured mini mixer
// *
// *
// * Copyright Chrisitan Esken <esken@kde.org>
// *
// * This program is free software; you can redistribute it and/or
// * modify it under the terms of the GNU Library General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the License, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// * Library General Public License for more details.
// *
// * You should have received a copy of the GNU Library General Public
// * License along with this program; if not, write to the Free
// * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// */
//
//#ifndef MDWSWITCH_H
//#define MDWSWITCH_H
//
//#include <QWidget>
//#include "core/volume.h"
//#include <qpixmap.h>
//
//#include <QBoxLayout>
//#include <QLabel>
//
//#include <QCheckBox>
//class KAction;
//
//class MixDevice;
//class VerticalText;
//class Mixer;
//class ViewBase;
//
//#include "gui/mixdevicewidget.h"
//
//class MDWSwitch : public MixDeviceWidget
//{
// Q_OBJECT
//
//public:
// MDWSwitch( MixDevice* md,
// bool small, Qt::Orientation orientation,
// QWidget* parent = 0, ViewBase* mw = 0);
// ~MDWSwitch();
//
// void addActionToPopup( KAction *action );
// QSizePolicy sizePolicy() const;
// void setBackgroundRole(QPalette::ColorRole m);
// bool eventFilter( QObject* obj, QEvent* e );
//
//public slots:
// // GUI hide and show
// void setDisabled();
// void setDisabled(bool);
//
// // Switch on/off
// void toggleSwitch();
// void setSwitch(bool value);
//
// void update();
// virtual void showContextMenu();
//
//private:
// void createWidgets();
//
// QLabel *_label;
// VerticalText *_labelV;
// QCheckBox *_switchLED;
// QBoxLayout *_layout;
//};
//
//#endif

View file

@ -1,118 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "gui/mixdevicewidget.h"
#include <kactioncollection.h>
#include <klocale.h>
#include <kiconloader.h>
#include <kconfig.h>
#include <kaction.h>
#include <kmenu.h>
#include <kglobalaccel.h>
#include <kshortcutsdialog.h>
#include <kdebug.h>
#include <qcursor.h>
#include <qevent.h>
#include <qpixmap.h>
#include <qmatrix.h>
#include "core/mixer.h"
#include "core/mixertoolbox.h"
#include "viewbase.h"
#include "ksmallslider.h"
#include "verticaltext.h"
/**
* Base Class for any Widget that represents a MixDevice.
* The mix device can be a real (hardware bound) MixDevice or a virtual mix device.
*
* The direction (horizontal, vertical) can be configured and whether it should
* be "small" (uses KSmallSlider instead of a normal slider widget). The actual implementations
* SHOULD honor these values - those who do not might not be suitable for placing in
* the panel applet or any other smallish settings.
*/
MixDeviceWidget::MixDeviceWidget(std::shared_ptr<MixDevice> md,
bool small, Qt::Orientation orientation,
QWidget* parent, ViewBase* view, ProfControl* par_pctl) :
QWidget( parent ), m_mixdevice( md ), m_view( view ), _pctl(par_pctl),
_orientation( orientation ), m_small( small )
, m_shortcutsDialog(0)
{
_mdwActions = new KActionCollection( this );
_mdwPopupActions = new KActionCollection( this );
QString name (md->id());
/* char* whatsThisChar = whatsthis.toUtf8().data();
QString w;
w = ki18n(whatsThisChar).toString(MixerToolBox::whatsthisControlLocale() );
this->setWhatsThis(w);
*/
QString whatsthisText = mixDevice()->mixer()->translateKernelToWhatsthis(name);
if ( whatsthisText != "---") {
setWhatsThis(whatsthisText);
}
}
MixDeviceWidget::~MixDeviceWidget()
{
}
void MixDeviceWidget::addActionToPopup( KAction *action )
{
_mdwActions->addAction( action->objectName(), action );
}
void MixDeviceWidget::defineKeys()
{
// Dialog for *global* shortcuts of this MDW
if ( m_shortcutsDialog == 0 ) {
m_shortcutsDialog = new KShortcutsDialog( KShortcutsEditor::GlobalAction );
m_shortcutsDialog->addCollection(_mdwPopupActions);
}
m_shortcutsDialog->configure();
}
void MixDeviceWidget::volumeChange( int ) { /* is virtual */ }
//void MixDeviceWidget::setDisabled( bool ) { /* is virtual */ }
//void MixDeviceWidget::setVolume( int /*channel*/, int /*vol*/ ) { /* is virtual */ }
//void MixDeviceWidget::setVolume( Volume /*vol*/ ) { /* is virtual */ }
//void MixDeviceWidget::update() { /* is virtual */ }
//void MixDeviceWidget::showContextMenu( const QPoint &pos ) { /* is virtual */ }
void MixDeviceWidget::setColors( QColor , QColor , QColor ) { /* is virtual */ }
void MixDeviceWidget::setIcons( bool ) { /* is virtual */ }
void MixDeviceWidget::setLabeled( bool ) { /* is virtual */ }
void MixDeviceWidget::setMutedColors( QColor , QColor , QColor ) { /* is virtual */ }
void MixDeviceWidget::mousePressEvent( QMouseEvent *e )
{
if ( e->button() == Qt::RightButton )
showContextMenu();
else {
QWidget::mousePressEvent(e);
}
}
#include "moc_mixdevicewidget.cpp"

View file

@ -1,97 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
* 1996-2000 Christian Esken <esken@kde.org>
* Sven Fischer <herpes@kawo2.rwth-aachen.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MIXDEVICEWIDGET_H
#define MIXDEVICEWIDGET_H
#include <QWidget>
#include "core/mixdevice.h"
#include "core/volume.h"
#include <qpixmap.h>
class KAction;
class KActionCollection;
class KShortcutsDialog;
class MixDevice;
class ProfControl;
class ViewBase;
class MixDeviceWidget
: public QWidget
{
Q_OBJECT
public:
MixDeviceWidget( std::shared_ptr<MixDevice> md,
bool small, Qt::Orientation orientation,
QWidget* parent, ViewBase*, ProfControl * );
virtual ~MixDeviceWidget();
void addActionToPopup( KAction *action );
std::shared_ptr<MixDevice> mixDevice() { return m_mixdevice; }
virtual void setColors( QColor high, QColor low, QColor back );
virtual void setIcons( bool value );
virtual void setMutedColors( QColor high, QColor low, QColor back );
virtual bool isStereoLinked() const { return false; }
virtual void setStereoLinked( bool ) {}
virtual void setLabeled( bool );
virtual void setTicks( bool ) {}
public slots:
virtual void defineKeys();
virtual void showContextMenu( const QPoint &pos = QCursor::pos() ) = 0;
/**
* update() is called whenever there are volume updates pending from the hardware for this MDW.
*/
virtual void update() = 0;
signals:
virtual void guiVisibilityChange(MixDeviceWidget* source, bool enable) = 0;
protected slots:
virtual void setDisabled( bool value ) = 0;
void volumeChange( int );
protected:
std::shared_ptr<MixDevice> m_mixdevice;
KActionCollection* _mdwActions;
KActionCollection* _mdwPopupActions;
ViewBase* m_view;
ProfControl* _pctl;
Qt::Orientation _orientation;
bool m_small;
KShortcutsDialog* m_shortcutsDialog;
private:
void mousePressEvent( QMouseEvent *e );
};
#endif

View file

@ -1,220 +0,0 @@
/*******************************************************************
* osdwidget.cpp
* Copyright 2009 Aurélien Gâteau <agateau@kde.org>
* Copyright 2009 Dario Andres Rodriguez <andresbajotierra@gmail.com>
* Copyright 2009 Christian Esken <christian.esken@arcor.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
******************************************************************/
#include "gui/osdwidget.h"
// Qt
#include <QGraphicsLinearLayout>
#include <QPainter>
#include <QTimer>
#include <QLabel>
// KDE
#include <KIcon>
#include <KDialog>
#include <KWindowSystem>
#include <Plasma/Svg>
#include <Plasma/Label>
#include <Plasma/Meter>
#include <Plasma/Theme>
#include <Plasma/WindowEffects>
#include "core/ControlManager.h"
#include <core/mixer.h>
OSDWidget::OSDWidget(QWidget * parent)
: Plasma::Dialog(parent, Qt::ToolTip),
m_scene(new QGraphicsScene(this)),
m_container(new QGraphicsWidget),
m_iconLabel(new Plasma::Label),
m_volumeLabel(new Plasma::Label),
m_meter(new Plasma::Meter),
m_hideTimer(new QTimer(this))
{
//Setup the window properties
KWindowSystem::setState(winId(), NET::KeepAbove);
KWindowSystem::setType(winId(), NET::Tooltip);
setAttribute(Qt::WA_X11NetWmWindowTypeToolTip, true);
m_meter->setMeterType(Plasma::Meter::BarMeterHorizontal);
m_meter->setMaximum(100);
//Set a fixed width for the volume label. To do that we need the text with the maximum width
//(this is true if the volume is at 100%). We simply achieve that by calling "setCurrentVolume".
setCurrentVolume(100, false);
/* We are registering for volume changes of all cards. An alternative
* would be to register to volume changes of the global master and additionally
* register to MasterChanges. That could be slightly more efficient
*/
ControlManager::instance().addListener(
QString(), // all mixers
ControlChangeType::Volume,
this,
QString("OSDWidget")
);
//Setup the auto-hide timer
m_hideTimer->setInterval(2000);
m_hideTimer->setSingleShot(true);
connect(m_hideTimer, SIGNAL(timeout()), this, SLOT(hide()));
//Setup the OSD layout
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(m_container);
layout->setContentsMargins(0, 0, 0, 0);
layout->addItem(m_iconLabel);
layout->addItem(m_meter);
layout->addItem(m_volumeLabel);
m_scene->addItem(m_container);
setGraphicsWidget(m_container);
themeUpdated();
connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeUpdated())); // e.g. for updating font
}
OSDWidget::~OSDWidget()
{
ControlManager::instance().removeListener(this);
}
void OSDWidget::controlsChange(int changeType)
{
ControlChangeType::Type type = ControlChangeType::fromInt(changeType);
std::shared_ptr<MixDevice> master = Mixer::getGlobalMasterMD();
switch (type )
{
case ControlChangeType::Volume:
if ( master )
{
setCurrentVolume(master->playbackVolume().getAvgVolumePercent(Volume::MALL), master->isMuted());
}
break;
default:
ControlManager::warnUnexpectedChangeType(type, this);
break;
}
}
void OSDWidget::activateOSD()
{
m_hideTimer->start();
}
void OSDWidget::themeUpdated()
{
//Set a font which makes the text appear as big (height-wise) as the meter.
//QFont font = QFont(m_volumeLabel->nativeWidget()->font());
Plasma::Theme* theme = Plasma::Theme::defaultTheme();
QPalette palette = m_volumeLabel->palette();
palette.setColor(QPalette::WindowText, theme->color(Plasma::Theme::TextColor));
m_volumeLabel->setPalette(palette);
QFont font = theme->font(Plasma::Theme::DefaultFont);
font.setPointSize(15);
m_volumeLabel->setFont(font);
QFontMetrics qfm(font);
QRect textSize = qfm.boundingRect("100 % ");
int widthHint = textSize.width();
int heightHint = textSize.height();
//setCurrentVolume(100,false);
m_volumeLabel->setMinimumWidth(widthHint);
m_volumeLabel->setMaximumHeight(heightHint);
m_volumeLabel->nativeWidget()->setFixedWidth(widthHint);
// m_volumeLabel->setText(oldText);
//Cache the icon pixmaps
QSize iconSize;
if (!Plasma::Theme::defaultTheme()->imagePath("icons/audio").isEmpty()) {
QFontMetrics fm(m_volumeLabel->font());
iconSize = QSize(fm.height(), fm.height());
Plasma::Svg svgIcon;
svgIcon.setImagePath("icons/audio");
svgIcon.setContainsMultipleImages(true);
svgIcon.resize(iconSize);
m_volumeHighPixmap = svgIcon.pixmap("audio-volume-high");
m_volumeMediumPixmap = svgIcon.pixmap("audio-volume-medium");
m_volumeLowPixmap = svgIcon.pixmap("audio-volume-low");
m_volumeMutedPixmap = svgIcon.pixmap("audio-volume-muted");
} else {
iconSize = QSize(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium);
m_volumeHighPixmap = KIcon( QLatin1String( "audio-volume-high" )).pixmap(iconSize);
m_volumeMediumPixmap = KIcon( QLatin1String( "audio-volume-medium" )).pixmap(iconSize);
m_volumeLowPixmap = KIcon( QLatin1String( "audio-volume-low" )).pixmap(iconSize);
m_volumeMutedPixmap = KIcon( QLatin1String( "audio-volume-muted" )).pixmap(iconSize);
}
m_iconLabel->nativeWidget()->setPixmap(m_volumeHighPixmap);
m_iconLabel->nativeWidget()->setFixedSize(iconSize);
m_iconLabel->setMinimumSize(iconSize);
m_iconLabel->setMaximumSize(iconSize);
m_meter->setMaximumHeight(iconSize.height());
m_volumeLabel->setMinimumHeight(iconSize.height());
m_volumeLabel->setMaximumHeight(iconSize.height());
m_volumeLabel->nativeWidget()->setFixedHeight(iconSize.height());
m_volumeLabel->setAlignment(Qt::AlignCenter);
m_volumeLabel->setWordWrap(false);
m_container->setMinimumSize(iconSize.width() * 13 + m_volumeLabel->nativeWidget()->width(), iconSize.height());
m_container->setMaximumSize(iconSize.width() * 13 + m_volumeLabel->nativeWidget()->width(), iconSize.height());
syncToGraphicsWidget();
}
/**
* Set volume level in percent
*/
void OSDWidget::setCurrentVolume(int volumeLevel, bool muted)
{
// kDebug() << "Meter is visible: " << m_meter->isVisible();
if ( muted )
{
volumeLevel = 0;
}
m_meter->setValue(volumeLevel);
if (!muted && (volumeLevel > 0)) {
if (volumeLevel < 25) {
m_iconLabel->nativeWidget()->setPixmap(m_volumeLowPixmap);
} else if (volumeLevel < 75) {
m_iconLabel->nativeWidget()->setPixmap(m_volumeMediumPixmap);
} else {
m_iconLabel->nativeWidget()->setPixmap(m_volumeHighPixmap);
}
} else {
m_iconLabel->nativeWidget()->setPixmap(m_volumeMutedPixmap);
}
//Show the volume %
m_volumeLabel->setText(QString::number(volumeLevel) + " %"); // if you change the text, please adjust textSize in themeUpdated()
}
#include "moc_osdwidget.cpp"

View file

@ -1,68 +0,0 @@
/*******************************************************************
* osdwidget.h
* Copyright 2009 Aurélien Gâteau <agateau@kde.org>
* Copyright 2009 Dario Andres Rodriguez <andresbajotierra@gmail.com>
* Copyright 2009 Christian Esken <christian.esken@arcor.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
******************************************************************/
#ifndef OSDWIDGET__H
#define OSDWIDGET__H
#include <Plasma/Dialog>
#include <QPixmap>
#include <QTimer>
#include <QGraphicsWidget>
namespace Plasma
{
class Label;
class Meter;
}
class OSDWidget : public Plasma::Dialog
{
Q_OBJECT
public:
OSDWidget(QWidget * parent = 0);
virtual ~OSDWidget();
void setCurrentVolume(int volumeLevel, bool muted);
void activateOSD();
public slots:
void controlsChange(int changeType);
private:
QGraphicsScene *m_scene;
QGraphicsWidget *m_container;
Plasma::Label *m_iconLabel;
Plasma::Label *m_volumeLabel;
Plasma::Meter *m_meter;
QTimer *m_hideTimer;
QPixmap m_volumeHighPixmap;
QPixmap m_volumeMediumPixmap;
QPixmap m_volumeLowPixmap;
QPixmap m_volumeMutedPixmap;
private slots:
void themeUpdated();
};
#endif

View file

@ -1,76 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2003-2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "verticaltext.h"
#include <QPainter>
#include <kdebug.h>
VerticalText::VerticalText(QWidget * parent, const QString& text, Qt::WFlags f) : QWidget(parent,f)
{
m_labelText = text;
}
VerticalText::~VerticalText() {
}
void VerticalText::setText(const QString& text) {
if (m_labelText != text) {
m_labelText = text;
updateGeometry();
}
}
void VerticalText::paintEvent ( QPaintEvent * /*event*/ ) {
QPainter paint(this);
paint.rotate(270);
// paint.translate(0,-4); // Silly "solution" to make underlengths work
// Fix for bug 72520
//- paint.drawText(-height()+2,width(),name());
//+ paint.drawText( -height()+2, width(), QString::fromUtf8(name()) );
int posX = -height();
int posY = width();
paint.drawText( posX, posY, m_labelText );
}
QSize VerticalText::sizeHint() const {
const QFontMetrics& fontMetr = fontMetrics();
QSize textSize(fontMetr.width(m_labelText), fontMetr.height());
textSize.transpose();
return textSize;
}
QSize VerticalText::minimumSizeHint() const
{
const QFontMetrics& fontMetr = fontMetrics();
QSize textSize(fontMetr.width("MMMM"), fontMetr.height());
textSize.transpose();
return textSize;
}
QSizePolicy VerticalText::sizePolicy () const
{
return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding);
}

View file

@ -1,45 +0,0 @@
//-*-C++-*-
/*
* KMix -- KDE's full featured mini mixer
*
* Copyright Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef VerticalText_h
#define VerticalText_h
#include <QString>
#include <QWidget>
class VerticalText : public QWidget
{
Q_OBJECT
public:
VerticalText(QWidget * parent, const QString&, Qt::WFlags f = 0);
~VerticalText();
void setText(const QString& text);
QSize sizeHint() const;
QSizePolicy sizePolicy () const;
QSize minimumSizeHint() const;
protected:
void paintEvent ( QPaintEvent * event );
private:
QString m_labelText;
};
#endif

View file

@ -1,481 +0,0 @@
/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 1996-2004 Christian Esken <esken@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// * Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "viewbase.h"
// QT
#include <qcursor.h>
#include <QtGui/qevent.h>
// KDE
#include <kaction.h>
#include <kmenu.h>
#include <klocale.h>
#include <kiconloader.h>
#include <kactioncollection.h>
#include <ktoggleaction.h>
#include <kstandardaction.h>
#include <kmessagebox.h>
// KMix
#include "dialogviewconfiguration.h"
#include "gui/guiprofile.h"
#include "gui/kmixtoolbox.h"
#include "gui/mixdevicewidget.h"
#include "gui/mdwslider.h"
#include "core/ControlManager.h"
#include "core/GlobalConfig.h"
#include "core/mixer.h"
#include "core/mixertoolbox.h"
/**
* Creates an empty View. To populate it with MixDevice instances, you must implement
* _setMixSet() in your derived class.
*/
ViewBase::ViewBase(QWidget* parent, QString id, Qt::WFlags f, ViewBase::ViewFlags vflags, QString guiProfileId, KActionCollection *actionColletion)
: QWidget(parent, f), _popMenu(NULL), _actions(actionColletion), _vflags(vflags), _guiProfileId(guiProfileId)
{
setObjectName(id);
m_viewId = id;
guiComplexity = ViewBase::SIMPLE;
configureIcon = new KIcon( QLatin1String( "configure" ));
if ( _actions == 0 ) {
// We create our own action collection, if the actionColletion was 0.
// This is currently done for the ViewDockAreaPopup, but only because it has not been converted to use the app-wide
// actionCollection(). This is a @todo.
_actions = new KActionCollection( this );
}
_localActionColletion = new KActionCollection( this );
// Plug in the "showMenubar" action, if the caller wants it. Typically this is only necessary for views in the KMix main window.
if ( vflags & ViewBase::HasMenuBar )
{
KToggleAction *m = static_cast<KToggleAction*>( _actions->action( name(KStandardAction::ShowMenubar) ) ) ;
if ( m != 0 ) {
bool visible = ( vflags & ViewBase::MenuBarVisible );
m->setChecked(visible);
}
}
}
ViewBase::~ViewBase()
{
delete configureIcon;
// Hint: The GUI profile will not be removed, as it is pooled and might be applied to a new View.
}
void ViewBase::addMixer(Mixer *mixer)
{
_mixers.append(mixer);
}
//void ViewBase::configurationUpdate() {
//}
QPushButton* ViewBase::createConfigureViewButton()
{
QPushButton* configureViewButton = new QPushButton(*configureIcon, "", this);
configureViewButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
configureViewButton->setToolTip(i18n( "Configure Channels" ));
connect(configureViewButton, SIGNAL(clicked(bool)), SLOT(configureView()));
return configureViewButton;
}
void ViewBase::updateGuiOptions()
{
setTicks(GlobalConfig::instance().data.showTicks);
setLabels(GlobalConfig::instance().data.showLabels);
updateMediaPlaybackIcons();
}
QString ViewBase::id() const {
return m_viewId;
}
bool ViewBase::isValid() const
{
return ( !_mixSet.isEmpty() || isDynamic() );
}
void ViewBase::setIcons (bool on) { KMixToolBox::setIcons (_mdws, on ); }
void ViewBase::setLabels(bool on) { KMixToolBox::setLabels(_mdws, on ); }
void ViewBase::setTicks (bool on) { KMixToolBox::setTicks (_mdws, on ); }
/**
* Updates all playback icons to their (new) state, e.g. Paused, or Playing
*/
void ViewBase::updateMediaPlaybackIcons()
{
for (int i = 0; i < _mdws.count(); ++i)
{
// Currently media controls are always attached to sliders => use MDWSlider
MDWSlider* mdw = qobject_cast<MDWSlider*>(_mdws[i]);
if (mdw != 0)
{
mdw->updateMediaButton();
}
}
}
/**
* Create all widgets.
* This is a loop over all supported devices of the corresponding view.
* On each device add() is called - the derived class must implement add() for creating and placing
* the real MixDeviceWidget.
* The added MixDeviceWidget is appended to the _mdws list.
*/
void ViewBase::createDeviceWidgets()
{
_setMixSet();
foreach ( std::shared_ptr<MixDevice> md, _mixSet )
{
QWidget* mdw = add(md); // a) Let the View implementation do its work
_mdws.append(mdw); // b) Add it to the local list
connect(mdw, SIGNAL(guiVisibilityChange(MixDeviceWidget*, bool)), SLOT(guiVisibilitySlot(MixDeviceWidget*, bool)));
}
if ( !isDynamic() )
{
QAction *action = _localActionColletion->addAction("toggle_channels");
action->setText(i18n("&Channels"));
connect(action, SIGNAL(triggered(bool)), SLOT(configureView()));
}
// allow view to "polish" itself
constructionFinished();
}
/**
* Called when a specific control is to be shown or hidden. At the moment it is only called via
* the "hide" action in the MDW context menu.
*
* @param mdw
* @param enable
*/
void ViewBase::guiVisibilitySlot(MixDeviceWidget* mdw, bool enable)
{
MixDevice* md = mdw->mixDevice().get();
kDebug() << "Change " << md->id() << " to visible=" << enable;
ProfControl* pctl = findMdw(md->id());
if (pctl == 0)
{
kWarning() << "MixDevice not found, and cannot be hidden, id=" << md->id();
return; // Ignore
}
pctl->setVisible(enable);
ControlManager::instance().announce(md->mixer()->id(), ControlChangeType::ControlList, QString("ViewBase::guiVisibilitySlot"));
}
// ---------- Popup stuff START ---------------------
void ViewBase::mousePressEvent( QMouseEvent *e )
{
if ( e->button() == Qt::RightButton )
showContextMenu();
}
/**
* Return a popup menu. This contains basic entries.
* More can be added by the caller.
*/
KMenu* ViewBase::getPopup()
{
popupReset();
return _popMenu;
}
void ViewBase::popupReset()
{
QAction *act;
delete _popMenu;
_popMenu = new KMenu( this );
_popMenu->addTitle( KIcon( QLatin1String( "kmix" ) ), i18n("Device Settings" ));
act = _localActionColletion->action( "toggle_channels" );
if ( act ) _popMenu->addAction(act);
act = _actions->action( "options_show_menubar" );
if ( act ) _popMenu->addAction(act);
}
/**
This will only get executed, when the user has removed all items from the view.
Don't remove this method, because then the user cannot get a menu for getting his
channels back
*/
void ViewBase::showContextMenu()
{
//kDebug(67100) << "ViewBase::showContextMenu()";
popupReset();
QPoint pos = QCursor::pos();
_popMenu->popup( pos );
}
void ViewBase::refreshVolumeLevels()
{
// is virtual
}
/**
* Check all Mixer instances of this view.
* If at least on is dynamic, return true.
* Please note that usually there is only one Mixer instance per View.
* The only exception as of today (June 2011) is the Tray Popup, which
* can contain controls from e.g. ALSA and MPRIS2 backends.
*/
bool ViewBase::isDynamic() const
{
foreach (Mixer* mixer , _mixers )
{
if ( mixer->isDynamic() )
return true;
}
return false;
}
void ViewBase::resetMdws()
{
// We need to delete the current MixDeviceWidgets so we can redraw them
while (!_mdws.isEmpty())
delete _mdws.takeFirst();
// _mixSet contains std::shared_ptr instances, so clear() should be enough to prevent mem leak
_mixSet.clear(); // Clean up our _mixSet so we can reapply our GUIProfile
}
int ViewBase::visibleControls()
{
int visibleCount = 0;
foreach (QWidget* qw, _mdws)
{
if (qw->isVisible())
++ visibleCount;
}
return visibleCount;
}
/**
* Open the View configuration dialog. The user can select which channels he wants
* to see and which not.
*/
void ViewBase::configureView()
{
Q_ASSERT( !isDynamic() );
DialogViewConfiguration* dvc = new DialogViewConfiguration(0, *this);
dvc->show();
}
void ViewBase::toggleMenuBarSlot() {
//kDebug(67100) << "ViewBase::toggleMenuBarSlot() start\n";
emit toggleMenuBar();
//kDebug(67100) << "ViewBase::toggleMenuBarSlot() done\n";
}
/**
* Loads the configuration of this view.
* <p>
* Future directions: The view should probably know its config in advance, so we can use it in #load() and #save()
*
*
* @param config The view for this config
*/
void ViewBase::load(KConfig *config)
{
ViewBase *view = this;
QString grp = "View.";
grp += view->id();
//KConfigGroup cg = config->group( grp );
kDebug(67100)
<< "KMixToolBox::loadView() grp=" << grp.toAscii();
static QString guiComplexityNames[3] =
{ GUIProfile::PNameSimple, GUIProfile::PNameExtended, GUIProfile::PNameAll };
// Certain bits are not saved for dynamic mixers (e.g. PulseAudio)
bool dynamic = isDynamic();
for (GUIComplexity guiCompl = ViewBase::SIMPLE; guiCompl <= ViewBase::ALL; ++guiCompl)
{
bool atLeastOneControlIsShown = false;
foreach(QWidget *qmdw, view->_mdws)
// for (int i = 0; i < view->_mdws.count(); ++i)
{
// QWidget *qmdw = view->_mdws[i];
if (qmdw->inherits("MixDeviceWidget"))
{
MixDeviceWidget* mdw = (MixDeviceWidget*) qmdw;
std::shared_ptr<MixDevice> md = mdw->mixDevice();
QString devgrp = md->configGroupName(grp);
KConfigGroup devcg = config->group(devgrp);
if (mdw->inherits("MDWSlider"))
{
// only sliders have the ability to split apart in mutliple channels
bool splitChannels = devcg.readEntry("Split", !mdw->isStereoLinked());
mdw->setStereoLinked(!splitChannels);
}
// Future directions: "Visibility" is very dirty: It is read from either config file or
// GUIProfile. Thus we have a lot of doubled mdw visibility code all throughout KMix.
bool mdwEnabled = false;
if (!dynamic && devcg.hasKey("Show"))
{
mdwEnabled = (true == devcg.readEntry("Show", true));
}
else
{
// If not configured in config file, use the default from the profile
if (findMdw(mdw->mixDevice()->id(), guiComplexityNames[guiCompl]) != 0)
{
mdwEnabled = true;
}
}
if (mdwEnabled)
{
atLeastOneControlIsShown = true;
}
mdw->setVisible(mdwEnabled);
} // inherits MixDeviceWidget
} // for all MDW's
if (atLeastOneControlIsShown)
{
this->guiComplexity = guiCompl;
break; // If there were controls in this complexity level, don't try more
}
} // for try = 0 ... 1
}
/**
* Checks whether the given mixDevice shall be shown according to the requested
* GUI complexity. All ProfControl objects are inspected. The first found is returned.
*
* @param mdwId The control ID
* @param requestedGuiComplexityName The GUI name
* @return The corresponding ProfControl*
*
*/
ProfControl* ViewBase::findMdw(const QString& mdwId, QString requestedGuiComplexityName)
{
foreach ( ProfControl* pControl, guiProfile()->getControls() )
{
QRegExp idRegExp(pControl->id);
//kDebug(67100) << "KMixToolBox::loadView() try match " << (*pControl).id << " for " << mdw->mixDevice()->id();
if ( mdwId.contains(idRegExp) &&
pControl->show == requestedGuiComplexityName )
{
return pControl;
}
} // iterate over all ProfControl entries
return 0;// not found
}
/**
* Returns the ProfControl* to the given id. If none is found, 0 is returned.
* GUI complexity. All ProfControl objects are inspected. The first found is returned.
*
* @param id The control ID
* @return The corresponding ProfControl*
*/
ProfControl* ViewBase::findMdw(const QString& id)
{
foreach ( ProfControl* pControl, guiProfile()->getControls() )
{
QRegExp idRegExp(pControl->id);
//kDebug(67100) << "KMixToolBox::loadView() try match " << (*pControl).id << " for " << mdw->mixDevice()->id();
if ( id.contains(idRegExp) )
{
return pControl;
}
} // iterate over all ProfControl entries
return 0;// not found
}
/*
* Saves the View configuration
*/
void ViewBase::save(KConfig *config)
{
ViewBase *view = this;
QString grp = "View.";
grp += view->id();
// Certain bits are not saved for dynamic mixers (e.g. PulseAudio)
bool dynamic = isDynamic(); // TODO 11 Dynamic view configuration
for (int i = 0; i < view->_mdws.count(); ++i)
{
QWidget *qmdw = view->_mdws[i];
if (qmdw->inherits("MixDeviceWidget"))
{
MixDeviceWidget* mdw = (MixDeviceWidget*) qmdw;
std::shared_ptr<MixDevice> md = mdw->mixDevice();
//kDebug(67100) << " grp=" << grp.toAscii();
//kDebug(67100) << " mixer=" << view->id().toAscii();
//kDebug(67100) << " mdwPK=" << mdw->mixDevice()->id().toAscii();
QString devgrp = QString("%1.%2.%3").arg(grp).arg(md->mixer()->id()).arg(md->id());
KConfigGroup devcg = config->group(devgrp);
if (mdw->inherits("MDWSlider"))
{
// only sliders have the ability to split apart in mutliple channels
devcg.writeEntry("Split", !mdw->isStereoLinked());
}
if (!dynamic)
{
devcg.writeEntry("Show", mdw->isVisibleTo(view));
// kDebug() << "Save devgrp" << devgrp << "show=" << mdw->isVisibleTo(view);
}
} // inherits MixDeviceWidget
} // for all MDW's
if (!dynamic)
{
// We do not save GUIProfiles (as they cannot be customized) for dynamic mixers (e.g. PulseAudio)
if (guiProfile()->isDirty())
{
kDebug(67100)
<< "Writing dirty profile. grp=" << grp;
guiProfile()->writeProfile();
}
}
}
// ---------- Popup stuff END ---------------------
#include "moc_viewbase.cpp"

Some files were not shown because too many files have changed in this diff Show more