├── AUTHORS ├── CHANGELOG ├── CMakeLists.txt ├── LICENSE ├── README.md └── src ├── CMakeLists.txt ├── cardwidget.cc ├── cardwidget.h ├── cardwidget.ui ├── channel.cc ├── channel.h ├── channelwidget.ui ├── devicewidget.cc ├── devicewidget.h ├── devicewidget.ui ├── elidinglabel.cc ├── elidinglabel.h ├── mainwindow.cc ├── mainwindow.h ├── mainwindow.ui ├── minimalstreamwidget.cc ├── minimalstreamwidget.h ├── pavucontrol-qt.desktop.in ├── pavucontrol.cc ├── pavucontrol.h ├── rolewidget.cc ├── rolewidget.h ├── sinkinputwidget.cc ├── sinkinputwidget.h ├── sinkwidget.cc ├── sinkwidget.h ├── sourceoutputwidget.cc ├── sourceoutputwidget.h ├── sourcewidget.cc ├── sourcewidget.h ├── streamwidget.cc ├── streamwidget.h ├── streamwidget.ui └── translations ├── CMakeLists.txt ├── pavucontrol-qt.desktop.yaml ├── pavucontrol-qt.ts ├── pavucontrol-qt_ar.desktop.yaml ├── pavucontrol-qt_ar.ts ├── pavucontrol-qt_arn.ts ├── pavucontrol-qt_as.ts ├── pavucontrol-qt_ast.ts ├── pavucontrol-qt_bg.desktop.yaml ├── pavucontrol-qt_bg.ts ├── pavucontrol-qt_bn_IN.ts ├── pavucontrol-qt_ca.desktop.yaml ├── pavucontrol-qt_ca.ts ├── pavucontrol-qt_cs.desktop.yaml ├── pavucontrol-qt_cs.ts ├── pavucontrol-qt_cy.ts ├── pavucontrol-qt_da.desktop.yaml ├── pavucontrol-qt_da.ts ├── pavucontrol-qt_de.desktop.yaml ├── pavucontrol-qt_de.ts ├── pavucontrol-qt_el.desktop.yaml ├── pavucontrol-qt_el.ts ├── pavucontrol-qt_en_GB.desktop.yaml ├── pavucontrol-qt_en_GB.ts ├── pavucontrol-qt_es.desktop.yaml ├── pavucontrol-qt_es.ts ├── pavucontrol-qt_et.desktop.yaml ├── pavucontrol-qt_et.ts ├── pavucontrol-qt_fi.desktop.yaml ├── pavucontrol-qt_fi.ts ├── pavucontrol-qt_fr.desktop.yaml ├── pavucontrol-qt_fr.ts ├── pavucontrol-qt_gl.desktop.yaml ├── pavucontrol-qt_gl.ts ├── pavucontrol-qt_gu.ts ├── pavucontrol-qt_he.desktop.yaml ├── pavucontrol-qt_he.ts ├── pavucontrol-qt_hi.desktop.yaml ├── pavucontrol-qt_hi.ts ├── pavucontrol-qt_hr.desktop.yaml ├── pavucontrol-qt_hr.ts ├── pavucontrol-qt_hu.desktop.yaml ├── pavucontrol-qt_hu.ts ├── pavucontrol-qt_id.desktop.yaml ├── pavucontrol-qt_id.ts ├── pavucontrol-qt_it.desktop.yaml ├── pavucontrol-qt_it.ts ├── pavucontrol-qt_ja.desktop.yaml ├── pavucontrol-qt_ja.ts ├── pavucontrol-qt_ka.desktop.yaml ├── pavucontrol-qt_ka.ts ├── pavucontrol-qt_kab.desktop.yaml ├── pavucontrol-qt_kk.desktop.yaml ├── pavucontrol-qt_kn.ts ├── pavucontrol-qt_ko.desktop.yaml ├── pavucontrol-qt_ko.ts ├── pavucontrol-qt_lg.desktop.yaml ├── pavucontrol-qt_lg.ts ├── pavucontrol-qt_lt.desktop.yaml ├── pavucontrol-qt_lt.ts ├── pavucontrol-qt_ml.ts ├── pavucontrol-qt_mr.ts ├── pavucontrol-qt_nb_NO.desktop.yaml ├── pavucontrol-qt_nb_NO.ts ├── pavucontrol-qt_nl.desktop.yaml ├── pavucontrol-qt_nl.ts ├── pavucontrol-qt_oc.desktop.yaml ├── pavucontrol-qt_oc.ts ├── pavucontrol-qt_or.ts ├── pavucontrol-qt_pa.ts ├── pavucontrol-qt_pl.desktop.yaml ├── pavucontrol-qt_pl.ts ├── pavucontrol-qt_pt.desktop.yaml ├── pavucontrol-qt_pt.ts ├── pavucontrol-qt_pt_BR.ts ├── pavucontrol-qt_ro.desktop.yaml ├── pavucontrol-qt_ro.ts ├── pavucontrol-qt_ru.desktop.yaml ├── pavucontrol-qt_ru.ts ├── pavucontrol-qt_si.desktop.yaml ├── pavucontrol-qt_si.ts ├── pavucontrol-qt_sk.desktop.yaml ├── pavucontrol-qt_sk.ts ├── pavucontrol-qt_sl.desktop.yaml ├── pavucontrol-qt_sr.ts ├── pavucontrol-qt_sr@latin.ts ├── pavucontrol-qt_sv.ts ├── pavucontrol-qt_ta.ts ├── pavucontrol-qt_te.ts ├── pavucontrol-qt_th.ts ├── pavucontrol-qt_tr.desktop.yaml ├── pavucontrol-qt_tr.ts ├── pavucontrol-qt_uk.desktop.yaml ├── pavucontrol-qt_uk.ts ├── pavucontrol-qt_vi.desktop.yaml ├── pavucontrol-qt_zh_CN.desktop.yaml ├── pavucontrol-qt_zh_CN.ts ├── pavucontrol-qt_zh_TW.desktop.yaml └── pavucontrol-qt_zh_TW.ts /AUTHORS: -------------------------------------------------------------------------------- 1 | This is the Qt port of pavucontrol . 2 | 3 | The original authors of the gtk+ version of pavucontrol are: 4 | * Copyright 2006-2009 Lennart Poettering 5 | * Copyright 2009-2011 Colin Guthrie 6 | 7 | The Qt port pavucontrol-qt is derived from the gtk+ version. 8 | It's done by LXQt developers: 9 | * Copyright 2016 Hong Jen-Yee (PCMan) 10 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | pavucontrol-qt-2.2.0 / 2025-04-17 2 | ================================== 3 | * Updated translations and bumped the version. 4 | 5 | pavucontrol-qt-2.1.0 / 2024-11-05 6 | ================================== 7 | * Updated `pavucontrol-qt.desktop.yaml`. 8 | * Fixed the limits of offset spin-box. 9 | 10 | pavucontrol-qt-2.0.0 / 2024-04-17 11 | ================================== 12 | * Ported to Qt6. 13 | 14 | pavucontrol-qt-1.4.0 / 2023-11-05 15 | ================================== 16 | * Updated translations and bumped the version. 17 | 18 | pavucontrol-qt-1.3.0 / 2023-04-15 19 | ================================== 20 | * Fixed pa_stream leaks. 21 | * Updated many translations. 22 | 23 | pavucontrol-qt-1.2.0 / 2022-11-05 24 | ================================== 25 | * Improved `Categories` `GenericName`, and `Comment` in the desktop entry. 26 | 27 | pavucontrol-qt-1.1.0 / 2022-04-15 28 | ================================== 29 | * Modernized some trivial destructors. 30 | * Removed a redundant void argument in function definition. 31 | * Pass by value and use `std::move()`. 32 | 33 | pavucontrol-qt-1.0.0 / 2021-11-04 34 | ================================== 35 | * Bumped minimum required Qt version to 5.15. 36 | 37 | pavucontrol-qt-0.17.0 / 2021-04-15 38 | ================================== 39 | * Removed the unused dbus dependency. 40 | 41 | pavucontrol-qt-0.16.0 / 2020-11-01 42 | ================================== 43 | * Safely iterate over card profile info array. 44 | * Fall back to default icon when no candidate is found. 45 | * Elide playback name label if needed 46 | * Don't force a height on progressbars. 47 | * Remove warnings about unused function parameters. 48 | * Wrapped the text of connecting error label. 49 | 50 | pavucontrol-qt-0.15.0 / 2020-04-23 51 | ================================== 52 | * Bumped version to 0.15.0. 53 | * Avoid char raw strings literals memory allocations. 54 | * Don't use automatic string conversions. 55 | * Removed (duplicated) string casts definitions. 56 | 57 | pavucontrol-qt-0.14.1 / 2019-02-25 58 | ================================== 59 | 60 | * Bumped version to 0.14.1 61 | * Only translations was changed. 62 | 63 | pavucontrol-qt-0.14.0 / 2019-01-25 64 | ================================== 65 | 66 | * Bumped version to 0.14.0 67 | * Added FreeBSD installation instructions 68 | * Implementation fixed: 69 | - Some code modernizations like range based loops, 70 | auto declared iterators and use of nullptr 71 | - Don't use automatic string conversions 72 | - Remove unused Makefile.am 73 | - Treat the 'off' card profile specifically 74 | - Mainwindow: Don't use deprecated libpulse members 75 | * Improved cmake scripting 76 | - Set cmake_minimum_required to 3.1.0 77 | - Removed locale compile definitons 78 | * Moved translations from lxqt-l10n back to pavucontrol-qt 79 | - Removed obsolete translation fuctionality 80 | - Added translation promo in README.md 81 | * Translation updates 82 | 83 | pavucontrol-qt-0.4.0 / 2018-05-21 84 | ================================= 85 | 86 | * Bumped minor version to 4 87 | * Spanish translation update 88 | * Qt5.11 beta: xxxwidget: include QAction explicitely 89 | * CMake: Prevent in-source builds 90 | * build: Use pkg-config's *_LDFLAGS 91 | * fix http -> https 92 | * Fixed mentions of LXDE 93 | * pavucontrol: Remove unique application constraint 94 | * Add Simplified Chinese desktop entry 95 | 96 | pavucontrol-qt-0.3.0 / 2017-09-24 97 | ================================= 98 | 99 | * Release 0.3.0: Update changelog 100 | * Bump version to 0.3.0 101 | * correct spelling mistake 102 | * Added Lithuanian .desktop file 103 | * liblxqt make no sense here 104 | * Copied issue template 105 | * set Qt::AA_UseHighDpiPixmaps to true 106 | * Added version to cli options 107 | * Added default CMAKE_BUILD_TYPE and versioning 108 | * added pl.desktop 109 | * ui: Use "changes-prevent-symbolic" icon 110 | * MainWindow: Remove old GTK remnants 111 | * Add Catalan translation 112 | * Channel: Add slider snapping to 100% 113 | * Channel: Address compiler warnings 114 | * Channel: Align slider controls correclty 115 | * Channel: Add ticks to volume slider 116 | * Channel: Use percentage for volume scale 117 | * devicewidgte: Re-add device renaming 118 | * MainWindow: Force using the size from previous run 119 | * MainWindow: Add quit shortcut 120 | * Added pavucontrol-qt_da.desktop 121 | 122 | pavucontrol-qt-0.2.0 / 2016-12-11 123 | ================================= 124 | 125 | * Release 0.2.0: Update changelog 126 | * Release 0.2.0: Update changelog 127 | * Use QSettings for storing configuration 128 | * Create pavucontrol-qt_pt.desktop 129 | * Use the new lxqt-build-tools package 130 | * - Add a simple single instance controller to avoid run multiple simultaneous applications 131 | * Use same approach as avidemux 132 | * Simply differentiate the namnig to avoid menu duplication 133 | * CMakeLists: Remove XdgUserDirs requirement 134 | * mainwindow: Set icon for eventRoleWidget 135 | 136 | pavucontrol-qt-0.1.0 / 2016-09-24 137 | ================================= 138 | 139 | * Release 0.1.0: Add changelog 140 | * devicewidget: Remove debug 141 | * Address compiler warnings 142 | * streamwidget: Fix specific termination text 143 | * Re-add support for changing source output 144 | * streamwidget: Re-add "Terminate" context menu 145 | * Re-add support for changing sink input 146 | * ui: Lock channels by default 147 | * ui: Enable sink's advanced options only if digital 148 | * ui: Condense device advanced settings 149 | * ui: Align titles left 150 | * ui: Use "changes-prevent" icon for "Lock channels.." 151 | * ui: Don't stretch content horizontaly 152 | * ui: Simplify peak bar 153 | * channels: Align channel sliders 154 | * Add README.md 155 | * devicewidget: Set the PCM encoding always enabled 156 | * Properly install pavucontrol-qt.desktop file. 157 | * Prevent recursive signal handler invocation. * Replace some push buttons with tool buttons. 158 | * Add authors information. 159 | * Remove glibmm dependency. 160 | * Fix translations. 161 | * Replace GNU gettext with Qt tr() in all places. 162 | * Add missing ui files to CMakeLists.txt 163 | * Trivial fix for Timeout handlers. 164 | * Porting to Qt 5 - Finish the basic UI. 165 | * Make all source code compile with Qt5. 166 | * Port DeviceWidget and ChannelWidget to Qt5. 167 | * Add missing file. 168 | * Fix broken combo box in CardWidget. Remove unused code. 169 | * Port the sound card configuration page to Qt. 170 | 171 | start-qt / 2016-08-31 172 | ===================== 173 | 174 | * Start working on a Qt5 port. 175 | * mainwindow: force icons to have sane size 176 | * Add checkbutton for disabling volume meters 177 | * .gitignore: add compile 178 | * mainwindow: Don't add a border on the outermost vbox 179 | * build-sys: Add m4 file for AX_CXX_COMPILE_STDCXX_11 180 | * doc: Update link to git repository 181 | * build-sys: Use C++11 for building 182 | * Remove Encoding key from .desktop file 183 | * i18n: improve one Italian string 184 | * i18n: Some fixes for the Italian translation 185 | * Add --version command line option 186 | * build-sys: Depend on libpulse >= 5.0 187 | * mainwindow: unavailable profiles are marked as such in their description 188 | * i18n: Updated Polish translation 189 | * build-sys: Release pavucontrol 3.0 190 | * doc: Adapt the sed expression for cleaning up links 191 | * build-sys: Don't dist m4/ChangeLog 192 | * build-sys: Fix README building 193 | * mainwindow: Remember device visibility selections 194 | * devicewidget, streamwidget: Show only one volume slider when channels are locked 195 | * mainwindow: Catch Gdk::PixbufError 196 | * Handle IO errors in icon setting code 197 | * Don't crash if pa_stream_peek returns NULL 198 | * Do not instantiate cardwidget, devicewidget and rolewidget with a type of EventBox 199 | * Reference the widget before returning it from ::create methods 200 | * Add horizontal scrollbars automatically 201 | * Add TRANSLATORS_README.txt 202 | * Add a --maximize command line option 203 | * pavucontrol: ellipsize labels to fix window resizing 204 | * build-sys: Use autoreconf in bootstrap.sh 205 | * sinkwidget: add support for AAC pass-through 206 | * added back support for gtkmm-2.4 builds 207 | * build-sys: Drop ChangeLog generation from git 208 | * build-sys: Prepare for a 2.0 release 209 | * sinkwidget: Move format selection options to 'Advanced' expander 210 | * devicewidget: Move latency offset into an expander 211 | * mainwindow: Fix iterator dereferencing style. 212 | * mainwindow: Show the availability of the ports and profiles. 213 | * mainwindow: Don't clear the "updating" flag in updateCard() too early. 214 | * Enable Automake's silent-rules option. 215 | * Add a few known peak-detect stream clients to a source-output blacklist 216 | * devicewidget: Add a latency offset spinbutton 217 | * sink/sourcewidget: Move the index and card_index to the devicewidget 218 | * cardwidget: Add a port class 219 | * bootstrap: Enable the pre-commit hook. 220 | * gitignore: Add *.swp. 221 | * Add --retry command line option 222 | * Fix theme under gtk3 223 | * build-sys: Prepare 1.0 224 | * build-sys: Switch to the tar-ustar format (as per a lot of GNOME stuff for 3.2) and distribute .xz files. 225 | * i18n: Add devicewidget to POTFILES.in 226 | * source: Only autosuspend network sources 227 | * build-sys: Prepare 0.99.2 228 | * Update device format stuff for (pre-release) API changes. 229 | * Check if PA_SINK_SET_FORMATS is defined before using it 230 | * Use sink flags to figure out if we can set formats 231 | * Add UI to select the formats supported by the receiver attached to a digitial sink 232 | * build-sys: Prepare 0.99.1 233 | * Fix strange vertical alignment of sinks/source/cards 234 | * Fix GTK Warnings under GTK3 due to combo box sizes 235 | * source-output: We will officially support source output volumes in 0.99 236 | * source-output: Don't set the channel map for the SO if we do not support SO volumes. 237 | * sink: Oops. I didn't mean to comment this out in the previous commit. 238 | * stream-volumes: Support source-output volume controls now available in PA. 239 | * i18n: Fix markup error in de translation. 240 | * Handle simple key events. 241 | * Deal more gracefully with disconnections. 242 | * Add support for gtkmm-3.0 and prefer it over gtkmm-2.* by default. 243 | * Ensure the marker for base volume is set on all channels. 244 | * volume: Don't bother setting the steps. 245 | * volume: Support volumes up to 11. 246 | * ui: Remove ellipsize attribute from some labels (causes ellipsis to be displayed rather than real text) 247 | * ui: Resave glade file without any changes to tidy it up 248 | * Drop the need to use libglademm 249 | * Add DONT_INHIBIT_AUTO_SUSPEND flag to monitor streams 250 | * source-outputs: Source outputs do not support volume control, so hide the mute and lock channels buttons 251 | * main: Format string fixes 252 | * main: Add a --tab command line argument to force a given tab to be displayed. 253 | * mainwindow: Fix clearing out of clients 254 | * mainwindow: Save/restore window size 255 | * mainwindow: Compact iterator decls 256 | * main: Cleanup labels after connection rework 257 | * source-outputs: Fix a bug where the 'no streams' label is sometimes shown along with the actual widget. 258 | * connection: Show a nice label when connecting to PA. 259 | * main: Automatically reconnect to PA upon disconnection 260 | * mainwindow: Add a method to remove all widgets (e.g. on disconnect) 261 | * streamwidget: Fix a compile warning. 262 | * Split out the creation of the PA context a little. 263 | * glade: ellipsize user supplied text strings 264 | * Allow for the sink input peak detect to survive across sink moves. 265 | * Update for the latest incarnation of module-device-manager 266 | * Use the correct update mode for device-restore database write 267 | * Add support for the device-manager module. 268 | * build-sys: prepare 0.9.10 269 | * add ja to LINGUAS 270 | * run make update-po 271 | * Sending translation for Hungarian 272 | * volume: Set the scale properly for delta changes via mouse/keyboard. 273 | * events: Hide output streams with a stream restore designator for the event widget. 274 | * Sending translation for Czech 275 | * Sending translation for Kannada 276 | * Sending translation for Japanese 277 | * Sending translation for French 278 | * Sending translation for Brazilian Portuguese 279 | * Sending translation for Hindi 280 | * Sending translation for Slovak 281 | * Require at least PulseAudio 0.9.16. 282 | * Sending translation for Danish 283 | * Sending translation for Portuguese 284 | * Sending translation for Serbian (Latin) 285 | * Sending translation for Serbian 286 | * Sending translation for Finnish 287 | * Sending translation for German 288 | * Sending translation for Ukrainian 289 | * build-sys: require at least gtkmn 2.16 290 | * Add missing source file to POTFILES.in. 291 | * Sending translation for Catalan 292 | * Sending translation for Polish 293 | * Sending translation for Spanish 294 | * Sending translation for Oriya 295 | * Sending translation for Dutch 296 | * Sending translation for po/ja.po 297 | * Sending translation for Telugu 298 | * Sending translation for Bengali (India) 299 | * Sending translation for Hindi 300 | * Sending translation for Marathi 301 | * Sending translation for Tamil 302 | * Sending translation for Malayalam 303 | * Sending translation for Gujarati 304 | * Sending translation for Malayalam 305 | * Sending translation for Punjabi 306 | * i18n: run make update-po 307 | * i18n: drop ko.po since it does not include a single translated string 308 | * Prepare 0.9.9 relase 309 | * drop reference to beepDevice 310 | * Sending translation for Tamil 311 | * Sending translation for Finnish 312 | * Sending translation for po/LINGUAS 313 | * fix event sound generation for volume changes 314 | * only assume that the minimal volume is silence for devices that can do dB volumes 315 | * use fallback icon scheme for stream icons, too 316 | * Sending translation for po/uk.po 317 | * Sending translation for po/ko.po 318 | * Sending translation for Swedish 319 | * Sending translation for Hungarian 320 | * glade: minor alignment fixes 321 | * Restructure the class inheritance a bit. 322 | * Rename event method to be more indicative of what it does. 323 | * Rename a variable to make it more generic 324 | * Add support for changing ports. 325 | * Don't use a hyperlink to display the device, just use a regular button. 326 | * Do not disable the default button when it's set. 327 | * Hide the terminate button. 328 | * More changes in the UI to try and make things neater. 329 | * Create the event role widget early so that it's always top of the list. 330 | * Change 'Default' to 'Use as fallback' 331 | * Move the setBaseVolume()/setSteps() methods to devicewidget (problem found after rebasing) 332 | * Include config.h in the cc file (after rebase) 333 | * Add a tooltip 334 | * Wire up all the new UI code. 335 | * Wire up the default toggle button (replaces the old menu) 336 | * Hook up the device combo box on change event 337 | * Remove the 'Default [Out|In]put' option from the combo for now. 338 | * Set the correct combo index for display purposes 339 | * Record in a map the combobox indexes vs. the sink/source indexes 340 | * Add setter/getter methods for sink/source indexes so we can update our combo boxes current item 341 | * Remove the kill menu item now we have a dedicated button 342 | * Wire up the terminate button 343 | * Set the pack_start on the deviceCombo 344 | * Set the mainwindow pointer via an init() method rather than directly via public member variable. 345 | * Adapt to new UI 346 | * Rebase the sink/source widgets on the device widget 347 | * Create a devicewidget based on streamwidget. 348 | * Glade UI changes. Code still needs to be updated 349 | * update po/ 350 | * make sure that two card profiles with the same priority don't collide with each other 351 | * don't abort when icon is missing from the theme 352 | * Fix my name. 353 | * Sending translation for Punjabi 354 | * Sending translation for po/hi.po 355 | * Sending translation for Punjabi 356 | * Sending translation for Punjabi 357 | * Sending translation for Bengali (India) 358 | * Sending translation for Malayalam 359 | * Sending translation for Slovak 360 | * Sending translation for po/sr@latin.po 361 | * Sending translation for Serbian 362 | * Sending translation for French 363 | * Sending translation for French 364 | * Sending translation for Portuguese 365 | * Sending translation for Czech 366 | * Sending translation for Assamese 367 | * Sending translation for po/ru.po 368 | * Sending translation for Italian 369 | * Sending translation for Brazilian Portuguese 370 | * Sending translation for French 371 | * Sending translation for German 372 | * Sending translation for Danish 373 | * Sending translation for Dutch 374 | * Sending translation for Polish 375 | * Sending translation for Telugu 376 | * Sending translation for Marathi 377 | * Sending translation for Gujarati 378 | * Sending translation for Oriya 379 | * Sending translation for Kannada 380 | * Sending translation for Spanish 381 | * fix year 382 | * update screenshot 383 | * fix POTFILES.in 384 | * update README 385 | * bump version 386 | * don't show failure msgbox twice 387 | * reenable monitor streams 388 | * sort LINGUAS 389 | * add missing languages to LINGUAS 390 | * run make update-po 391 | * Sending translation for Portuguese 392 | * Sending translation for Portuguese 393 | * Sending translation for Polish 394 | * Sending translation for Assamese 395 | * don't show wait cursor forever if stream_restore extension is not available 396 | * Sending translation for po/LINGUAS 397 | * Sending translation for Portuguese 398 | * Sending translation for po/bn_IN.po 399 | * Sending translation for po/ta.po 400 | * Sending translation for po/as.po 401 | * Sending translation for po/te.po 402 | * Sending translation for po/kn.po 403 | * Sending translation for po/gu.po 404 | * Sending translation for po/sr@latin.po 405 | * Sending translation for Serbian 406 | * Sending translation for Portuguese 407 | * Sending translation for Serbian 408 | * Sending translation for Finnish 409 | * Sending translation for po/da.po 410 | * Sending translation for Dutch 411 | * Sending translation for po/mr.po 412 | * Sending translation for Portuguese 413 | * Sending translation for Portuguese 414 | * Sending translation for po/pt.po 415 | * Sending translation for po/or.po 416 | * Sending translation for Slovak 417 | * Sending translation for po/ml.po 418 | * Sending translation for po/it.po 419 | * Sending translation for Chinese (Simplified) 420 | * Order the card profiles by their priority 421 | * Silence a warning 422 | * Sending translation for Hungarian 423 | * configure steps for scales 424 | * visualize base volume 425 | * Updated catalan po 426 | * Shuffle some headers around a bit so that some systems don't have build errors 427 | * Use C style comments as per Lennart's fetish :p 428 | * Move config.h inclusion to the .cc files 429 | * Split mainwindow into it's own files. 430 | * Split rolewidget into it's own files. 431 | * Split sourceoutputwidget out into it's own files. 432 | * Split sinkinputwidget into it's own files. 433 | * Split sourcewidget into it's own files. 434 | * Split sinkwidget into it's own files. 435 | * Split cardwidget out into it's own files. 436 | * Move common setup into a shared header 437 | * Split out streamwidget into it's own files. 438 | * Split channelwidget into separate files. Also separate out the definition of streamwidget into it's own header 439 | * Include config.h in minimalstreamwidget.h 440 | * Split the minimalstreamwidget into it's own file 441 | * Updated Thai translation 442 | * Adding Punjabi (pa) Language to LINGUAS 443 | * updated Spanish translation 444 | * Remove the minimalStreamWindow. It's no longer used 445 | * Updated Polish translation 446 | * use the server provided icons 447 | * Initialise our dropdown at contruction and just repopulate it when the cardinfo callback is received 448 | * various small glade updates 449 | * fix version of libcanberra dependency, fixes PA bug #494 450 | * Fix the number of outstanding operations. 451 | * Typo: spell configuration right 452 | * Add a new widget for the card configuration and use it. 453 | * Create a new 'Configuration' tab 454 | * Silence some warnings 455 | * Initial support for changing card profiles. 456 | * run make update-po 457 | * Add 'Mixer' categorization to .desktop file 458 | * deal with non-initialized data from m-s-r 459 | * Punjabi translation for Module: A S Alam 460 | * Add fi (Finnish) to LINGUAS. 461 | * Add Finnish (fi) translation by Timo Jyrinki. 462 | * Added zh_CN to LINGUAS 463 | * Add zh_CN.po 464 | * Added sr and sr@latin to LINGUAS 465 | * Initial Serbian Latin translation 466 | * Initial Serbian translation 467 | * Updated LINGUAS 468 | * LINGUAS: Added cs. 469 | * Added Czech translation. 470 | * updated spanish translation 471 | * Added tr to LINGUAS 472 | * Added Turkish Translation 473 | * remove debug output 474 | * Added ca locale 475 | * Add sk to LINGUAS 476 | * First Slovak translation. 477 | * Updated Greek translation 478 | * Added catalan po 479 | * Add pt_BR to LINGUAS 480 | * Initial Brazilian Portuguese translation 481 | * Add nl to LINGUAS 482 | * Initial Dutch translation by Reinout van Schouwen 483 | * Updated de translation with input from gnome-de (thanks Hendrik Richter). 484 | * prepare release 0.9.7 485 | * initialize maximum buffer length to automatic value (uint32_t) -1 instead of smallest value 0 486 | * Updated de translation with input from gnome-de (thanks andre klapper). 487 | * Added sv to LINGUAS. 488 | * Added Swedish translation. 489 | * Updated Polish translation 490 | * Added el, fr, hu, pl to LINGUAS 491 | * Initial German translation 492 | * Added Hungarian translation 493 | * Updated French translation 494 | * Initial Polish translation 495 | * Started Greek translation 496 | * beep when changing volume 497 | * Install README files in doc/html directories. 498 | * Make use of common ZP_LYNX_DOC macro. 499 | * Create m4 directory, and set it up. 500 | * Bump autoconf requirement to 2.62 (latest released version). 501 | * Remove gettext macro, the package uses intltool instead. 502 | * improve german translation 503 | * add basic german translation 504 | * internationalize pavucontrol.cc 505 | * hide .desktop file from git 506 | * add i18n 507 | * generate changelog from git history 508 | * use system installed glade file 509 | * update license headers for GPL 2.0+ 510 | * update bootstrap.sh a bit 511 | * add a special track for controlling event sound volume 512 | * bump required version 513 | * fix glade widget name case 514 | * hide tarballs from git 515 | * change 'git' tag in tarball name to 'svn' 516 | * bump revision 517 | * bump deps 518 | * remove $Id$ 519 | * add gitignore 520 | * fix a typo spotted by Romain Tartière 521 | * Make icons work 522 | * show stream icons, don't show our own monitor streams, initialize client proplist properly 523 | * add volume meter support to each sink/source/record stream 524 | * more intelligent initial page selection 525 | * Also point the minimalStreamWindow downward 526 | * move no_xxx labels in the middle of the dialogs 527 | * properly initialize source output type dropdown; show string for record streams properly 528 | * switch to sink page by default if no streams are active 529 | * unclutter UI, remove redundant hint bar 530 | * show volume in decibel 531 | * Remove the white header, drop all references to PulseAudio to become a more normal program 532 | * move the lock/mute buttons to the same spot as the menu buttons 533 | * Merge patch from sjoerd that adds a new panel for listing recording streams, and makes the menu more visible by adding a button for it 534 | * prepare next pavucontrol release 535 | * don't crash if server has no default sink/source set 536 | * show radio buttons for the the sink menu items 537 | * prepare next release 538 | * add changelog to tarball 539 | * make stream title selectable, Closes #140 in pa trac 540 | * add option to kill a stream to popup menu 541 | * add popup menu to sink/source view to choose default device 542 | * use pretty channel names 543 | * add a combobox to the streams page, to filter application streams 544 | * Don't pass the button index to popup() for the GtkMenu to avoid strange GTK behaviour in newer versions. Identified by coling 545 | * add fedora-snapshot target 546 | * bump deps 547 | * support muting sink inputs, avoid duplicate events 548 | * fix button tooltips 549 | * fix lock icon 550 | * minor tweaks 551 | * Fix license mess: change everything to GPL 552 | * fix typo 553 | * update docs for 0.9.5 554 | * bump requirements 555 | * add lightbulb 556 | * - add new option "All Except Monitor Sources" to sources option menu - show a nice hint that one can right click on a stream 557 | * use the sink descripion instead of the name for the stream popup menu 558 | * In times where the sink/source names are generated from HAL UDIs don't show the cryptic device name any more 559 | * optimize redraw a little 560 | * add popup menu to stream list, allowing "hot" moving of playback streams between sinks 561 | * add focus target 562 | * show an watch cursor while loading device data 563 | * add combobox to show only a subset of sinks/sources, replacing the old monitor expander 564 | * prepare release 0.9.3 565 | * bump version 566 | * Make jhbuild happy by adding a file autgen.sh 567 | * show client name only if we have it 568 | * prepare 0.9.2 release 569 | * include config.h properly 570 | * s/polyp/pulse/ in README 571 | * s/polyp/pulse/g 572 | * update docs for 0.9.1 573 | * bump version number to 0.9.1 574 | * actually ship the .desktop file 575 | * use gtk_widget_queue_redraw() to try to workaround an issues that the sources are not shown 576 | * Add .desktop file. 577 | * set icon for the main window 578 | * use some icons from the icon spec 579 | * minor updates 580 | * prepare release 0.9.0 581 | * Show client name for playback streams 582 | * change scale behaviour to be more resposive * make "Streams" page the default 583 | * bump version number for polypaudio 0.9.0 584 | * bump for polypadio 0.8.1 585 | * make SIGPIPE warning disappear 586 | * fix glade file path 587 | * add screenshot for website 588 | * build system updates 589 | * add coumentation and license 590 | * show proper message boxes in case of error instead of printing just to STDERR 591 | * initial commit 592 | * Create trunk directory 593 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18.0 FATAL_ERROR) 2 | # CMP0000: Call the cmake_minimum_required() command at the beginning of the top-level 3 | # CMakeLists.txt file even before calling the project() command. 4 | # The cmake_minimum_required(VERSION) command implicitly invokes the cmake_policy(VERSION) 5 | # command to specify that the current project code is written for the given range of CMake 6 | # versions. 7 | project(pavucontrol-qt) 8 | 9 | option(UPDATE_TRANSLATIONS "Update source translation translations/*.ts files" OFF) 10 | 11 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 12 | 13 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 14 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 15 | set(CMAKE_AUTOMOC ON) 16 | set(CMAKE_AUTOUIC ON) 17 | 18 | include(GNUInstallDirs) 19 | 20 | # Minimum Versions 21 | set(GLIB_MINIMUM_VERSION "2.50.0") 22 | set(LXQTBT_MINIMUM_VERSION "2.2.0") 23 | set(QT_MINIMUM_VERSION "6.6.0") 24 | 25 | find_package(Qt6 ${QT_MINIMUM_VERSION} COMPONENTS REQUIRED Widgets LinguistTools) 26 | find_package(lxqt2-build-tools ${LXQTBT_MINIMUM_VERSION} REQUIRED) 27 | 28 | find_package(GLIB ${GLIB_MINIMUM_VERSION} REQUIRED) 29 | 30 | set(PAVUCONTROLQT_MAJOR_VERSION 2) 31 | set(PAVUCONTROLQT_MINOR_VERSION 2) 32 | set(PAVUCONTROLQT_PATCH_VERSION 0) 33 | set(PAVUCONTROLQT_VERSION ${PAVUCONTROLQT_MAJOR_VERSION}.${PAVUCONTROLQT_MINOR_VERSION}.${PAVUCONTROLQT_PATCH_VERSION}) 34 | add_definitions("-DPAVUCONTROLQT_VERSION=\"${PAVUCONTROLQT_VERSION}\"") 35 | 36 | include(LXQtPreventInSourceBuilds) 37 | include(LXQtCompilerSettings NO_POLICY_SCOPE) 38 | include(LXQtTranslate) 39 | 40 | find_package(PkgConfig REQUIRED) 41 | pkg_check_modules( 42 | PULSE REQUIRED 43 | libpulse>=5.0 44 | libpulse-mainloop-glib>=0.9.16 45 | ) 46 | 47 | add_subdirectory(src) 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pavucontrol-qt 2 | 3 | ## Overview 4 | 5 | pavucontrol-qt is the Qt port of the volume control [pavucontrol](https://freedesktop.org/software/pulseaudio/pavucontrol/) for the sound server [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/). 6 | 7 | As such it can be used to adjust all controls provided by PulseAudio and ALSA as well as some additional settings. 8 | 9 | The software belongs to the LXQt project but its usage isn't limited to this desktop environment. 10 | 11 | ## Installation 12 | 13 | ### Compiling source code 14 | 15 | Runtime dependencies are qtbase and PulseAudio client library libpulse. 16 | Additional build dependencies are CMake and [liblxqt](https://github.com/lxqt/liblxqt) as well as optionally Git to pull latest VCS checkouts. 17 | 18 | Code configuration is handled by CMake. CMake variable `CMAKE_INSTALL_PREFIX` has to be set to `/usr` on most operating systems. 19 | 20 | To build run `make`, to install `make install` which accepts variable `DESTDIR` as usual. 21 | 22 | ### Binary packages 23 | 24 | Official binary packages are available in Arch Linux, Debian, Fedora and openSUSE (Leap and Tumbleweed) and most other distributions. 25 | 26 | ## Usage 27 | 28 | In LXQt sessions the binary is placed in sub-menu "Sound & Video" of the panel's main menu. 29 | 30 | The usage itself should be self-explanatory. 31 | 32 | 33 | ## Translations 34 | 35 | Translations can be done in [LXQt-Weblate](https://translate.lxqt-project.org/projects/lxqt-configuration/pavucontrol-qt/). 36 | 37 | 38 | Translation status 39 | 40 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories( 2 | ${PULSE_INCLUDE_DIRS} 3 | ${GLIB_INCLUDE_DIRS} 4 | ) 5 | 6 | set(pavucontrol-qt_HDRS 7 | pavucontrol.h 8 | mainwindow.h 9 | cardwidget.h 10 | channel.h 11 | devicewidget.h 12 | minimalstreamwidget.h 13 | rolewidget.h 14 | sinkinputwidget.h 15 | sinkwidget.h 16 | sourceoutputwidget.h 17 | sourcewidget.h 18 | streamwidget.h 19 | elidinglabel.h 20 | ) 21 | 22 | set(pavucontrol-qt_SRCS 23 | pavucontrol.cc 24 | mainwindow.cc 25 | cardwidget.cc 26 | channel.cc 27 | devicewidget.cc 28 | minimalstreamwidget.cc 29 | rolewidget.cc 30 | sinkinputwidget.cc 31 | sinkwidget.cc 32 | sourceoutputwidget.cc 33 | sourcewidget.cc 34 | streamwidget.cc 35 | elidinglabel.cc 36 | ) 37 | 38 | set(pavucontrol-qt_UI 39 | cardwidget.ui 40 | devicewidget.ui 41 | mainwindow.ui 42 | streamwidget.ui 43 | ) 44 | 45 | # Translations 46 | lxqt_translate_ts(pavucontrol-qt_QM_FILES 47 | UPDATE_TRANSLATIONS 48 | ${UPDATE_TRANSLATIONS} 49 | SOURCES 50 | ${pavucontrol-qt_HDRS} 51 | ${pavucontrol-qt_SRCS} 52 | ${pavucontrol-qt_UI} 53 | INSTALL_DIR 54 | "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/translations" 55 | ) 56 | 57 | lxqt_translate_desktop(DESKTOP_FILES 58 | SOURCES 59 | pavucontrol-qt.desktop.in 60 | USE_YAML 61 | ) 62 | 63 | add_executable(pavucontrol-qt 64 | ${pavucontrol-qt_SRCS} 65 | ${pavucontrol-qt_QM_FILES} 66 | ${pavucontrol-qt_QM_LOADER} 67 | ${DESKTOP_FILES} 68 | ) 69 | set_property( 70 | TARGET pavucontrol-qt APPEND 71 | PROPERTY COMPILE_DEFINITIONS 72 | PAVUCONTROL_QT_DATA_DIR="${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}" 73 | ) 74 | 75 | target_link_libraries(pavucontrol-qt 76 | Qt6::Widgets 77 | ${PULSE_LDFLAGS} 78 | ${GLIB_LDFLAGS} 79 | ) 80 | 81 | install(TARGETS 82 | pavucontrol-qt 83 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 84 | COMPONENT Runtime 85 | ) 86 | 87 | install(FILES 88 | ${DESKTOP_FILES} 89 | DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications" 90 | COMPONENT Runtime 91 | ) 92 | -------------------------------------------------------------------------------- /src/cardwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "cardwidget.h" 26 | 27 | /*** CardWidget ***/ 28 | CardWidget::CardWidget(QWidget* parent) : 29 | QWidget(parent) { 30 | setupUi(this); 31 | connect(profileList, static_cast(&QComboBox::currentIndexChanged), this, &CardWidget::onProfileChange); 32 | connect(profileCB, &QAbstractButton::toggled, this, &CardWidget::onProfileCheck); 33 | } 34 | 35 | 36 | void CardWidget::prepareMenu() { 37 | int idx = 0; 38 | const bool off = activeProfile == noInOutProfile; 39 | 40 | profileList->clear(); 41 | /* Fill the ComboBox */ 42 | for (const auto & profile : profiles) { 43 | QByteArray name = profile.first; 44 | // skip the "off" profile 45 | if (name == noInOutProfile) 46 | continue; 47 | QString desc = QString::fromUtf8(profile.second); 48 | profileList->addItem(desc, name); 49 | if (profile.first == activeProfile 50 | || (off && profile.first == lastActiveProfile) 51 | ) 52 | { 53 | profileList->setCurrentIndex(idx); 54 | lastActiveProfile = profile.first; 55 | } 56 | ++idx; 57 | } 58 | 59 | profileCB->setChecked(!off); 60 | } 61 | 62 | void CardWidget::changeProfile(const QByteArray & name) 63 | { 64 | pa_operation* o; 65 | 66 | if (!(o = pa_context_set_card_profile_by_index(get_context(), index, name.constData(), nullptr, nullptr))) { 67 | show_error(tr("pa_context_set_card_profile_by_index() failed").toUtf8().constData()); 68 | return; 69 | } 70 | 71 | pa_operation_unref(o); 72 | } 73 | 74 | void CardWidget::onProfileChange(int active) { 75 | if (updating) 76 | return; 77 | 78 | if (active != -1) 79 | changeProfile(profileList->itemData(active).toByteArray()); 80 | } 81 | 82 | void CardWidget::onProfileCheck(bool on) 83 | { 84 | if (updating) 85 | return; 86 | 87 | if (on) 88 | onProfileChange(profileList->currentIndex()); 89 | else 90 | changeProfile(noInOutProfile); 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/cardwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef cardwidget_h 22 | #define cardwidget_h 23 | 24 | #include "pavucontrol.h" 25 | #include "ui_cardwidget.h" 26 | #include 27 | 28 | class PortInfo { 29 | public: 30 | QByteArray name; 31 | QByteArray description; 32 | uint32_t priority; 33 | int available; 34 | int direction; 35 | int64_t latency_offset; 36 | std::vector profiles; 37 | }; 38 | 39 | class CardWidget : public QWidget, public Ui::CardWidget { 40 | Q_OBJECT 41 | public: 42 | CardWidget(QWidget *parent = nullptr); 43 | 44 | QByteArray name; 45 | uint32_t index; 46 | bool updating; 47 | 48 | std::vector< std::pair > profiles; 49 | std::map ports; 50 | QByteArray activeProfile; 51 | QByteArray noInOutProfile; 52 | QByteArray lastActiveProfile; 53 | bool hasSinks; 54 | bool hasSources; 55 | 56 | void prepareMenu(); 57 | 58 | protected: 59 | void changeProfile(const QByteArray & name); 60 | void onProfileChange(int active); 61 | void onProfileCheck(bool on); 62 | 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/cardwidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | CardWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 368 10 | 79 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Card Name 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | true 41 | 42 | 43 | 44 | 45 | 46 | 47 | Profile: 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Qt::Horizontal 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | profileCB 69 | toggled(bool) 70 | profileLabel 71 | setEnabled(bool) 72 | 73 | 74 | 137 75 | 47 76 | 77 | 78 | 147 79 | 47 80 | 81 | 82 | 83 | 84 | profileCB 85 | toggled(bool) 86 | profileList 87 | setEnabled(bool) 88 | 89 | 90 | 137 91 | 47 92 | 93 | 94 | 315 95 | 47 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/channel.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "channel.h" 30 | #include "minimalstreamwidget.h" 31 | 32 | constexpr int SLIDER_SNAP = 2; 33 | static inline int paVolume2Percent(pa_volume_t vol) 34 | { 35 | if (vol > PA_VOLUME_UI_MAX) 36 | vol = PA_VOLUME_UI_MAX; 37 | return qRound(static_cast(vol - PA_VOLUME_MUTED) / PA_VOLUME_NORM * 100); 38 | } 39 | 40 | static inline pa_volume_t percent2PaVolume(int percent) 41 | { 42 | return PA_VOLUME_MUTED + qRound(static_cast(percent) / 100 * PA_VOLUME_NORM); 43 | } 44 | 45 | /*** ChannelWidget ***/ 46 | 47 | Channel::Channel(QGridLayout* parent) : 48 | QObject(parent), 49 | can_decibel(false), 50 | volumeScaleEnabled(true), 51 | last(false) 52 | { 53 | channelLabel = new QLabel(nullptr); 54 | volumeScale = new QSlider(Qt::Horizontal, nullptr); 55 | volumeLabel = new QLabel(nullptr); 56 | 57 | const int row = parent->rowCount(); 58 | parent->addWidget(channelLabel, row, 0); 59 | parent->addWidget(volumeScale, row, 1); 60 | parent->addWidget(volumeLabel, row, 2); 61 | 62 | // make the info font smaller 63 | QFont label_font = volumeLabel->font(); 64 | if (label_font.pixelSize() == -1) 65 | label_font.setPointSizeF(label_font.pointSizeF() * 0.8); 66 | else 67 | label_font.setPixelSize(qRound(static_cast(label_font.pixelSize()) * 0.8)); 68 | volumeLabel->setFont(label_font); 69 | volumeLabel->setFixedWidth(QFontMetrics{volumeLabel->font()}.size(Qt::TextSingleLine, QStringLiteral("100%(-99.99dB)")).width()); 70 | volumeLabel->setAlignment(Qt::AlignHCenter); 71 | volumeLabel->setTextFormat(Qt::RichText); 72 | 73 | volumeScale->setRange(paVolume2Percent(PA_VOLUME_MUTED), paVolume2Percent(PA_VOLUME_UI_MAX)); 74 | volumeScale->setValue(paVolume2Percent(PA_VOLUME_NORM)); 75 | volumeScale->setPageStep(5); 76 | volumeScale->setTickInterval(paVolume2Percent(PA_VOLUME_NORM)); 77 | volumeScale->setTickPosition(QSlider::TicksBelow); 78 | volumeScale->setTracking(false); 79 | setBaseVolume(PA_VOLUME_NORM); 80 | 81 | connect(volumeScale, &QSlider::valueChanged, this, &Channel::onVolumeScaleValueChanged); 82 | connect(volumeScale, &QSlider::sliderMoved, this, &Channel::onVolumeScaleSliderMoved); 83 | } 84 | 85 | void Channel::setVolume(pa_volume_t volume) { 86 | const int v = paVolume2Percent(volume); 87 | if (can_decibel) { 88 | const double dB = pa_sw_volume_to_dB(volume); 89 | volumeLabel->setText(tr("%1% (%2dB)", "volume slider label [X% (YdB)]").arg(v) 90 | .arg(dB > PA_DECIBEL_MININFTY ? QString::asprintf("%0.2f", dB) : QStringLiteral("-∞"))); 91 | } 92 | else 93 | volumeLabel->setText(tr("%1%", "volume slider label [X%]").arg(v)); 94 | 95 | volumeScaleEnabled = false; 96 | volumeScale->setValue(v); 97 | volumeScaleEnabled = true; 98 | } 99 | 100 | void Channel::setVisible(bool visible) 101 | { 102 | channelLabel->setVisible(visible); 103 | volumeScale->setVisible(visible); 104 | volumeLabel->setVisible(visible); 105 | } 106 | 107 | void Channel::setEnabled(bool enabled) 108 | { 109 | channelLabel->setEnabled(enabled); 110 | volumeScale->setEnabled(enabled); 111 | volumeLabel->setEnabled(enabled); 112 | } 113 | 114 | void Channel::onVolumeScaleValueChanged(int value) { 115 | 116 | if (!volumeScaleEnabled) 117 | return; 118 | 119 | if (minimalStreamWidget->updating) 120 | return; 121 | 122 | minimalStreamWidget->updateChannelVolume(channel, percent2PaVolume(value)); 123 | } 124 | 125 | void Channel::onVolumeScaleSliderMoved(int value) 126 | { 127 | if (!volumeScaleEnabled) 128 | return; 129 | 130 | if (minimalStreamWidget->updating) 131 | return; 132 | 133 | const int current_value = volumeScale->value(); 134 | if (current_value == 100 && qAbs(value - current_value) <= SLIDER_SNAP) 135 | { 136 | volumeScale->blockSignals(true); 137 | volumeScale->setSliderPosition(current_value); 138 | volumeScale->blockSignals(false); 139 | return; 140 | } 141 | 142 | volumeScale->setValue(value); 143 | } 144 | 145 | /* 146 | void Channel::set_sensitive(bool enabled) { 147 | setEnabled(enabled); 148 | 149 | channelLabel->setEnabled(enabled); 150 | volumeLabel->setEnabled(enabled); 151 | volumeScale->setEnabled(enabled); 152 | } 153 | */ 154 | 155 | void Channel::setBaseVolume(pa_volume_t /*v*/) { 156 | #if 0 // FIXME: Qt does not support this functionality 157 | gtk_scale_clear_marks(GTK_SCALE(volumeScale->gobj())); 158 | 159 | gtk_scale_add_mark(GTK_SCALE(volumeScale->gobj()), (double)PA_VOLUME_MUTED, (GtkPositionType) GTK_POS_BOTTOM, 160 | last ? (can_decibel ? tr("Silence").toUtf8().constData() : tr("Min").toUtf8().constData()) : NULL); 161 | gtk_scale_add_mark(GTK_SCALE(volumeScale->gobj()), (double)PA_VOLUME_NORM, (GtkPositionType) GTK_POS_BOTTOM, 162 | last ? tr("100% (0dB)").toUtf8().constData() : NULL); 163 | if (v > PA_VOLUME_MUTED && v < PA_VOLUME_NORM) { 164 | gtk_scale_add_mark(GTK_SCALE(volumeScale->gobj()), (double)v, (GtkPositionType) GTK_POS_BOTTOM, 165 | last ? tr("Base").toUtf8().constData() : NULL); 166 | } 167 | #endif 168 | 169 | } 170 | -------------------------------------------------------------------------------- /src/channel.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef channel_h 22 | #define channel_h 23 | 24 | #include 25 | #include "pavucontrol.h" 26 | 27 | class QGridLayout; 28 | class QLabel; 29 | class QSlider; 30 | class MinimalStreamWidget; 31 | 32 | class Channel : public QObject { 33 | Q_OBJECT 34 | public: 35 | Channel(QGridLayout* parent=nullptr); 36 | 37 | void setVolume(pa_volume_t volume); 38 | void setVisible(bool visible); 39 | void setEnabled(bool enabled); 40 | 41 | int channel; 42 | MinimalStreamWidget *minimalStreamWidget; 43 | 44 | protected Q_SLOTS: 45 | void onVolumeScaleValueChanged(int value); 46 | void onVolumeScaleSliderMoved(int value); 47 | 48 | public: 49 | bool can_decibel; 50 | bool volumeScaleEnabled; 51 | bool last; 52 | 53 | QLabel *channelLabel; 54 | QSlider *volumeScale; 55 | QLabel *volumeLabel; 56 | 57 | //virtual void set_sensitive(bool enabled); 58 | virtual void setBaseVolume(pa_volume_t); 59 | }; 60 | 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/channelwidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ChannelWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 34 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | <b>left-front</b> 21 | 22 | 23 | 24 | 25 | 26 | 27 | Qt::Horizontal 28 | 29 | 30 | 31 | 32 | 33 | 34 | <small>50%</small> 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/devicewidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include 26 | 27 | #include "mainwindow.h" 28 | #include "devicewidget.h" 29 | #include "channel.h" 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | /*** DeviceWidget ***/ 38 | DeviceWidget::DeviceWidget(MainWindow* parent, QByteArray deviceType) : 39 | MinimalStreamWidget(parent), 40 | offsetButtonEnabled(false), 41 | mpMainWindow(parent), 42 | rename{new QAction{tr("Rename device..."), this}}, 43 | mDeviceType(std::move(deviceType)) { 44 | 45 | setupUi(this); 46 | advancedWidget->hide(); 47 | initPeakProgressBar(channelsGrid); 48 | 49 | timeout.setSingleShot(true); 50 | timeout.setInterval(100); 51 | connect(&timeout, &QTimer::timeout, this, &DeviceWidget::timeoutEvent); 52 | 53 | connect(muteToggleButton, &QToolButton::toggled, this, &DeviceWidget::onMuteToggleButton); 54 | connect(lockToggleButton, &QToolButton::toggled, this, &DeviceWidget::onLockToggleButton); 55 | connect(defaultToggleButton, &QToolButton::toggled, this, &DeviceWidget::onDefaultToggleButton); 56 | 57 | connect(rename, &QAction::triggered, this, &DeviceWidget::renamePopup); 58 | addAction(rename); 59 | setContextMenuPolicy(Qt::ActionsContextMenu); 60 | 61 | connect(portList, static_cast(&QComboBox::currentIndexChanged), this, &DeviceWidget::onPortChange); 62 | connect(offsetButton, static_cast(&QSpinBox::valueChanged), this, &DeviceWidget::onOffsetChange); 63 | 64 | for (auto & channel : channels) 65 | channel = nullptr; 66 | 67 | 68 | // FIXME: 69 | // offsetAdjustment = Gtk::Adjustment::create(0.0, -2000.0, 2000.0, 10.0, 50.0, 0.0); 70 | // offsetButton->configure(offsetAdjustment, 0, 2); 71 | } 72 | 73 | void DeviceWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) { 74 | channelMap = m; 75 | 76 | for (int i = 0; i < m.channels; i++) { 77 | Channel *ch = channels[i] = new Channel(channelsGrid); 78 | ch->channel = i; 79 | ch->can_decibel = can_decibel; 80 | ch->minimalStreamWidget = this; 81 | char text[64]; 82 | snprintf(text, sizeof(text), "%s", pa_channel_position_to_pretty_string(m.map[i])); 83 | ch->channelLabel->setText(QString::fromUtf8(text)); 84 | } 85 | channels[m.channels-1]->last = true; 86 | 87 | lockToggleButton->setEnabled(m.channels > 1); 88 | hideLockedChannels(lockToggleButton->isChecked()); 89 | } 90 | 91 | void DeviceWidget::setVolume(const pa_cvolume &v, bool force) { 92 | g_assert(v.channels == channelMap.channels); 93 | 94 | volume = v; 95 | 96 | if (!timeout.isActive() || force) { /* do not update the volume when a volume change is still in flux */ 97 | for (int i = 0; i < volume.channels; i++) 98 | channels[i]->setVolume(volume.values[i]); 99 | } 100 | } 101 | 102 | void DeviceWidget::updateChannelVolume(int channel, pa_volume_t v) { 103 | pa_cvolume n; 104 | g_assert(channel < volume.channels); 105 | 106 | n = volume; 107 | if (lockToggleButton->isChecked()) 108 | pa_cvolume_set(&n, n.channels, v); 109 | else 110 | n.values[channel] = v; 111 | 112 | setVolume(n, true); 113 | 114 | if (!timeout.isActive()) { 115 | timeout.start(); 116 | } 117 | } 118 | 119 | void DeviceWidget::hideLockedChannels(bool hide) { 120 | for (int i = 0; i < channelMap.channels - 1; i++) 121 | channels[i]->setVisible(!hide); 122 | 123 | channels[channelMap.channels - 1]->channelLabel->setVisible(!hide); 124 | } 125 | 126 | void DeviceWidget::onMuteToggleButton() { 127 | 128 | lockToggleButton->setEnabled(!muteToggleButton->isChecked()); 129 | 130 | for (int i = 0; i < channelMap.channels; i++) 131 | channels[i]->setEnabled(!muteToggleButton->isChecked()); 132 | } 133 | 134 | void DeviceWidget::onLockToggleButton() { 135 | hideLockedChannels(lockToggleButton->isChecked()); 136 | } 137 | 138 | void DeviceWidget::onDefaultToggleButton() { 139 | } 140 | 141 | void DeviceWidget::onOffsetChange() { 142 | pa_operation *o; 143 | int64_t offset; 144 | std::ostringstream card_stream; 145 | QByteArray card_name; 146 | 147 | if (!offsetButtonEnabled) 148 | return; 149 | 150 | offset = offsetButton->value() * 1000.0; 151 | card_stream << card_index; 152 | card_name = QByteArray::fromStdString(card_stream.str()); 153 | 154 | if (!(o = pa_context_set_port_latency_offset(get_context(), 155 | card_name.constData(), activePort.constData(), offset, nullptr, nullptr))) { 156 | show_error(tr("pa_context_set_port_latency_offset() failed").toUtf8().constData()); 157 | return; 158 | } 159 | pa_operation_unref(o); 160 | } 161 | 162 | void DeviceWidget::setDefault(bool isDefault) { 163 | defaultToggleButton->setChecked(isDefault); 164 | /*defaultToggleButton->setEnabled(!isDefault);*/ 165 | } 166 | 167 | bool DeviceWidget::timeoutEvent() { 168 | executeVolumeUpdate(); 169 | return false; 170 | } 171 | 172 | void DeviceWidget::executeVolumeUpdate() { 173 | } 174 | 175 | void DeviceWidget::setLatencyOffset(int64_t offset) { 176 | offsetButtonEnabled = false; 177 | offsetButton->setValue(offset / 1000.0); 178 | offsetButtonEnabled = true; 179 | } 180 | 181 | void DeviceWidget::setBaseVolume(pa_volume_t v) { 182 | 183 | for (int i = 0; i < channelMap.channels; i++) 184 | channels[i]->setBaseVolume(v); 185 | } 186 | 187 | void DeviceWidget::prepareMenu() { 188 | int idx = 0; 189 | int active_idx = -1; 190 | 191 | portList->clear(); 192 | /* Fill the ComboBox's Model */ 193 | for (auto & port : ports) { 194 | QByteArray name = port.first; 195 | QString desc = QString::fromUtf8(port.second); 196 | portList->addItem(desc, name); 197 | if (port.first == activePort) 198 | active_idx = idx; 199 | idx++; 200 | } 201 | 202 | if (active_idx >= 0) 203 | portList->setCurrentIndex(active_idx); 204 | 205 | if (!ports.empty()) { 206 | portSelect->show(); 207 | 208 | if (pa_context_get_server_protocol_version(get_context()) >= 27) { 209 | offsetSelect->show(); 210 | advancedOptions->setEnabled(true); 211 | } else { 212 | /* advancedOptions has sensitive=false by default */ 213 | offsetSelect->hide(); 214 | } 215 | 216 | } else { 217 | portSelect->hide(); 218 | advancedOptions->setEnabled(false); 219 | offsetSelect->hide(); 220 | } 221 | } 222 | 223 | void DeviceWidget::renamePopup() { 224 | if (updating) 225 | return; 226 | if (!mpMainWindow->canRenameDevices) { 227 | QMessageBox::warning(this, tr("Sorry, but device renaming is not supported.") 228 | , tr("You need to load module-device-manager in the PulseAudio server in order to rename devices")); 229 | return; 230 | } 231 | 232 | const QString old_name = QString::fromUtf8(description); 233 | bool ok; 234 | const QString new_name = QInputDialog::getText(this, QCoreApplication::organizationName(), tr("Rename device %1 to:").arg(old_name) 235 | , QLineEdit::Normal, old_name, &ok); 236 | if (ok && new_name != old_name) { 237 | pa_operation* o; 238 | gchar *key = g_markup_printf_escaped("%s:%s", mDeviceType.constData(), name.constData()); 239 | 240 | if (!(o = pa_ext_device_manager_set_device_description(get_context(), key, new_name.toUtf8().constData(), nullptr, nullptr))) 241 | show_error(tr("pa_ext_device_manager_set_device_description() failed").toUtf8().constData()); 242 | else 243 | pa_operation_unref(o); 244 | g_free(key); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/devicewidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef devicewidget_h 22 | #define devicewidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | #include "minimalstreamwidget.h" 27 | #include "ui_devicewidget.h" 28 | #include 29 | #include 30 | 31 | class MainWindow; 32 | class Channel; 33 | class QAction; 34 | 35 | class DeviceWidget : public MinimalStreamWidget, public Ui::DeviceWidget { 36 | Q_OBJECT 37 | public: 38 | DeviceWidget(MainWindow *parent, QByteArray deviceType = ""); 39 | 40 | void setChannelMap(const pa_channel_map &m, bool can_decibel); 41 | void setVolume(const pa_cvolume &volume, bool force = false); 42 | virtual void updateChannelVolume(int channel, pa_volume_t v); 43 | 44 | void hideLockedChannels(bool hide = true); 45 | 46 | QByteArray name; 47 | QByteArray description; 48 | uint32_t index, card_index; 49 | 50 | bool offsetButtonEnabled; 51 | 52 | pa_channel_map channelMap; 53 | pa_cvolume volume; 54 | 55 | Channel *channels[PA_CHANNELS_MAX]; 56 | 57 | public Q_SLOTS: 58 | virtual void onMuteToggleButton(); 59 | virtual void onLockToggleButton(); 60 | virtual void onDefaultToggleButton(); 61 | virtual void setDefault(bool isDefault); 62 | // virtual bool onContextTriggerEvent(GdkEventButton*); 63 | virtual void setLatencyOffset(int64_t offset); 64 | void onOffsetChange(); 65 | bool timeoutEvent(); 66 | 67 | public: 68 | QTimer timeout; 69 | 70 | virtual void executeVolumeUpdate(); 71 | virtual void setBaseVolume(pa_volume_t v); 72 | 73 | std::vector< std::pair > ports; 74 | QByteArray activePort; 75 | 76 | void prepareMenu(); 77 | 78 | void renamePopup(); 79 | 80 | protected: 81 | MainWindow *mpMainWindow; 82 | 83 | virtual void onPortChange() = 0; 84 | 85 | QAction * rename; 86 | 87 | private: 88 | QByteArray mDeviceType; 89 | 90 | }; 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /src/devicewidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | DeviceWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 306 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Device Title 37 | 38 | 39 | 40 | 41 | 42 | 43 | Qt::Horizontal 44 | 45 | 46 | 47 | 40 48 | 20 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | Mute audio 57 | 58 | 59 | 60 | 61 | 62 | true 63 | 64 | 65 | 66 | 67 | 68 | 69 | Lock channels together 70 | 71 | 72 | 73 | 74 | 75 | true 76 | 77 | 78 | true 79 | 80 | 81 | 82 | 83 | 84 | 85 | Set as fallback 86 | 87 | 88 | 89 | 90 | 91 | true 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | <b>Port:</b> 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 0 117 | 118 | 119 | 120 | 121 | 122 | 123 | false 124 | 125 | 126 | Show advanced options 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 0 135 | 136 | 137 | 9 138 | 139 | 140 | 0 141 | 142 | 143 | 0 144 | 145 | 146 | 0 147 | 148 | 149 | 150 | 151 | 152 | 0 153 | 154 | 155 | 0 156 | 157 | 158 | 0 159 | 160 | 161 | 0 162 | 163 | 164 | 0 165 | 166 | 167 | 168 | 169 | false 170 | 171 | 172 | PCM 173 | 174 | 175 | true 176 | 177 | 178 | 179 | 180 | 181 | 182 | AC3 183 | 184 | 185 | 186 | 187 | 188 | 189 | EAC3 190 | 191 | 192 | 193 | 194 | 195 | 196 | DTS 197 | 198 | 199 | 200 | 201 | 202 | 203 | MPEG 204 | 205 | 206 | 207 | 208 | 209 | 210 | AAC 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 0 222 | 223 | 224 | 0 225 | 226 | 227 | 0 228 | 229 | 230 | 0 231 | 232 | 233 | 0 234 | 235 | 236 | 237 | 238 | <b>Latency offset:</b> 239 | 240 | 241 | 242 | 243 | 244 | 245 | ms 246 | 247 | 248 | -2000 249 | 250 | 251 | 5000 252 | 253 | 254 | 10 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | Qt::Horizontal 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | advancedOptions 277 | toggled(bool) 278 | advancedWidget 279 | setVisible(bool) 280 | 281 | 282 | 151 283 | 103 284 | 285 | 286 | 364 287 | 122 288 | 289 | 290 | 291 | 292 | 293 | -------------------------------------------------------------------------------- /src/elidinglabel.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol-qt. 3 | 4 | pavucontrol-qt is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | pavucontrol-qt is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with pavucontrol-qt. If not, see . 16 | ***/ 17 | 18 | #include "elidinglabel.h" 19 | #include 20 | #include 21 | 22 | ElidingLabel::ElidingLabel(QWidget *parent, Qt::WindowFlags f): 23 | QLabel(parent, f), 24 | lastWidth_(0) { 25 | setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); 26 | // set a min width to prevent the window from widening with long texts 27 | setMinimumWidth(fontMetrics().averageCharWidth() * 10); 28 | } 29 | 30 | // A simplified version of QLabel::paintEvent() without pixmap or shortcut but with eliding 31 | void ElidingLabel::paintEvent(QPaintEvent */*event*/) { 32 | QRect cr = contentsRect().adjusted(margin(), margin(), -margin(), -margin()); 33 | QString txt = text(); 34 | // if the text is changed or its rect is resized (due to window resizing), 35 | // find whether it needs to be elided... 36 | if (txt != lastText_ || cr.width() != lastWidth_) { 37 | lastText_ = txt; 38 | lastWidth_ = cr.width(); 39 | elidedText_ = fontMetrics().elidedText(txt, Qt::ElideMiddle, cr.width()); 40 | } 41 | // ... then, draw the (elided) text */ 42 | QPainter painter(this); 43 | QStyleOption opt; 44 | opt.initFrom(this); 45 | style()->drawItemText(&painter, cr, alignment(), opt.palette, isEnabled(), elidedText_, foregroundRole()); 46 | } 47 | -------------------------------------------------------------------------------- /src/elidinglabel.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol-qt. 3 | 4 | pavucontrol-qt is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | pavucontrol-qt is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with pavucontrol-qt. If not, see . 16 | ***/ 17 | 18 | #ifndef elidinglabel_h 19 | #define elidinglabel_h 20 | 21 | #include 22 | 23 | class ElidingLabel : public QLabel { 24 | Q_OBJECT 25 | 26 | public: 27 | explicit ElidingLabel(QWidget *parent = 0, Qt::WindowFlags f = Qt::WindowFlags()); 28 | 29 | protected: 30 | void paintEvent(QPaintEvent *event) override; 31 | 32 | private: 33 | QString elidedText_; 34 | QString lastText_; 35 | int lastWidth_; 36 | }; 37 | 38 | #endif // elidinglabel_h 39 | -------------------------------------------------------------------------------- /src/mainwindow.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef mainwindow_h 22 | #define mainwindow_h 23 | 24 | #include "pavucontrol.h" 25 | #include 26 | #if HAVE_EXT_DEVICE_RESTORE_API 27 | # include 28 | #endif 29 | 30 | #include 31 | #include "ui_mainwindow.h" 32 | 33 | class CardWidget; 34 | class SinkWidget; 35 | class SourceWidget; 36 | class SinkInputWidget; 37 | class SourceOutputWidget; 38 | class RoleWidget; 39 | 40 | class MainWindow : public QDialog, public Ui::MainWindow { 41 | Q_OBJECT 42 | public: 43 | MainWindow(); 44 | virtual ~MainWindow(); 45 | 46 | void updateCard(const pa_card_info &info); 47 | bool updateSink(const pa_sink_info &info); 48 | void updateSource(const pa_source_info &info); 49 | void updateSinkInput(const pa_sink_input_info &info); 50 | void updateSourceOutput(const pa_source_output_info &info); 51 | void updateClient(const pa_client_info &info); 52 | void updateServer(const pa_server_info &info); 53 | void updateVolumeMeter(uint32_t source_index, uint32_t sink_input_index, double v); 54 | void updateRole(const pa_ext_stream_restore_info &info); 55 | #if HAVE_EXT_DEVICE_RESTORE_API 56 | void updateDeviceInfo(const pa_ext_device_restore_info &info); 57 | #endif 58 | 59 | void removeCard(uint32_t index); 60 | void removeSink(uint32_t index); 61 | void removeSource(uint32_t index); 62 | void removeSinkInput(uint32_t index); 63 | void removeSourceOutput(uint32_t index); 64 | void removeClient(uint32_t index); 65 | 66 | void removeAllWidgets(); 67 | 68 | void setConnectingMessage(const char *string = NULL); 69 | 70 | std::map cardWidgets; 71 | std::map sinkWidgets; 72 | std::map sourceWidgets; 73 | std::map sinkInputWidgets; 74 | std::map sourceOutputWidgets; 75 | 76 | std::map clientNames; 77 | SinkInputType showSinkInputType; 78 | SinkType showSinkType; 79 | SourceOutputType showSourceOutputType; 80 | SourceType showSourceType; 81 | 82 | protected Q_SLOTS: 83 | virtual void onSinkInputTypeComboBoxChanged(int index); 84 | virtual void onSourceOutputTypeComboBoxChanged(int index); 85 | virtual void onSinkTypeComboBoxChanged(int index); 86 | virtual void onSourceTypeComboBoxChanged(int index); 87 | virtual void onShowVolumeMetersCheckButtonToggled(bool toggled); 88 | 89 | public: 90 | void setConnectionState(gboolean connected); 91 | void updateDeviceVisibility(); 92 | void reallyUpdateDeviceVisibility(); 93 | pa_stream* createMonitorStreamForSource(uint32_t source_idx, uint32_t stream_idx, bool suspend); 94 | void createMonitorStreamForSinkInput(SinkInputWidget* w, uint32_t sink_idx); 95 | 96 | void setIconFromProplist(QLabel *icon, pa_proplist *l, const char *name); 97 | 98 | RoleWidget *eventRoleWidget; 99 | 100 | bool createEventRoleWidget(); 101 | void deleteEventRoleWidget(); 102 | 103 | QByteArray defaultSinkName, defaultSourceName; 104 | 105 | bool canRenameDevices; 106 | 107 | private: 108 | gboolean m_connected; 109 | gchar* m_config_filename; 110 | }; 111 | 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 766 10 | 548 11 | 12 | 13 | 14 | Volume Control 15 | 16 | 17 | 18 | .. 19 | 20 | 21 | 22 | 23 | 24 | 0 25 | 26 | 27 | 28 | &Playback 29 | 30 | 31 | 32 | 33 | 34 | true 35 | 36 | 37 | 38 | 39 | 0 40 | 0 41 | 730 42 | 423 43 | 44 | 45 | 46 | 47 | 0 48 | 0 49 | 50 | 51 | 52 | 53 | 54 | 55 | <i>No application is currently playing audio.</i> 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Show: 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | All Streams 75 | 76 | 77 | 78 | 79 | Applications 80 | 81 | 82 | 83 | 84 | Virtual Streams 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | &Recording 94 | 95 | 96 | 97 | 98 | 99 | true 100 | 101 | 102 | 103 | 104 | 0 105 | 0 106 | 730 107 | 423 108 | 109 | 110 | 111 | 112 | 0 113 | 0 114 | 115 | 116 | 117 | 118 | 119 | 120 | <i>No application is currently recording audio.</i> 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | Show: 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | All Streams 140 | 141 | 142 | 143 | 144 | Applications 145 | 146 | 147 | 148 | 149 | Virtual Streams 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | &Output Devices 159 | 160 | 161 | 162 | 163 | 164 | true 165 | 166 | 167 | 168 | 169 | 0 170 | 0 171 | 730 172 | 423 173 | 174 | 175 | 176 | 177 | 0 178 | 0 179 | 180 | 181 | 182 | 183 | 184 | 185 | <i>No output devices available</i> 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | Show: 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | All Output Devices 205 | 206 | 207 | 208 | 209 | Hardware Output Devices 210 | 211 | 212 | 213 | 214 | Virtual Output Devices 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | &Input Devices 224 | 225 | 226 | 227 | 228 | 229 | true 230 | 231 | 232 | 233 | 234 | 0 235 | 0 236 | 730 237 | 423 238 | 239 | 240 | 241 | 242 | 0 243 | 0 244 | 245 | 246 | 247 | 248 | 249 | 250 | <i>No input devices available</i> 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | Show: 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | All Input Devices 270 | 271 | 272 | 273 | 274 | All Except Monitors 275 | 276 | 277 | 278 | 279 | Hardware Input Devices 280 | 281 | 282 | 283 | 284 | Virtual Input Devices 285 | 286 | 287 | 288 | 289 | Monitors 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | &Configuration 299 | 300 | 301 | 302 | 303 | 304 | true 305 | 306 | 307 | 308 | 309 | 0 310 | 0 311 | 730 312 | 433 313 | 314 | 315 | 316 | 317 | 0 318 | 0 319 | 320 | 321 | 322 | 323 | 324 | 325 | <i>No cards available for configuration</i> 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | Show volume meters 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | ... 348 | 349 | 350 | Qt::AlignCenter 351 | 352 | 353 | true 354 | 355 | 356 | Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | -------------------------------------------------------------------------------- /src/minimalstreamwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "minimalstreamwidget.h" 26 | #include 27 | #include 28 | #include 29 | 30 | /*** MinimalStreamWidget ***/ 31 | MinimalStreamWidget::MinimalStreamWidget(QWidget *parent) : 32 | QWidget(parent), 33 | peakProgressBar(new QProgressBar(this)), 34 | lastPeak(0), 35 | peak(nullptr), 36 | updating(false), 37 | volumeMeterEnabled(false), 38 | volumeMeterVisible(true) { 39 | 40 | peakProgressBar->setTextVisible(false); 41 | peakProgressBar->hide(); 42 | } 43 | 44 | MinimalStreamWidget::~MinimalStreamWidget() { 45 | if (peak != nullptr) { 46 | pa_stream_disconnect(peak); 47 | pa_stream_unref(peak); 48 | peak = nullptr; 49 | } 50 | } 51 | 52 | void MinimalStreamWidget::initPeakProgressBar(QGridLayout* channelsGrid) { 53 | channelsGrid->addWidget(peakProgressBar, channelsGrid->rowCount(), 0, 1, -1); 54 | } 55 | 56 | #define DECAY_STEP .04 57 | 58 | void MinimalStreamWidget::updatePeak(double v) { 59 | 60 | if (lastPeak >= DECAY_STEP) 61 | if (v < lastPeak - DECAY_STEP) 62 | v = lastPeak - DECAY_STEP; 63 | 64 | lastPeak = v; 65 | 66 | if (v >= 0) { 67 | peakProgressBar->setEnabled(TRUE); 68 | int value = qRound(v * peakProgressBar->maximum()); 69 | peakProgressBar->setValue(value); 70 | } else { 71 | peakProgressBar->setEnabled(FALSE); 72 | peakProgressBar->setValue(0); 73 | } 74 | 75 | enableVolumeMeter(); 76 | } 77 | 78 | void MinimalStreamWidget::enableVolumeMeter() { 79 | if (volumeMeterEnabled) 80 | return; 81 | 82 | volumeMeterEnabled = true; 83 | if (volumeMeterVisible) { 84 | peakProgressBar->show(); 85 | } 86 | } 87 | 88 | void MinimalStreamWidget::setVolumeMeterVisible(bool v) { 89 | volumeMeterVisible = v; 90 | if (v) { 91 | if (volumeMeterEnabled) { 92 | peakProgressBar->show(); 93 | } 94 | } else { 95 | peakProgressBar->hide(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/minimalstreamwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef minimalstreamwidget_h 22 | #define minimalstreamwidget_h 23 | 24 | #include "pavucontrol.h" 25 | #include 26 | 27 | class QProgressBar; 28 | class QGridLayout; 29 | 30 | class MinimalStreamWidget : public QWidget { 31 | Q_OBJECT 32 | public: 33 | MinimalStreamWidget(QWidget* parent = nullptr); 34 | ~MinimalStreamWidget() override; 35 | void initPeakProgressBar(QGridLayout* channelsGrid); 36 | 37 | QProgressBar* peakProgressBar; 38 | double lastPeak; 39 | pa_stream *peak; 40 | 41 | bool updating; 42 | 43 | virtual void onMuteToggleButton() = 0; 44 | virtual void onLockToggleButton() = 0; 45 | virtual void updateChannelVolume(int channel, pa_volume_t v) = 0; 46 | 47 | bool volumeMeterEnabled; 48 | void enableVolumeMeter(); 49 | void updatePeak(double v); 50 | void setVolumeMeterVisible(bool v); 51 | 52 | private : 53 | bool volumeMeterVisible; 54 | 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/pavucontrol-qt.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Exec=pavucontrol-qt 4 | Icon=multimedia-volume-control 5 | StartupNotify=true 6 | Type=Application 7 | Categories=Qt;AudioVideo;Audio;Settings;DesktopSettings; 8 | -------------------------------------------------------------------------------- /src/pavucontrol.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2008 Sjoerd Simons 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #define PACKAGE_VERSION "0.1" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | // #include 33 | 34 | #include "pavucontrol.h" 35 | #include "minimalstreamwidget.h" 36 | #include "channel.h" 37 | #include "streamwidget.h" 38 | #include "cardwidget.h" 39 | #include "sinkwidget.h" 40 | #include "sourcewidget.h" 41 | #include "sinkinputwidget.h" 42 | #include "sourceoutputwidget.h" 43 | #include "rolewidget.h" 44 | #include "mainwindow.h" 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | static pa_context* context = nullptr; 55 | static pa_mainloop_api* api = nullptr; 56 | static int n_outstanding = 0; 57 | static int default_tab = 0; 58 | static bool retry = false; 59 | static int reconnect_timeout = 1; 60 | 61 | void show_error(const char *txt) { 62 | char buf[256]; 63 | 64 | snprintf(buf, sizeof(buf), "%s: %s", txt, pa_strerror(pa_context_errno(context))); 65 | 66 | QMessageBox::critical(nullptr, QObject::tr("Error"), QString::fromUtf8(buf)); 67 | qApp->quit(); 68 | } 69 | 70 | static void dec_outstanding(MainWindow *w) { 71 | if (n_outstanding <= 0) 72 | return; 73 | 74 | if (--n_outstanding <= 0) { 75 | // w->get_window()->set_cursor(); 76 | w->setConnectionState(true); 77 | } 78 | } 79 | 80 | void card_cb(pa_context *, const pa_card_info *i, int eol, void *userdata) { 81 | MainWindow *w = static_cast(userdata); 82 | 83 | if (eol < 0) { 84 | if (pa_context_errno(context) == PA_ERR_NOENTITY) 85 | return; 86 | 87 | show_error(QObject::tr("Card callback failure").toUtf8().constData()); 88 | return; 89 | } 90 | 91 | if (eol > 0) { 92 | dec_outstanding(w); 93 | return; 94 | } 95 | 96 | w->updateCard(*i); 97 | } 98 | 99 | #if HAVE_EXT_DEVICE_RESTORE_API 100 | static void ext_device_restore_subscribe_cb(pa_context *c, pa_device_type_t type, uint32_t idx, void *userdata); 101 | #endif 102 | 103 | void sink_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) { 104 | MainWindow *w = static_cast(userdata); 105 | 106 | if (eol < 0) { 107 | if (pa_context_errno(context) == PA_ERR_NOENTITY) 108 | return; 109 | 110 | show_error(QObject::tr("Sink callback failure").toUtf8().constData()); 111 | return; 112 | } 113 | 114 | if (eol > 0) { 115 | dec_outstanding(w); 116 | return; 117 | } 118 | #if HAVE_EXT_DEVICE_RESTORE_API 119 | if (w->updateSink(*i)) 120 | ext_device_restore_subscribe_cb(c, PA_DEVICE_TYPE_SINK, i->index, w); 121 | #else 122 | w->updateSink(*i); 123 | #endif 124 | } 125 | 126 | void source_cb(pa_context *, const pa_source_info *i, int eol, void *userdata) { 127 | MainWindow *w = static_cast(userdata); 128 | 129 | if (eol < 0) { 130 | if (pa_context_errno(context) == PA_ERR_NOENTITY) 131 | return; 132 | 133 | show_error(QObject::tr("Source callback failure").toUtf8().constData()); 134 | return; 135 | } 136 | 137 | if (eol > 0) { 138 | dec_outstanding(w); 139 | return; 140 | } 141 | 142 | w->updateSource(*i); 143 | } 144 | 145 | void sink_input_cb(pa_context *, const pa_sink_input_info *i, int eol, void *userdata) { 146 | MainWindow *w = static_cast(userdata); 147 | 148 | if (eol < 0) { 149 | if (pa_context_errno(context) == PA_ERR_NOENTITY) 150 | return; 151 | 152 | show_error(QObject::tr("Sink input callback failure").toUtf8().constData()); 153 | return; 154 | } 155 | 156 | if (eol > 0) { 157 | dec_outstanding(w); 158 | return; 159 | } 160 | 161 | w->updateSinkInput(*i); 162 | } 163 | 164 | void source_output_cb(pa_context *, const pa_source_output_info *i, int eol, void *userdata) { 165 | MainWindow *w = static_cast(userdata); 166 | 167 | if (eol < 0) { 168 | if (pa_context_errno(context) == PA_ERR_NOENTITY) 169 | return; 170 | 171 | show_error(QObject::tr("Source output callback failure").toUtf8().constData()); 172 | return; 173 | } 174 | 175 | if (eol > 0) { 176 | 177 | if (n_outstanding > 0) { 178 | /* At this point all notebook pages have been populated, so 179 | * let's open one that isn't empty */ 180 | if (default_tab != -1) { 181 | if (default_tab < 1 || default_tab > w->notebook->count()) { 182 | if (!w->sinkInputWidgets.empty()) 183 | w->notebook->setCurrentIndex(0); 184 | else if (!w->sourceOutputWidgets.empty()) 185 | w->notebook->setCurrentIndex(1); 186 | else if (!w->sourceWidgets.empty() && w->sinkWidgets.empty()) 187 | w->notebook->setCurrentIndex(3); 188 | else 189 | w->notebook->setCurrentIndex(2); 190 | } else { 191 | w->notebook->setCurrentIndex(default_tab - 1); 192 | } 193 | default_tab = -1; 194 | } 195 | } 196 | 197 | dec_outstanding(w); 198 | return; 199 | } 200 | 201 | w->updateSourceOutput(*i); 202 | } 203 | 204 | void client_cb(pa_context *, const pa_client_info *i, int eol, void *userdata) { 205 | MainWindow *w = static_cast(userdata); 206 | 207 | if (eol < 0) { 208 | if (pa_context_errno(context) == PA_ERR_NOENTITY) 209 | return; 210 | 211 | show_error(QObject::tr("Client callback failure").toUtf8().constData()); 212 | return; 213 | } 214 | 215 | if (eol > 0) { 216 | dec_outstanding(w); 217 | return; 218 | } 219 | 220 | w->updateClient(*i); 221 | } 222 | 223 | void server_info_cb(pa_context *, const pa_server_info *i, void *userdata) { 224 | MainWindow *w = static_cast(userdata); 225 | 226 | if (!i) { 227 | show_error(QObject::tr("Server info callback failure").toUtf8().constData()); 228 | return; 229 | } 230 | 231 | w->updateServer(*i); 232 | dec_outstanding(w); 233 | } 234 | 235 | void ext_stream_restore_read_cb( 236 | pa_context *, 237 | const pa_ext_stream_restore_info *i, 238 | int eol, 239 | void *userdata) { 240 | 241 | MainWindow *w = static_cast(userdata); 242 | 243 | if (eol < 0) { 244 | dec_outstanding(w); 245 | g_debug(QObject::tr("Failed to initialize stream_restore extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context))); 246 | w->deleteEventRoleWidget(); 247 | return; 248 | } 249 | 250 | if (eol > 0) { 251 | dec_outstanding(w); 252 | return; 253 | } 254 | 255 | w->updateRole(*i); 256 | } 257 | 258 | static void ext_stream_restore_subscribe_cb(pa_context *c, void *userdata) { 259 | MainWindow *w = static_cast(userdata); 260 | pa_operation *o; 261 | 262 | if (!(o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, w))) { 263 | show_error(QObject::tr("pa_ext_stream_restore_read() failed").toUtf8().constData()); 264 | return; 265 | } 266 | 267 | pa_operation_unref(o); 268 | } 269 | 270 | #if HAVE_EXT_DEVICE_RESTORE_API 271 | void ext_device_restore_read_cb( 272 | pa_context *, 273 | const pa_ext_device_restore_info *i, 274 | int eol, 275 | void *userdata) { 276 | 277 | MainWindow *w = static_cast(userdata); 278 | 279 | if (eol < 0) { 280 | dec_outstanding(w); 281 | g_debug(QObject::tr("Failed to initialize device restore extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context))); 282 | return; 283 | } 284 | 285 | if (eol > 0) { 286 | dec_outstanding(w); 287 | return; 288 | } 289 | 290 | /* Do something with a widget when this part is written */ 291 | w->updateDeviceInfo(*i); 292 | } 293 | 294 | static void ext_device_restore_subscribe_cb(pa_context *c, pa_device_type_t type, uint32_t idx, void *userdata) { 295 | MainWindow *w = static_cast(userdata); 296 | pa_operation *o; 297 | 298 | if (type != PA_DEVICE_TYPE_SINK) 299 | return; 300 | 301 | if (!(o = pa_ext_device_restore_read_formats(c, type, idx, ext_device_restore_read_cb, w))) { 302 | show_error(QObject::tr("pa_ext_device_restore_read_sink_formats() failed").toUtf8().constData()); 303 | return; 304 | } 305 | 306 | pa_operation_unref(o); 307 | } 308 | #endif 309 | 310 | void ext_device_manager_read_cb( 311 | pa_context *, 312 | const pa_ext_device_manager_info *, 313 | int eol, 314 | void *userdata) { 315 | 316 | MainWindow *w = static_cast(userdata); 317 | 318 | if (eol < 0) { 319 | dec_outstanding(w); 320 | g_debug(QObject::tr("Failed to initialize device manager extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context))); 321 | return; 322 | } 323 | 324 | w->canRenameDevices = true; 325 | 326 | if (eol > 0) { 327 | dec_outstanding(w); 328 | return; 329 | } 330 | 331 | /* Do something with a widget when this part is written */ 332 | } 333 | 334 | static void ext_device_manager_subscribe_cb(pa_context *c, void *userdata) { 335 | MainWindow *w = static_cast(userdata); 336 | pa_operation *o; 337 | 338 | if (!(o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, w))) { 339 | show_error(QObject::tr("pa_ext_device_manager_read() failed").toUtf8().constData()); 340 | return; 341 | } 342 | 343 | pa_operation_unref(o); 344 | } 345 | 346 | void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata) { 347 | MainWindow *w = static_cast(userdata); 348 | 349 | switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { 350 | case PA_SUBSCRIPTION_EVENT_SINK: 351 | if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) 352 | w->removeSink(index); 353 | else { 354 | pa_operation *o; 355 | if (!(o = pa_context_get_sink_info_by_index(c, index, sink_cb, w))) { 356 | show_error(QObject::tr("pa_context_get_sink_info_by_index() failed").toUtf8().constData()); 357 | return; 358 | } 359 | pa_operation_unref(o); 360 | } 361 | break; 362 | 363 | case PA_SUBSCRIPTION_EVENT_SOURCE: 364 | if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) 365 | w->removeSource(index); 366 | else { 367 | pa_operation *o; 368 | if (!(o = pa_context_get_source_info_by_index(c, index, source_cb, w))) { 369 | show_error(QObject::tr("pa_context_get_source_info_by_index() failed").toUtf8().constData()); 370 | return; 371 | } 372 | pa_operation_unref(o); 373 | } 374 | break; 375 | 376 | case PA_SUBSCRIPTION_EVENT_SINK_INPUT: 377 | if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) 378 | w->removeSinkInput(index); 379 | else { 380 | pa_operation *o; 381 | if (!(o = pa_context_get_sink_input_info(c, index, sink_input_cb, w))) { 382 | show_error(QObject::tr("pa_context_get_sink_input_info() failed").toUtf8().constData()); 383 | return; 384 | } 385 | pa_operation_unref(o); 386 | } 387 | break; 388 | 389 | case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: 390 | if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) 391 | w->removeSourceOutput(index); 392 | else { 393 | pa_operation *o; 394 | if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, w))) { 395 | show_error(QObject::tr("pa_context_get_sink_input_info() failed").toUtf8().constData()); 396 | return; 397 | } 398 | pa_operation_unref(o); 399 | } 400 | break; 401 | 402 | case PA_SUBSCRIPTION_EVENT_CLIENT: 403 | if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) 404 | w->removeClient(index); 405 | else { 406 | pa_operation *o; 407 | if (!(o = pa_context_get_client_info(c, index, client_cb, w))) { 408 | show_error(QObject::tr("pa_context_get_client_info() failed").toUtf8().constData()); 409 | return; 410 | } 411 | pa_operation_unref(o); 412 | } 413 | break; 414 | 415 | case PA_SUBSCRIPTION_EVENT_SERVER: { 416 | pa_operation *o; 417 | if (!(o = pa_context_get_server_info(c, server_info_cb, w))) { 418 | show_error(QObject::tr("pa_context_get_server_info() failed").toUtf8().constData()); 419 | return; 420 | } 421 | pa_operation_unref(o); 422 | } 423 | break; 424 | 425 | case PA_SUBSCRIPTION_EVENT_CARD: 426 | if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) 427 | w->removeCard(index); 428 | else { 429 | pa_operation *o; 430 | if (!(o = pa_context_get_card_info_by_index(c, index, card_cb, w))) { 431 | show_error(QObject::tr("pa_context_get_card_info_by_index() failed").toUtf8().constData()); 432 | return; 433 | } 434 | pa_operation_unref(o); 435 | } 436 | break; 437 | 438 | } 439 | } 440 | 441 | /* Forward Declaration */ 442 | gboolean connect_to_pulse(gpointer userdata); 443 | 444 | void context_state_callback(pa_context *c, void *userdata) { 445 | MainWindow *w = static_cast(userdata); 446 | 447 | g_assert(c); 448 | 449 | switch (pa_context_get_state(c)) { 450 | case PA_CONTEXT_UNCONNECTED: 451 | case PA_CONTEXT_CONNECTING: 452 | case PA_CONTEXT_AUTHORIZING: 453 | case PA_CONTEXT_SETTING_NAME: 454 | break; 455 | 456 | case PA_CONTEXT_READY: { 457 | pa_operation *o; 458 | 459 | reconnect_timeout = 1; 460 | 461 | /* Create event widget immediately so it's first in the list */ 462 | w->createEventRoleWidget(); 463 | 464 | pa_context_set_subscribe_callback(c, subscribe_cb, w); 465 | 466 | if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t) 467 | (PA_SUBSCRIPTION_MASK_SINK| 468 | PA_SUBSCRIPTION_MASK_SOURCE| 469 | PA_SUBSCRIPTION_MASK_SINK_INPUT| 470 | PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT| 471 | PA_SUBSCRIPTION_MASK_CLIENT| 472 | PA_SUBSCRIPTION_MASK_SERVER| 473 | PA_SUBSCRIPTION_MASK_CARD), nullptr, nullptr))) { 474 | show_error(QObject::tr("pa_context_subscribe() failed").toUtf8().constData()); 475 | return; 476 | } 477 | pa_operation_unref(o); 478 | 479 | /* Keep track of the outstanding callbacks for UI tweaks */ 480 | n_outstanding = 0; 481 | 482 | if (!(o = pa_context_get_server_info(c, server_info_cb, w))) { 483 | show_error(QObject::tr("pa_context_get_server_info() failed").toUtf8().constData()); 484 | return; 485 | } 486 | pa_operation_unref(o); 487 | n_outstanding++; 488 | 489 | if (!(o = pa_context_get_client_info_list(c, client_cb, w))) { 490 | show_error(QObject::tr("pa_context_client_info_list() failed").toUtf8().constData()); 491 | return; 492 | } 493 | pa_operation_unref(o); 494 | n_outstanding++; 495 | 496 | if (!(o = pa_context_get_card_info_list(c, card_cb, w))) { 497 | show_error(QObject::tr("pa_context_get_card_info_list() failed").toUtf8().constData()); 498 | return; 499 | } 500 | pa_operation_unref(o); 501 | n_outstanding++; 502 | 503 | if (!(o = pa_context_get_sink_info_list(c, sink_cb, w))) { 504 | show_error(QObject::tr("pa_context_get_sink_info_list() failed").toUtf8().constData()); 505 | return; 506 | } 507 | pa_operation_unref(o); 508 | n_outstanding++; 509 | 510 | if (!(o = pa_context_get_source_info_list(c, source_cb, w))) { 511 | show_error(QObject::tr("pa_context_get_source_info_list() failed").toUtf8().constData()); 512 | return; 513 | } 514 | pa_operation_unref(o); 515 | n_outstanding++; 516 | 517 | if (!(o = pa_context_get_sink_input_info_list(c, sink_input_cb, w))) { 518 | show_error(QObject::tr("pa_context_get_sink_input_info_list() failed").toUtf8().constData()); 519 | return; 520 | } 521 | pa_operation_unref(o); 522 | n_outstanding++; 523 | 524 | if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, w))) { 525 | show_error(QObject::tr("pa_context_get_source_output_info_list() failed").toUtf8().constData()); 526 | return; 527 | } 528 | pa_operation_unref(o); 529 | n_outstanding++; 530 | 531 | /* These calls are not always supported */ 532 | if ((o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, w))) { 533 | pa_operation_unref(o); 534 | n_outstanding++; 535 | 536 | pa_ext_stream_restore_set_subscribe_cb(c, ext_stream_restore_subscribe_cb, w); 537 | 538 | if ((o = pa_ext_stream_restore_subscribe(c, 1, nullptr, nullptr))) 539 | pa_operation_unref(o); 540 | 541 | } else 542 | g_debug(QObject::tr("Failed to initialize stream_restore extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context))); 543 | 544 | #if HAVE_EXT_DEVICE_RESTORE_API 545 | /* TODO Change this to just the test function */ 546 | if ((o = pa_ext_device_restore_read_formats_all(c, ext_device_restore_read_cb, w))) { 547 | pa_operation_unref(o); 548 | n_outstanding++; 549 | 550 | pa_ext_device_restore_set_subscribe_cb(c, ext_device_restore_subscribe_cb, w); 551 | 552 | if ((o = pa_ext_device_restore_subscribe(c, 1, nullptr, nullptr))) 553 | pa_operation_unref(o); 554 | 555 | } else 556 | g_debug(QObject::tr("Failed to initialize device restore extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context))); 557 | #endif 558 | 559 | if ((o = pa_ext_device_manager_read(c, ext_device_manager_read_cb, w))) { 560 | pa_operation_unref(o); 561 | n_outstanding++; 562 | 563 | pa_ext_device_manager_set_subscribe_cb(c, ext_device_manager_subscribe_cb, w); 564 | 565 | if ((o = pa_ext_device_manager_subscribe(c, 1, nullptr, nullptr))) 566 | pa_operation_unref(o); 567 | 568 | } else 569 | g_debug(QObject::tr("Failed to initialize device manager extension: %s").toUtf8().constData(), pa_strerror(pa_context_errno(context))); 570 | 571 | 572 | break; 573 | } 574 | 575 | case PA_CONTEXT_FAILED: 576 | w->setConnectionState(false); 577 | 578 | w->removeAllWidgets(); 579 | w->updateDeviceVisibility(); 580 | pa_context_unref(context); 581 | context = nullptr; 582 | 583 | if (reconnect_timeout > 0) { 584 | g_debug("%s", QObject::tr("Connection failed, attempting reconnect").toUtf8().constData()); 585 | g_timeout_add_seconds(reconnect_timeout, connect_to_pulse, w); 586 | } 587 | return; 588 | 589 | case PA_CONTEXT_TERMINATED: 590 | default: 591 | qApp->quit(); 592 | return; 593 | } 594 | } 595 | 596 | pa_context* get_context() { 597 | return context; 598 | } 599 | 600 | gboolean connect_to_pulse(gpointer userdata) { 601 | MainWindow *w = static_cast(userdata); 602 | 603 | if (context) 604 | return false; 605 | 606 | pa_proplist *proplist = pa_proplist_new(); 607 | pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, QObject::tr("PulseAudio Volume Control").toUtf8().constData()); 608 | pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "org.PulseAudio.pavucontrol"); 609 | pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "audio-card"); 610 | pa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION); 611 | 612 | context = pa_context_new_with_proplist(api, nullptr, proplist); 613 | g_assert(context); 614 | 615 | pa_proplist_free(proplist); 616 | 617 | pa_context_set_state_callback(context, context_state_callback, w); 618 | 619 | w->setConnectingMessage(); 620 | if (pa_context_connect(context, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) { 621 | if (pa_context_errno(context) == PA_ERR_INVALID) { 622 | w->setConnectingMessage(QObject::tr("Connection to PulseAudio failed. Automatic retry in 5s.

