2006-04-21 14:51:33 +00:00
|
|
|
/***
|
|
|
|
This file is part of pavucontrol.
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2008-08-05 17:42:21 +00:00
|
|
|
Copyright 2006-2008 Lennart Poettering
|
2008-04-20 22:13:20 +00:00
|
|
|
Copyright 2008 Sjoerd Simons <sjoerd@luon.net>
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
pavucontrol is free software; you can redistribute it and/or modify
|
2008-08-05 17:42:21 +00:00
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
pavucontrol is distributed in the hope that it will be useful, but
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
General Public License for more details.
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2007-05-23 23:34:52 +00:00
|
|
|
You should have received a copy of the GNU General Public License
|
2008-08-05 17:42:21 +00:00
|
|
|
along with pavucontrol. If not, see <http://www.gnu.org/licenses/>.
|
2006-04-21 14:51:33 +00:00
|
|
|
***/
|
|
|
|
|
2006-06-21 14:10:22 +00:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
2006-04-22 18:21:12 +00:00
|
|
|
#include <signal.h>
|
2008-05-21 22:52:23 +00:00
|
|
|
#include <string.h>
|
2006-04-22 18:21:12 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
#include <gtkmm.h>
|
|
|
|
#include <libglademm.h>
|
2008-08-05 18:45:24 +00:00
|
|
|
#include <libintl.h>
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2008-08-19 15:36:18 +00:00
|
|
|
#include <canberra-gtk.h>
|
|
|
|
|
2006-06-20 23:48:27 +00:00
|
|
|
#include <pulse/pulseaudio.h>
|
|
|
|
#include <pulse/glib-mainloop.h>
|
2008-08-05 17:05:33 +00:00
|
|
|
#include <pulse/ext-stream-restore.h>
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2008-08-05 18:45:24 +00:00
|
|
|
#include "i18n.h"
|
|
|
|
|
2006-04-21 20:38:39 +00:00
|
|
|
#ifndef GLADE_FILE
|
2006-04-21 14:51:33 +00:00
|
|
|
#define GLADE_FILE "pavucontrol.glade"
|
2006-04-21 20:38:39 +00:00
|
|
|
#endif
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
static pa_context *context = NULL;
|
2006-08-07 13:50:02 +00:00
|
|
|
static int n_outstanding = 0;
|
2008-04-20 23:56:36 +00:00
|
|
|
static bool show_decibel = true;
|
2006-08-07 13:29:46 +00:00
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
enum SinkInputType {
|
|
|
|
SINK_INPUT_ALL,
|
|
|
|
SINK_INPUT_CLIENT,
|
|
|
|
SINK_INPUT_VIRTUAL
|
|
|
|
};
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
enum SinkType {
|
|
|
|
SINK_ALL,
|
|
|
|
SINK_HARDWARE,
|
|
|
|
SINK_VIRTUAL,
|
|
|
|
};
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
enum SourceOutputType {
|
|
|
|
SOURCE_OUTPUT_ALL,
|
|
|
|
SOURCE_OUTPUT_CLIENT,
|
|
|
|
SOURCE_OUTPUT_VIRTUAL
|
|
|
|
};
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
enum SourceType{
|
|
|
|
SOURCE_ALL,
|
2006-08-21 00:56:38 +00:00
|
|
|
SOURCE_NO_MONITOR,
|
2006-08-07 13:29:46 +00:00
|
|
|
SOURCE_HARDWARE,
|
|
|
|
SOURCE_VIRTUAL,
|
|
|
|
SOURCE_MONITOR,
|
|
|
|
};
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
class StreamWidget;
|
2006-08-07 15:26:52 +00:00
|
|
|
class MainWindow;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
class ChannelWidget : public Gtk::EventBox {
|
2006-04-21 14:51:33 +00:00
|
|
|
public:
|
|
|
|
ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
static ChannelWidget* create();
|
|
|
|
|
|
|
|
void setVolume(pa_volume_t volume);
|
|
|
|
|
|
|
|
Gtk::Label *channelLabel;
|
|
|
|
Gtk::Label *volumeLabel;
|
|
|
|
Gtk::HScale *volumeScale;
|
|
|
|
|
|
|
|
int channel;
|
|
|
|
StreamWidget *streamWidget;
|
|
|
|
|
|
|
|
void onVolumeScaleValueChanged();
|
|
|
|
|
2008-04-20 23:56:36 +00:00
|
|
|
bool can_decibel;
|
2006-04-21 14:51:33 +00:00
|
|
|
bool volumeScaleEnabled;
|
|
|
|
|
2008-08-19 15:36:18 +00:00
|
|
|
Glib::ustring beepDevice;
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
virtual void set_sensitive(bool enabled);
|
|
|
|
};
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
class MinimalStreamWidget : public Gtk::VBox {
|
|
|
|
public:
|
|
|
|
MinimalStreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
Gtk::VBox *channelsVBox;
|
2008-04-20 22:13:20 +00:00
|
|
|
Gtk::Label *nameLabel, *boldNameLabel;
|
|
|
|
Gtk::ToggleButton *streamToggleButton;
|
|
|
|
Gtk::Menu menu;
|
2008-06-12 11:26:06 +00:00
|
|
|
Gtk::Image *iconImage;
|
2008-06-17 20:24:54 +00:00
|
|
|
Gtk::ProgressBar peakProgressBar;
|
|
|
|
double lastPeak;
|
2008-04-20 22:13:20 +00:00
|
|
|
|
|
|
|
bool updating;
|
|
|
|
|
|
|
|
void onStreamToggleButton();
|
|
|
|
void onMenuDeactivated();
|
|
|
|
void popupMenuPosition(int& x, int& y, bool& push_in);
|
|
|
|
|
|
|
|
virtual void prepareMenu(void);
|
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
bool volumeMeterEnabled;
|
|
|
|
void enableVolumeMeter();
|
|
|
|
void updatePeak(double v);
|
|
|
|
|
2008-08-19 15:36:18 +00:00
|
|
|
Glib::ustring beepDevice;
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
protected:
|
|
|
|
virtual bool on_button_press_event(GdkEventButton* event);
|
|
|
|
};
|
|
|
|
|
|
|
|
class StreamWidget : public MinimalStreamWidget {
|
2006-04-21 14:51:33 +00:00
|
|
|
public:
|
|
|
|
StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
|
2008-04-20 23:56:36 +00:00
|
|
|
void setChannelMap(const pa_channel_map &m, bool can_decibel);
|
2006-05-20 17:28:50 +00:00
|
|
|
void setVolume(const pa_cvolume &volume, bool force);
|
2006-04-21 14:51:33 +00:00
|
|
|
virtual void updateChannelVolume(int channel, pa_volume_t v);
|
|
|
|
|
|
|
|
Gtk::ToggleButton *lockToggleButton, *muteToggleButton;
|
|
|
|
|
|
|
|
pa_channel_map channelMap;
|
|
|
|
pa_cvolume volume;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
ChannelWidget *channelWidgets[PA_CHANNELS_MAX];
|
|
|
|
|
|
|
|
virtual void onMuteToggleButton();
|
2006-05-16 21:13:41 +00:00
|
|
|
|
|
|
|
sigc::connection timeoutConnection;
|
|
|
|
|
|
|
|
bool timeoutEvent();
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-05-16 21:13:41 +00:00
|
|
|
virtual void executeVolumeUpdate();
|
2006-04-21 14:51:33 +00:00
|
|
|
};
|
|
|
|
|
2009-02-28 17:11:41 +00:00
|
|
|
class CardWidget : public Gtk::VBox {
|
|
|
|
public:
|
|
|
|
CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
static CardWidget* create();
|
|
|
|
virtual ~CardWidget();
|
|
|
|
|
|
|
|
Gtk::Label *nameLabel, *boldNameLabel;
|
|
|
|
Gtk::ToggleButton *streamToggleButton;
|
|
|
|
Gtk::Menu menu;
|
|
|
|
Gtk::Image *iconImage;
|
|
|
|
Glib::ustring name;
|
|
|
|
uint32_t index;
|
|
|
|
bool updating;
|
|
|
|
|
|
|
|
void onStreamToggleButton();
|
|
|
|
void onMenuDeactivated();
|
|
|
|
void popupMenuPosition(int& x, int& y, bool& push_in);
|
|
|
|
|
|
|
|
std::map<Glib::ustring,Glib::ustring> profiles;
|
|
|
|
Glib::ustring activeProfile;
|
|
|
|
bool hasSinks;
|
|
|
|
bool hasSources;
|
|
|
|
|
|
|
|
MainWindow *mainWindow;
|
|
|
|
Gtk::Menu submenu;
|
|
|
|
Gtk::MenuItem profilesMenuItem;
|
|
|
|
|
|
|
|
struct ProfileMenuItem {
|
|
|
|
ProfileMenuItem(CardWidget *w, const char *label, Glib::ustring profile, bool active) :
|
|
|
|
widget(w),
|
|
|
|
menuItem(label),
|
|
|
|
profile(profile) {
|
|
|
|
menuItem.set_active(active);
|
|
|
|
menuItem.set_draw_as_radio(true);
|
|
|
|
menuItem.signal_toggled().connect(sigc::mem_fun(*this, &ProfileMenuItem::onToggle));
|
|
|
|
}
|
|
|
|
|
|
|
|
CardWidget *widget;
|
|
|
|
Gtk::CheckMenuItem menuItem;
|
|
|
|
Glib::ustring profile;
|
|
|
|
void onToggle();
|
|
|
|
};
|
|
|
|
|
|
|
|
std::map<uint32_t, ProfileMenuItem*> profileMenuItems;
|
|
|
|
|
|
|
|
void clearMenu();
|
|
|
|
void buildMenu();
|
|
|
|
virtual void prepareMenu(void);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual bool on_button_press_event(GdkEventButton* event);
|
|
|
|
};
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
class SinkWidget : public StreamWidget {
|
|
|
|
public:
|
|
|
|
SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
static SinkWidget* create();
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
SinkType type;
|
2006-08-11 23:56:57 +00:00
|
|
|
Glib::ustring description;
|
2007-09-02 23:38:55 +00:00
|
|
|
Glib::ustring name;
|
2009-02-28 17:11:41 +00:00
|
|
|
uint32_t index, monitor_index, card_index;
|
2008-04-20 23:56:36 +00:00
|
|
|
bool can_decibel;
|
2007-09-02 23:38:55 +00:00
|
|
|
|
|
|
|
Gtk::CheckMenuItem defaultMenuItem;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
virtual void onMuteToggleButton();
|
2006-05-16 21:13:41 +00:00
|
|
|
virtual void executeVolumeUpdate();
|
2007-09-02 23:38:55 +00:00
|
|
|
virtual void onDefaultToggle();
|
2006-04-21 14:51:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SourceWidget : public StreamWidget {
|
|
|
|
public:
|
|
|
|
SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
static SourceWidget* create();
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
SourceType type;
|
2007-09-02 23:38:55 +00:00
|
|
|
Glib::ustring name;
|
2008-04-20 22:13:20 +00:00
|
|
|
Glib::ustring description;
|
2009-02-28 17:11:41 +00:00
|
|
|
uint32_t index, card_index;
|
2008-04-20 23:56:36 +00:00
|
|
|
bool can_decibel;
|
2007-09-02 23:38:55 +00:00
|
|
|
|
|
|
|
Gtk::CheckMenuItem defaultMenuItem;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
virtual void onMuteToggleButton();
|
2006-05-16 21:13:41 +00:00
|
|
|
virtual void executeVolumeUpdate();
|
2007-09-02 23:38:55 +00:00
|
|
|
virtual void onDefaultToggle();
|
2006-04-21 14:51:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SinkInputWidget : public StreamWidget {
|
|
|
|
public:
|
|
|
|
SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
static SinkInputWidget* create();
|
2006-08-07 15:26:52 +00:00
|
|
|
virtual ~SinkInputWidget();
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
SinkInputType type;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
uint32_t index, clientIndex, sinkIndex;
|
2006-05-16 21:13:41 +00:00
|
|
|
virtual void executeVolumeUpdate();
|
2007-08-15 19:37:23 +00:00
|
|
|
virtual void onMuteToggleButton();
|
2007-09-02 23:53:16 +00:00
|
|
|
virtual void onKill();
|
2008-04-20 22:13:20 +00:00
|
|
|
virtual void prepareMenu();
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
MainWindow *mainWindow;
|
2008-04-20 22:13:20 +00:00
|
|
|
Gtk::Menu submenu;
|
2007-09-02 23:53:16 +00:00
|
|
|
Gtk::MenuItem titleMenuItem, killMenuItem;
|
2006-08-07 15:26:52 +00:00
|
|
|
|
|
|
|
struct SinkMenuItem {
|
|
|
|
SinkMenuItem(SinkInputWidget *w, const char *label, uint32_t i, bool active) :
|
|
|
|
widget(w),
|
|
|
|
menuItem(label),
|
|
|
|
index(i) {
|
|
|
|
menuItem.set_active(active);
|
2007-11-20 02:14:59 +00:00
|
|
|
menuItem.set_draw_as_radio(true);
|
2006-08-07 15:26:52 +00:00
|
|
|
menuItem.signal_toggled().connect(sigc::mem_fun(*this, &SinkMenuItem::onToggle));
|
|
|
|
}
|
|
|
|
|
|
|
|
SinkInputWidget *widget;
|
|
|
|
Gtk::CheckMenuItem menuItem;
|
|
|
|
uint32_t index;
|
|
|
|
void onToggle();
|
|
|
|
};
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
std::map<uint32_t, SinkMenuItem*> sinkMenuItems;
|
|
|
|
|
|
|
|
void clearMenu();
|
|
|
|
void buildMenu();
|
2008-04-20 22:13:20 +00:00
|
|
|
};
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
class SourceOutputWidget : public MinimalStreamWidget {
|
|
|
|
public:
|
|
|
|
SourceOutputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
static SourceOutputWidget* create();
|
|
|
|
virtual ~SourceOutputWidget();
|
|
|
|
|
|
|
|
SourceOutputType type;
|
|
|
|
|
|
|
|
uint32_t index, clientIndex, sourceIndex;
|
|
|
|
virtual void onKill();
|
|
|
|
|
|
|
|
MainWindow *mainWindow;
|
|
|
|
Gtk::Menu submenu;
|
|
|
|
Gtk::MenuItem titleMenuItem, killMenuItem;
|
|
|
|
|
|
|
|
struct SourceMenuItem {
|
|
|
|
SourceMenuItem(SourceOutputWidget *w, const char *label, uint32_t i, bool active) :
|
|
|
|
widget(w),
|
|
|
|
menuItem(label),
|
|
|
|
index(i) {
|
|
|
|
menuItem.set_active(active);
|
|
|
|
menuItem.set_draw_as_radio(true);
|
|
|
|
menuItem.signal_toggled().connect(sigc::mem_fun(*this, &SourceMenuItem::onToggle));
|
|
|
|
}
|
|
|
|
|
|
|
|
SourceOutputWidget *widget;
|
|
|
|
Gtk::CheckMenuItem menuItem;
|
|
|
|
uint32_t index;
|
|
|
|
void onToggle();
|
|
|
|
};
|
|
|
|
|
|
|
|
std::map<uint32_t, SourceMenuItem*> sourceMenuItems;
|
|
|
|
|
|
|
|
void clearMenu();
|
|
|
|
void buildMenu();
|
|
|
|
virtual void prepareMenu();
|
2006-04-21 14:51:33 +00:00
|
|
|
};
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
class RoleWidget : public StreamWidget {
|
|
|
|
public:
|
|
|
|
RoleWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
static RoleWidget* create();
|
|
|
|
|
|
|
|
Glib::ustring role;
|
|
|
|
Glib::ustring device;
|
|
|
|
|
|
|
|
virtual void onMuteToggleButton();
|
|
|
|
virtual void executeVolumeUpdate();
|
|
|
|
};
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
class MainWindow : public Gtk::Window {
|
|
|
|
public:
|
|
|
|
MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
|
|
|
|
static MainWindow* create();
|
2006-05-20 17:28:50 +00:00
|
|
|
virtual ~MainWindow();
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2009-02-28 17:11:41 +00:00
|
|
|
void updateCard(const pa_card_info &info);
|
2006-04-21 14:51:33 +00:00
|
|
|
void updateSink(const pa_sink_info &info);
|
|
|
|
void updateSource(const pa_source_info &info);
|
|
|
|
void updateSinkInput(const pa_sink_input_info &info);
|
2008-04-20 22:13:20 +00:00
|
|
|
void updateSourceOutput(const pa_source_output_info &info);
|
2006-05-20 17:28:50 +00:00
|
|
|
void updateClient(const pa_client_info &info);
|
2007-09-02 23:38:55 +00:00
|
|
|
void updateServer(const pa_server_info &info);
|
2008-06-17 20:24:54 +00:00
|
|
|
void updateVolumeMeter(uint32_t source_index, uint32_t sink_input_index, double v);
|
2008-08-05 17:05:33 +00:00
|
|
|
void updateRole(const pa_ext_stream_restore_info &info);
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2009-02-28 17:11:41 +00:00
|
|
|
void removeCard(uint32_t index);
|
2006-04-21 14:51:33 +00:00
|
|
|
void removeSink(uint32_t index);
|
|
|
|
void removeSource(uint32_t index);
|
|
|
|
void removeSinkInput(uint32_t index);
|
2008-04-20 22:13:20 +00:00
|
|
|
void removeSourceOutput(uint32_t index);
|
2006-05-20 17:28:50 +00:00
|
|
|
void removeClient(uint32_t index);
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2008-04-21 00:21:04 +00:00
|
|
|
Gtk::Notebook *notebook;
|
2009-02-28 18:01:07 +00:00
|
|
|
Gtk::VBox *streamsVBox, *recsVBox, *sinksVBox, *sourcesVBox, *cardsVBox;
|
|
|
|
Gtk::Label *noStreamsLabel, *noRecsLabel, *noSinksLabel, *noSourcesLabel, *noCardsLabel;
|
2008-04-20 22:13:20 +00:00
|
|
|
Gtk::ComboBox *sinkInputTypeComboBox, *sourceOutputTypeComboBox, *sinkTypeComboBox, *sourceTypeComboBox;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2009-02-28 17:11:41 +00:00
|
|
|
std::map<uint32_t, CardWidget*> cardWidgets;
|
2006-08-07 15:26:52 +00:00
|
|
|
std::map<uint32_t, SinkWidget*> sinkWidgets;
|
|
|
|
std::map<uint32_t, SourceWidget*> sourceWidgets;
|
2007-08-31 01:16:50 +00:00
|
|
|
std::map<uint32_t, SinkInputWidget*> sinkInputWidgets;
|
2008-04-20 22:13:20 +00:00
|
|
|
std::map<uint32_t, SourceOutputWidget*> sourceOutputWidgets;
|
2006-08-07 15:26:52 +00:00
|
|
|
std::map<uint32_t, char*> clientNames;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
SinkInputType showSinkInputType;
|
2006-08-07 13:29:46 +00:00
|
|
|
SinkType showSinkType;
|
2008-04-20 22:13:20 +00:00
|
|
|
SourceOutputType showSourceOutputType;
|
2006-08-07 13:29:46 +00:00
|
|
|
SourceType showSourceType;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
virtual void onSinkInputTypeComboBoxChanged();
|
2008-04-20 22:13:20 +00:00
|
|
|
virtual void onSourceOutputTypeComboBoxChanged();
|
2006-08-07 13:29:46 +00:00
|
|
|
virtual void onSinkTypeComboBoxChanged();
|
|
|
|
virtual void onSourceTypeComboBoxChanged();
|
|
|
|
|
|
|
|
void updateDeviceVisibility();
|
2008-08-05 17:05:33 +00:00
|
|
|
void reallyUpdateDeviceVisibility();
|
2008-06-17 20:24:54 +00:00
|
|
|
void createMonitorStreamForSource(uint32_t source_idx);
|
|
|
|
void createMonitorStreamForSinkInput(uint32_t sink_input_idx, uint32_t sink_idx);
|
|
|
|
|
2008-06-12 11:26:06 +00:00
|
|
|
void setIconFromProplist(Gtk::Image *icon, pa_proplist *l, const char *name);
|
2006-08-07 15:26:52 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
RoleWidget *eventRoleWidget;
|
|
|
|
|
|
|
|
bool createEventRoleWidget();
|
|
|
|
void deleteEventRoleWidget();
|
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
Glib::ustring defaultSinkName, defaultSourceName;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-08-07 13:50:02 +00:00
|
|
|
protected:
|
|
|
|
virtual void on_realize();
|
2006-04-21 14:51:33 +00:00
|
|
|
};
|
|
|
|
|
2006-04-21 19:24:32 +00:00
|
|
|
void show_error(const char *txt) {
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "%s: %s", txt, pa_strerror(pa_context_errno(context)));
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 19:24:32 +00:00
|
|
|
Gtk::MessageDialog dialog(buf, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
|
|
|
|
dialog.run();
|
|
|
|
|
|
|
|
Gtk::Main::quit();
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
/*** ChannelWidget ***/
|
|
|
|
|
|
|
|
ChannelWidget::ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
2006-08-07 15:26:52 +00:00
|
|
|
Gtk::EventBox(cobject),
|
2006-04-21 14:51:33 +00:00
|
|
|
volumeScaleEnabled(true) {
|
|
|
|
|
|
|
|
x->get_widget("channelLabel", channelLabel);
|
|
|
|
x->get_widget("volumeLabel", volumeLabel);
|
|
|
|
x->get_widget("volumeScale", volumeScale);
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
volumeScale->set_value(100);
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
volumeScale->signal_value_changed().connect(sigc::mem_fun(*this, &ChannelWidget::onVolumeScaleValueChanged));
|
|
|
|
}
|
|
|
|
|
|
|
|
ChannelWidget* ChannelWidget::create() {
|
|
|
|
ChannelWidget* w;
|
|
|
|
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "channelWidget");
|
|
|
|
x->get_widget_derived("channelWidget", w);
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChannelWidget::setVolume(pa_volume_t volume) {
|
2008-04-20 23:56:36 +00:00
|
|
|
double v;
|
2006-04-21 14:51:33 +00:00
|
|
|
char txt[64];
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2008-04-20 23:56:36 +00:00
|
|
|
v = ((gdouble) volume * 100) / PA_VOLUME_NORM;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2008-04-20 23:56:36 +00:00
|
|
|
if (can_decibel && show_decibel) {
|
|
|
|
double dB = pa_sw_volume_to_dB(volume);
|
|
|
|
|
|
|
|
if (dB > PA_DECIBEL_MININFTY) {
|
|
|
|
snprintf(txt, sizeof(txt), "%0.2f dB", dB);
|
2008-08-19 15:36:18 +00:00
|
|
|
volumeLabel->set_tooltip_text(txt);
|
2008-04-20 23:56:36 +00:00
|
|
|
} else
|
2008-08-19 15:36:18 +00:00
|
|
|
volumeLabel->set_tooltip_markup("-∞dB");
|
|
|
|
volumeLabel->set_has_tooltip(TRUE);
|
|
|
|
} else
|
|
|
|
volumeLabel->set_has_tooltip(FALSE);
|
|
|
|
|
|
|
|
snprintf(txt, sizeof(txt), "%0.0f%%", v);
|
|
|
|
volumeLabel->set_text(txt);
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
volumeScaleEnabled = false;
|
2008-04-20 23:56:36 +00:00
|
|
|
volumeScale->set_value(v > 100 ? 100 : v);
|
2006-04-21 14:51:33 +00:00
|
|
|
volumeScaleEnabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChannelWidget::onVolumeScaleValueChanged() {
|
|
|
|
|
|
|
|
if (!volumeScaleEnabled)
|
|
|
|
return;
|
|
|
|
|
2007-08-15 19:37:23 +00:00
|
|
|
if (streamWidget->updating)
|
|
|
|
return;
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_volume_t volume = (pa_volume_t) ((volumeScale->get_value() * PA_VOLUME_NORM) / 100);
|
|
|
|
streamWidget->updateChannelVolume(channel, volume);
|
2008-08-19 15:36:18 +00:00
|
|
|
|
|
|
|
if (beepDevice != "") {
|
|
|
|
ca_context_change_device(ca_gtk_context_get(), beepDevice.c_str());
|
|
|
|
|
|
|
|
ca_context_cancel(ca_gtk_context_get(), 2);
|
|
|
|
|
2009-02-28 17:35:36 +00:00
|
|
|
ca_gtk_play_for_widget(GTK_WIDGET(volumeScale->gobj()),
|
2008-08-19 15:36:18 +00:00
|
|
|
2,
|
|
|
|
CA_PROP_EVENT_DESCRIPTION, _("Volume Control Feedback Sound"),
|
|
|
|
CA_PROP_EVENT_ID, "audio-volume-change",
|
|
|
|
CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
|
|
|
|
CA_PROP_CANBERRA_VOLUME, "0",
|
|
|
|
CA_PROP_CANBERRA_ENABLE, "1",
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
ca_context_change_device(ca_gtk_context_get(), NULL);
|
|
|
|
}
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ChannelWidget::set_sensitive(bool enabled) {
|
2006-08-07 15:26:52 +00:00
|
|
|
Gtk::EventBox::set_sensitive(enabled);
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
channelLabel->set_sensitive(enabled);
|
|
|
|
volumeLabel->set_sensitive(enabled);
|
|
|
|
volumeScale->set_sensitive(enabled);
|
|
|
|
}
|
|
|
|
|
2009-02-28 17:11:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*** CardWidget ***/
|
|
|
|
CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
|
|
|
Gtk::VBox(cobject),
|
|
|
|
profilesMenuItem(_("_Set Profile..."), true) {
|
|
|
|
|
|
|
|
x->get_widget("nameLabel", nameLabel);
|
|
|
|
x->get_widget("boldNameLabel", boldNameLabel);
|
|
|
|
x->get_widget("streamToggle", streamToggleButton);
|
|
|
|
x->get_widget("iconImage", iconImage);
|
|
|
|
|
|
|
|
streamToggleButton->set_active(false);
|
|
|
|
streamToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &CardWidget::onStreamToggleButton));
|
|
|
|
menu.signal_deactivate().connect(sigc::mem_fun(*this, &CardWidget::onMenuDeactivated));
|
|
|
|
|
|
|
|
menu.append(profilesMenuItem);
|
|
|
|
profilesMenuItem.set_submenu(submenu);
|
|
|
|
}
|
|
|
|
|
|
|
|
CardWidget::~CardWidget() {
|
|
|
|
clearMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
CardWidget* CardWidget::create() {
|
|
|
|
CardWidget* w;
|
|
|
|
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "minimalStreamWidget");
|
|
|
|
x->get_widget_derived("minimalStreamWidget", w);
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CardWidget::prepareMenu() {
|
|
|
|
clearMenu();
|
|
|
|
buildMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CardWidget::clearMenu() {
|
|
|
|
|
|
|
|
while (!profileMenuItems.empty()) {
|
|
|
|
std::map<uint32_t, ProfileMenuItem*>::iterator i = profileMenuItems.begin();
|
|
|
|
delete i->second;
|
|
|
|
profileMenuItems.erase(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CardWidget::buildMenu() {
|
|
|
|
int num = 0;
|
|
|
|
for (std::map<Glib::ustring, Glib::ustring>::iterator i = profiles.begin(); i != profiles.end(); ++i) {
|
|
|
|
ProfileMenuItem *m;
|
|
|
|
profileMenuItems[num++] = m = new ProfileMenuItem(this, i->second.c_str(), i->first.c_str(), i->first == activeProfile);
|
|
|
|
submenu.append(m->menuItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.show_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CardWidget::ProfileMenuItem::onToggle() {
|
|
|
|
|
|
|
|
if (widget->updating)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!menuItem.get_active())
|
|
|
|
return;
|
|
|
|
|
|
|
|
pa_operation* o;
|
|
|
|
if (!(o = pa_context_set_card_profile_by_index(context, widget->index, profile.c_str(), NULL, NULL))) {
|
|
|
|
show_error(_("pa_context_set_card_profile_by_index() failed"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CardWidget::onMenuDeactivated(void) {
|
|
|
|
streamToggleButton->set_active(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CardWidget::popupMenuPosition(int& x, int& y, bool& push_in G_GNUC_UNUSED) {
|
|
|
|
Gtk::Requisition r;
|
|
|
|
|
|
|
|
streamToggleButton->get_window()->get_origin(x, y);
|
|
|
|
r = menu.size_request();
|
|
|
|
|
|
|
|
/* Align the right side of the menu with the right side of the togglebutton */
|
|
|
|
x += streamToggleButton->get_allocation().get_x();
|
|
|
|
x += streamToggleButton->get_allocation().get_width();
|
|
|
|
x -= r.width;
|
|
|
|
|
|
|
|
/* Align the top of the menu with the buttom of the togglebutton */
|
|
|
|
y += streamToggleButton->get_allocation().get_y();
|
|
|
|
y += streamToggleButton->get_allocation().get_height();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CardWidget::onStreamToggleButton(void) {
|
|
|
|
if (streamToggleButton->get_active()) {
|
|
|
|
prepareMenu();
|
|
|
|
menu.popup(sigc::mem_fun(*this, &CardWidget::popupMenuPosition), 0, gtk_get_current_event_time());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CardWidget::on_button_press_event (GdkEventButton* event) {
|
|
|
|
if (Gtk::VBox::on_button_press_event(event))
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
|
|
|
|
prepareMenu();
|
|
|
|
menu.popup(0, event->time);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
/*** MinimalStreamWidget ***/
|
|
|
|
MinimalStreamWidget::MinimalStreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
2007-08-15 19:37:23 +00:00
|
|
|
Gtk::VBox(cobject),
|
2008-06-17 20:24:54 +00:00
|
|
|
peakProgressBar(),
|
|
|
|
lastPeak(0),
|
|
|
|
updating(false),
|
|
|
|
volumeMeterEnabled(false) {
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
x->get_widget("channelsVBox", channelsVBox);
|
2006-04-21 14:51:33 +00:00
|
|
|
x->get_widget("nameLabel", nameLabel);
|
2006-05-20 17:28:50 +00:00
|
|
|
x->get_widget("boldNameLabel", boldNameLabel);
|
2008-04-20 22:13:20 +00:00
|
|
|
x->get_widget("streamToggle", streamToggleButton);
|
2008-06-12 11:26:06 +00:00
|
|
|
x->get_widget("iconImage", iconImage);
|
2008-04-20 22:13:20 +00:00
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
peakProgressBar.set_size_request(-1, 10);
|
|
|
|
channelsVBox->pack_end(peakProgressBar, false, false);
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
streamToggleButton->set_active(false);
|
|
|
|
streamToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &MinimalStreamWidget::onStreamToggleButton));
|
|
|
|
menu.signal_deactivate().connect(sigc::mem_fun(*this, &MinimalStreamWidget::onMenuDeactivated));
|
2008-08-05 17:05:33 +00:00
|
|
|
|
|
|
|
peakProgressBar.hide();
|
2008-04-20 22:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MinimalStreamWidget::prepareMenu(void) {
|
|
|
|
}
|
|
|
|
|
|
|
|
void MinimalStreamWidget::onMenuDeactivated(void) {
|
2008-08-05 17:05:33 +00:00
|
|
|
streamToggleButton->set_active(false);
|
2008-04-20 22:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MinimalStreamWidget::popupMenuPosition(int& x, int& y, bool& push_in G_GNUC_UNUSED) {
|
2008-08-05 17:05:33 +00:00
|
|
|
Gtk::Requisition r;
|
2008-04-20 22:13:20 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
streamToggleButton->get_window()->get_origin(x, y);
|
|
|
|
r = menu.size_request();
|
2008-04-20 22:13:20 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
/* Align the right side of the menu with the right side of the togglebutton */
|
|
|
|
x += streamToggleButton->get_allocation().get_x();
|
|
|
|
x += streamToggleButton->get_allocation().get_width();
|
|
|
|
x -= r.width;
|
2008-04-20 22:13:20 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
/* Align the top of the menu with the buttom of the togglebutton */
|
|
|
|
y += streamToggleButton->get_allocation().get_y();
|
|
|
|
y += streamToggleButton->get_allocation().get_height();
|
2008-04-20 22:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MinimalStreamWidget::onStreamToggleButton(void) {
|
2008-08-05 17:05:33 +00:00
|
|
|
if (streamToggleButton->get_active()) {
|
|
|
|
prepareMenu();
|
|
|
|
menu.popup(sigc::mem_fun(*this, &MinimalStreamWidget::popupMenuPosition), 0, gtk_get_current_event_time());
|
|
|
|
}
|
2008-04-20 22:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool MinimalStreamWidget::on_button_press_event (GdkEventButton* event) {
|
2008-08-05 17:05:33 +00:00
|
|
|
if (Gtk::VBox::on_button_press_event(event))
|
2008-04-20 22:13:20 +00:00
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
|
|
|
|
prepareMenu();
|
|
|
|
menu.popup(0, event->time);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2008-05-21 22:52:23 +00:00
|
|
|
#define DECAY_STEP .04
|
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
void MinimalStreamWidget::updatePeak(double v) {
|
2008-05-21 22:52:23 +00:00
|
|
|
|
|
|
|
if (lastPeak >= DECAY_STEP)
|
|
|
|
if (v < lastPeak - DECAY_STEP)
|
|
|
|
v = lastPeak - DECAY_STEP;
|
|
|
|
|
|
|
|
lastPeak = v;
|
|
|
|
|
|
|
|
if (v >= 0) {
|
|
|
|
peakProgressBar.set_sensitive(TRUE);
|
|
|
|
peakProgressBar.set_fraction(v);
|
|
|
|
} else {
|
|
|
|
peakProgressBar.set_sensitive(FALSE);
|
|
|
|
peakProgressBar.set_fraction(0);
|
|
|
|
}
|
2008-08-05 17:05:33 +00:00
|
|
|
|
|
|
|
enableVolumeMeter();
|
2008-05-21 22:52:23 +00:00
|
|
|
}
|
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
void MinimalStreamWidget::enableVolumeMeter() {
|
2008-05-21 22:52:23 +00:00
|
|
|
if (volumeMeterEnabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
volumeMeterEnabled = true;
|
|
|
|
peakProgressBar.show();
|
|
|
|
}
|
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
/*** StreamWidget ***/
|
|
|
|
|
|
|
|
StreamWidget::StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
|
|
|
MinimalStreamWidget(cobject, x) {
|
|
|
|
|
|
|
|
x->get_widget("lockToggleButton", lockToggleButton);
|
|
|
|
x->get_widget("muteToggleButton", muteToggleButton);
|
|
|
|
|
|
|
|
muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onMuteToggleButton));
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < PA_CHANNELS_MAX; i++)
|
|
|
|
channelWidgets[i] = NULL;
|
|
|
|
}
|
|
|
|
|
2008-04-20 23:56:36 +00:00
|
|
|
void StreamWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) {
|
2006-04-21 14:51:33 +00:00
|
|
|
channelMap = m;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
for (int i = 0; i < m.channels; i++) {
|
|
|
|
ChannelWidget *cw = channelWidgets[i] = ChannelWidget::create();
|
2008-08-19 15:36:18 +00:00
|
|
|
cw->beepDevice = beepDevice;
|
2006-04-21 14:51:33 +00:00
|
|
|
cw->channel = i;
|
2008-04-20 23:56:36 +00:00
|
|
|
cw->can_decibel = can_decibel;
|
2006-04-21 14:51:33 +00:00
|
|
|
cw->streamWidget = this;
|
|
|
|
char text[64];
|
2007-09-02 21:29:48 +00:00
|
|
|
snprintf(text, sizeof(text), "<b>%s</b>", pa_channel_position_to_pretty_string(m.map[i]));
|
2006-04-21 14:51:33 +00:00
|
|
|
cw->channelLabel->set_markup(text);
|
|
|
|
channelsVBox->pack_start(*cw, false, false, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
lockToggleButton->set_sensitive(m.channels > 1);
|
|
|
|
}
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
void StreamWidget::setVolume(const pa_cvolume &v, bool force = false) {
|
2006-04-21 14:51:33 +00:00
|
|
|
g_assert(v.channels == channelMap.channels);
|
|
|
|
|
|
|
|
volume = v;
|
2006-05-20 17:28:50 +00:00
|
|
|
|
2006-08-07 16:45:30 +00:00
|
|
|
if (timeoutConnection.empty() || force) { /* do not update the volume when a volume change is still in flux */
|
2006-05-20 17:28:50 +00:00
|
|
|
for (int i = 0; i < volume.channels; i++)
|
|
|
|
channelWidgets[i]->setVolume(volume.values[i]);
|
2006-08-07 16:45:30 +00:00
|
|
|
}
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void StreamWidget::updateChannelVolume(int channel, pa_volume_t v) {
|
2006-05-20 17:28:50 +00:00
|
|
|
pa_cvolume n;
|
2006-04-21 14:51:33 +00:00
|
|
|
g_assert(channel < volume.channels);
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
n = volume;
|
2006-04-21 14:51:33 +00:00
|
|
|
if (lockToggleButton->get_active()) {
|
2006-05-20 17:28:50 +00:00
|
|
|
for (int i = 0; i < n.channels; i++)
|
|
|
|
n.values[i] = v;
|
2007-11-20 02:14:59 +00:00
|
|
|
} else
|
2006-05-20 17:28:50 +00:00
|
|
|
n.values[channel] = v;
|
2006-05-16 21:13:41 +00:00
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
setVolume(n, true);
|
2006-05-16 21:13:41 +00:00
|
|
|
|
|
|
|
if (timeoutConnection.empty())
|
2006-08-07 16:45:30 +00:00
|
|
|
timeoutConnection = Glib::signal_timeout().connect(sigc::mem_fun(*this, &StreamWidget::timeoutEvent), 100);
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void StreamWidget::onMuteToggleButton() {
|
2007-08-15 19:37:23 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
lockToggleButton->set_sensitive(!muteToggleButton->get_active());
|
|
|
|
|
|
|
|
for (int i = 0; i < channelMap.channels; i++)
|
|
|
|
channelWidgets[i]->set_sensitive(!muteToggleButton->get_active());
|
|
|
|
}
|
|
|
|
|
2006-05-16 21:13:41 +00:00
|
|
|
bool StreamWidget::timeoutEvent() {
|
|
|
|
executeVolumeUpdate();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StreamWidget::executeVolumeUpdate() {
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
2007-09-02 23:38:55 +00:00
|
|
|
StreamWidget(cobject, x),
|
2007-09-02 23:53:16 +00:00
|
|
|
defaultMenuItem("_Default", true){
|
2007-09-02 23:38:55 +00:00
|
|
|
|
|
|
|
add_events(Gdk::BUTTON_PRESS_MASK);
|
|
|
|
|
|
|
|
defaultMenuItem.set_active(false);
|
|
|
|
defaultMenuItem.signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onDefaultToggle));
|
|
|
|
menu.append(defaultMenuItem);
|
|
|
|
menu.show_all();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SinkWidget* SinkWidget::create() {
|
|
|
|
SinkWidget* w;
|
|
|
|
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
|
|
|
|
x->get_widget_derived("streamWidget", w);
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
2006-05-16 21:13:41 +00:00
|
|
|
void SinkWidget::executeVolumeUpdate() {
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_operation* o;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
if (!(o = pa_context_set_sink_volume_by_index(context, index, &volume, NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_set_sink_volume_by_index() failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SinkWidget::onMuteToggleButton() {
|
|
|
|
StreamWidget::onMuteToggleButton();
|
|
|
|
|
2007-08-15 19:37:23 +00:00
|
|
|
if (updating)
|
|
|
|
return;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_operation* o;
|
|
|
|
if (!(o = pa_context_set_sink_mute_by_index(context, index, muteToggleButton->get_active(), NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_set_sink_mute_by_index() failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
void SinkWidget::onDefaultToggle() {
|
|
|
|
pa_operation* o;
|
|
|
|
|
|
|
|
if (updating)
|
|
|
|
return;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
if (!(o = pa_context_set_default_sink(context, name.c_str(), NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_set_default_sink() failed"));
|
2007-09-02 23:38:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
SourceWidget::SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
2007-09-02 23:38:55 +00:00
|
|
|
StreamWidget(cobject, x),
|
2008-08-05 18:45:24 +00:00
|
|
|
defaultMenuItem(_("_Default"), true){
|
2007-09-02 23:38:55 +00:00
|
|
|
|
|
|
|
add_events(Gdk::BUTTON_PRESS_MASK);
|
|
|
|
|
|
|
|
defaultMenuItem.set_active(false);
|
|
|
|
defaultMenuItem.signal_toggled().connect(sigc::mem_fun(*this, &SourceWidget::onDefaultToggle));
|
|
|
|
menu.append(defaultMenuItem);
|
|
|
|
menu.show_all();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SourceWidget* SourceWidget::create() {
|
|
|
|
SourceWidget* w;
|
|
|
|
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
|
|
|
|
x->get_widget_derived("streamWidget", w);
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
2006-05-16 21:13:41 +00:00
|
|
|
void SourceWidget::executeVolumeUpdate() {
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_operation* o;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
if (!(o = pa_context_set_source_volume_by_index(context, index, &volume, NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_set_source_volume_by_index() failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SourceWidget::onMuteToggleButton() {
|
|
|
|
StreamWidget::onMuteToggleButton();
|
2007-08-15 19:37:23 +00:00
|
|
|
|
|
|
|
if (updating)
|
|
|
|
return;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_operation* o;
|
|
|
|
if (!(o = pa_context_set_source_mute_by_index(context, index, muteToggleButton->get_active(), NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_set_source_mute_by_index() failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
void SourceWidget::onDefaultToggle() {
|
|
|
|
pa_operation* o;
|
|
|
|
|
|
|
|
if (updating)
|
|
|
|
return;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
if (!(o = pa_context_set_default_source(context, name.c_str(), NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_set_default_source() failed"));
|
2007-09-02 23:38:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
SinkInputWidget::SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
2006-08-07 15:26:52 +00:00
|
|
|
StreamWidget(cobject, x),
|
|
|
|
mainWindow(NULL),
|
2008-08-05 18:45:24 +00:00
|
|
|
titleMenuItem(_("_Move Stream..."), true),
|
|
|
|
killMenuItem(_("_Terminate Stream"), true) {
|
2006-08-07 15:26:52 +00:00
|
|
|
|
|
|
|
add_events(Gdk::BUTTON_PRESS_MASK);
|
|
|
|
|
|
|
|
menu.append(titleMenuItem);
|
|
|
|
titleMenuItem.set_submenu(submenu);
|
2007-09-02 23:53:16 +00:00
|
|
|
|
|
|
|
menu.append(killMenuItem);
|
|
|
|
killMenuItem.signal_activate().connect(sigc::mem_fun(*this, &SinkInputWidget::onKill));
|
2006-08-07 15:26:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SinkInputWidget::~SinkInputWidget() {
|
|
|
|
clearMenu();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SinkInputWidget* SinkInputWidget::create() {
|
|
|
|
SinkInputWidget* w;
|
|
|
|
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
|
|
|
|
x->get_widget_derived("streamWidget", w);
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
2006-05-16 21:13:41 +00:00
|
|
|
void SinkInputWidget::executeVolumeUpdate() {
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_operation* o;
|
2006-08-07 16:45:30 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
if (!(o = pa_context_set_sink_input_volume(context, index, &volume, NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_set_sink_input_volume() failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
2007-08-15 19:37:23 +00:00
|
|
|
void SinkInputWidget::onMuteToggleButton() {
|
|
|
|
StreamWidget::onMuteToggleButton();
|
|
|
|
|
|
|
|
if (updating)
|
|
|
|
return;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2007-08-15 19:37:23 +00:00
|
|
|
pa_operation* o;
|
|
|
|
if (!(o = pa_context_set_sink_input_mute(context, index, muteToggleButton->get_active(), NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_set_sink_input_mute() failed"));
|
2007-08-15 19:37:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
void SinkInputWidget::prepareMenu() {
|
|
|
|
clearMenu();
|
|
|
|
buildMenu();
|
2006-08-07 15:26:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SinkInputWidget::clearMenu() {
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
while (!sinkMenuItems.empty()) {
|
|
|
|
std::map<uint32_t, SinkMenuItem*>::iterator i = sinkMenuItems.begin();
|
|
|
|
delete i->second;
|
|
|
|
sinkMenuItems.erase(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SinkInputWidget::buildMenu() {
|
|
|
|
for (std::map<uint32_t, SinkWidget*>::iterator i = mainWindow->sinkWidgets.begin(); i != mainWindow->sinkWidgets.end(); ++i) {
|
|
|
|
SinkMenuItem *m;
|
2006-08-11 23:56:57 +00:00
|
|
|
sinkMenuItems[i->second->index] = m = new SinkMenuItem(this, i->second->description.c_str(), i->second->index, i->second->index == sinkIndex);
|
2006-08-07 15:26:52 +00:00
|
|
|
submenu.append(m->menuItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.show_all();
|
|
|
|
}
|
|
|
|
|
2007-09-02 23:53:16 +00:00
|
|
|
void SinkInputWidget::onKill() {
|
|
|
|
pa_operation* o;
|
|
|
|
if (!(o = pa_context_kill_sink_input(context, index, NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_kill_sink_input() failed"));
|
2007-09-02 23:53:16 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
void SinkInputWidget::SinkMenuItem::onToggle() {
|
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
if (widget->updating)
|
|
|
|
return;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
if (!menuItem.get_active())
|
|
|
|
return;
|
|
|
|
|
|
|
|
pa_operation* o;
|
|
|
|
if (!(o = pa_context_move_sink_input_by_index(context, widget->index, index, NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_move_sink_input_by_index() failed"));
|
2006-08-07 15:26:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
SourceOutputWidget::SourceOutputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
|
|
|
MinimalStreamWidget(cobject, x),
|
|
|
|
mainWindow(NULL),
|
2008-08-05 18:45:24 +00:00
|
|
|
titleMenuItem(_("_Move Stream..."), true),
|
|
|
|
killMenuItem(_("_Terminate Stream"), true) {
|
2008-04-20 22:13:20 +00:00
|
|
|
|
|
|
|
add_events(Gdk::BUTTON_PRESS_MASK);
|
|
|
|
|
|
|
|
menu.append(titleMenuItem);
|
|
|
|
titleMenuItem.set_submenu(submenu);
|
|
|
|
|
|
|
|
menu.append(killMenuItem);
|
|
|
|
killMenuItem.signal_activate().connect(sigc::mem_fun(*this, &SourceOutputWidget::onKill));
|
|
|
|
}
|
|
|
|
|
|
|
|
SourceOutputWidget::~SourceOutputWidget() {
|
|
|
|
clearMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
SourceOutputWidget* SourceOutputWidget::create() {
|
|
|
|
SourceOutputWidget* w;
|
2008-07-21 13:54:52 +00:00
|
|
|
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
|
|
|
|
x->get_widget_derived("streamWidget", w);
|
2008-04-20 22:13:20 +00:00
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SourceOutputWidget::onKill() {
|
|
|
|
pa_operation* o;
|
|
|
|
if (!(o = pa_context_kill_source_output(context, index, NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_kill_source_output() failed"));
|
2008-04-20 22:13:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SourceOutputWidget::clearMenu() {
|
|
|
|
|
|
|
|
while (!sourceMenuItems.empty()) {
|
|
|
|
std::map<uint32_t, SourceMenuItem*>::iterator i = sourceMenuItems.begin();
|
|
|
|
delete i->second;
|
|
|
|
sourceMenuItems.erase(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SourceOutputWidget::buildMenu() {
|
|
|
|
for (std::map<uint32_t, SourceWidget*>::iterator i = mainWindow->sourceWidgets.begin(); i != mainWindow->sourceWidgets.end(); ++i) {
|
|
|
|
SourceMenuItem *m;
|
|
|
|
sourceMenuItems[i->second->index] = m = new SourceMenuItem(this, i->second->description.c_str(), i->second->index, i->second->index == sourceIndex);
|
|
|
|
submenu.append(m->menuItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
menu.show_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SourceOutputWidget::prepareMenu(void) {
|
|
|
|
clearMenu();
|
|
|
|
buildMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SourceOutputWidget::SourceMenuItem::onToggle() {
|
|
|
|
|
|
|
|
if (widget->updating)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!menuItem.get_active())
|
|
|
|
return;
|
|
|
|
|
|
|
|
pa_operation* o;
|
|
|
|
if (!(o = pa_context_move_source_output_by_index(context, widget->index, index, NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_move_source_output_by_index() failed"));
|
2008-04-20 22:13:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
RoleWidget::RoleWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
|
|
|
StreamWidget(cobject, x) {
|
|
|
|
|
|
|
|
lockToggleButton->hide();
|
|
|
|
streamToggleButton->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
RoleWidget* RoleWidget::create() {
|
|
|
|
RoleWidget* w;
|
|
|
|
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
|
|
|
|
x->get_widget_derived("streamWidget", w);
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RoleWidget::onMuteToggleButton() {
|
|
|
|
StreamWidget::onMuteToggleButton();
|
|
|
|
|
|
|
|
executeVolumeUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RoleWidget::executeVolumeUpdate() {
|
|
|
|
pa_ext_stream_restore_info info;
|
|
|
|
|
|
|
|
if (updating)
|
|
|
|
return;
|
|
|
|
|
|
|
|
info.name = role.c_str();
|
|
|
|
info.channel_map.channels = 1;
|
|
|
|
info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
|
|
|
|
info.volume = volume;
|
2009-02-04 17:40:12 +00:00
|
|
|
info.device = device == "" ? NULL : device.c_str();
|
2008-08-05 17:05:33 +00:00
|
|
|
info.mute = muteToggleButton->get_active();
|
|
|
|
|
|
|
|
pa_operation* o;
|
|
|
|
if (!(o = pa_ext_stream_restore_write(context, PA_UPDATE_REPLACE, &info, 1, TRUE, NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_ext_stream_restore_write() failed"));
|
2008-08-05 17:05:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
/*** MainWindow ***/
|
|
|
|
|
|
|
|
MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
|
2006-08-07 13:29:46 +00:00
|
|
|
Gtk::Window(cobject),
|
2007-08-31 01:16:50 +00:00
|
|
|
showSinkInputType(SINK_INPUT_CLIENT),
|
2006-08-21 00:56:38 +00:00
|
|
|
showSinkType(SINK_ALL),
|
2008-04-22 00:45:40 +00:00
|
|
|
showSourceOutputType(SOURCE_OUTPUT_CLIENT),
|
2008-08-05 17:05:33 +00:00
|
|
|
showSourceType(SOURCE_NO_MONITOR),
|
|
|
|
eventRoleWidget(NULL){
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2009-02-28 18:01:07 +00:00
|
|
|
x->get_widget("cardsVBox", cardsVBox);
|
2006-04-21 14:51:33 +00:00
|
|
|
x->get_widget("streamsVBox", streamsVBox);
|
2008-04-20 22:13:20 +00:00
|
|
|
x->get_widget("recsVBox", recsVBox);
|
2006-04-21 14:51:33 +00:00
|
|
|
x->get_widget("sinksVBox", sinksVBox);
|
|
|
|
x->get_widget("sourcesVBox", sourcesVBox);
|
2009-02-28 18:01:07 +00:00
|
|
|
x->get_widget("noCardsLabel", noCardsLabel);
|
2006-04-21 14:51:33 +00:00
|
|
|
x->get_widget("noStreamsLabel", noStreamsLabel);
|
2008-04-20 22:13:20 +00:00
|
|
|
x->get_widget("noRecsLabel", noRecsLabel);
|
2006-04-21 14:51:33 +00:00
|
|
|
x->get_widget("noSinksLabel", noSinksLabel);
|
|
|
|
x->get_widget("noSourcesLabel", noSourcesLabel);
|
2007-08-31 01:16:50 +00:00
|
|
|
x->get_widget("sinkInputTypeComboBox", sinkInputTypeComboBox);
|
2008-04-20 22:13:20 +00:00
|
|
|
x->get_widget("sourceOutputTypeComboBox", sourceOutputTypeComboBox);
|
2006-08-07 13:29:46 +00:00
|
|
|
x->get_widget("sinkTypeComboBox", sinkTypeComboBox);
|
|
|
|
x->get_widget("sourceTypeComboBox", sourceTypeComboBox);
|
2008-04-21 00:21:04 +00:00
|
|
|
x->get_widget("notebook", notebook);
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2009-02-28 18:01:07 +00:00
|
|
|
cardsVBox->set_reallocate_redraws(true);
|
2006-04-21 14:51:33 +00:00
|
|
|
sourcesVBox->set_reallocate_redraws(true);
|
2006-06-01 13:39:47 +00:00
|
|
|
streamsVBox->set_reallocate_redraws(true);
|
2008-04-22 00:45:40 +00:00
|
|
|
recsVBox->set_reallocate_redraws(true);
|
2006-06-01 13:39:47 +00:00
|
|
|
sinksVBox->set_reallocate_redraws(true);
|
2006-08-07 13:29:46 +00:00
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
sinkInputTypeComboBox->set_active((int) showSinkInputType);
|
2008-04-20 22:13:20 +00:00
|
|
|
sourceOutputTypeComboBox->set_active((int) showSourceOutputType);
|
2006-08-07 13:29:46 +00:00
|
|
|
sinkTypeComboBox->set_active((int) showSinkType);
|
|
|
|
sourceTypeComboBox->set_active((int) showSourceType);
|
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
sinkInputTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSinkInputTypeComboBoxChanged));
|
2008-04-20 22:13:20 +00:00
|
|
|
sourceOutputTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSourceOutputTypeComboBoxChanged));
|
2006-08-07 13:29:46 +00:00
|
|
|
sinkTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSinkTypeComboBoxChanged));
|
|
|
|
sourceTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSourceTypeComboBoxChanged));
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MainWindow* MainWindow::create() {
|
|
|
|
MainWindow* w;
|
|
|
|
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "mainWindow");
|
|
|
|
x->get_widget_derived("mainWindow", w);
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
2006-08-07 13:50:02 +00:00
|
|
|
void MainWindow::on_realize() {
|
|
|
|
Gtk::Window::on_realize();
|
|
|
|
|
|
|
|
get_window()->set_cursor(Gdk::Cursor(Gdk::WATCH));
|
|
|
|
}
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
MainWindow::~MainWindow() {
|
2006-08-07 15:26:52 +00:00
|
|
|
while (!clientNames.empty()) {
|
|
|
|
std::map<uint32_t, char*>::iterator i = clientNames.begin();
|
2006-05-20 17:28:50 +00:00
|
|
|
g_free(i->second);
|
2006-08-07 15:26:52 +00:00
|
|
|
clientNames.erase(i);
|
|
|
|
}
|
2006-05-20 17:28:50 +00:00
|
|
|
}
|
|
|
|
|
2009-02-28 17:11:41 +00:00
|
|
|
void MainWindow::updateCard(const pa_card_info &info) {
|
|
|
|
CardWidget *w;
|
|
|
|
bool is_new = false;
|
|
|
|
|
|
|
|
if (cardWidgets.count(info.index))
|
|
|
|
w = cardWidgets[info.index];
|
|
|
|
else {
|
|
|
|
cardWidgets[info.index] = w = CardWidget::create();
|
2009-02-28 18:01:07 +00:00
|
|
|
cardsVBox->pack_start(*w, false, false, 0);
|
2009-02-28 17:11:41 +00:00
|
|
|
w->index = info.index;
|
|
|
|
is_new = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updating = true;
|
|
|
|
|
|
|
|
if (NULL != info.proplist && pa_proplist_contains(info.proplist, "alsa.card_name"))
|
|
|
|
w->name = pa_proplist_gets(info.proplist, "alsa.card_name");
|
|
|
|
else
|
|
|
|
w->name = info.name;
|
|
|
|
|
2009-02-28 18:01:07 +00:00
|
|
|
w->boldNameLabel->set_text("");
|
2009-02-28 17:11:41 +00:00
|
|
|
gchar *txt;
|
|
|
|
w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s", w->name.c_str()));
|
|
|
|
g_free(txt);
|
|
|
|
|
|
|
|
w->iconImage->set_from_icon_name("audio-card", Gtk::ICON_SIZE_SMALL_TOOLBAR);
|
|
|
|
|
|
|
|
w->hasSinks = w->hasSources = false;
|
|
|
|
w->profiles.clear();
|
2009-02-28 17:35:36 +00:00
|
|
|
for (uint32_t i=0; i<info.n_profiles; ++i) {
|
2009-02-28 17:11:41 +00:00
|
|
|
w->hasSinks = w->hasSinks || (info.profiles[i].n_sinks > 0);
|
|
|
|
w->hasSources = w->hasSources || (info.profiles[i].n_sources > 0);
|
|
|
|
w->profiles.insert(std::pair<Glib::ustring,Glib::ustring>(info.profiles[i].name, info.profiles[i].description));
|
|
|
|
}
|
|
|
|
w->activeProfile = info.active_profile->name;
|
|
|
|
//w->defaultMenuItem.set_active(w->name == defaultSinkName);
|
|
|
|
|
|
|
|
w->updating = false;
|
|
|
|
|
|
|
|
if (is_new)
|
|
|
|
updateDeviceVisibility();
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
void MainWindow::updateSink(const pa_sink_info &info) {
|
|
|
|
SinkWidget *w;
|
2006-08-07 16:45:30 +00:00
|
|
|
bool is_new = false;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
|
|
|
if (sinkWidgets.count(info.index))
|
|
|
|
w = sinkWidgets[info.index];
|
|
|
|
else {
|
|
|
|
sinkWidgets[info.index] = w = SinkWidget::create();
|
2008-08-19 15:36:18 +00:00
|
|
|
w->beepDevice = info.name;
|
2008-04-20 23:56:36 +00:00
|
|
|
w->setChannelMap(info.channel_map, !!(info.flags & PA_SINK_DECIBEL_VOLUME));
|
2006-04-21 14:51:33 +00:00
|
|
|
sinksVBox->pack_start(*w, false, false, 0);
|
|
|
|
w->index = info.index;
|
2008-05-21 22:52:23 +00:00
|
|
|
w->monitor_index = info.monitor_source;
|
2006-08-07 16:45:30 +00:00
|
|
|
is_new = true;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2007-08-15 19:37:23 +00:00
|
|
|
w->updating = true;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2009-02-28 17:11:41 +00:00
|
|
|
w->card_index = info.card;
|
2007-09-02 23:38:55 +00:00
|
|
|
w->name = info.name;
|
2006-08-11 23:56:57 +00:00
|
|
|
w->description = info.description;
|
2007-09-02 23:38:55 +00:00
|
|
|
w->type = info.flags & PA_SINK_HARDWARE ? SINK_HARDWARE : SINK_VIRTUAL;
|
2006-08-07 13:29:46 +00:00
|
|
|
|
2006-08-11 17:25:55 +00:00
|
|
|
w->boldNameLabel->set_text("");
|
2006-05-20 17:28:50 +00:00
|
|
|
gchar *txt;
|
2006-08-11 17:25:55 +00:00
|
|
|
w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s", info.description));
|
2006-05-20 17:28:50 +00:00
|
|
|
g_free(txt);
|
|
|
|
|
2008-06-12 11:26:06 +00:00
|
|
|
w->iconImage->set_from_icon_name("audio-card", Gtk::ICON_SIZE_SMALL_TOOLBAR);
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
w->setVolume(info.volume);
|
|
|
|
w->muteToggleButton->set_active(info.mute);
|
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
w->defaultMenuItem.set_active(w->name == defaultSinkName);
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
w->updating = false;
|
|
|
|
|
2006-08-07 16:45:30 +00:00
|
|
|
if (is_new)
|
|
|
|
updateDeviceVisibility();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2008-05-21 22:52:23 +00:00
|
|
|
static void suspended_callback(pa_stream *s, void *userdata) {
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
|
|
|
if (pa_stream_is_suspended(s))
|
2008-06-17 20:24:54 +00:00
|
|
|
w->updateVolumeMeter(pa_stream_get_device_index(s), PA_INVALID_INDEX, -1);
|
2008-05-21 22:52:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void read_callback(pa_stream *s, size_t length, void *userdata) {
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
const void *data;
|
|
|
|
double v;
|
|
|
|
|
|
|
|
if (pa_stream_peek(s, &data, &length) < 0) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("Failed to read data from stream"));
|
2008-05-21 22:52:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(length > 0);
|
|
|
|
assert(length % sizeof(float) == 0);
|
|
|
|
|
|
|
|
v = ((const float*) data)[length / sizeof(float) -1];
|
|
|
|
|
|
|
|
pa_stream_drop(s);
|
|
|
|
|
|
|
|
if (v < 0)
|
|
|
|
v = 0;
|
|
|
|
if (v > 1)
|
|
|
|
v = 1;
|
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
w->updateVolumeMeter(pa_stream_get_device_index(s), pa_stream_get_monitor_stream(s), v);
|
2008-05-21 22:52:23 +00:00
|
|
|
}
|
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
void MainWindow::createMonitorStreamForSource(uint32_t source_idx) {
|
2008-05-21 22:52:23 +00:00
|
|
|
pa_stream *s;
|
|
|
|
char t[16];
|
2008-06-17 20:24:54 +00:00
|
|
|
pa_buffer_attr attr;
|
2008-05-21 22:52:23 +00:00
|
|
|
pa_sample_spec ss;
|
2009-02-28 18:01:07 +00:00
|
|
|
return;
|
2008-06-17 20:24:54 +00:00
|
|
|
|
|
|
|
ss.channels = 1;
|
|
|
|
ss.format = PA_SAMPLE_FLOAT32;
|
|
|
|
ss.rate = 25;
|
|
|
|
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
|
|
attr.fragsize = sizeof(float);
|
2008-09-08 23:09:44 +00:00
|
|
|
attr.maxlength = (uint32_t) -1;
|
2008-06-17 20:24:54 +00:00
|
|
|
|
|
|
|
snprintf(t, sizeof(t), "%u", source_idx);
|
|
|
|
|
2008-08-05 18:45:24 +00:00
|
|
|
if (!(s = pa_stream_new(context, _("Peak detect"), &ss, NULL))) {
|
|
|
|
show_error(_("Failed to create monitoring stream"));
|
2008-06-17 20:24:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pa_stream_set_read_callback(s, read_callback, this);
|
|
|
|
pa_stream_set_suspended_callback(s, suspended_callback, this);
|
|
|
|
|
|
|
|
if (pa_stream_connect_record(s, t, &attr, (pa_stream_flags_t) (PA_STREAM_DONT_MOVE|PA_STREAM_PEAK_DETECT|PA_STREAM_ADJUST_LATENCY)) < 0) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("Failed to connect monitoring stream"));
|
2008-06-17 20:24:54 +00:00
|
|
|
pa_stream_unref(s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::createMonitorStreamForSinkInput(uint32_t sink_input_idx, uint32_t sink_idx) {
|
|
|
|
pa_stream *s;
|
|
|
|
char t[16];
|
2008-05-21 22:52:23 +00:00
|
|
|
pa_buffer_attr attr;
|
2008-06-17 20:24:54 +00:00
|
|
|
pa_sample_spec ss;
|
|
|
|
uint32_t monitor_source_idx;
|
2009-02-28 18:01:07 +00:00
|
|
|
return;
|
2008-05-21 22:52:23 +00:00
|
|
|
|
|
|
|
ss.channels = 1;
|
|
|
|
ss.format = PA_SAMPLE_FLOAT32;
|
|
|
|
ss.rate = 25;
|
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
if (!sinkWidgets.count(sink_idx))
|
|
|
|
return;
|
|
|
|
|
|
|
|
monitor_source_idx = sinkWidgets[sink_idx]->monitor_index;
|
|
|
|
|
2008-05-21 22:52:23 +00:00
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
|
|
attr.fragsize = sizeof(float);
|
2008-09-08 23:09:44 +00:00
|
|
|
attr.maxlength = (uint32_t) -1;
|
2008-05-21 22:52:23 +00:00
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
snprintf(t, sizeof(t), "%u", monitor_source_idx);
|
2008-05-21 22:52:23 +00:00
|
|
|
|
2008-08-05 18:45:24 +00:00
|
|
|
if (!(s = pa_stream_new(context, _("Peak detect"), &ss, NULL))) {
|
|
|
|
show_error(_("Failed to create monitoring stream"));
|
2008-05-21 22:52:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
pa_stream_set_monitor_stream(s, sink_input_idx);
|
2008-05-21 22:52:23 +00:00
|
|
|
pa_stream_set_read_callback(s, read_callback, this);
|
|
|
|
pa_stream_set_suspended_callback(s, suspended_callback, this);
|
|
|
|
|
|
|
|
if (pa_stream_connect_record(s, t, &attr, (pa_stream_flags_t) (PA_STREAM_DONT_MOVE|PA_STREAM_PEAK_DETECT|PA_STREAM_ADJUST_LATENCY)) < 0) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("Failed to connect monitoring stream"));
|
2008-05-21 22:52:23 +00:00
|
|
|
pa_stream_unref(s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
void MainWindow::updateSource(const pa_source_info &info) {
|
|
|
|
SourceWidget *w;
|
2006-08-07 16:45:30 +00:00
|
|
|
bool is_new = false;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
if (sourceWidgets.count(info.index))
|
|
|
|
w = sourceWidgets[info.index];
|
2006-04-21 14:51:33 +00:00
|
|
|
else {
|
2006-08-07 13:29:46 +00:00
|
|
|
sourceWidgets[info.index] = w = SourceWidget::create();
|
2008-04-20 23:56:36 +00:00
|
|
|
w->setChannelMap(info.channel_map, !!(info.flags & PA_SOURCE_DECIBEL_VOLUME));
|
2006-08-07 13:29:46 +00:00
|
|
|
sourcesVBox->pack_start(*w, false, false, 0);
|
2006-04-21 14:51:33 +00:00
|
|
|
w->index = info.index;
|
2006-08-07 16:45:30 +00:00
|
|
|
is_new = true;
|
2008-05-21 22:52:23 +00:00
|
|
|
|
|
|
|
if (pa_context_get_server_protocol_version(context) >= 13)
|
2008-06-17 20:24:54 +00:00
|
|
|
createMonitorStreamForSource(info.index);
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2007-08-15 19:37:23 +00:00
|
|
|
w->updating = true;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2009-02-28 17:11:41 +00:00
|
|
|
w->card_index = info.card;
|
2007-09-02 23:38:55 +00:00
|
|
|
w->name = info.name;
|
2008-04-20 22:13:20 +00:00
|
|
|
w->description = info.description;
|
2006-08-07 13:29:46 +00:00
|
|
|
w->type = info.monitor_of_sink != PA_INVALID_INDEX ? SOURCE_MONITOR : (info.flags & PA_SOURCE_HARDWARE ? SOURCE_HARDWARE : SOURCE_VIRTUAL);
|
|
|
|
|
2006-08-11 17:25:55 +00:00
|
|
|
w->boldNameLabel->set_text("");
|
2006-05-20 17:28:50 +00:00
|
|
|
gchar *txt;
|
2006-08-11 17:25:55 +00:00
|
|
|
w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s", info.description));
|
2006-05-20 17:28:50 +00:00
|
|
|
g_free(txt);
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2008-06-12 11:26:06 +00:00
|
|
|
w->iconImage->set_from_icon_name("audio-input-microphone", Gtk::ICON_SIZE_SMALL_TOOLBAR);
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
w->setVolume(info.volume);
|
|
|
|
w->muteToggleButton->set_active(info.mute);
|
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
w->defaultMenuItem.set_active(w->name == defaultSourceName);
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
w->updating = false;
|
|
|
|
|
2006-08-07 16:45:30 +00:00
|
|
|
if (is_new)
|
|
|
|
updateDeviceVisibility();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2008-06-12 11:26:06 +00:00
|
|
|
void MainWindow::setIconFromProplist(Gtk::Image *icon, pa_proplist *l, const char *def) {
|
|
|
|
const char *t;
|
|
|
|
|
|
|
|
if ((t = pa_proplist_gets(l, PA_PROP_MEDIA_ICON_NAME)))
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
if ((t = pa_proplist_gets(l, PA_PROP_WINDOW_ICON_NAME)))
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
if ((t = pa_proplist_gets(l, PA_PROP_APPLICATION_ICON_NAME)))
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
if ((t = pa_proplist_gets(l, PA_PROP_MEDIA_ROLE))) {
|
|
|
|
|
|
|
|
if (strcmp(t, "video") == 0 ||
|
|
|
|
strcmp(t, "phone") == 0)
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
if (strcmp(t, "music") == 0) {
|
|
|
|
t = "audio";
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(t, "game") == 0) {
|
|
|
|
t = "applications-games";
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(t, "event") == 0) {
|
|
|
|
t = "dialog-information";
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
t = def;
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
|
|
|
icon->set_from_icon_name(t, Gtk::ICON_SIZE_SMALL_TOOLBAR);
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
|
|
|
|
SinkInputWidget *w;
|
2007-08-31 01:16:50 +00:00
|
|
|
bool is_new = false;
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
if (sinkInputWidgets.count(info.index))
|
|
|
|
w = sinkInputWidgets[info.index];
|
2006-04-21 14:51:33 +00:00
|
|
|
else {
|
2007-08-31 01:16:50 +00:00
|
|
|
sinkInputWidgets[info.index] = w = SinkInputWidget::create();
|
2008-04-20 23:56:36 +00:00
|
|
|
w->setChannelMap(info.channel_map, true);
|
2006-04-21 14:51:33 +00:00
|
|
|
streamsVBox->pack_start(*w, false, false, 0);
|
|
|
|
w->index = info.index;
|
2006-05-20 17:28:50 +00:00
|
|
|
w->clientIndex = info.client;
|
2006-08-07 15:26:52 +00:00
|
|
|
w->mainWindow = this;
|
2007-08-31 01:16:50 +00:00
|
|
|
is_new = true;
|
2008-06-17 20:24:54 +00:00
|
|
|
|
|
|
|
if (pa_context_get_server_protocol_version(context) >= 13)
|
|
|
|
createMonitorStreamForSinkInput(info.index, info.sink);
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2007-08-15 19:37:23 +00:00
|
|
|
w->updating = true;
|
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
w->type = info.client != PA_INVALID_INDEX ? SINK_INPUT_CLIENT : SINK_INPUT_VIRTUAL;
|
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
w->sinkIndex = info.sink;
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
char *txt;
|
2006-07-21 20:17:49 +00:00
|
|
|
if (clientNames.count(info.client)) {
|
|
|
|
w->boldNameLabel->set_markup(txt = g_markup_printf_escaped("<b>%s</b>", clientNames[info.client]));
|
|
|
|
g_free(txt);
|
|
|
|
w->nameLabel->set_markup(txt = g_markup_printf_escaped(": %s", info.name));
|
|
|
|
g_free(txt);
|
|
|
|
} else {
|
2006-08-11 17:25:55 +00:00
|
|
|
w->boldNameLabel->set_text("");
|
2006-07-21 20:17:49 +00:00
|
|
|
w->nameLabel->set_label(info.name);
|
|
|
|
}
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2008-06-12 11:26:06 +00:00
|
|
|
setIconFromProplist(w->iconImage, info.proplist, "audio-card");
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
w->setVolume(info.volume);
|
2007-08-31 01:16:50 +00:00
|
|
|
w->muteToggleButton->set_active(info.mute);
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
w->updating = false;
|
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
if (is_new)
|
|
|
|
updateDeviceVisibility();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
void MainWindow::updateSourceOutput(const pa_source_output_info &info) {
|
|
|
|
SourceOutputWidget *w;
|
2008-06-12 11:26:06 +00:00
|
|
|
const char *app;
|
2008-08-05 17:05:33 +00:00
|
|
|
bool is_new = false;
|
2008-06-12 11:26:06 +00:00
|
|
|
|
|
|
|
if ((app = pa_proplist_gets(info.proplist, PA_PROP_APPLICATION_ID)))
|
|
|
|
if (strcmp(app, "org.PulseAudio.pavucontrol") == 0)
|
|
|
|
return;
|
2008-04-20 22:13:20 +00:00
|
|
|
|
|
|
|
if (sourceOutputWidgets.count(info.index))
|
|
|
|
w = sourceOutputWidgets[info.index];
|
|
|
|
else {
|
|
|
|
sourceOutputWidgets[info.index] = w = SourceOutputWidget::create();
|
|
|
|
recsVBox->pack_start(*w, false, false, 0);
|
|
|
|
w->index = info.index;
|
|
|
|
w->clientIndex = info.client;
|
|
|
|
w->mainWindow = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updating = true;
|
|
|
|
|
|
|
|
w->type = info.client != PA_INVALID_INDEX ? SOURCE_OUTPUT_CLIENT : SOURCE_OUTPUT_VIRTUAL;
|
|
|
|
|
|
|
|
w->sourceIndex = info.source;
|
|
|
|
|
|
|
|
char *txt;
|
|
|
|
if (clientNames.count(info.client)) {
|
|
|
|
w->boldNameLabel->set_markup(txt = g_markup_printf_escaped("<b>%s</b>", clientNames[info.client]));
|
|
|
|
g_free(txt);
|
|
|
|
w->nameLabel->set_markup(txt = g_markup_printf_escaped(": %s", info.name));
|
|
|
|
g_free(txt);
|
|
|
|
} else {
|
|
|
|
w->boldNameLabel->set_text("");
|
|
|
|
w->nameLabel->set_label(info.name);
|
|
|
|
}
|
|
|
|
|
2008-06-12 11:26:06 +00:00
|
|
|
setIconFromProplist(w->iconImage, info.proplist, "audio-input-microphone");
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
w->updating = false;
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
if (is_new)
|
|
|
|
updateDeviceVisibility();
|
|
|
|
}
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
void MainWindow::updateClient(const pa_client_info &info) {
|
|
|
|
|
|
|
|
g_free(clientNames[info.index]);
|
|
|
|
clientNames[info.index] = g_strdup(info.name);
|
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
for (std::map<uint32_t, SinkInputWidget*>::iterator i = sinkInputWidgets.begin(); i != sinkInputWidgets.end(); ++i) {
|
2006-05-20 17:28:50 +00:00
|
|
|
SinkInputWidget *w = i->second;
|
|
|
|
|
|
|
|
if (!w)
|
|
|
|
continue;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
if (w->clientIndex == info.index) {
|
|
|
|
gchar *txt;
|
|
|
|
w->boldNameLabel->set_markup(txt = g_markup_printf_escaped("<b>%s</b>", info.name));
|
|
|
|
g_free(txt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
void MainWindow::updateServer(const pa_server_info &info) {
|
|
|
|
|
2008-03-28 18:44:46 +00:00
|
|
|
defaultSourceName = info.default_source_name ? info.default_source_name : "";
|
|
|
|
defaultSinkName = info.default_sink_name ? info.default_sink_name : "";
|
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) {
|
|
|
|
SinkWidget *w = i->second;
|
|
|
|
|
|
|
|
if (!w)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
w->updating = true;
|
2008-03-28 18:44:46 +00:00
|
|
|
w->defaultMenuItem.set_active(w->name == defaultSinkName);
|
2007-09-02 23:38:55 +00:00
|
|
|
w->updating = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (std::map<uint32_t, SourceWidget*>::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) {
|
|
|
|
SourceWidget *w = i->second;
|
|
|
|
|
|
|
|
if (!w)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
w->updating = true;
|
2008-03-28 18:44:46 +00:00
|
|
|
w->defaultMenuItem.set_active(w->name == defaultSourceName);
|
2007-09-02 23:38:55 +00:00
|
|
|
w->updating = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
bool MainWindow::createEventRoleWidget() {
|
|
|
|
if (eventRoleWidget)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
pa_channel_map cm = {
|
|
|
|
1, { PA_CHANNEL_POSITION_MONO }
|
|
|
|
};
|
|
|
|
|
|
|
|
eventRoleWidget = RoleWidget::create();
|
|
|
|
streamsVBox->pack_start(*eventRoleWidget, false, false, 0);
|
|
|
|
eventRoleWidget->role = "sink-input-by-media-role:event";
|
|
|
|
eventRoleWidget->setChannelMap(cm, true);
|
|
|
|
|
|
|
|
eventRoleWidget->boldNameLabel->set_text("");
|
2008-08-05 18:45:24 +00:00
|
|
|
eventRoleWidget->nameLabel->set_label(_("System Sounds"));
|
2008-08-05 17:05:33 +00:00
|
|
|
|
|
|
|
eventRoleWidget->iconImage->set_from_icon_name("multimedia-volume-control", Gtk::ICON_SIZE_SMALL_TOOLBAR);
|
|
|
|
|
|
|
|
eventRoleWidget->device = "";
|
|
|
|
|
|
|
|
eventRoleWidget->updating = true;
|
|
|
|
|
|
|
|
pa_cvolume volume;
|
|
|
|
volume.channels = 1;
|
|
|
|
volume.values[0] = PA_VOLUME_NORM;
|
|
|
|
|
|
|
|
eventRoleWidget->setVolume(volume);
|
|
|
|
eventRoleWidget->muteToggleButton->set_active(false);
|
|
|
|
|
|
|
|
eventRoleWidget->updating = false;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::deleteEventRoleWidget() {
|
|
|
|
|
|
|
|
if (eventRoleWidget)
|
|
|
|
delete eventRoleWidget;
|
|
|
|
|
|
|
|
eventRoleWidget = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::updateRole(const pa_ext_stream_restore_info &info) {
|
|
|
|
pa_cvolume volume;
|
|
|
|
bool is_new = false;
|
|
|
|
|
|
|
|
if (strcmp(info.name, "sink-input-by-media-role:event") != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
is_new = createEventRoleWidget();
|
|
|
|
|
|
|
|
eventRoleWidget->updating = true;
|
|
|
|
|
2009-02-04 17:40:12 +00:00
|
|
|
eventRoleWidget->device = info.device ? info.device : "";
|
2008-08-05 17:05:33 +00:00
|
|
|
|
|
|
|
volume.channels = 1;
|
2009-02-04 17:40:12 +00:00
|
|
|
volume.values[0] = pa_cvolume_max(&info.volume);
|
2008-08-05 17:05:33 +00:00
|
|
|
|
|
|
|
eventRoleWidget->setVolume(volume);
|
|
|
|
eventRoleWidget->muteToggleButton->set_active(info.mute);
|
|
|
|
|
|
|
|
eventRoleWidget->updating = false;
|
|
|
|
|
|
|
|
if (is_new)
|
|
|
|
updateDeviceVisibility();
|
|
|
|
}
|
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
void MainWindow::updateVolumeMeter(uint32_t source_index, uint32_t sink_input_idx, double v) {
|
2008-05-21 22:52:23 +00:00
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
if (sink_input_idx != PA_INVALID_INDEX) {
|
|
|
|
SinkInputWidget *w;
|
2008-05-21 22:52:23 +00:00
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
if (sinkInputWidgets.count(sink_input_idx)) {
|
|
|
|
w = sinkInputWidgets[sink_input_idx];
|
2008-05-21 22:52:23 +00:00
|
|
|
w->updatePeak(v);
|
2008-06-17 20:24:54 +00:00
|
|
|
}
|
2008-05-21 22:52:23 +00:00
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
} else {
|
2008-05-21 22:52:23 +00:00
|
|
|
|
2008-06-17 20:24:54 +00:00
|
|
|
for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) {
|
|
|
|
SinkWidget* w = i->second;
|
|
|
|
|
|
|
|
if (w->monitor_index == source_index)
|
|
|
|
w->updatePeak(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (std::map<uint32_t, SourceWidget*>::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) {
|
|
|
|
SourceWidget* w = i->second;
|
|
|
|
|
|
|
|
if (w->index == source_index)
|
|
|
|
w->updatePeak(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (std::map<uint32_t, SourceOutputWidget*>::iterator i = sourceOutputWidgets.begin(); i != sourceOutputWidgets.end(); ++i) {
|
|
|
|
SourceOutputWidget* w = i->second;
|
|
|
|
|
|
|
|
if (w->sourceIndex == source_index)
|
|
|
|
w->updatePeak(v);
|
|
|
|
}
|
2008-05-21 22:52:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
static guint idle_source = 0;
|
|
|
|
|
|
|
|
gboolean idle_cb(gpointer data) {
|
|
|
|
((MainWindow*) data)->reallyUpdateDeviceVisibility();
|
|
|
|
idle_source = 0;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
void MainWindow::updateDeviceVisibility() {
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
if (idle_source)
|
|
|
|
return;
|
|
|
|
|
|
|
|
idle_source = g_idle_add(idle_cb, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::reallyUpdateDeviceVisibility() {
|
2006-08-07 13:29:46 +00:00
|
|
|
bool is_empty = true;
|
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
for (std::map<uint32_t, SinkInputWidget*>::iterator i = sinkInputWidgets.begin(); i != sinkInputWidgets.end(); ++i) {
|
|
|
|
SinkInputWidget* w = i->second;
|
|
|
|
|
|
|
|
if (showSinkInputType == SINK_INPUT_ALL || w->type == showSinkInputType) {
|
2008-08-05 17:05:33 +00:00
|
|
|
w->show();
|
2007-08-31 01:16:50 +00:00
|
|
|
is_empty = false;
|
2008-08-05 17:05:33 +00:00
|
|
|
} else
|
|
|
|
w->hide();
|
2007-08-31 01:16:50 +00:00
|
|
|
}
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
if (eventRoleWidget)
|
|
|
|
is_empty = false;
|
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
if (is_empty)
|
|
|
|
noStreamsLabel->show();
|
2008-08-05 17:05:33 +00:00
|
|
|
else
|
|
|
|
noStreamsLabel->hide();
|
2007-08-31 01:16:50 +00:00
|
|
|
|
2008-04-22 00:45:40 +00:00
|
|
|
is_empty = true;
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
for (std::map<uint32_t, SourceOutputWidget*>::iterator i = sourceOutputWidgets.begin(); i != sourceOutputWidgets.end(); ++i) {
|
|
|
|
SourceOutputWidget* w = i->second;
|
|
|
|
|
|
|
|
if (showSourceOutputType == SOURCE_OUTPUT_ALL || w->type == showSourceOutputType) {
|
2008-08-05 17:05:33 +00:00
|
|
|
w->show();
|
2008-04-20 22:13:20 +00:00
|
|
|
is_empty = false;
|
2008-08-05 17:05:33 +00:00
|
|
|
} else
|
|
|
|
w->hide();
|
2008-04-20 22:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (is_empty)
|
|
|
|
noRecsLabel->show();
|
2008-08-05 17:05:33 +00:00
|
|
|
else
|
|
|
|
noRecsLabel->hide();
|
2008-04-20 22:13:20 +00:00
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
is_empty = true;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) {
|
2006-08-07 13:29:46 +00:00
|
|
|
SinkWidget* w = i->second;
|
|
|
|
|
|
|
|
if (showSinkType == SINK_ALL || w->type == showSinkType) {
|
2008-08-05 17:05:33 +00:00
|
|
|
w->show();
|
2006-08-07 13:29:46 +00:00
|
|
|
is_empty = false;
|
2009-02-28 17:11:41 +00:00
|
|
|
} else
|
|
|
|
w->hide();
|
|
|
|
}
|
|
|
|
|
2009-02-28 18:01:07 +00:00
|
|
|
if (is_empty)
|
|
|
|
noSinksLabel->show();
|
|
|
|
else
|
|
|
|
noSinksLabel->hide();
|
|
|
|
|
|
|
|
is_empty = true;
|
|
|
|
|
2009-02-28 17:11:41 +00:00
|
|
|
for (std::map<uint32_t, CardWidget*>::iterator i = cardWidgets.begin(); i != cardWidgets.end(); ++i) {
|
|
|
|
CardWidget* w = i->second;
|
|
|
|
|
2009-02-28 18:01:07 +00:00
|
|
|
w->show();
|
|
|
|
is_empty = false;
|
2006-08-07 13:29:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (is_empty)
|
2009-02-28 18:01:07 +00:00
|
|
|
noCardsLabel->show();
|
2008-08-05 17:05:33 +00:00
|
|
|
else
|
2009-02-28 18:01:07 +00:00
|
|
|
noCardsLabel->hide();
|
2006-08-07 13:29:46 +00:00
|
|
|
|
|
|
|
is_empty = true;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-08-07 15:26:52 +00:00
|
|
|
for (std::map<uint32_t, SourceWidget*>::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) {
|
2006-08-07 13:29:46 +00:00
|
|
|
SourceWidget* w = i->second;
|
|
|
|
|
2006-08-21 00:56:38 +00:00
|
|
|
if (showSourceType == SOURCE_ALL ||
|
|
|
|
w->type == showSourceType ||
|
|
|
|
(showSourceType == SOURCE_NO_MONITOR && w->type != SOURCE_MONITOR)) {
|
2008-08-05 17:05:33 +00:00
|
|
|
w->show();
|
2006-08-07 13:29:46 +00:00
|
|
|
is_empty = false;
|
2008-08-05 17:05:33 +00:00
|
|
|
} else
|
|
|
|
w->hide();
|
2006-08-07 13:29:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (is_empty)
|
2006-04-21 14:51:33 +00:00
|
|
|
noSourcesLabel->show();
|
2008-08-05 17:05:33 +00:00
|
|
|
else
|
|
|
|
noSourcesLabel->hide();
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
/* Hmm, if I don't call hide()/show() here some widgets will never
|
|
|
|
* get their proper space allocated */
|
|
|
|
sinksVBox->hide();
|
2006-08-07 13:29:46 +00:00
|
|
|
sinksVBox->show();
|
2008-08-05 17:05:33 +00:00
|
|
|
sourcesVBox->hide();
|
|
|
|
sourcesVBox->show();
|
|
|
|
streamsVBox->hide();
|
2007-08-31 01:16:50 +00:00
|
|
|
streamsVBox->show();
|
2008-08-05 17:05:33 +00:00
|
|
|
recsVBox->hide();
|
|
|
|
recsVBox->show();
|
2009-02-28 18:01:07 +00:00
|
|
|
cardsVBox->hide();
|
|
|
|
cardsVBox->show();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2009-02-28 17:11:41 +00:00
|
|
|
void MainWindow::removeCard(uint32_t index) {
|
|
|
|
if (!cardWidgets.count(index))
|
|
|
|
return;
|
|
|
|
|
|
|
|
delete cardWidgets[index];
|
|
|
|
cardWidgets.erase(index);
|
|
|
|
updateDeviceVisibility();
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
void MainWindow::removeSink(uint32_t index) {
|
2006-08-07 13:29:46 +00:00
|
|
|
if (!sinkWidgets.count(index))
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
delete sinkWidgets[index];
|
2006-04-21 14:51:33 +00:00
|
|
|
sinkWidgets.erase(index);
|
2006-08-07 13:29:46 +00:00
|
|
|
updateDeviceVisibility();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::removeSource(uint32_t index) {
|
2006-08-07 13:29:46 +00:00
|
|
|
if (!sourceWidgets.count(index))
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
delete sourceWidgets[index];
|
|
|
|
sourceWidgets.erase(index);
|
|
|
|
updateDeviceVisibility();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::removeSinkInput(uint32_t index) {
|
2007-08-31 01:16:50 +00:00
|
|
|
if (!sinkInputWidgets.count(index))
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
delete sinkInputWidgets[index];
|
|
|
|
sinkInputWidgets.erase(index);
|
2006-08-07 13:29:46 +00:00
|
|
|
updateDeviceVisibility();
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
void MainWindow::removeSourceOutput(uint32_t index) {
|
|
|
|
if (!sourceOutputWidgets.count(index))
|
|
|
|
return;
|
|
|
|
|
|
|
|
delete sourceOutputWidgets[index];
|
|
|
|
sourceOutputWidgets.erase(index);
|
|
|
|
updateDeviceVisibility();
|
|
|
|
}
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
void MainWindow::removeClient(uint32_t index) {
|
|
|
|
g_free(clientNames[index]);
|
|
|
|
clientNames.erase(index);
|
|
|
|
}
|
|
|
|
|
2006-08-07 13:29:46 +00:00
|
|
|
void MainWindow::onSinkTypeComboBoxChanged() {
|
|
|
|
showSinkType = (SinkType) sinkTypeComboBox->get_active_row_number();
|
|
|
|
|
|
|
|
if (showSinkType == (SinkType) -1)
|
2006-08-21 00:56:38 +00:00
|
|
|
sinkTypeComboBox->set_active((int) SINK_ALL);
|
2006-08-07 13:29:46 +00:00
|
|
|
|
|
|
|
updateDeviceVisibility();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::onSourceTypeComboBoxChanged() {
|
|
|
|
showSourceType = (SourceType) sourceTypeComboBox->get_active_row_number();
|
|
|
|
|
|
|
|
if (showSourceType == (SourceType) -1)
|
2006-08-21 00:56:38 +00:00
|
|
|
sourceTypeComboBox->set_active((int) SOURCE_NO_MONITOR);
|
2006-08-07 13:29:46 +00:00
|
|
|
|
|
|
|
updateDeviceVisibility();
|
|
|
|
}
|
|
|
|
|
2007-08-31 01:16:50 +00:00
|
|
|
void MainWindow::onSinkInputTypeComboBoxChanged() {
|
|
|
|
showSinkInputType = (SinkInputType) sinkInputTypeComboBox->get_active_row_number();
|
|
|
|
|
|
|
|
if (showSinkInputType == (SinkInputType) -1)
|
|
|
|
sinkInputTypeComboBox->set_active((int) SINK_INPUT_CLIENT);
|
|
|
|
|
|
|
|
updateDeviceVisibility();
|
|
|
|
}
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
void MainWindow::onSourceOutputTypeComboBoxChanged() {
|
|
|
|
showSourceOutputType = (SourceOutputType) sourceOutputTypeComboBox->get_active_row_number();
|
|
|
|
|
|
|
|
if (showSourceOutputType == (SourceOutputType) -1)
|
|
|
|
sourceOutputTypeComboBox->set_active((int) SOURCE_OUTPUT_CLIENT);
|
|
|
|
|
|
|
|
updateDeviceVisibility();
|
|
|
|
}
|
|
|
|
|
2006-08-07 13:50:02 +00:00
|
|
|
static void dec_outstanding(MainWindow *w) {
|
2006-08-07 15:26:52 +00:00
|
|
|
if (n_outstanding <= 0)
|
|
|
|
return;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-08-07 13:50:02 +00:00
|
|
|
if (--n_outstanding <= 0)
|
|
|
|
w->get_window()->set_cursor();
|
|
|
|
}
|
|
|
|
|
2009-02-28 17:11:41 +00:00
|
|
|
void card_cb(pa_context *, const pa_card_info *i, int eol, void *userdata) {
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
|
|
|
if (eol < 0) {
|
|
|
|
if (pa_context_errno(context) == PA_ERR_NOENTITY)
|
|
|
|
return;
|
|
|
|
|
|
|
|
show_error(_("Card callback failure"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (eol > 0) {
|
|
|
|
dec_outstanding(w);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updateCard(*i);
|
|
|
|
}
|
|
|
|
|
2006-04-21 19:24:32 +00:00
|
|
|
void sink_cb(pa_context *, const pa_sink_info *i, int eol, void *userdata) {
|
2006-04-21 14:51:33 +00:00
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
if (eol < 0) {
|
2008-08-19 15:36:18 +00:00
|
|
|
if (pa_context_errno(context) == PA_ERR_NOENTITY)
|
|
|
|
return;
|
|
|
|
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("Sink callback failure"));
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
2006-08-07 13:50:02 +00:00
|
|
|
}
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
if (eol > 0) {
|
|
|
|
dec_outstanding(w);
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updateSink(*i);
|
|
|
|
}
|
|
|
|
|
2006-04-21 19:24:32 +00:00
|
|
|
void source_cb(pa_context *, const pa_source_info *i, int eol, void *userdata) {
|
2006-04-21 14:51:33 +00:00
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
if (eol < 0) {
|
2008-08-19 15:36:18 +00:00
|
|
|
if (pa_context_errno(context) == PA_ERR_NOENTITY)
|
|
|
|
return;
|
|
|
|
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("Source callback failure"));
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
2006-08-07 13:50:02 +00:00
|
|
|
}
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
if (eol > 0) {
|
|
|
|
dec_outstanding(w);
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updateSource(*i);
|
|
|
|
}
|
|
|
|
|
2006-04-21 19:24:32 +00:00
|
|
|
void sink_input_cb(pa_context *, const pa_sink_input_info *i, int eol, void *userdata) {
|
2006-04-21 14:51:33 +00:00
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
if (eol < 0) {
|
2008-08-19 15:36:18 +00:00
|
|
|
if (pa_context_errno(context) == PA_ERR_NOENTITY)
|
|
|
|
return;
|
|
|
|
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("Sink input callback failure"));
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
2006-08-07 13:50:02 +00:00
|
|
|
}
|
2006-04-21 14:51:33 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
if (eol > 0) {
|
|
|
|
dec_outstanding(w);
|
2006-04-21 14:51:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updateSinkInput(*i);
|
|
|
|
}
|
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
void source_output_cb(pa_context *, const pa_source_output_info *i, int eol, void *userdata) {
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
if (eol < 0) {
|
2008-08-19 15:36:18 +00:00
|
|
|
if (pa_context_errno(context) == PA_ERR_NOENTITY)
|
|
|
|
return;
|
|
|
|
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("Source output callback failure"));
|
2008-08-05 17:05:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (eol > 0) {
|
2008-06-17 20:24:54 +00:00
|
|
|
|
|
|
|
if (n_outstanding > 0) {
|
|
|
|
/* At this point all notebook pages have been populated, so
|
|
|
|
* let's open one that isn't empty */
|
|
|
|
|
|
|
|
if (w->sinkInputWidgets.size() > 0)
|
|
|
|
w->notebook->set_current_page(0);
|
|
|
|
else if (w->sourceOutputWidgets.size() > 0)
|
|
|
|
w->notebook->set_current_page(1);
|
|
|
|
else if (w->sourceWidgets.size() > 0 && w->sinkWidgets.size() == 0)
|
|
|
|
w->notebook->set_current_page(3);
|
|
|
|
else
|
|
|
|
w->notebook->set_current_page(2);
|
|
|
|
}
|
2008-05-15 23:07:40 +00:00
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
dec_outstanding(w);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updateSourceOutput(*i);
|
|
|
|
}
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
void client_cb(pa_context *, const pa_client_info *i, int eol, void *userdata) {
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
if (eol < 0) {
|
2008-08-19 15:36:18 +00:00
|
|
|
if (pa_context_errno(context) == PA_ERR_NOENTITY)
|
|
|
|
return;
|
|
|
|
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("Client callback failure"));
|
2006-05-20 17:28:50 +00:00
|
|
|
return;
|
2006-08-07 13:50:02 +00:00
|
|
|
}
|
2006-05-20 17:28:50 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
if (eol > 0) {
|
|
|
|
dec_outstanding(w);
|
2006-05-20 17:28:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updateClient(*i);
|
|
|
|
}
|
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
void server_info_cb(pa_context *, const pa_server_info *i, void *userdata) {
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
|
|
|
if (!i) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("Server info callback failure"));
|
2007-09-02 23:38:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updateServer(*i);
|
|
|
|
dec_outstanding(w);
|
|
|
|
}
|
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
void ext_stream_restore_read_cb(
|
|
|
|
pa_context *c,
|
|
|
|
const pa_ext_stream_restore_info *i,
|
|
|
|
int eol,
|
|
|
|
void *userdata) {
|
|
|
|
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
|
|
|
if (eol < 0) {
|
2008-08-05 18:45:24 +00:00
|
|
|
g_debug(_("Failed to initialized stream_restore extension: %s"), pa_strerror(pa_context_errno(context)));
|
2008-08-05 17:05:33 +00:00
|
|
|
w->deleteEventRoleWidget();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->createEventRoleWidget();
|
|
|
|
|
|
|
|
if (eol > 0) {
|
|
|
|
dec_outstanding(w);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
w->updateRole(*i);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ext_stream_restore_subscribe_cb(pa_context *c, void *userdata) {
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
pa_operation *o;
|
|
|
|
|
|
|
|
if (!(o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, w))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_ext_stream_restore_read() failed"));
|
2008-08-05 17:05:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata) {
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
|
|
|
|
|
|
|
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
|
|
|
|
case PA_SUBSCRIPTION_EVENT_SINK:
|
|
|
|
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
|
|
|
|
w->removeSink(index);
|
|
|
|
else {
|
|
|
|
pa_operation *o;
|
|
|
|
if (!(o = pa_context_get_sink_info_by_index(c, index, sink_cb, w))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_get_sink_info_by_index() failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
break;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
case PA_SUBSCRIPTION_EVENT_SOURCE:
|
|
|
|
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
|
|
|
|
w->removeSource(index);
|
|
|
|
else {
|
|
|
|
pa_operation *o;
|
|
|
|
if (!(o = pa_context_get_source_info_by_index(c, index, source_cb, w))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_get_source_info_by_index() failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
break;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
|
|
|
|
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
|
|
|
|
w->removeSinkInput(index);
|
|
|
|
else {
|
|
|
|
pa_operation *o;
|
|
|
|
if (!(o = pa_context_get_sink_input_info(c, index, sink_input_cb, w))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_get_sink_input_info() failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
break;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
|
|
|
|
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
|
|
|
|
w->removeSourceOutput(index);
|
|
|
|
else {
|
|
|
|
pa_operation *o;
|
|
|
|
if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, w))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_get_sink_input_info() failed"));
|
2008-04-20 22:13:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
case PA_SUBSCRIPTION_EVENT_CLIENT:
|
|
|
|
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
|
|
|
|
w->removeClient(index);
|
|
|
|
else {
|
|
|
|
pa_operation *o;
|
|
|
|
if (!(o = pa_context_get_client_info(c, index, client_cb, w))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_get_client_info() failed"));
|
2006-05-20 17:28:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
break;
|
2007-09-02 23:38:55 +00:00
|
|
|
|
|
|
|
case PA_SUBSCRIPTION_EVENT_SERVER: {
|
2009-02-28 17:11:41 +00:00
|
|
|
pa_operation *o;
|
|
|
|
if (!(o = pa_context_get_server_info(c, server_info_cb, w))) {
|
|
|
|
show_error(_("pa_context_get_server_info() failed"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
2007-09-02 23:38:55 +00:00
|
|
|
}
|
2009-02-28 17:11:41 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PA_SUBSCRIPTION_EVENT_CARD:
|
|
|
|
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
|
|
|
|
w->removeCard(index);
|
|
|
|
else {
|
|
|
|
pa_operation *o;
|
|
|
|
if (!(o = pa_context_get_card_info_by_index(c, index, card_cb, w))) {
|
|
|
|
show_error(_("pa_context_get_card_info_by_index() failed"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void context_state_callback(pa_context *c, void *userdata) {
|
|
|
|
MainWindow *w = static_cast<MainWindow*>(userdata);
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
g_assert(c);
|
|
|
|
|
|
|
|
switch (pa_context_get_state(c)) {
|
|
|
|
case PA_CONTEXT_UNCONNECTED:
|
|
|
|
case PA_CONTEXT_CONNECTING:
|
|
|
|
case PA_CONTEXT_AUTHORIZING:
|
|
|
|
case PA_CONTEXT_SETTING_NAME:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PA_CONTEXT_READY: {
|
|
|
|
pa_operation *o;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_context_set_subscribe_callback(c, subscribe_cb, w);
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)
|
|
|
|
(PA_SUBSCRIPTION_MASK_SINK|
|
|
|
|
PA_SUBSCRIPTION_MASK_SOURCE|
|
|
|
|
PA_SUBSCRIPTION_MASK_SINK_INPUT|
|
2008-04-20 22:13:20 +00:00
|
|
|
PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
|
2007-09-02 23:38:55 +00:00
|
|
|
PA_SUBSCRIPTION_MASK_CLIENT|
|
2009-02-28 17:11:41 +00:00
|
|
|
PA_SUBSCRIPTION_MASK_SERVER|
|
|
|
|
PA_SUBSCRIPTION_MASK_CARD), NULL, NULL))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_subscribe() failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
2007-09-02 23:38:55 +00:00
|
|
|
if (!(o = pa_context_get_server_info(c, server_info_cb, w))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_get_server_info() failed"));
|
2007-09-02 23:38:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-05-20 17:28:50 +00:00
|
|
|
if (!(o = pa_context_get_client_info_list(c, client_cb, w))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_client_info_list() failed"));
|
2006-05-20 17:28:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
2009-02-28 17:11:41 +00:00
|
|
|
if (!(o = pa_context_get_card_info_list(c, card_cb, w))) {
|
|
|
|
show_error(_("pa_context_get_card_info_list() failed"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
if (!(o = pa_context_get_sink_info_list(c, sink_cb, w))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_get_sink_info_list() failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
|
|
|
if (!(o = pa_context_get_source_info_list(c, source_cb, w))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_get_source_info_list() failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
|
|
|
if (!(o = pa_context_get_sink_input_info_list(c, sink_input_cb, w))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_get_sink_input_info_list() failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
2006-08-07 13:50:02 +00:00
|
|
|
|
2008-04-20 22:13:20 +00:00
|
|
|
if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, w))) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("pa_context_get_source_output_info_list() failed"));
|
2008-04-20 22:13:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
|
|
|
n_outstanding = 6;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2008-08-05 17:05:33 +00:00
|
|
|
/* This call is not always supported */
|
|
|
|
if ((o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, w))) {
|
|
|
|
pa_operation_unref(o);
|
|
|
|
n_outstanding++;
|
|
|
|
|
|
|
|
pa_ext_stream_restore_set_subscribe_cb(c, ext_stream_restore_subscribe_cb, w);
|
|
|
|
|
|
|
|
if ((o = pa_ext_stream_restore_subscribe(c, 1, NULL, NULL)))
|
|
|
|
pa_operation_unref(o);
|
|
|
|
|
|
|
|
} else
|
2008-08-05 18:45:24 +00:00
|
|
|
g_debug(_("Failed to initialized stream_restore extension: %s"), pa_strerror(pa_context_errno(context)));
|
2008-08-05 17:05:33 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
case PA_CONTEXT_FAILED:
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("Connection failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
return;
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
case PA_CONTEXT_TERMINATED:
|
|
|
|
default:
|
2006-04-21 19:24:32 +00:00
|
|
|
Gtk::Main::quit();
|
|
|
|
return;
|
2006-04-21 14:51:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
2008-08-05 18:45:24 +00:00
|
|
|
|
|
|
|
/* Initialize the i18n stuff */
|
|
|
|
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
|
|
|
|
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
|
|
|
|
textdomain(GETTEXT_PACKAGE);
|
|
|
|
|
2006-04-22 18:21:12 +00:00
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
Gtk::Main kit(argc, argv);
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2008-08-19 15:36:18 +00:00
|
|
|
ca_context_set_driver(ca_gtk_context_get(), "pulse");
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
Gtk::Window* mainWindow = MainWindow::create();
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_glib_mainloop *m = pa_glib_mainloop_new(g_main_context_default());
|
|
|
|
g_assert(m);
|
|
|
|
pa_mainloop_api *api = pa_glib_mainloop_get_api(m);
|
|
|
|
g_assert(api);
|
2008-06-12 11:26:06 +00:00
|
|
|
|
|
|
|
pa_proplist *proplist = pa_proplist_new();
|
2008-08-05 18:45:24 +00:00
|
|
|
pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, _("PulseAudio Volume Control"));
|
2008-06-12 11:26:06 +00:00
|
|
|
pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "org.PulseAudio.pavucontrol");
|
|
|
|
pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "audio-card");
|
|
|
|
pa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
|
|
|
|
|
|
|
|
context = pa_context_new_with_proplist(api, NULL, proplist);
|
2006-04-21 14:51:33 +00:00
|
|
|
g_assert(context);
|
|
|
|
|
2008-06-12 11:26:06 +00:00
|
|
|
pa_proplist_free(proplist);
|
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_context_set_state_callback(context, context_state_callback, mainWindow);
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 19:24:32 +00:00
|
|
|
if (pa_context_connect(context, NULL, (pa_context_flags_t) 0, NULL) < 0) {
|
2008-08-05 18:45:24 +00:00
|
|
|
show_error(_("Connection failed"));
|
2006-04-21 19:24:32 +00:00
|
|
|
goto finish;
|
|
|
|
}
|
2007-11-20 02:14:59 +00:00
|
|
|
|
2006-04-21 14:51:33 +00:00
|
|
|
Gtk::Main::run(*mainWindow);
|
|
|
|
delete mainWindow;
|
|
|
|
|
2006-04-21 19:24:32 +00:00
|
|
|
finish:
|
2006-04-21 14:51:33 +00:00
|
|
|
pa_context_unref(context);
|
|
|
|
pa_glib_mainloop_free(m);
|
|
|
|
}
|