" 623 | "In this case this is likely because PULSE_SERVER in the Environment/X11 Root Window Properties" 624 | "or default-server in client.conf is misconfigured.
" 625 | "This situation can also arrise when PulseAudio crashed and left stale details in the X11 Root Window.
" 626 | "If this is the case, then PulseAudio should autospawn again, or if this is not configured you should" 627 | "run start-pulseaudio-x11 manually.").toUtf8().constData()); 628 | reconnect_timeout = 5; 629 | } 630 | else { 631 | if(!retry) { 632 | reconnect_timeout = -1; 633 | qApp->quit(); 634 | } else { 635 | g_debug("%s", QObject::tr("Connection failed, attempting reconnect").toUtf8().constData()); 636 | reconnect_timeout = 5; 637 | g_timeout_add_seconds(reconnect_timeout, connect_to_pulse, userdata); 638 | } 639 | } 640 | } 641 | 642 | return false; 643 | } 644 | 645 | int main(int argc, char *argv[]) { 646 | 647 | signal(SIGPIPE, SIG_IGN); 648 | 649 | QApplication app(argc, argv); 650 | 651 | app.setOrganizationName(QStringLiteral("pavucontrol-qt")); 652 | 653 | QString locale = QLocale::system().name(); 654 | QTranslator qtTranslator; 655 | if(qtTranslator.load(QStringLiteral("qt_") + locale, QLibraryInfo::path(QLibraryInfo::TranslationsPath))) 656 | qApp->installTranslator(&qtTranslator); 657 | 658 | QTranslator appTranslator; 659 | if(appTranslator.load(QStringLiteral("pavucontrol-qt_") + locale, QStringLiteral(PAVUCONTROL_QT_DATA_DIR) + QStringLiteral("/translations"))) 660 | qApp->installTranslator(&appTranslator); 661 | 662 | QCommandLineParser parser; 663 | parser.setApplicationDescription(QObject::tr("PulseAudio Volume Control")); 664 | parser.addHelpOption(); 665 | 666 | const QString VERINFO = QStringLiteral(PAVUCONTROLQT_VERSION 667 | "\nQt " QT_VERSION_STR); 668 | app.setApplicationVersion(VERINFO); 669 | parser.addVersionOption(); 670 | 671 | QCommandLineOption tabOption(QStringList() << QStringLiteral("tab") << QStringLiteral("t"), QObject::tr("Select a specific tab on load."), QStringLiteral("tab")); 672 | parser.addOption(tabOption); 673 | 674 | QCommandLineOption retryOption(QStringList() << QStringLiteral("retry") << QStringLiteral("r"), QObject::tr("Retry forever if pa quits (every 5 seconds).")); 675 | parser.addOption(retryOption); 676 | 677 | QCommandLineOption maximizeOption(QStringList() << QStringLiteral("maximize") << QStringLiteral("m"), QObject::tr("Maximize the window.")); 678 | parser.addOption(maximizeOption); 679 | 680 | parser.process(app); 681 | default_tab = parser.value(tabOption).toInt(); 682 | retry = parser.isSet(retryOption); 683 | 684 | // ca_context_set_driver(ca_gtk_context_get(), "pulse"); 685 | 686 | MainWindow* mainWindow = new MainWindow(); 687 | if(parser.isSet(maximizeOption)) 688 | mainWindow->showMaximized(); 689 | 690 | pa_glib_mainloop *m = pa_glib_mainloop_new(g_main_context_default()); 691 | g_assert(m); 692 | api = pa_glib_mainloop_get_api(m); 693 | g_assert(api); 694 | 695 | connect_to_pulse(mainWindow); 696 | if (reconnect_timeout >= 0) { 697 | mainWindow->show(); 698 | app.exec(); 699 | } 700 | 701 | if (reconnect_timeout < 0) 702 | show_error(QObject::tr("Fatal Error: Unable to connect to PulseAudio").toUtf8().constData()); 703 | 704 | delete mainWindow; 705 | 706 | if (context) 707 | pa_context_unref(context); 708 | pa_glib_mainloop_free(m); 709 | 710 | return 0; 711 | } 712 | -------------------------------------------------------------------------------- /src/pavucontrol.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef pavucontrol_h 22 | #define pavucontrol_h 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | /* Can be removed when PulseAudio 0.9.23 or newer is required */ 31 | #ifndef PA_VOLUME_UI_MAX 32 | # define PA_VOLUME_UI_MAX (pa_sw_volume_from_dB(+11.0)) 33 | #endif 34 | 35 | #define HAVE_SOURCE_OUTPUT_VOLUMES PA_CHECK_VERSION(0,99,0) 36 | #define HAVE_EXT_DEVICE_RESTORE_API PA_CHECK_VERSION(0,99,0) 37 | 38 | enum SinkInputType { 39 | SINK_INPUT_ALL, 40 | SINK_INPUT_CLIENT, 41 | SINK_INPUT_VIRTUAL 42 | }; 43 | 44 | enum SinkType { 45 | SINK_ALL, 46 | SINK_HARDWARE, 47 | SINK_VIRTUAL, 48 | }; 49 | 50 | enum SourceOutputType { 51 | SOURCE_OUTPUT_ALL, 52 | SOURCE_OUTPUT_CLIENT, 53 | SOURCE_OUTPUT_VIRTUAL 54 | }; 55 | 56 | enum SourceType { 57 | SOURCE_ALL, 58 | SOURCE_NO_MONITOR, 59 | SOURCE_HARDWARE, 60 | SOURCE_VIRTUAL, 61 | SOURCE_MONITOR, 62 | }; 63 | 64 | pa_context* get_context(void); 65 | void show_error(const char *txt); 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/rolewidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "rolewidget.h" 26 | 27 | #include 28 | 29 | RoleWidget::RoleWidget(MainWindow *parent) : 30 | StreamWidget(parent) { 31 | 32 | lockToggleButton->hide(); 33 | directionLabel->hide(); 34 | deviceButton->hide(); 35 | setContextMenuPolicy(Qt::DefaultContextMenu); 36 | } 37 | 38 | void RoleWidget::onMuteToggleButton() { 39 | StreamWidget::onMuteToggleButton(); 40 | 41 | executeVolumeUpdate(); 42 | } 43 | 44 | void RoleWidget::executeVolumeUpdate() { 45 | pa_ext_stream_restore_info info; 46 | 47 | if (updating) 48 | return; 49 | 50 | info.name = role.constData(); 51 | info.channel_map.channels = 1; 52 | info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO; 53 | info.volume = volume; 54 | info.device = device == "" ? nullptr : device.constData(); 55 | info.mute = muteToggleButton->isChecked(); 56 | 57 | pa_operation* o; 58 | if (!(o = pa_ext_stream_restore_write(get_context(), PA_UPDATE_REPLACE, &info, 1, TRUE, nullptr, nullptr))) { 59 | show_error(tr("pa_ext_stream_restore_write() failed").toUtf8().constData()); 60 | return; 61 | } 62 | 63 | pa_operation_unref(o); 64 | } 65 | 66 | -------------------------------------------------------------------------------- /src/rolewidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef rolewidget_h 22 | #define rolewidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | #include "streamwidget.h" 27 | 28 | class RoleWidget : public StreamWidget { 29 | Q_OBJECT 30 | public: 31 | RoleWidget(MainWindow* parent); 32 | 33 | QByteArray role; 34 | QByteArray device; 35 | 36 | virtual void onMuteToggleButton(); 37 | virtual void executeVolumeUpdate(); 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/sinkinputwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "sinkinputwidget.h" 26 | #include "mainwindow.h" 27 | #include "sinkwidget.h" 28 | #include 29 | 30 | 31 | SinkInputWidget::SinkInputWidget(MainWindow *parent) : 32 | StreamWidget(parent), 33 | menu{new QMenu{this}} { 34 | 35 | gchar *txt; 36 | directionLabel->setText(QString::fromUtf8(txt = g_markup_printf_escaped("%s", tr("on").toUtf8().constData()))); 37 | g_free(txt); 38 | 39 | terminate->setText(tr("Terminate Playback")); 40 | } 41 | 42 | SinkInputWidget::~SinkInputWidget(void) = default; 43 | 44 | void SinkInputWidget::setSinkIndex(uint32_t idx) { 45 | mSinkIndex = idx; 46 | 47 | if (mpMainWindow->sinkWidgets.count(idx)) { 48 | SinkWidget *w = mpMainWindow->sinkWidgets[idx]; 49 | deviceButton->setText(QString::fromUtf8(w->description)); 50 | } 51 | else 52 | deviceButton->setText(tr("Unknown output")); 53 | } 54 | 55 | uint32_t SinkInputWidget::sinkIndex() { 56 | return mSinkIndex; 57 | } 58 | 59 | void SinkInputWidget::executeVolumeUpdate() { 60 | pa_operation* o; 61 | 62 | if (!(o = pa_context_set_sink_input_volume(get_context(), index, &volume, nullptr, nullptr))) { 63 | show_error(tr("pa_context_set_sink_input_volume() failed").toUtf8().constData()); 64 | return; 65 | } 66 | 67 | pa_operation_unref(o); 68 | } 69 | 70 | void SinkInputWidget::onMuteToggleButton() { 71 | StreamWidget::onMuteToggleButton(); 72 | 73 | if (updating) 74 | return; 75 | 76 | pa_operation* o; 77 | if (!(o = pa_context_set_sink_input_mute(get_context(), index, muteToggleButton->isChecked(), nullptr, nullptr))) { 78 | show_error(tr("pa_context_set_sink_input_mute() failed").toUtf8().constData()); 79 | return; 80 | } 81 | 82 | pa_operation_unref(o); 83 | } 84 | 85 | void SinkInputWidget::onKill() { 86 | pa_operation* o; 87 | if (!(o = pa_context_kill_sink_input(get_context(), index, nullptr, nullptr))) { 88 | show_error(tr("pa_context_kill_sink_input() failed").toUtf8().constData()); 89 | return; 90 | } 91 | 92 | pa_operation_unref(o); 93 | } 94 | 95 | void SinkInputWidget::buildMenu() { 96 | for (auto & sinkWidget : mpMainWindow->sinkWidgets) { 97 | menu->addAction(new SinkMenuItem{this, sinkWidget.second->description.constData(), sinkWidget.second->index, sinkWidget.second->index == mSinkIndex, menu}); 98 | } 99 | } 100 | 101 | void SinkInputWidget::SinkMenuItem::onToggle() { 102 | if (widget->updating) 103 | return; 104 | 105 | if (!isChecked()) 106 | return; 107 | 108 | /*if (!mpMainWindow->sinkWidgets.count(widget->index)) 109 | return;*/ 110 | 111 | pa_operation* o; 112 | if (!(o = pa_context_move_sink_input_by_index(get_context(), widget->index, index, nullptr, nullptr))) { 113 | show_error(tr("pa_context_move_sink_input_by_index() failed").toUtf8().constData()); 114 | return; 115 | } 116 | 117 | pa_operation_unref(o); 118 | } 119 | 120 | void SinkInputWidget::onDeviceChangePopup() { 121 | menu->clear(); 122 | buildMenu(); 123 | menu->popup(QCursor::pos()); 124 | } 125 | -------------------------------------------------------------------------------- /src/sinkinputwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef sinkinputwidget_h 22 | #define sinkinputwidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | #include "streamwidget.h" 27 | #include 28 | 29 | class MainWindow; 30 | class QMenu; 31 | 32 | class SinkInputWidget : public StreamWidget { 33 | Q_OBJECT 34 | public: 35 | SinkInputWidget(MainWindow *parent); 36 | ~SinkInputWidget(void); 37 | 38 | SinkInputType type; 39 | 40 | uint32_t index, clientIndex; 41 | void setSinkIndex(uint32_t idx); 42 | uint32_t sinkIndex(); 43 | virtual void executeVolumeUpdate(); 44 | virtual void onMuteToggleButton(); 45 | virtual void onDeviceChangePopup(); 46 | virtual void onKill(); 47 | 48 | private: 49 | uint32_t mSinkIndex; 50 | 51 | void buildMenu(); 52 | 53 | QMenu * menu; 54 | 55 | struct SinkMenuItem : public QAction 56 | { 57 | SinkMenuItem(SinkInputWidget *w 58 | , const char *label 59 | , uint32_t i 60 | , bool active 61 | , QObject * parent = nullptr) 62 | : QAction(QString::fromUtf8(label), parent) 63 | , widget(w) 64 | , index(i) 65 | { 66 | setCheckable(true); 67 | setChecked(active); 68 | connect(this, &QAction::toggled, [this] { onToggle(); }); 69 | } 70 | 71 | SinkInputWidget *widget; 72 | uint32_t index; 73 | void onToggle(); 74 | }; 75 | }; 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/sinkwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "sinkwidget.h" 26 | 27 | // #include 28 | #if HAVE_EXT_DEVICE_RESTORE_API 29 | # include 30 | # include 31 | #endif 32 | 33 | SinkWidget::SinkWidget(MainWindow *parent) : 34 | DeviceWidget(parent, "sink") { 35 | 36 | #if HAVE_EXT_DEVICE_RESTORE_API 37 | uint8_t i = 0; 38 | 39 | encodings[i].encoding = PA_ENCODING_PCM; 40 | encodings[i].widget = encodingFormatPCM; 41 | connect(encodings[i].widget, &QCheckBox::toggled, this, &SinkWidget::onEncodingsChange); 42 | 43 | ++i; 44 | encodings[i].encoding = PA_ENCODING_AC3_IEC61937; 45 | encodings[i].widget = encodingFormatAC3; 46 | connect(encodings[i].widget, &QCheckBox::toggled, this, &SinkWidget::onEncodingsChange); 47 | 48 | ++i; 49 | encodings[i].encoding = PA_ENCODING_EAC3_IEC61937; 50 | encodings[i].widget = encodingFormatEAC3; 51 | connect(encodings[i].widget, &QCheckBox::toggled, this, &SinkWidget::onEncodingsChange); 52 | 53 | ++i; 54 | encodings[i].encoding = PA_ENCODING_MPEG_IEC61937; 55 | encodings[i].widget = encodingFormatMPEG; 56 | connect(encodings[i].widget, &QCheckBox::toggled, this, &SinkWidget::onEncodingsChange); 57 | 58 | ++i; 59 | encodings[i].encoding = PA_ENCODING_DTS_IEC61937; 60 | encodings[i].widget = encodingFormatDTS; 61 | connect(encodings[i].widget, &QCheckBox::toggled, this, &SinkWidget::onEncodingsChange); 62 | 63 | ++i; 64 | encodings[i].encoding = PA_ENCODING_INVALID; 65 | encodings[i].widget = encodingFormatAAC; 66 | encodings[i].widget->setEnabled(false); 67 | #ifdef PA_ENCODING_MPEG2_AAC_IEC61937 68 | if (pa_context_get_server_protocol_version(get_context()) >= 28) { 69 | encodings[i].encoding = PA_ENCODING_MPEG2_AAC_IEC61937; 70 | connect(encodings[i].widget, &QCheckBox::toggled, this, &SinkWidget::onEncodingsChange); 71 | encodings[i].widget->setEnabled(true); 72 | } 73 | #endif 74 | #endif 75 | 76 | } 77 | 78 | void SinkWidget::executeVolumeUpdate() { 79 | pa_operation* o; 80 | if (!(o = pa_context_set_sink_volume_by_index(get_context(), index, &volume, nullptr, nullptr))) { 81 | show_error(tr("pa_context_set_sink_volume_by_index() failed").toUtf8().constData()); 82 | return; 83 | } 84 | 85 | pa_operation_unref(o); 86 | } 87 | 88 | void SinkWidget::onMuteToggleButton() { 89 | DeviceWidget::onMuteToggleButton(); 90 | 91 | if (updating) 92 | return; 93 | 94 | pa_operation* o; 95 | if (!(o = pa_context_set_sink_mute_by_index(get_context(), index, muteToggleButton->isChecked(), nullptr, nullptr))) { 96 | show_error(tr("pa_context_set_sink_mute_by_index() failed").toUtf8().constData()); 97 | return; 98 | } 99 | 100 | pa_operation_unref(o); 101 | } 102 | 103 | void SinkWidget::onDefaultToggleButton() { 104 | pa_operation* o; 105 | 106 | if (updating) 107 | return; 108 | 109 | if (!(o = pa_context_set_default_sink(get_context(), name.constData(), nullptr, nullptr))) { 110 | show_error(tr("pa_context_set_default_sink() failed").toUtf8().constData()); 111 | return; 112 | } 113 | pa_operation_unref(o); 114 | } 115 | 116 | void SinkWidget::onPortChange() { 117 | if (updating) 118 | return; 119 | 120 | int sel = portList->currentIndex(); 121 | if (sel != -1) { 122 | pa_operation* o; 123 | QByteArray port = portList->itemData(sel).toString().toUtf8(); 124 | 125 | if (!(o = pa_context_set_sink_port_by_index(get_context(), index, port.constData(), nullptr, nullptr))) { 126 | show_error(tr("pa_context_set_sink_port_by_index() failed").toUtf8().constData()); 127 | return; 128 | } 129 | 130 | pa_operation_unref(o); 131 | } 132 | } 133 | 134 | void SinkWidget::setDigital(bool digital) { 135 | #if HAVE_EXT_DEVICE_RESTORE_API 136 | if (digital) { 137 | encodingSelect->show(); 138 | advancedOptions->setEnabled(true); 139 | } else { 140 | /* advancedOptions is disabled by default */ 141 | encodingSelect->hide(); 142 | } 143 | #endif 144 | } 145 | 146 | void SinkWidget::onEncodingsChange() { 147 | #if HAVE_EXT_DEVICE_RESTORE_API 148 | pa_operation* o; 149 | uint8_t n_formats = 0; 150 | pa_format_info **formats; 151 | 152 | if (updating) 153 | return; 154 | 155 | formats = (pa_format_info**)malloc(sizeof(pa_format_info*) * PAVU_NUM_ENCODINGS); 156 | 157 | for (auto & encoding : encodings) { 158 | if (encoding.widget->isChecked()) { 159 | formats[n_formats] = pa_format_info_new(); 160 | formats[n_formats]->encoding = encoding.encoding; 161 | ++n_formats; 162 | } 163 | } 164 | 165 | if (!(o = pa_ext_device_restore_save_formats(get_context(), PA_DEVICE_TYPE_SINK, index, n_formats, formats, nullptr, nullptr))) { 166 | show_error(tr("pa_ext_device_restore_save_sink_formats() failed").toUtf8().constData()); 167 | free(formats); 168 | return; 169 | } 170 | 171 | free(formats); 172 | pa_operation_unref(o); 173 | #endif 174 | } 175 | -------------------------------------------------------------------------------- /src/sinkwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef sinkwidget_h 22 | #define sinkwidget_h 23 | 24 | #include "pavucontrol.h" 25 | #include "devicewidget.h" 26 | 27 | 28 | #if HAVE_EXT_DEVICE_RESTORE_API 29 | # include 30 | 31 | # define PAVU_NUM_ENCODINGS 6 32 | 33 | class QCheckBox; 34 | 35 | typedef struct { 36 | pa_encoding encoding; 37 | QCheckBox *widget; 38 | } encodingList; 39 | #endif 40 | 41 | class SinkWidget : public DeviceWidget { 42 | Q_OBJECT 43 | public: 44 | SinkWidget(MainWindow *parent); 45 | 46 | SinkType type; 47 | uint32_t monitor_index; 48 | bool can_decibel; 49 | 50 | #if HAVE_EXT_DEVICE_RESTORE_API 51 | encodingList encodings[PAVU_NUM_ENCODINGS]; 52 | #endif 53 | 54 | virtual void onMuteToggleButton(); 55 | virtual void executeVolumeUpdate(); 56 | virtual void onDefaultToggleButton(); 57 | void setDigital(bool); 58 | 59 | protected Q_SLOTS: 60 | virtual void onPortChange(); 61 | virtual void onEncodingsChange(); 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/sourceoutputwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "sourceoutputwidget.h" 26 | #include "mainwindow.h" 27 | #include "sourcewidget.h" 28 | #include 29 | 30 | SourceOutputWidget::SourceOutputWidget(MainWindow *parent) : 31 | StreamWidget(parent), 32 | menu{new QMenu{this}} 33 | { 34 | 35 | gchar *txt = g_markup_printf_escaped("%s", tr("from").toUtf8().constData()); 36 | directionLabel->setText(QString::fromUtf8(static_cast(txt))); 37 | g_free(txt); 38 | 39 | terminate->setText(tr("Terminate Recording")); 40 | 41 | #if !HAVE_SOURCE_OUTPUT_VOLUMES 42 | /* Source Outputs do not have volume controls in versions of PA < 1.0 */ 43 | muteToggleButton->hide(); 44 | lockToggleButton->hide(); 45 | #endif 46 | } 47 | 48 | 49 | SourceOutputWidget::~SourceOutputWidget(void) = default; 50 | 51 | void SourceOutputWidget::setSourceIndex(uint32_t idx) { 52 | mSourceIndex = idx; 53 | 54 | if (mpMainWindow->sourceWidgets.count(idx)) { 55 | SourceWidget *w = mpMainWindow->sourceWidgets[idx]; 56 | deviceButton->setText(QString::fromUtf8(w->description)); 57 | } 58 | else 59 | deviceButton->setText(tr("Unknown input")); 60 | } 61 | 62 | uint32_t SourceOutputWidget::sourceIndex() { 63 | return mSourceIndex; 64 | } 65 | 66 | #if HAVE_SOURCE_OUTPUT_VOLUMES 67 | void SourceOutputWidget::executeVolumeUpdate() { 68 | pa_operation* o; 69 | 70 | if (!(o = pa_context_set_source_output_volume(get_context(), index, &volume, nullptr, nullptr))) { 71 | show_error(tr("pa_context_set_source_output_volume() failed").toUtf8().constData()); 72 | return; 73 | } 74 | 75 | pa_operation_unref(o); 76 | } 77 | 78 | void SourceOutputWidget::onMuteToggleButton() { 79 | StreamWidget::onMuteToggleButton(); 80 | 81 | if (updating) 82 | return; 83 | 84 | pa_operation* o; 85 | if (!(o = pa_context_set_source_output_mute(get_context(), index, muteToggleButton->isChecked(), nullptr, nullptr))) { 86 | show_error(tr("pa_context_set_source_output_mute() failed").toUtf8().constData()); 87 | return; 88 | } 89 | 90 | pa_operation_unref(o); 91 | } 92 | #endif 93 | 94 | void SourceOutputWidget::onKill() { 95 | pa_operation* o; 96 | if (!(o = pa_context_kill_source_output(get_context(), index, nullptr, nullptr))) { 97 | show_error(tr("pa_context_kill_source_output() failed").toUtf8().constData()); 98 | return; 99 | } 100 | 101 | pa_operation_unref(o); 102 | } 103 | 104 | 105 | void SourceOutputWidget::buildMenu() { 106 | for (auto & sourceWidget : mpMainWindow->sourceWidgets) { 107 | menu->addAction(new SourceMenuItem{this, sourceWidget.second->description.constData(), sourceWidget.second->index, sourceWidget.second->index == mSourceIndex, menu}); 108 | } 109 | } 110 | 111 | void SourceOutputWidget::SourceMenuItem::onToggle() { 112 | 113 | if (widget->updating) 114 | return; 115 | 116 | if (!isChecked()) 117 | return; 118 | 119 | /*if (!mpMainWindow->sourceWidgets.count(widget->index)) 120 | return;*/ 121 | 122 | pa_operation* o; 123 | if (!(o = pa_context_move_source_output_by_index(get_context(), widget->index, index, nullptr, nullptr))) { 124 | show_error(tr("pa_context_move_source_output_by_index() failed").toUtf8().constData()); 125 | return; 126 | } 127 | 128 | pa_operation_unref(o); 129 | } 130 | 131 | void SourceOutputWidget::onDeviceChangePopup() { 132 | menu->clear(); 133 | buildMenu(); 134 | menu->popup(QCursor::pos()); 135 | } 136 | -------------------------------------------------------------------------------- /src/sourceoutputwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef sourceoutputwidget_h 22 | #define sourceoutputwidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | #include "streamwidget.h" 27 | #include 28 | 29 | class MainWindow; 30 | class QMenu; 31 | 32 | class SourceOutputWidget : public StreamWidget { 33 | Q_OBJECT 34 | public: 35 | SourceOutputWidget(MainWindow *parent); 36 | ~SourceOutputWidget(void); 37 | 38 | SourceOutputType type; 39 | 40 | uint32_t index, clientIndex; 41 | void setSourceIndex(uint32_t idx); 42 | uint32_t sourceIndex(); 43 | #if HAVE_SOURCE_OUTPUT_VOLUMES 44 | virtual void executeVolumeUpdate(); 45 | virtual void onMuteToggleButton(); 46 | #endif 47 | virtual void onDeviceChangePopup(); 48 | virtual void onKill(); 49 | 50 | private: 51 | uint32_t mSourceIndex; 52 | 53 | void clearMenu(); 54 | void buildMenu(); 55 | 56 | QMenu * menu; 57 | 58 | struct SourceMenuItem : public QAction 59 | { 60 | SourceMenuItem(SourceOutputWidget *w 61 | , const char *label 62 | , uint32_t i 63 | , bool active 64 | , QObject * parent = nullptr) 65 | : QAction{QString::fromUtf8(label), parent} 66 | , widget(w) 67 | , index(i) 68 | { 69 | setCheckable(true); 70 | setChecked(active); 71 | connect(this, &QAction::toggled, [this] { onToggle(); }); 72 | } 73 | 74 | SourceOutputWidget *widget; 75 | uint32_t index; 76 | void onToggle(); 77 | }; 78 | }; 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /src/sourcewidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "sourcewidget.h" 26 | 27 | SourceWidget::SourceWidget(MainWindow *parent) : 28 | DeviceWidget(parent, "source") { 29 | } 30 | 31 | void SourceWidget::executeVolumeUpdate() { 32 | pa_operation* o; 33 | 34 | if (!(o = pa_context_set_source_volume_by_index(get_context(), index, &volume, nullptr, nullptr))) { 35 | show_error(tr("pa_context_set_source_volume_by_index() failed").toUtf8().constData()); 36 | return; 37 | } 38 | 39 | pa_operation_unref(o); 40 | } 41 | 42 | void SourceWidget::onMuteToggleButton() { 43 | DeviceWidget::onMuteToggleButton(); 44 | 45 | if (updating) 46 | return; 47 | 48 | pa_operation* o; 49 | if (!(o = pa_context_set_source_mute_by_index(get_context(), index, muteToggleButton->isChecked(), nullptr, nullptr))) { 50 | show_error(tr("pa_context_set_source_mute_by_index() failed").toUtf8().constData()); 51 | return; 52 | } 53 | 54 | pa_operation_unref(o); 55 | } 56 | 57 | void SourceWidget::onDefaultToggleButton() { 58 | pa_operation* o; 59 | 60 | if (updating) 61 | return; 62 | 63 | if (!(o = pa_context_set_default_source(get_context(), name.constData(), nullptr, nullptr))) { 64 | show_error(tr("pa_context_set_default_source() failed").toUtf8().constData()); 65 | return; 66 | } 67 | pa_operation_unref(o); 68 | } 69 | 70 | void SourceWidget::onPortChange() { 71 | if (updating) 72 | return; 73 | 74 | int current = portList->currentIndex(); 75 | if (current != -1) { 76 | pa_operation* o; 77 | QByteArray port = portList->itemData(current).toByteArray(); 78 | 79 | if (!(o = pa_context_set_source_port_by_index(get_context(), index, port.constData(), nullptr, nullptr))) { 80 | show_error(tr("pa_context_set_source_port_by_index() failed").toUtf8().constData()); 81 | return; 82 | } 83 | 84 | pa_operation_unref(o); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/sourcewidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef sourcewidget_h 22 | #define sourcewidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | #include "devicewidget.h" 27 | 28 | class SourceWidget : public DeviceWidget { 29 | Q_OBJECT 30 | public: 31 | SourceWidget(MainWindow *parent); 32 | static SourceWidget* create(MainWindow* mainWindow); 33 | 34 | SourceType type; 35 | bool can_decibel; 36 | 37 | virtual void onMuteToggleButton(); 38 | virtual void executeVolumeUpdate(); 39 | virtual void onDefaultToggleButton(); 40 | 41 | protected: 42 | virtual void onPortChange(); 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/streamwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "streamwidget.h" 26 | #include "mainwindow.h" 27 | #include "channel.h" 28 | #include 29 | 30 | /*** StreamWidget ***/ 31 | StreamWidget::StreamWidget(MainWindow *parent) : 32 | MinimalStreamWidget(parent), 33 | mpMainWindow(parent), 34 | terminate{new QAction{tr("Terminate"), this}} { 35 | 36 | setupUi(this); 37 | initPeakProgressBar(channelsGrid); 38 | 39 | timeout.setSingleShot(true); 40 | timeout.setInterval(100); 41 | connect(&timeout, &QTimer::timeout, this, &StreamWidget::timeoutEvent); 42 | 43 | connect(muteToggleButton, &QToolButton::toggled, this, &StreamWidget::onMuteToggleButton); 44 | connect(lockToggleButton, &QToolButton::toggled, this, &StreamWidget::onLockToggleButton); 45 | connect(deviceButton, &QAbstractButton::released, this, &StreamWidget::onDeviceChangePopup); 46 | 47 | connect(terminate, &QAction::triggered, this, &StreamWidget::onKill); 48 | addAction(terminate); 49 | setContextMenuPolicy(Qt::ActionsContextMenu); 50 | 51 | for (auto & channel : channels) 52 | channel = nullptr; 53 | } 54 | 55 | void StreamWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) { 56 | channelMap = m; 57 | 58 | for (int i = 0; i < m.channels; i++) { 59 | Channel *ch = channels[i] = new Channel(channelsGrid); 60 | ch->channel = i; 61 | ch->can_decibel = can_decibel; 62 | ch->minimalStreamWidget = this; 63 | char text[64]; 64 | snprintf(text, sizeof(text), "%s", pa_channel_position_to_pretty_string(m.map[i])); 65 | ch->channelLabel->setText(QString::fromUtf8(text)); 66 | } 67 | channels[m.channels-1]->last = true; 68 | channels[m.channels-1]->setBaseVolume(PA_VOLUME_NORM); 69 | 70 | lockToggleButton->setEnabled(m.channels > 1); 71 | hideLockedChannels(lockToggleButton->isChecked()); 72 | } 73 | 74 | void StreamWidget::setVolume(const pa_cvolume &v, bool force) { 75 | g_assert(v.channels == channelMap.channels); 76 | 77 | volume = v; 78 | 79 | if (!timeout.isActive() || force) { /* do not update the volume when a volume change is still in flux */ 80 | for (int i = 0; i < volume.channels; i++) 81 | channels[i]->setVolume(volume.values[i]); 82 | } 83 | } 84 | 85 | void StreamWidget::updateChannelVolume(int channel, pa_volume_t v) { 86 | pa_cvolume n; 87 | g_assert(channel < volume.channels); 88 | 89 | n = volume; 90 | if (lockToggleButton->isChecked()) { 91 | for (int i = 0; i < n.channels; i++) 92 | n.values[i] = v; 93 | } else 94 | n.values[channel] = v; 95 | 96 | setVolume(n, true); 97 | 98 | if(!timeout.isActive()) { 99 | timeout.start(); 100 | } 101 | } 102 | 103 | void StreamWidget::hideLockedChannels(bool hide) { 104 | for (int i = 0; i < channelMap.channels - 1; i++) 105 | channels[i]->setVisible(!hide); 106 | 107 | channels[channelMap.channels - 1]->channelLabel->setVisible(!hide); 108 | } 109 | 110 | void StreamWidget::onMuteToggleButton() { 111 | 112 | lockToggleButton->setEnabled(!muteToggleButton->isChecked()); 113 | 114 | for (int i = 0; i < channelMap.channels; i++) 115 | channels[i]->setEnabled(!muteToggleButton->isChecked()); 116 | } 117 | 118 | void StreamWidget::onLockToggleButton() { 119 | hideLockedChannels(lockToggleButton->isChecked()); 120 | } 121 | 122 | bool StreamWidget::timeoutEvent() { 123 | executeVolumeUpdate(); 124 | return false; 125 | } 126 | 127 | void StreamWidget::executeVolumeUpdate() { 128 | } 129 | 130 | void StreamWidget::onDeviceChangePopup() { 131 | } 132 | 133 | void StreamWidget::onKill() { 134 | } 135 | -------------------------------------------------------------------------------- /src/streamwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef streamwidget_h 22 | #define streamwidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | #include "minimalstreamwidget.h" 27 | #include "ui_streamwidget.h" 28 | #include 29 | 30 | class MainWindow; 31 | class Channel; 32 | class QAction; 33 | 34 | class StreamWidget : public MinimalStreamWidget, public Ui::StreamWidget { 35 | Q_OBJECT 36 | public: 37 | StreamWidget(MainWindow *parent); 38 | 39 | void setChannelMap(const pa_channel_map &m, bool can_decibel); 40 | void setVolume(const pa_cvolume &volume, bool force = false); 41 | virtual void updateChannelVolume(int channel, pa_volume_t v); 42 | 43 | void hideLockedChannels(bool hide = true); 44 | 45 | pa_channel_map channelMap; 46 | pa_cvolume volume; 47 | 48 | Channel *channels[PA_CHANNELS_MAX]; 49 | 50 | virtual void onMuteToggleButton(); 51 | virtual void onLockToggleButton(); 52 | virtual void onDeviceChangePopup(); 53 | // virtual bool onContextTriggerEvent(GdkEventButton*); 54 | 55 | QTimer timeout; 56 | 57 | bool timeoutEvent(); 58 | 59 | virtual void executeVolumeUpdate(); 60 | virtual void onKill(); 61 | 62 | protected: 63 | MainWindow* mpMainWindow; 64 | 65 | QAction * terminate; 66 | }; 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/streamwidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | StreamWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 416 10 | 103 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Device Title 33 | 34 | 35 | 36 | 37 | 38 | 39 | Qt::Horizontal 40 | 41 | 42 | 43 | 40 44 | 20 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | direction 53 | 54 | 55 | 56 | 57 | 58 | 59 | device 60 | 61 | 62 | 63 | 64 | 65 | 66 | Mute audio 67 | 68 | 69 | 70 | 71 | 72 | true 73 | 74 | 75 | 76 | 77 | 78 | 79 | Lock channels together 80 | 81 | 82 | 83 | 84 | 85 | true 86 | 87 | 88 | true 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | Qt::Horizontal 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | ElidingLabel 109 | QLabel 110 |
elidinglabel.h
111 |
112 |
113 | 114 | 115 |
116 | -------------------------------------------------------------------------------- /src/translations/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(pavucontrol-qt) 2 | 3 | build_component("." "${CMAKE_INSTALL_FULL_DATADIR}/pavucontrol-qt/translations") 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio Volume Control" 2 | Desktop Entry/GenericName: "Qt GUI for Controlling PulseAudio Volume" 3 | Desktop Entry/Comment: "Adjust volume levels and select audio devices" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_ar.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio للتحكم بالصوت" 2 | Desktop Entry/GenericName: "واجهة المستخدم الرسومية لبرنامج Qt للتحكم في مستوى صوت PulseAudio" 3 | Desktop Entry/Comment: "ضبط مستويات الصوت وحدد أجهزة الصوت" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_bg.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Контрол на звука" 2 | Desktop Entry/GenericName: "Графичен интерфейс за регулиране на силата на звука за PulseAudio" 3 | Desktop Entry/Comment: "Регулиране на нивата на звука и избор на аудио устройства" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_ca.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Control del volum de PulseAudio" 2 | Desktop Entry/GenericName: "IGU de Qt per controlar el volum de PulseAudio" 3 | Desktop Entry/Comment: "Ajusta els nivells de volum i selecciona dispositius d'àudio" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_cs.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio ovládání hlasitosti" 2 | Desktop Entry/GenericName: "Přizpůsobení úrovně hlasitosti a výběr zvukových zařízení" 3 | Desktop Entry/Comment: "Úprava úrovně hlasitosti a výběr zvukových zařízení" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_cy.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CardWidget 6 | 7 | 8 | Form 9 | 10 | 11 | 12 | 13 | Card Name 14 | 15 | 16 | 17 | 18 | Profile: 19 | 20 | 21 | 22 | 23 | pa_context_set_card_profile_by_index() failed 24 | 25 | 26 | 27 | 28 | Channel 29 | 30 | 31 | %1% (%2dB) 32 | volume slider label [X% (YdB)] 33 | 34 | 35 | 36 | 37 | %1% 38 | volume slider label [X%] 39 | 40 | 41 | 42 | 43 | <small>Silence</small> 44 | 45 | 46 | 47 | 48 | <small>Min</small> 49 | 50 | 51 | 52 | 53 | <small>100% (0dB)</small> 54 | 55 | 56 | 57 | 58 | <small><i>Base</i></small> 59 | 60 | 61 | 62 | 63 | ChannelWidget 64 | 65 | 66 | Form 67 | 68 | 69 | 70 | 71 | <b>left-front</b> 72 | 73 | 74 | 75 | 76 | <small>50%</small> 77 | 78 | 79 | 80 | 81 | DeviceWidget 82 | 83 | 84 | Form 85 | 86 | 87 | 88 | 89 | Device Title 90 | 91 | 92 | 93 | 94 | Mute audio 95 | 96 | 97 | 98 | 99 | Lock channels together 100 | 101 | 102 | 103 | 104 | Set as fallback 105 | 106 | 107 | 108 | 109 | <b>Port:</b> 110 | 111 | 112 | 113 | 114 | Show advanced options 115 | 116 | 117 | 118 | 119 | PCM 120 | 121 | 122 | 123 | 124 | AC3 125 | 126 | 127 | 128 | 129 | EAC3 130 | 131 | 132 | 133 | 134 | DTS 135 | 136 | 137 | 138 | 139 | MPEG 140 | 141 | 142 | 143 | 144 | AAC 145 | 146 | 147 | 148 | 149 | <b>Latency offset:</b> 150 | 151 | 152 | 153 | 154 | ms 155 | 156 | 157 | 158 | 159 | Rename device... 160 | 161 | 162 | 163 | 164 | pa_context_set_port_latency_offset() failed 165 | 166 | 167 | 168 | 169 | Sorry, but device renaming is not supported. 170 | 171 | 172 | 173 | 174 | You need to load module-device-manager in the PulseAudio server in order to rename devices 175 | 176 | 177 | 178 | 179 | Rename device %1 to: 180 | 181 | 182 | 183 | 184 | pa_ext_device_manager_set_device_description() failed 185 | 186 | 187 | 188 | 189 | MainWindow 190 | 191 | 192 | Volume Control 193 | 194 | 195 | 196 | 197 | <i>No application is currently playing audio.</i> 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | Show: 206 | 207 | 208 | 209 | 210 | 211 | All Streams 212 | 213 | 214 | 215 | 216 | 217 | Applications 218 | 219 | 220 | 221 | 222 | 223 | Virtual Streams 224 | 225 | 226 | 227 | 228 | <i>No application is currently recording audio.</i> 229 | 230 | 231 | 232 | 233 | <i>No output devices available</i> 234 | 235 | 236 | 237 | 238 | All Output Devices 239 | 240 | 241 | 242 | 243 | Hardware Output Devices 244 | 245 | 246 | 247 | 248 | Virtual Output Devices 249 | 250 | 251 | 252 | 253 | &Playback 254 | 255 | 256 | 257 | 258 | &Recording 259 | 260 | 261 | 262 | 263 | &Output Devices 264 | 265 | 266 | 267 | 268 | &Input Devices 269 | 270 | 271 | 272 | 273 | <i>No input devices available</i> 274 | 275 | 276 | 277 | 278 | All Input Devices 279 | 280 | 281 | 282 | 283 | All Except Monitors 284 | 285 | 286 | 287 | 288 | Hardware Input Devices 289 | 290 | 291 | 292 | 293 | Virtual Input Devices 294 | 295 | 296 | 297 | 298 | Monitors 299 | 300 | 301 | 302 | 303 | &Configuration 304 | 305 | 306 | 307 | 308 | <i>No cards available for configuration</i> 309 | 310 | 311 | 312 | 313 | Show volume meters 314 | 315 | 316 | 317 | 318 | ... 319 | 320 | 321 | 322 | 323 | (plugged in) 324 | 325 | 326 | 327 | 328 | 329 | (unavailable) 330 | 331 | 332 | 333 | 334 | 335 | (unplugged) 336 | 337 | 338 | 339 | 340 | Failed to read data from stream 341 | 342 | 343 | 344 | 345 | Peak detect 346 | 347 | 348 | 349 | 350 | Failed to create monitoring stream 351 | 352 | 353 | 354 | 355 | Failed to connect monitoring stream 356 | 357 | 358 | 359 | 360 | Ignoring sink-input due to it being designated as an event and thus handled by the Event widget 361 | 362 | 363 | 364 | 365 | System Sounds 366 | 367 | 368 | 369 | 370 | Establishing connection to PulseAudio. Please wait... 371 | 372 | 373 | 374 | 375 | QObject 376 | 377 | 378 | Error 379 | 380 | 381 | 382 | 383 | Card callback failure 384 | 385 | 386 | 387 | 388 | Sink callback failure 389 | 390 | 391 | 392 | 393 | Source callback failure 394 | 395 | 396 | 397 | 398 | Sink input callback failure 399 | 400 | 401 | 402 | 403 | Source output callback failure 404 | 405 | 406 | 407 | 408 | Client callback failure 409 | 410 | 411 | 412 | 413 | Server info callback failure 414 | 415 | 416 | 417 | 418 | 419 | Failed to initialize stream_restore extension: %s 420 | 421 | 422 | 423 | 424 | pa_ext_stream_restore_read() failed 425 | 426 | 427 | 428 | 429 | 430 | Failed to initialize device restore extension: %s 431 | 432 | 433 | 434 | 435 | pa_ext_device_restore_read_sink_formats() failed 436 | 437 | 438 | 439 | 440 | 441 | Failed to initialize device manager extension: %s 442 | 443 | 444 | 445 | 446 | pa_ext_device_manager_read() failed 447 | 448 | 449 | 450 | 451 | pa_context_get_sink_info_by_index() failed 452 | 453 | 454 | 455 | 456 | pa_context_get_source_info_by_index() failed 457 | 458 | 459 | 460 | 461 | 462 | pa_context_get_sink_input_info() failed 463 | 464 | 465 | 466 | 467 | pa_context_get_client_info() failed 468 | 469 | 470 | 471 | 472 | 473 | pa_context_get_server_info() failed 474 | 475 | 476 | 477 | 478 | pa_context_get_card_info_by_index() failed 479 | 480 | 481 | 482 | 483 | pa_context_subscribe() failed 484 | 485 | 486 | 487 | 488 | pa_context_client_info_list() failed 489 | 490 | 491 | 492 | 493 | pa_context_get_card_info_list() failed 494 | 495 | 496 | 497 | 498 | pa_context_get_sink_info_list() failed 499 | 500 | 501 | 502 | 503 | pa_context_get_source_info_list() failed 504 | 505 | 506 | 507 | 508 | pa_context_get_sink_input_info_list() failed 509 | 510 | 511 | 512 | 513 | pa_context_get_source_output_info_list() failed 514 | 515 | 516 | 517 | 518 | 519 | Connection failed, attempting reconnect 520 | 521 | 522 | 523 | 524 | 525 | PulseAudio Volume Control 526 | 527 | 528 | 529 | 530 | Connection to PulseAudio failed. Automatic retry in 5s.<br><br>In this case this is likely because PULSE_SERVER in the Environment/X11 Root Window Propertiesor default-server in client.conf is misconfigured.<br>This situation can also arrise when PulseAudio crashed and left stale details in the X11 Root Window.<br>If this is the case, then PulseAudio should autospawn again, or if this is not configured you shouldrun start-pulseaudio-x11 manually. 531 | 532 | 533 | 534 | 535 | Select a specific tab on load. 536 | 537 | 538 | 539 | 540 | Retry forever if pa quits (every 5 seconds). 541 | 542 | 543 | 544 | 545 | Maximize the window. 546 | 547 | 548 | 549 | 550 | Fatal Error: Unable to connect to PulseAudio 551 | 552 | 553 | 554 | 555 | RoleWidget 556 | 557 | 558 | pa_ext_stream_restore_write() failed 559 | 560 | 561 | 562 | 563 | SinkInputWidget 564 | 565 | 566 | on 567 | 568 | 569 | 570 | 571 | Terminate Playback 572 | 573 | 574 | 575 | 576 | Unknown output 577 | 578 | 579 | 580 | 581 | pa_context_set_sink_input_volume() failed 582 | 583 | 584 | 585 | 586 | pa_context_set_sink_input_mute() failed 587 | 588 | 589 | 590 | 591 | pa_context_kill_sink_input() failed 592 | 593 | 594 | 595 | 596 | pa_context_move_sink_input_by_index() failed 597 | 598 | 599 | 600 | 601 | SinkWidget 602 | 603 | 604 | pa_context_set_sink_volume_by_index() failed 605 | 606 | 607 | 608 | 609 | pa_context_set_sink_mute_by_index() failed 610 | 611 | 612 | 613 | 614 | pa_context_set_default_sink() failed 615 | 616 | 617 | 618 | 619 | pa_context_set_sink_port_by_index() failed 620 | 621 | 622 | 623 | 624 | pa_ext_device_restore_save_sink_formats() failed 625 | 626 | 627 | 628 | 629 | SourceOutputWidget 630 | 631 | 632 | from 633 | 634 | 635 | 636 | 637 | Terminate Recording 638 | 639 | 640 | 641 | 642 | Unknown input 643 | 644 | 645 | 646 | 647 | pa_context_set_source_output_volume() failed 648 | 649 | 650 | 651 | 652 | pa_context_set_source_output_mute() failed 653 | 654 | 655 | 656 | 657 | pa_context_kill_source_output() failed 658 | 659 | 660 | 661 | 662 | pa_context_move_source_output_by_index() failed 663 | 664 | 665 | 666 | 667 | SourceWidget 668 | 669 | 670 | pa_context_set_source_volume_by_index() failed 671 | 672 | 673 | 674 | 675 | pa_context_set_source_mute_by_index() failed 676 | 677 | 678 | 679 | 680 | pa_context_set_default_source() failed 681 | 682 | 683 | 684 | 685 | pa_context_set_source_port_by_index() failed 686 | 687 | 688 | 689 | 690 | StreamWidget 691 | 692 | 693 | Form 694 | 695 | 696 | 697 | 698 | Device Title 699 | 700 | 701 | 702 | 703 | direction 704 | 705 | 706 | 707 | 708 | device 709 | 710 | 711 | 712 | 713 | Mute audio 714 | 715 | 716 | 717 | 718 | Lock channels together 719 | 720 | 721 | 722 | 723 | Terminate 724 | 725 | 726 | 727 | 728 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_da.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio-lydstyrkekontrol" 2 | Desktop Entry/GenericName: "Qt GUI til styring af PulseAudio-lydstyrke" 3 | Desktop Entry/Comment: "Juster lydstyrken og vælg lydenheder" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_de.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Lautstärkeregelung PulseAudio" 2 | Desktop Entry/GenericName: "Qt-GUI zur Steuerung der PulseAudio-Lautstärke" 3 | Desktop Entry/Comment: "Lautstärkepegel anpassen und Audiogeräte auswählen" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_el.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Έλεγχος έντασης του PulseAudio" 2 | Desktop Entry/GenericName: "Γραφικό περιβάλλον Qt για έλεγχο έντασης του PulseAudio" 3 | Desktop Entry/Comment: "Ρύθμιση των επιπέδων έντασης και επιλογή των συσκευών ήχου" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_en_GB.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio Volume Control" 2 | Desktop Entry/GenericName: "Qt GUI for Controlling PulseAudio Volume" 3 | Desktop Entry/Comment: "Adjust volume levels and select audio devices" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_es.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Control de volumen de PulseAudio" 2 | Desktop Entry/GenericName: "Interfaz gráfica de QT para controlar el volumen de Pulse Audio" 3 | Desktop Entry/Comment: "Ajustar los niveles de volumen y seleccionar los dispositivos de audio" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_et.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio helitugevuse juhtimine" 2 | Desktop Entry/GenericName: "Qt-põhine kasutajaliides PulseAudio helitugevuse juhtimiseks" 3 | Desktop Entry/Comment: "Seadista heli valjust ning vali audioseadmeid" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_fi.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudion äänenvoimakkuuden hallinta" 2 | Desktop Entry/GenericName: "Qt-graafinen käyttöliittymä PulseAudio-voimakkuuden hallintaan" 3 | Desktop Entry/Comment: "Säädä äänenvoimakkuuta ja valitse äänilaitteet" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_fr.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Contrôle du volume PulseAudio" 2 | Desktop Entry/GenericName: "Interface graphique Qt pour contrôler le volume de PulseAudio" 3 | Desktop Entry/Comment: "Régler les niveaux de volume et sélectionner les périphériques audio" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_gl.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Control de volume do PulseAudio" 2 | Desktop Entry/GenericName: "Axusta os niveis de volume e selecciona dispositivos de audio" 3 | Desktop Entry/Comment: "Axusta os niveis de volume e selecciona dispositivos de audio" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_he.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "בקרת עצמת השמע של PulseAudio" 2 | Desktop Entry/GenericName: "מנשק משתמש ב־Qt לשליטה בעוצמת השמע ב־PulseAudio" 3 | Desktop Entry/Comment: "כוונון עוצמות השמע ובחירת התקני שמע" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_hi.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "पल्सऑडियो वॉल्यूम कंट्रोल" 2 | Desktop Entry/GenericName: "वॉल्यूम कंट्रोल" 3 | Desktop Entry/Comment: "वॉल्यूम लेवल ठीक करें" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_hr.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio kontrola glasnoće" 2 | Desktop Entry/GenericName: "QT sučelje za upravljanje PulseAudio glasnoće" 3 | Desktop Entry/Comment: "Prilagodi razine glasnoće i odaberi audio uređaje" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_hu.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio hangerőszabályzó" 2 | Desktop Entry/GenericName: "Hangerőszabályzó és audioeszköz vezérlés" 3 | Desktop Entry/Comment: "Hangerő beállítása és audioeszköz vezérlés" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_id.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Kontrol Volume PulseAudio" 2 | Desktop Entry/GenericName: "Menyesuaikan tingkat volume dan memilih perangkat suara" 3 | Desktop Entry/Comment: "Menyesuaikan tingkat volume dan memilih perangkat suara" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_it.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Controllo Volumi PulseAudio" 2 | Desktop Entry/GenericName: "Interfaccia grafica Qt per il controllo del volume di PulseAudio" 3 | Desktop Entry/Comment: "Regola i livelli del volume e seleziona i dispositivi audio" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_ja.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio 音量調節" 2 | Desktop Entry/GenericName: "PulseAudio 音量を調節するための Qt GUI" 3 | Desktop Entry/Comment: "音量レベルの調整とオーディオデバイスを選択します" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_ka.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio-ის ხმის კონტროლი" 2 | Desktop Entry/GenericName: "Qt GUI PulseAudio-ის ხმის დონის კონტროლისთვის" 3 | Desktop Entry/Comment: "მოირგეთ ხმის დონეები და აირჩიეთ აუდიომოწყბილობები" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_kab.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "" 2 | Desktop Entry/GenericName: "" 3 | Desktop Entry/Comment: "" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_kk.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio дыбыс деңгейін басқару" 2 | Desktop Entry/GenericName: "Дыбыс деңгейін басқарып, аудио құрылғыларын таңдаңыз" 3 | Desktop Entry/Comment: "Дыбыс деңгейін басқарып, аудио құрылғыларын таңдаңыз" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_ko.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio 음량 제어" 2 | Desktop Entry/GenericName: "PulseAudio 음량 제어를 위한 Qt GUI" 3 | Desktop Entry/Comment: "음량 수준을 조정하고 오디오 장치를 선택합니다" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_lg.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Ekifuga butimbifu bw'edoboozi erikolebwako PulseAudio" 2 | Desktop Entry/GenericName: "Obufugiro bw'obukangufu bw'eddoboozi eriva mu PulseAudio obwesigamizidwa ku Qt" 3 | Desktop Entry/Comment: "Teekateeka butumbifu bw'eddoboozi era tegeka ebyuma ebirikolako" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_lt.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio garsio reguliavimas" 2 | Desktop Entry/GenericName: "Qt naudotojo sąsają, skirta PulseAudio garsio reguliavimui" 3 | Desktop Entry/Comment: "Reguliuoti garsio lygius ir pasirinkti garso įrenginius" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_nb_NO.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio-lydstyrkekontroll" 2 | Desktop Entry/GenericName: "Qt-brukerflate for styring av PulseAudio-volum" 3 | Desktop Entry/Comment: "Juster lydstyrke og velg lydenheter" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_nl.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio-volumeregeling" 2 | Desktop Entry/GenericName: "Een Qt-programma om PulseAudio te bedienen" 3 | Desktop Entry/Comment: "Pas volumeniveaus aan en schakel tussen audio-apparaten" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_oc.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Contraròtle del volum de PulseAudio" 2 | Desktop Entry/GenericName: "Interfàcia grafica Qt per contrarotlar lo volum de PulseAudio" 3 | Desktop Entry/Comment: "Ajustar lo nivèl del volum e seleccionar los periferics àudio" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_pl.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Kontrola PulseAudio" 2 | Desktop Entry/GenericName: "GUI Qt do sterowania głośnością PulseAudio" 3 | Desktop Entry/Comment: "Dostosuj poziomy głośności i wybierz urządzenia audio" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_pt.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Controlo de volume PulseAudio" 2 | Desktop Entry/GenericName: "GUI Qt para controlar o volume do PulseAudio" 3 | Desktop Entry/Comment: "Ajustar os níveis de volume e selecionar dispositivos de áudio" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_ro.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Control volum PulseAudio" 2 | Desktop Entry/GenericName: "Reglați nivelurile de volum și selectați dispozitivele audio" 3 | Desktop Entry/Comment: "Reglați nivelurile de volum și selectați dispozitivele audio" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_ru.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Регулятор громкости PulseAudio" 2 | Desktop Entry/GenericName: "Пользовательский интерфейс для управления уровнем громкости PulseAudio" 3 | Desktop Entry/Comment: "Регулировка уровня громкости и выбор аудиоустройств" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_si.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "" 2 | Desktop Entry/GenericName: "" 3 | Desktop Entry/Comment: "" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_sk.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio na ovládanie hlasitosti" 2 | Desktop Entry/GenericName: "Grafické rozhranie Qt na ovládanie hlasitosti PulseAudio" 3 | Desktop Entry/Comment: "Upraviť úroveň hlasitosti a vybrať zvukové zariadenia" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_sl.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Nastavitev glasnosti PulseAudio" 2 | Desktop Entry/GenericName: "Nastavitev glasnosti" 3 | Desktop Entry/Comment: "Nastavi glasnost" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_tr.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio Ses Denetimi" 2 | Desktop Entry/GenericName: "PulseAudio Ses Seviyesini Kontrol Etmek için Qt GUI" 3 | Desktop Entry/Comment: "Ses seviyelerini ayarlayın ve ses cihazlarını seçin" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_uk.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Керування гучністю PulseAudio" 2 | Desktop Entry/GenericName: "Qt GUI для керування гучністю PulseAudio" 3 | Desktop Entry/Comment: "Налаштувати рівні гучності та вибрати аудіопристрої" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_vi.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Điều khiển Âm lượng PulseAudio" 2 | Desktop Entry/GenericName: "Điều chỉnh âm lượng và chọn thiết bị âm thanh" 3 | Desktop Entry/Comment: "Điều chỉnh âm lượng và chọn thiết bị âm thanh" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_zh_CN.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio 音量控制" 2 | Desktop Entry/GenericName: "控制 PulseAudio 音量的 Qt 图形界面" 3 | Desktop Entry/Comment: "设备选择和调整音量" 4 | -------------------------------------------------------------------------------- /src/translations/pavucontrol-qt_zh_TW.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "PulseAudio 音量控制" 2 | Desktop Entry/GenericName: "Qt GUI 用於控制 PulseAudio 音量" 3 | Desktop Entry/Comment: "調整音量與選擇音訊裝置" 4 | --------------------------------------------------------------------------------