diff --git a/daemon/src/audio/alsa/alsalayer.cpp b/daemon/src/audio/alsa/alsalayer.cpp index f8d7b915bf5ee2034e1e9b7ee173c9cfc9ef821e..29cf9a088c4d6ff91cc0ac8bc14a0cc2c982e9a2 100644 --- a/daemon/src/audio/alsa/alsalayer.cpp +++ b/daemon/src/audio/alsa/alsalayer.cpp @@ -87,6 +87,8 @@ AlsaLayer::AlsaLayer() , is_capture_open_(false) , audioThread_(NULL) { + setCaptureGain(Manager::instance().audioPreference.getVolumemic()); + setPlaybackGain(Manager::instance().audioPreference.getVolumespkr()); } // Destructor @@ -538,15 +540,6 @@ AlsaLayer::getAudioDeviceIndex(const std::string &description) const return 0; } -namespace { -void adjustVolume(SFLDataFormat *src , int samples, int volumePercentage) -{ - if (volumePercentage != 100) - for (int i = 0 ; i < samples; i++) - src[i] = src[i] * volumePercentage * 0.01; -} -} - void AlsaLayer::capture() { unsigned int mainBufferSampleRate = Manager::instance().getMainBuffer()->getInternalSamplingRate(); @@ -573,7 +566,7 @@ void AlsaLayer::capture() goto end; } - adjustVolume(in, toGetSamples, Manager::instance().getSpkrVolume()); + AudioLayer::applyGain(in, toGetSamples, getCaptureGain()); if (resample) { int outSamples = toGetSamples * ((double) audioSampleRate_ / mainBufferSampleRate); @@ -594,7 +587,6 @@ end: void AlsaLayer::playback(int maxSamples) { - unsigned short spkrVolume = Manager::instance().getSpkrVolume(); unsigned int mainBufferSampleRate = Manager::instance().getMainBuffer()->getInternalSamplingRate(); bool resample = audioSampleRate_ != mainBufferSampleRate; @@ -608,10 +600,12 @@ void AlsaLayer::playback(int maxSamples) SFLDataFormat *out = (SFLDataFormat *) malloc(toPut); - if (tone) - tone->getNext(out, maxSamples, spkrVolume); - else if (file_tone && !ringtoneHandle_) - file_tone->getNext(out, maxSamples, spkrVolume); + if (tone) { + tone->getNext(out, maxSamples, getPlaybackGain()); + } + else if (file_tone && !ringtoneHandle_) { + file_tone->getNext(out, maxSamples, getPlaybackGain()); + } else memset(out, 0, toPut); @@ -636,7 +630,7 @@ void AlsaLayer::playback(int maxSamples) SFLDataFormat *out = (SFLDataFormat*) malloc(toGet); Manager::instance().getMainBuffer()->getData(out, toGet); - adjustVolume(out, toGet / sizeof(SFLDataFormat), spkrVolume); + AudioLayer::applyGain(out, toGet / sizeof(SFLDataFormat), getPlaybackGain()); if (resample) { int inSamples = toGet / sizeof(SFLDataFormat); @@ -659,8 +653,6 @@ void AlsaLayer::audioCallback() notifyincomingCall(); - unsigned short spkrVolume = Manager::instance().getSpkrVolume(); - snd_pcm_wait(playbackHandle_, 20); int playbackAvailSmpl = snd_pcm_avail_update(playbackHandle_); @@ -675,7 +667,7 @@ void AlsaLayer::audioCallback() SFLDataFormat *out = (SFLDataFormat*) malloc(toGet); urgentRingBuffer_.Get(out, toGet); - adjustVolume(out, toGet / sizeof(SFLDataFormat), spkrVolume); + AudioLayer::applyGain(out, toGet / sizeof(SFLDataFormat), getPlaybackGain()); write(out, toGet, playbackHandle_); free(out); @@ -693,8 +685,10 @@ void AlsaLayer::audioCallback() SFLDataFormat *out = (SFLDataFormat *) malloc(ringtoneAvailBytes); - if (file_tone) - file_tone->getNext(out, ringtoneAvailSmpl, spkrVolume); + if (file_tone) { + DEBUG("playback gain %d", getPlaybackGain()); + file_tone->getNext(out, ringtoneAvailSmpl, getPlaybackGain()); + } else memset(out, 0, ringtoneAvailBytes); diff --git a/daemon/src/audio/alsa/alsalayer.h b/daemon/src/audio/alsa/alsalayer.h index f15d0d333a5a3d9592ff6397df5f1ee5d30f22bf..bcd5caa9ac786c6b57970938468fe297a7d70584 100644 --- a/daemon/src/audio/alsa/alsalayer.h +++ b/daemon/src/audio/alsa/alsalayer.h @@ -115,7 +115,7 @@ class AlsaLayer : public AudioLayer { * @return int Its index */ int getAudioDeviceIndex(const std::string &description) const; - + void playback(int maxSamples); void capture(); diff --git a/daemon/src/audio/audiolayer.cpp b/daemon/src/audio/audiolayer.cpp index 285f31978bc3864374dca74f8a7940b8b462d717..8e3363611ed6a915e36e386e6165e87237d1c54a 100644 --- a/daemon/src/audio/audiolayer.cpp +++ b/daemon/src/audio/audiolayer.cpp @@ -34,6 +34,9 @@ #include "audio/dcblocker.h" #include "manager.h" +unsigned int AudioLayer::captureGain_ = 100; +unsigned int AudioLayer::playbackGain_ = 100; + AudioLayer::AudioLayer() : isStarted_(false) , urgentRingBuffer_(SIZEBUF, Call::DEFAULT_ID) @@ -50,7 +53,10 @@ AudioLayer::AudioLayer() AudioLayer::~AudioLayer() { - delete converter_; + if(converter_) { + delete converter_; + converter_ = NULL; + } } void AudioLayer::flushMain() @@ -72,6 +78,13 @@ void AudioLayer::putUrgent(void* buffer, int toCopy) urgentRingBuffer_.Put(buffer, toCopy); } +void AudioLayer::applyGain(SFLDataFormat *src , int samples, int gain) +{ + if (gain != 100) + for (int i = 0 ; i < samples; i++) + src[i] = src[i] * gain* 0.01; +} + // Notify (with a beep) an incoming call when there is already a call in progress void AudioLayer::notifyincomingCall() { diff --git a/daemon/src/audio/audiolayer.h b/daemon/src/audio/audiolayer.h index 573002e9b5e09e571d36b5b929cb77948c9a4794..f095bd1180a4e1c2e745ae3b5c483e92ef37d0b1 100644 --- a/daemon/src/audio/audiolayer.h +++ b/daemon/src/audio/audiolayer.h @@ -105,7 +105,52 @@ class AudioLayer { */ void flushUrgent(); + /** + * Apply gain to audio frame + */ + static void applyGain(SFLDataFormat *src , int samples, int gain); + + /** + * Convert audio amplitude value from linear value to dB + */ + static double amplitudeLinearToDB(double value) { + return 20.0 * log10(value); + } + + /** + * Convert audio amplitude from dB to Linear value + */ + static double ampluitudeDBToLinear(double value) { + return pow(10.0, value / 20.0); + } + + /** + * Set capture stream gain (microphone) + */ + void setCaptureGain(unsigned int gain) { + captureGain_ = gain; + } + + /** + * Set capture stream gain (microphone) + */ + unsigned int getCaptureGain(void) { + return captureGain_; + } + /** + * Set playback stream gain (speaker) + */ + void setPlaybackGain(unsigned int gain) { + playbackGain_ = gain; + } + + /** + * Get playback stream gain (speaker) + */ + unsigned int getPlaybackGain(void) { + return playbackGain_; + } /** * Get the sample rate of the audio layer @@ -128,6 +173,16 @@ class AudioLayer { */ void notifyincomingCall(); + /** + * Gain applied to mic signal + */ + static unsigned int captureGain_; + + /** + * Gain applied to playback signal + */ + static unsigned int playbackGain_; + protected: /** @@ -150,8 +205,20 @@ class AudioLayer { * Lock for the entire audio layer */ ost::Mutex mutex_; + + /** + * Remove audio offset that can be introduced by certain cheap audio device + */ DcBlocker dcblocker_; + + /** + * Configuration file for this + */ AudioPreference &audioPref; + + /** + * Manage sampling rate conversion + */ SamplerateConverter *converter_; private: diff --git a/daemon/src/audio/pulseaudio/pulselayer.cpp b/daemon/src/audio/pulseaudio/pulselayer.cpp index 42e350728f5c134ca6dce0c56dd7ca7bfc1b5e29..d2536bfa124900e06ba531b7d102a8e826b3cc36 100644 --- a/daemon/src/audio/pulseaudio/pulselayer.cpp +++ b/daemon/src/audio/pulseaudio/pulselayer.cpp @@ -37,6 +37,7 @@ #include "managerimpl.h" namespace { + void playback_callback(pa_stream* s, size_t bytes, void* userdata) { assert(s && bytes); @@ -307,6 +308,7 @@ void PulseLayer::writeToSpeaker() pa_stream *s = playback_->pulseStream(); + // available bytes to be written in pulseaudio internal buffer int writable = pa_stream_writable_size(s); @@ -329,6 +331,7 @@ void PulseLayer::writeToSpeaker() if (urgentBytes) { pa_stream_begin_write(s, &data, &urgentBytes); urgentRingBuffer_.Get(data, urgentBytes); + applyGain(static_cast<SFLDataFormat *>(data), urgentBytes / sizeof(SFLDataFormat), getPlaybackGain()); pa_stream_write(s, data, urgentBytes, NULL, 0, PA_SEEK_RELATIVE); // Consume the regular one as well (same amount of bytes) Manager::instance().getMainBuffer()->discard(urgentBytes); @@ -341,6 +344,7 @@ void PulseLayer::writeToSpeaker() if (playback_->isReady()) { pa_stream_begin_write(s, &data, &bytes); toneToPlay->getNext((SFLDataFormat*)data, bytes / sizeof(SFLDataFormat), 100); + applyGain(static_cast<SFLDataFormat *>(data), bytes / sizeof(SFLDataFormat), getPlaybackGain()); pa_stream_write(s, data, bytes, NULL, 0, PA_SEEK_RELATIVE); } @@ -386,10 +390,13 @@ void PulseLayer::writeToSpeaker() if (resample) { SFLDataFormat* rsmpl_out = (SFLDataFormat*) pa_xmalloc(outBytes); converter_->resample((SFLDataFormat*)data, rsmpl_out, mainBufferSampleRate, audioSampleRate_, inSamples); + applyGain(rsmpl_out, outBytes / sizeof(SFLDataFormat), getPlaybackGain()); pa_stream_write(s, rsmpl_out, outBytes, NULL, 0, PA_SEEK_RELATIVE); pa_xfree(rsmpl_out); - } else + } else { + applyGain(static_cast<SFLDataFormat *>(data), inBytes / sizeof(SFLDataFormat), getPlaybackGain()); pa_stream_write(s, data, inBytes, NULL, 0, PA_SEEK_RELATIVE); + } } void PulseLayer::readFromMic() @@ -425,6 +432,7 @@ void PulseLayer::readFromMic() converter_->resample((SFLDataFormat*)data, mic_buffer_, mainBufferSampleRate, audioSampleRate_, samples); dcblocker_.process(mic_buffer_, resample ? mic_buffer_ : (SFLDataFormat*)data, samples); + applyGain(mic_buffer_, bytes / sizeof(SFLDataFormat), getCaptureGain()); Manager::instance().getMainBuffer()->putData(mic_buffer_, bytes); if (pa_stream_drop(record_->pulseStream()) < 0) @@ -453,8 +461,10 @@ void PulseLayer::ringtoneToSpeaker() pa_stream_begin_write(s, &data, &bytes); AudioLoop *fileToPlay = Manager::instance().getTelephoneFile(); - if (fileToPlay) + if (fileToPlay) { fileToPlay->getNext((SFLDataFormat *) data, bytes / sizeof(SFLDataFormat), 100); + applyGain(static_cast<SFLDataFormat *>(data), bytes / sizeof(SFLDataFormat), getPlaybackGain()); + } else memset(data, 0, bytes); diff --git a/daemon/src/audio/pulseaudio/pulselayer.h b/daemon/src/audio/pulseaudio/pulselayer.h index a8640f42cf35856552f3b174738314c5dac4eca1..f94cbdced5f76277ece0975be28ad3aae3b97729 100644 --- a/daemon/src/audio/pulseaudio/pulselayer.h +++ b/daemon/src/audio/pulseaudio/pulselayer.h @@ -68,6 +68,7 @@ class PulseLayer : public AudioLayer { virtual void stopStream(); + private: static void context_state_callback(pa_context* c, void* user_data); static void context_changed_callback(pa_context* c, diff --git a/daemon/src/dbus/callmanager.cpp b/daemon/src/dbus/callmanager.cpp index 809e0628edde25ef9b6c42703c929406caad15c7..e9f042fedb0d00bcf2d6d1dead0826193834d261 100644 --- a/daemon/src/dbus/callmanager.cpp +++ b/daemon/src/dbus/callmanager.cpp @@ -35,6 +35,7 @@ #include "sip/sipcall.h" #include "sip/sipvoiplink.h" +#include "audio/audiolayer.h" #include "audio/audiortp/audio_rtp_factory.h" #include "audio/audiortp/audio_zrtp_session.h" @@ -128,10 +129,20 @@ void CallManager::attendedTransfer(const std::string& transferID, const std::str void CallManager::setVolume(const std::string& device, const double& value) { - if (device == "speaker") - Manager::instance().setSpkrVolume((int)(value * 100.0)); - else if (device == "mic") - Manager::instance().setMicVolume((int)(value * 100.0)); + AudioLayer *audiolayer = Manager::instance().getAudioDriver(); + + if(!audiolayer) { + ERROR("CallManager: Audio layer not valid while updating volume"); + return; + } + + DEBUG("DBUS set volume for %s: %f", device.c_str(), value); + + if (device == "speaker") { + audiolayer->setPlaybackGain((int)(value * 100.0)); + } else if (device == "mic") { + audiolayer->setCaptureGain((int)(value * 100.0)); + } volumeChanged(device, value); } @@ -139,10 +150,17 @@ void CallManager::setVolume(const std::string& device, const double& value) double CallManager::getVolume(const std::string& device) { + AudioLayer *audiolayer = Manager::instance().getAudioDriver(); + + if(!audiolayer) { + ERROR("CallManager: Audio layer not valid while updating volume"); + return 0.0; + } + if (device == "speaker") - return Manager::instance().getSpkrVolume() / 100.0; + return audiolayer->getPlaybackGain() / 100.0; else if (device == "mic") - return Manager::instance().getMicVolume() / 100.0; + return audiolayer->getCaptureGain() / 100.0; return 0; } diff --git a/daemon/src/managerimpl.cpp b/daemon/src/managerimpl.cpp index 1f9f3283bbb85cc90c4a92b7195b5d28e6141c67..8d8c06ddcb21adbafb536b2d79212771d49402e3 100644 --- a/daemon/src/managerimpl.cpp +++ b/daemon/src/managerimpl.cpp @@ -73,8 +73,7 @@ ManagerImpl::ManagerImpl() : hookPreference(), audioPreference(), shortcutPreferences(), hasTriedToRegister_(false), audioCodecFactory(), dbus_(), config_(), currentCallId_(), currentCallMutex_(), audiodriver_(0), dtmfKey_(0), toneMutex_(), - telephoneTone_(0), audiofile_(0), speakerVolume_(0), micVolume_(0), - audioLayerMutex_(), waitingCall_(), waitingCallMutex_(), + telephoneTone_(0), audiofile_(0), audioLayerMutex_(), waitingCall_(), waitingCallMutex_(), nbIncomingWaitingCall_(0), path_(), callAccountMap_(), callAccountMapMutex_(), IPToIPMap_(), accountMap_(), mainBuffer_(), conferenceMap_(), history_(new History), @@ -118,7 +117,6 @@ void ManagerImpl::init(std::string config_file) loadAccountMap(parser); delete parser; - initVolume(); initAudioDriver(); { @@ -1248,8 +1246,11 @@ void ManagerImpl::removeStream(const std::string& call_id) void ManagerImpl::saveConfig() { DEBUG("Manager: Saving Configuration to XDG directory %s", path_.c_str()); - audioPreference.setVolumemic(getMicVolume()); - audioPreference.setVolumespkr(getSpkrVolume()); + AudioLayer *audiolayer = getAudioDriver(); + if(audiolayer != NULL) { + audioPreference.setVolumemic(audiolayer->getCaptureGain()); + audioPreference.setVolumespkr(audiolayer->getPlaybackGain()); + } try { Conf::YamlEmitter emitter(path_.c_str()); @@ -2320,26 +2321,6 @@ void ManagerImpl::audioSamplingRateChanged(int samplerate) audiodriver_->startStream(); } -/** - * Init the volume for speakers/micro from 0 to 100 value - * Initialization: Main Thread - */ -void ManagerImpl::initVolume() -{ - setSpkrVolume(audioPreference.getVolumespkr()); - setMicVolume(audioPreference.getVolumemic()); -} - -void ManagerImpl::setSpkrVolume(unsigned short spkr_vol) -{ - speakerVolume_ = spkr_vol; -} - -void ManagerImpl::setMicVolume(unsigned short mic_vol) -{ - micVolume_ = mic_vol; -} - int ManagerImpl::getLocalIp2IpPort() const { return preferences.getPortNum(); diff --git a/daemon/src/managerimpl.h b/daemon/src/managerimpl.h index 2c9dc2ebf372b05fee0d59124b4058d25b796a7e..a9ebb8f45a13a002c7162b8faa7c6b22694b680e 100644 --- a/daemon/src/managerimpl.h +++ b/daemon/src/managerimpl.h @@ -849,42 +849,6 @@ class ManagerImpl { */ bool incomingCallWaiting() const; - /* - * Inline functions to manage speaker volume control - * Read by main thread and AudioLayer thread - * Write by main thread only - * @return unsigned short The volume value - */ - unsigned short getSpkrVolume() const { - return speakerVolume_; - } - - /* - * Inline functions to manage speaker volume control - * Read by main thread and AudioLayer thread - * Write by main thread only - * @param spkr_vol The volume value - */ - void setSpkrVolume(unsigned short spkr_vol); - - /* - * Inline functions to manage mic volume control - * Read by main thread and AudioLayer thread - * Write by main thread only - * @return unsigned short The volume value - */ - unsigned short getMicVolume() const { - return micVolume_; - } - - /* - * Inline functions to manage mic volume control - * Read by main thread and AudioLayer thread - * Write by main thread only - * @param mic_vol The volume value - */ - void setMicVolume(unsigned short mic_vol); - /** * Return a new random callid that is not present in the list * @return std::string A brand new callid @@ -949,11 +913,6 @@ class ManagerImpl { */ void initZeroconf(); - /* - * Init the volume for speakers/micro from 0 to 100 value - */ - void initVolume(); - /** * Switch of current call id * @param id The new callid @@ -992,8 +951,8 @@ class ManagerImpl { AudioFile *audiofile_; // To handle volume control - short speakerVolume_; - short micVolume_; + // short speakerVolume_; + // short micVolume_; // End of sound variable /** diff --git a/daemon/src/sip/sipvoiplink.cpp b/daemon/src/sip/sipvoiplink.cpp index 9b0d18be145d1c86ef5e7e97c07976a67919c153..3eefdad8e1914429a78400014007fb4c66292f69 100644 --- a/daemon/src/sip/sipvoiplink.cpp +++ b/daemon/src/sip/sipvoiplink.cpp @@ -1288,13 +1288,34 @@ pj_status_t SIPVoIPLink::stunServerResolve(SIPAccount *account) return status; } +#define DEFAULT_TRANSPORT_ATTEMPT 5 void SIPVoIPLink::createDefaultSipUdpTransport() { + pjsip_transport *transport = NULL; + pj_uint16_t port = 0; + int counter = 0; + SIPAccount *account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(IP2IP_PROFILE)); - createUdpTransport(account); - assert(account->transport_); + while((transport == NULL) and (counter < DEFAULT_TRANSPORT_ATTEMPT)) { + // if default udp transport fails to init on 5060, try other ports with 2 step size increment (i.e. 5062, 5064, ...) + port = account->getLocalPort() + (counter * 2); + transport = createUdpTransport(account->getLocalInterface(), port); + counter++; + } + + if(transport == NULL) { + ERROR("UserAgent: Create UDP transport "); + return; + } + + DEBUG("UserAgent: Created defautl sip transport on %d", port); + + // set transport for this account + account->transport_ = transport; + + // set local udp transport localUDPTransport_ = account->transport_; } @@ -1340,6 +1361,11 @@ void SIPVoIPLink::createSipTransport(SIPAccount *account) { shutdownSipTransport(account); + if(account == NULL) { + ERROR("Acccount is NULL while creating sip transport"); + return; + } + if (account->isTlsEnabled()) { std::string remoteSipUri(account->getServerUri()); static const char SIPS_PREFIX[] = "<sips:"; @@ -1350,8 +1376,10 @@ void SIPVoIPLink::createSipTransport(SIPAccount *account) createTlsTransport(account, remoteAddr); } else if (account->isStunEnabled()) createStunTransport(account); - else - createUdpTransport(account); + else { + pjsip_transport *transport = createUdpTransport(account->getLocalInterface(), account->getLocalPort()); + account->transport_ = transport; + } if (!account->transport_) { // Could not create new transport, this transport may already exists @@ -1366,47 +1394,70 @@ void SIPVoIPLink::createSipTransport(SIPAccount *account) } } -void SIPVoIPLink::createUdpTransport(SIPAccount *account) +#define DEFAULT_INTERFACE "default" + +pjsip_transport *SIPVoIPLink::createUdpTransport(std::string interface, unsigned int port) { + pjsip_transport *transport = NULL; std::string listeningAddress; - pj_uint16_t listeningPort = account->getLocalPort(); + pj_uint16_t listeningPort = (pj_uint16_t)port; + pj_status_t status = PJ_SUCCESS; + // init socket to bind this transport to pj_sockaddr_in bound_addr; pj_bzero(&bound_addr, sizeof(bound_addr)); bound_addr.sin_port = pj_htons(listeningPort); bound_addr.sin_family = PJ_AF_INET; - if (account->getLocalInterface() == "default") { + // determine the ip address for this transport + if (interface == DEFAULT_INTERFACE) { listeningAddress = getSIPLocalIP(); bound_addr.sin_addr.s_addr = pj_htonl(PJ_INADDR_ANY); } else { - listeningAddress = getInterfaceAddrFromName(account->getLocalInterface()); + listeningAddress = getInterfaceAddrFromName(interface); bound_addr.sin_addr = pj_inet_addr2(listeningAddress.c_str()); } + /* if (!account->getPublishedSameasLocal()) { listeningAddress = account->getPublishedAddress(); listeningPort = account->getPublishedPort(); } + */ // We must specify this here to avoid the IP2IP_PROFILE creating a // transport with the name 0.0.0.0 appearing in the via header - if (account->getAccountID() == IP2IP_PROFILE) - listeningAddress = getSIPLocalIP(); + // if (account->getAccountID() == IP2IP_PROFILE) + // listeningAddress = getSIPLocalIP(); - if (listeningAddress.empty() or listeningPort == 0) - return; + if (listeningAddress.empty()) { + ERROR("SIP: Could not determine ip address for this transport"); + return NULL; + } + if(listeningPort == 0) { + ERROR("SIP: Could not determine port for this transport"); + return NULL; + } + + // The published address for this transport const pjsip_host_port a_name = { pj_str((char*) listeningAddress.c_str()), listeningPort }; - pjsip_udp_transport_start(endpt_, &bound_addr, &a_name, 1, &account->transport_); - pjsip_tpmgr_dump_transports(pjsip_endpt_get_tpmgr(endpt_)); // dump debug information to stdout + status = pjsip_udp_transport_start(endpt_, &bound_addr, &a_name, 1, &transport); + if(status != PJ_SUCCESS) { + ERROR("SIP: Could not create udp transport"); + return NULL; + } + + // dump debug information to stdout + pjsip_tpmgr_dump_transports(pjsip_endpt_get_tpmgr(endpt_)); + + transportMap_[listeningPort] = transport; - if (account->transport_) - transportMap_[account->getLocalPort()] = account->transport_; + return transport; } pjsip_tpselector *SIPVoIPLink::initTransportSelector(pjsip_transport *transport, pj_pool_t *tp_pool) const diff --git a/daemon/src/sip/sipvoiplink.h b/daemon/src/sip/sipvoiplink.h index 12d63f7263c5e042df537bcc679641a1ab1ba191..e43b91b03ddee34d09151338a3ff711a2f0c2afb 100644 --- a/daemon/src/sip/sipvoiplink.h +++ b/daemon/src/sip/sipvoiplink.h @@ -327,7 +327,7 @@ class SIPVoIPLink : public VoIPLink { * Create SIP UDP transport from account's setting * @param account The account for which a transport must be created. */ - void createUdpTransport(SIPAccount *account); + pjsip_transport *createUdpTransport(std::string interface, unsigned int port); /** * Create a TLS transport from the default TLS listener from diff --git a/gnome/src/actions.c b/gnome/src/actions.c index 17233fba91206a1e3c1b802e78915f93a834dbf7..b5bccc14235142f36586d8ee8260e6236ce14b41 100644 --- a/gnome/src/actions.c +++ b/gnome/src/actions.c @@ -62,6 +62,7 @@ #include "statusicon.h" #include "unused.h" #include "widget/imwidget.h" +#include "sliders.h" static GHashTable * ip2ip_profile; @@ -378,6 +379,11 @@ sflphone_hang_up() calltree_update_call(history_tab, selectedCall); statusbar_update_clock(""); + + // Allow screen saver to start + guint nbcall = calllist_get_size(current_calls_tab); + if(nbcall == 1) + dbus_screensaver_uninhibit(); } void @@ -385,6 +391,11 @@ sflphone_pick_up() { callable_obj_t *selectedCall = calltab_get_selected_call(active_calltree_tab); + // Disable screensaver if the list is empty call + guint nbcall = calllist_get_size(current_calls_tab); + if(nbcall == 0) + dbus_screensaver_inhibit(); + if (!selectedCall) { sflphone_new_call(); return; @@ -627,6 +638,11 @@ process_dialing(callable_obj_t *c, guint keyval, gchar *key) callable_obj_t * sflphone_new_call() { + // Disable screensaver if the list is empty call + guint nbcall = calllist_get_size(current_calls_tab); + if(nbcall == 0) + dbus_screensaver_inhibit(); + callable_obj_t *current_selected_call = calltab_get_selected_call(current_calls_tab); if ((current_selected_call != NULL) && (current_selected_call->_confID == NULL)) @@ -937,6 +953,14 @@ sflphone_rec_call() update_actions(); } +void +sflphone_mute_call() +{ + DEBUG("Actions: Mute call"); + + toggle_slider_mute_microphone(); +} + void sflphone_fill_codec_list() { guint account_list_size = account_list_get_size(); diff --git a/gnome/src/actions.h b/gnome/src/actions.h index efb2fe2f93b86f3ba830fcec044dfea94339de4e..145ba402505d43bfdbfa9c2c7a424af1e0cca032 100644 --- a/gnome/src/actions.h +++ b/gnome/src/actions.h @@ -197,6 +197,8 @@ void sflphone_record (callable_obj_t *c); void sflphone_rec_call (void); +void sflphone_mute_call (void); + void status_bar_display_account (); void sflphone_fill_history (void); diff --git a/gnome/src/config/audioconf.c b/gnome/src/config/audioconf.c index d449d3e51a9c91659e18d85a08d2dea9663e18d1..c7dfe388ea0d7c616a0f1a0cd116666765380016 100644 --- a/gnome/src/config/audioconf.c +++ b/gnome/src/config/audioconf.c @@ -601,7 +601,7 @@ select_audio_manager(void) dbus_set_audio_manager(PULSEAUDIO_API_STR); gtk_container_remove(GTK_CONTAINER(alsa_conf) , alsabox); gtk_widget_hide(alsa_conf); - + if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(volumeToggle_))) { main_window_volume_controls(FALSE); eel_gconf_set_integer(SHOW_VOLUME_CONTROLS, FALSE); @@ -610,6 +610,7 @@ select_audio_manager(void) gtk_action_set_sensitive(volumeToggle_, FALSE); } + } void diff --git a/gnome/src/dbus/dbus.c b/gnome/src/dbus/dbus.c index e321a671498d0ee17cbab3f8e69df78cbace24e2..91241f55d2c2a51a3437c9d122c1d54ec21d7268 100644 --- a/gnome/src/dbus/dbus.c +++ b/gnome/src/dbus/dbus.c @@ -56,6 +56,8 @@ static DBusGProxy *call_proxy; static DBusGProxy *config_proxy; static DBusGProxy *instance_proxy; +// static DBusGProxy *session_manager_proxy; +static GDBusProxy *session_manager_proxy; /* Returns TRUE if there was an error, FALSE otherwise */ static gboolean check_error(GError *error) @@ -119,7 +121,7 @@ static void volume_changed_cb(DBusGProxy *proxy UNUSED, const gchar *device, gdouble value, void *foo UNUSED) { - set_slider(device, value); + set_slider_no_update(device, value); } static void @@ -567,14 +569,56 @@ error_alert(DBusGProxy *proxy UNUSED, int err, void *foo UNUSED) gtk_widget_show(dialog); } +static void +screensaver_dbus_proxy_new_cb (GObject * source UNUSED, GAsyncResult *result, gpointer user_data UNUSED) +{ + DEBUG("DBUS: Session manager connection callback"); + + session_manager_proxy = g_dbus_proxy_new_for_bus_finish (result, NULL); + if (session_manager_proxy == NULL) + ERROR("DBUS: Error, could not initialize gnome session manager"); +} + +#define GS_SERVICE "org.gnome.SessionManager" +#define GS_PATH "/org/gnome/SessionManager" +#define GS_INTERFACE "org.gnome.SessionManager" + +gboolean dbus_connect_session_manager(DBusGConnection *connection) +{ + + if(connection == NULL) { + ERROR("DBUS: Error connection is NULL"); + return FALSE; + } +/* + session_manager_proxy = dbus_g_proxy_new_for_name(connection, + "org.gnome.SessionManager", "/org/gnome/SessionManager/Inhibitor", + "org.gnome.SessionManager.Inhibitor"); + + if(session_manager_proxy == NULL) { + ERROR("DBUS: Error, could not create session manager proxy"); + return FALSE; + } +*/ + + g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, GS_SERVICE, GS_PATH, GS_INTERFACE, NULL, screensaver_dbus_proxy_new_cb, NULL); + + DEBUG("DBUS: Connected to gnome session manager"); + + return TRUE; +} + gboolean dbus_connect(GError **error) { g_type_init(); DBusGConnection *connection = dbus_g_bus_get(DBUS_BUS_SESSION, error); - if (connection == NULL) + if (connection == NULL) { + ERROR("DBUS: Error, could not establish connection with session bus"); return FALSE; + } /* Create a proxy object for the "bus driver" (name "org.freedesktop.DBus") */ @@ -758,6 +802,12 @@ gboolean dbus_connect(GError **error) dbus_g_proxy_set_default_timeout(config_proxy, DEFAULT_DBUS_TIMEOUT); #endif + gboolean status = dbus_connect_session_manager(connection); + if(status == FALSE) { + ERROR("DBUS: Error, could not connect to gnome session manager"); + return FALSE; + } + return TRUE; } @@ -1767,3 +1817,90 @@ dbus_send_text_message(const gchar *callID, const gchar *message) org_sflphone_SFLphone_CallManager_send_text_message(call_proxy, callID, message, &error); check_error(error); } + +static guint cookie; +#define GNOME_SESSION_NO_IDLE_FLAG 8 + +static void screensaver_inhibit_cb (GObject * source_object, GAsyncResult * res, gpointer user_data UNUSED) +{ + DEBUG("Screensaver: Inhibit callback"); + + GDBusProxy *proxy = G_DBUS_PROXY (source_object); + // ScreenSaver *screensaver = (ScreenSaver *) user_data; + GVariant *value; + GError *error = NULL; + + value = g_dbus_proxy_call_finish (proxy, res, &error); + if (!value) { + ERROR ("Screensaver: Error: inhibiting the screensaver: %s", error->message); + g_error_free (error); + return; + } + + /* save the cookie */ + if (g_variant_is_of_type (value, G_VARIANT_TYPE ("(u)"))) + g_variant_get (value, "(u)", &cookie); + else + cookie = 0; + + g_variant_unref (value); +} + +static void screensaver_uninhibit_cb (GObject * source_object, GAsyncResult * res, gpointer user_data UNUSED) +{ + DEBUG("Screensaver: Uninhibit callback"); + + GDBusProxy *proxy = G_DBUS_PROXY (source_object); + // ScreenSaver *screensaver = (ScreenSaver *) user_data; + GVariant *value; + GError *error = NULL; + + value = g_dbus_proxy_call_finish (proxy, res, &error); + if (!value) { + ERROR ("Screensaver: Error uninhibiting the screensaver: %s", error->message); + g_error_free (error); + return; + } + + /* clear the cookie */ + cookie = 0; + g_variant_unref (value); + +} + +void dbus_screensaver_inhibit(void) +{ + guint xid = 0; + + DEBUG("Screensaver: inhibit"); + + const gchar *appname = g_get_application_name(); + if(appname == NULL) { + ERROR("Screensaver: Could not retreive application name"); + return; + } + + GVariant *parameters = g_variant_new ("(susu)", appname, xid, "Phone call ongoing", GNOME_SESSION_NO_IDLE_FLAG); + if(parameters == NULL) { + ERROR("Screensaver: Could not create session manager inhibit parameters"); + return; + } + + g_dbus_proxy_call (session_manager_proxy, "Inhibit", parameters, + G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, screensaver_inhibit_cb, NULL); +} + +void +dbus_screensaver_uninhibit(void) +{ + DEBUG("Screensaver: uninhibit"); + + GVariant *parameters = g_variant_new("(u)", cookie); + if(parameters == NULL) { + ERROR("Screensaver: Could not create session manager uninhibit parameters"); + return; + }; + + g_dbus_proxy_call (session_manager_proxy, "Uninhibit", g_variant_new ("(u)", cookie), + G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, screensaver_uninhibit_cb, NULL); +} diff --git a/gnome/src/dbus/dbus.h b/gnome/src/dbus/dbus.h index 65987bbba21b47c214ed0fb1c72e6e8357addfc7..3d2053e689b3e018fd7e789cf3a3c750f4fe45b0 100644 --- a/gnome/src/dbus/dbus.h +++ b/gnome/src/dbus/dbus.h @@ -561,4 +561,14 @@ gboolean dbus_start_recorded_file_playback(const gchar *); */ void dbus_stop_recorded_file_playback(const gchar *); +/** + * Prevent Gnome Session Manager from entering in screen-saver mode + */ +void dbus_screensaver_inhibit(void); + +/** + * Allow Gnome Session Manager to enter in screen-saver mode + */ +void dbus_screensaver_uninhibit(void); + #endif diff --git a/gnome/src/mainwindow.c b/gnome/src/mainwindow.c index 176d0be62fa6095695e750c8fd9fcdb7384a8fc5..3b14c6a35538f03c1f04569fd67e6bfd01a1f2c4 100644 --- a/gnome/src/mainwindow.c +++ b/gnome/src/mainwindow.c @@ -242,15 +242,21 @@ create_main_window() gtk_box_pack_start(GTK_BOX(vbox), subvbox, FALSE /*expand*/, FALSE /*fill*/, 0 /*padding*/); + speaker_control = create_slider("speaker"); + mic_control = create_slider("mic"); + g_object_ref(speaker_control); + g_object_ref(mic_control); + if (SHOW_VOLUME) { - speaker_control = create_slider("speaker"); gtk_box_pack_end(GTK_BOX(subvbox), speaker_control, FALSE /*expand*/, - TRUE /*fill*/, 0 /*padding*/); - gtk_widget_show_all(speaker_control); - mic_control = create_slider("mic"); + TRUE /*fill*/, 0 /*padding*/); gtk_box_pack_end(GTK_BOX(subvbox), mic_control, FALSE /*expand*/, TRUE /*fill*/, 0 /*padding*/); + gtk_widget_show_all(speaker_control); gtk_widget_show_all(mic_control); + } else { + gtk_widget_hide(speaker_control); + gtk_widget_hide(mic_control); } if (eel_gconf_get_boolean(CONF_SHOW_DIALPAD)) { @@ -315,11 +321,11 @@ void main_window_volume_controls(gboolean state) { if (state) { - speaker_control = create_slider("speaker"); + // speaker_control = create_slider("speaker"); gtk_box_pack_end(GTK_BOX(subvbox), speaker_control, FALSE /*expand*/, TRUE /*fill*/, 0 /*padding*/); gtk_widget_show_all(speaker_control); - mic_control = create_slider("mic"); + // mic_control = create_slider("mic"); gtk_box_pack_end(GTK_BOX(subvbox), mic_control, FALSE /*expand*/, TRUE /*fill*/, 0 /*padding*/); gtk_widget_show_all(mic_control); diff --git a/gnome/src/sliders.c b/gnome/src/sliders.c index 38e901a2d9dcbcf838faa0133fa77b84fcc33ab9..4a2fdd805a435b34a46b6074c9d4d5ed934dd246 100644 --- a/gnome/src/sliders.c +++ b/gnome/src/sliders.c @@ -41,22 +41,25 @@ static GtkWidget * button[2]; // icons static GtkWidget * images[2][4]; + enum device_t { - SPEAKER = 0, - MIKE, + DEVICE_SPEAKER = 0, + DEVICE_MIC, DEVICE_COUNT -} ; +}; enum volume_t { MUTED = 0, VOL25, VOL50, VOL75 -} ; +}; static guint toggledConnId[2]; // The button toggled signal connection ID static guint movedConnId[2]; // The slider_moved signal connection ID +static guint device_state = DEVICE_STATE_ACTIVE; + void update_icons(int dev) { @@ -86,20 +89,20 @@ slider_moved(GtkRange* range, gchar* device) dbus_set_volume(device, slider_value); if (g_strcmp0(device, "speaker") == 0) - update_icons(SPEAKER); + update_icons(DEVICE_SPEAKER); else - update_icons(MIKE); + update_icons(DEVICE_MIC); } -static void +void mute_cb(GtkWidget *widget, gchar* device) { int dev; if (g_strcmp0(device, "speaker") == 0) - dev = SPEAKER; + dev = DEVICE_SPEAKER; else - dev = MIKE; + dev = DEVICE_MIC; if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { // Save value DEBUG("Save"); @@ -113,15 +116,46 @@ mute_cb(GtkWidget *widget, gchar* device) update_icons(dev); } -void -set_slider(const gchar * device, gdouble newval) +void set_slider_value(const gchar *device, gdouble newval) { - int dev; + int dev = 0; - if (g_strcmp0(device, "speaker") == 0) - dev = SPEAKER; - else - dev = MIKE; + if (g_strcmp0(device, "speaker") == 0) { + dev = DEVICE_SPEAKER; + DEBUG("Slider: Set value for speaker: %f\n", newval); + } + else if (g_strcmp0(device, "mic") == 0) { + dev = DEVICE_MIC; + DEBUG("Slider: Set value for mic: %f\n", newval); + } + else { + ERROR("Slider: Unknown device: %s", device); + return; + } + + gtk_range_set_value(GTK_RANGE(slider[dev]), newval); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button[dev]), (newval == 0 ? TRUE: FALSE)); + + update_icons(dev); +} + +void set_slider_no_update (const gchar * device, gdouble newval) +{ + int dev = 0; + + if (g_strcmp0(device, "speaker") == 0) { + dev = DEVICE_SPEAKER; + DEBUG("Slider: Set value no update for speaker: %f\n", newval); + } + else if (g_strcmp0(device, "mic") == 0) { + dev = DEVICE_MIC; + DEBUG("Slider: Set value no update for mic: %f\n", newval); + } + else { + ERROR("Slider: Unknown device: %s", device); + return; + } g_signal_handler_block(G_OBJECT(slider[dev]), movedConnId[dev]); gtk_range_set_value(GTK_RANGE(slider[dev]), newval); @@ -134,6 +168,31 @@ set_slider(const gchar * device, gdouble newval) update_icons(dev); } +void toggle_slider_mute_microphone(void) +{ + DEBUG("Slider: Mute/Unmute toggle"); + + switch(device_state) { + case DEVICE_STATE_ACTIVE: + value[DEVICE_MIC] = gtk_range_get_value(GTK_RANGE(slider[DEVICE_MIC])); + dbus_set_volume("mic", 0.0); + device_state = DEVICE_STATE_MUTED; + break; + case DEVICE_STATE_MUTED: + dbus_set_volume("mic", value[DEVICE_MIC]); + device_state = DEVICE_STATE_ACTIVE; + break; + default: + ERROR("Slider: Unknown state"); + break; + } +} + +guint get_mute_unmute_audio_state(void) +{ + return device_state; +} + /** Generates the speaker slider and mute button */ GtkWidget * create_slider(const gchar * device) @@ -146,25 +205,25 @@ create_slider(const gchar * device) int dev=0; if (g_strcmp0(device, "speaker") == 0) { - dev = SPEAKER; - images[SPEAKER][MUTED] = gtk_image_new_from_file(ICONS_DIR "/speaker.svg"); - images[SPEAKER][VOL25] = gtk_image_new_from_file(ICONS_DIR "/speaker_25.svg"); - images[SPEAKER][VOL50] = gtk_image_new_from_file(ICONS_DIR "/speaker_50.svg"); - images[SPEAKER][VOL75] = gtk_image_new_from_file(ICONS_DIR "/speaker_75.svg"); - g_object_ref(images[SPEAKER][MUTED]); - g_object_ref(images[SPEAKER][VOL25]); - g_object_ref(images[SPEAKER][VOL50]); - g_object_ref(images[SPEAKER][VOL75]); + dev = DEVICE_SPEAKER; + images[DEVICE_SPEAKER][MUTED] = gtk_image_new_from_file(ICONS_DIR "/speaker.svg"); + images[DEVICE_SPEAKER][VOL25] = gtk_image_new_from_file(ICONS_DIR "/speaker_25.svg"); + images[DEVICE_SPEAKER][VOL50] = gtk_image_new_from_file(ICONS_DIR "/speaker_50.svg"); + images[DEVICE_SPEAKER][VOL75] = gtk_image_new_from_file(ICONS_DIR "/speaker_75.svg"); + g_object_ref(images[DEVICE_SPEAKER][MUTED]); + g_object_ref(images[DEVICE_SPEAKER][VOL25]); + g_object_ref(images[DEVICE_SPEAKER][VOL50]); + g_object_ref(images[DEVICE_SPEAKER][VOL75]); } else if (g_strcmp0(device, "mic") == 0) { - dev = MIKE; - images[MIKE][MUTED] = gtk_image_new_from_file(ICONS_DIR "/mic.svg"); - images[MIKE][VOL25] = gtk_image_new_from_file(ICONS_DIR "/mic_25.svg"); - images[MIKE][VOL50] = gtk_image_new_from_file(ICONS_DIR "/mic_50.svg"); - images[MIKE][VOL75] = gtk_image_new_from_file(ICONS_DIR "/mic_75.svg"); - g_object_ref(images[MIKE][MUTED]); - g_object_ref(images[MIKE][VOL25]); - g_object_ref(images[MIKE][VOL50]); - g_object_ref(images[MIKE][VOL75]); + dev = DEVICE_MIC; + images[DEVICE_MIC][MUTED] = gtk_image_new_from_file(ICONS_DIR "/mic.svg"); + images[DEVICE_MIC][VOL25] = gtk_image_new_from_file(ICONS_DIR "/mic_25.svg"); + images[DEVICE_MIC][VOL50] = gtk_image_new_from_file(ICONS_DIR "/mic_50.svg"); + images[DEVICE_MIC][VOL75] = gtk_image_new_from_file(ICONS_DIR "/mic_75.svg"); + g_object_ref(images[DEVICE_MIC][MUTED]); + g_object_ref(images[DEVICE_MIC][VOL25]); + g_object_ref(images[DEVICE_MIC][VOL50]); + g_object_ref(images[DEVICE_MIC][VOL75]); } ret = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5 /*spacing*/); @@ -185,7 +244,7 @@ create_slider(const gchar * device) G_CALLBACK(slider_moved), (gpointer) device); gtk_box_pack_start(GTK_BOX(ret), slider[dev], TRUE /*expand*/, TRUE /*fill*/, 0 /*padding*/); - set_slider(device, dbus_get_volume(device)); + set_slider_no_update(device, dbus_get_volume(device)); return ret; } diff --git a/gnome/src/sliders.h b/gnome/src/sliders.h index 738522c5847c010cba93ec5f249265e58a582d7a..dd48f8f85d464c27587112b429a4dfc3da88adeb 100644 --- a/gnome/src/sliders.h +++ b/gnome/src/sliders.h @@ -36,6 +36,12 @@ * @brief Volume sliders at the bottom of the main window. */ +enum device_state_t { + DEVICE_STATE_MUTED = 0, + DEVICE_STATE_ACTIVE, + DEVICE_STATE_COUNT +}; + /** * Build the sliders widget * @param device Mic or speaker @@ -43,6 +49,12 @@ */ GtkWidget * create_slider (const gchar * device); +/** + * Update the sliders sending the value to the server + * @param device The device slider to update + * @param value The value to set [0, 1.0] + */ +void set_slider_value(const gchar *device, gdouble value); /** * This function updates the sliders without sending the value to the server. @@ -51,6 +63,16 @@ GtkWidget * create_slider (const gchar * device); * @param device The device slider to update {speaker, mic} * @param value The value to set [0, 1.0] */ -void set_slider (const gchar * device, gdouble value); +void set_slider_no_update (const gchar * device, gdouble value); + +/** + * Mute the audio device setting the sliders to 0 + */ +void toggle_slider_mute_microphone(void); + +/** + * Returns the mute/unmute state + */ +guint get_mute_unmute_state(void); #endif diff --git a/gnome/src/ui.xml b/gnome/src/ui.xml index 52058b4947b5fea98095d8f4f8435b447dcd0be8..6dadc1bd3e4dcf8e1ba12b5974e64afdd1251601 100644 --- a/gnome/src/ui.xml +++ b/gnome/src/ui.xml @@ -8,6 +8,7 @@ <menuitem name="OnHoldMenu" action="OnHold"/> <menuitem action="InstantMessaging"/> <menuitem action="Record"/> + <menuitem action="Mute"/> <separator/> <menuitem action="AccountAssistant"/> <separator/> @@ -49,8 +50,10 @@ <toolitem name="TransferToolbar" action="Transfer"/> <toolitem name="InstantMessagingToolbar" action="InstantMessaging"/> <toolitem name="RecordToolbar" action="Record"/> + <toolitem name="MuteToolbar" action="Mute"/> <!-- FIXME: commented out because it is responsible for #7495 --> <!-- separator/--> + <toolitem name="VoicemailToolbar" action="Voicemail"/> <toolitem name="HistoryToolbar" action="History"/> <toolitem name="StartPlaybackRecordToolbar" action="StartPlaybackRecord"/> diff --git a/gnome/src/uimanager.c b/gnome/src/uimanager.c index 3145a55ca6d2c1ec5e250e13747d5ea181557459..abd37fc9af2243dd936970128c200415a8d278b3 100644 --- a/gnome/src/uimanager.c +++ b/gnome/src/uimanager.c @@ -59,12 +59,15 @@ #include <sys/stat.h> +#include <sliders.h> + void show_edit_number(callable_obj_t *call); static GtkWidget *toolbar_; static guint transferButtonConnId_; //The button toggled signal connection ID static guint recordButtonConnId_; //The button toggled signal connection ID +static guint muteCallButtonId_; //The button toggled signal connection ID static GtkAction * pickUpAction_; static GtkWidget * pickUpWidget_; @@ -79,7 +82,9 @@ static GtkWidget * transferToolbar_; static GtkAction * copyAction_; static GtkAction * pasteAction_; static GtkAction * recordAction_; +static GtkAction * muteAction_; static GtkWidget * recordWidget_; +static GtkWidget * muteWidget_; static GtkAction * voicemailAction_; static GtkWidget * voicemailToolbar_; static GtkWidget * imToolbar_; @@ -117,9 +122,18 @@ static void add_to_toolbar(GtkWidget *toolbar, GtkWidget *item, int pos) gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(item), pos); } +static void +call_mute(void) +{ + DEBUG("UIManager: Mute call button pressed"); + sflphone_mute_call(); +} + void update_actions() { + int pos = 0; + gtk_action_set_sensitive(newCallAction_, TRUE); gtk_action_set_sensitive(pickUpAction_, FALSE); gtk_action_set_sensitive(hangUpAction_, FALSE); @@ -127,6 +141,7 @@ update_actions() g_object_ref(hangUpWidget_); g_object_ref(recordWidget_); + g_object_ref(muteWidget_); g_object_ref(holdToolbar_); g_object_ref(offHoldToolbar_); @@ -140,6 +155,7 @@ update_actions() remove_from_toolbar(hangUpWidget_); remove_from_toolbar(recordWidget_); + remove_from_toolbar(muteWidget_); remove_from_toolbar(transferToolbar_); remove_from_toolbar(historyButton_); @@ -153,7 +169,9 @@ update_actions() gtk_widget_set_sensitive(holdToolbar_, FALSE); gtk_widget_set_sensitive(offHoldToolbar_, FALSE); gtk_action_set_sensitive(recordAction_, FALSE); + gtk_action_set_sensitive(muteAction_, FALSE); gtk_widget_set_sensitive(recordWidget_, FALSE); + gtk_widget_set_sensitive(muteWidget_, FALSE); gtk_action_set_sensitive(copyAction_, FALSE); if (addrbook) @@ -179,6 +197,12 @@ update_actions() gtk_widget_set_sensitive(historyButton_, TRUE); } + GtkToolItem *separator = gtk_separator_tool_item_new(); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar_), separator, -1); + // add mute button + add_to_toolbar(toolbar_, muteWidget_, -1); + gtk_action_set_sensitive(muteAction_, TRUE); + // If addressbook support has been enabled and all addressbooks are loaded, display the icon if (addrbook && addrbook->is_ready() && addressbook_config_load_parameters()->enable) { add_to_toolbar(toolbar_, contactButton_, -1); @@ -216,9 +240,9 @@ update_actions() // Replace the dial button with the hangup button g_object_ref(newCallWidget_); remove_from_toolbar(newCallWidget_); - int pos = 0; + pos = 0; add_to_toolbar(toolbar_, pickUpWidget_, pos++); - add_to_toolbar(toolbar_, hangUpWidget_, pos); + add_to_toolbar(toolbar_, hangUpWidget_, pos++); break; } case CALL_STATE_HOLD: @@ -230,13 +254,13 @@ update_actions() gtk_widget_set_sensitive(newCallWidget_, TRUE); // Replace the hold button with the off-hold button - int pos = 1; + pos = 1; add_to_toolbar(toolbar_, hangUpWidget_, pos++); add_to_toolbar(toolbar_, offHoldToolbar_, pos++); if (instant_messaging_enabled) { gtk_action_set_sensitive(imAction_, TRUE); - add_to_toolbar(toolbar_, imToolbar_, pos); + add_to_toolbar(toolbar_, imToolbar_, pos++); } break; @@ -246,8 +270,8 @@ update_actions() DEBUG("UIManager: Call State Ringing"); gtk_action_set_sensitive(pickUpAction_, TRUE); gtk_action_set_sensitive(hangUpAction_, TRUE); - int pos = 1; - add_to_toolbar(toolbar_, hangUpWidget_, pos); + pos = 1; + add_to_toolbar(toolbar_, hangUpWidget_, pos++); break; } case CALL_STATE_DIALING: @@ -260,7 +284,7 @@ update_actions() g_object_ref(newCallWidget_); remove_from_toolbar(newCallWidget_); - int pos = 0; + pos = 0; add_to_toolbar(toolbar_, pickUpWidget_, pos++); if (active_calltree_tab == current_calls_tab) @@ -268,9 +292,9 @@ update_actions() else if (active_calltree_tab == history_tab) { if (is_non_empty(selectedCall->_recordfile)) { if (selectedCall->_record_is_playing) - add_to_toolbar(toolbar_, stopRecordWidget_, pos); + add_to_toolbar(toolbar_, stopRecordWidget_, pos++); else - add_to_toolbar(toolbar_, playRecordWidget_, pos); + add_to_toolbar(toolbar_, playRecordWidget_, pos++); } } break; @@ -279,7 +303,7 @@ update_actions() { DEBUG("UIManager: Call State Current"); gtk_action_set_sensitive(hangUpAction_, TRUE); - int pos = 1; + pos = 1; add_to_toolbar(toolbar_, hangUpWidget_, pos++); gtk_widget_set_sensitive(holdMenu_, TRUE); gtk_widget_set_sensitive(holdToolbar_, TRUE); @@ -297,7 +321,7 @@ update_actions() if (instant_messaging_enabled) { gtk_action_set_sensitive(imAction_, TRUE); - add_to_toolbar(toolbar_, imToolbar_, pos); + add_to_toolbar(toolbar_, imToolbar_, pos++); } break; @@ -306,7 +330,7 @@ update_actions() case CALL_STATE_RECORD: { DEBUG("UIManager: Call State Record"); - int pos = 1; + pos = 1; gtk_action_set_sensitive(hangUpAction_, TRUE); add_to_toolbar(toolbar_, hangUpWidget_, pos++); gtk_widget_set_sensitive(holdMenu_, TRUE); @@ -325,7 +349,7 @@ update_actions() if (instant_messaging_enabled) { gtk_action_set_sensitive(imAction_, TRUE); - add_to_toolbar(toolbar_, imToolbar_, pos); + add_to_toolbar(toolbar_, imToolbar_, pos++); } break; @@ -333,17 +357,17 @@ update_actions() case CALL_STATE_BUSY: case CALL_STATE_FAILURE: { - int pos = 1; + pos = 1; DEBUG("UIManager: Call State Busy/Failure"); gtk_action_set_sensitive(hangUpAction_, TRUE); - add_to_toolbar(toolbar_, hangUpWidget_, pos); + add_to_toolbar(toolbar_, hangUpWidget_, pos++); break; } case CALL_STATE_TRANSFER: { - int pos = 1; + pos = 1; add_to_toolbar(toolbar_, hangUpWidget_, pos++); - add_to_toolbar(toolbar_, transferToolbar_, pos); + add_to_toolbar(toolbar_, transferToolbar_, pos++); g_signal_handler_block(transferToolbar_, transferButtonConnId_); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(transferToolbar_), TRUE); g_signal_handler_unblock(transferToolbar_, transferButtonConnId_); @@ -357,6 +381,7 @@ update_actions() ERROR("UIMAnager: Error: Unknown state in action update!"); break; } + } else if (selectedConf) { DEBUG("UIManager: Update actions for conference"); @@ -374,7 +399,7 @@ update_actions() gtk_action_set_sensitive(hangUpAction_, TRUE); gtk_widget_set_sensitive(holdToolbar_, TRUE); gtk_action_set_sensitive(recordAction_, TRUE); - int pos = 1; + pos = 1; add_to_toolbar(toolbar_, hangUpWidget_, pos++); add_to_toolbar(toolbar_, holdToolbar_, pos++); add_to_toolbar(toolbar_, recordWidget_, pos++); @@ -385,7 +410,7 @@ update_actions() } } else if (active_calltree_tab == history_tab) { if (is_non_empty(selectedConf->_recordfile)) { - int pos = 2; + pos = 2; if (selectedConf->_record_is_playing) add_to_toolbar(toolbar_, stopRecordWidget_, pos); else @@ -396,7 +421,7 @@ update_actions() break; case CONFERENCE_STATE_ACTIVE_ATTACHED_RECORD: case CONFERENCE_STATE_ACTIVE_DETACHED_RECORD: { - int pos = 1; + pos = 1; DEBUG("UIManager: Conference State Record"); gtk_action_set_sensitive(hangUpAction_, TRUE); gtk_widget_set_sensitive(holdToolbar_, TRUE); @@ -415,7 +440,7 @@ update_actions() case CONFERENCE_STATE_HOLD: case CONFERENCE_STATE_HOLD_RECORD: { DEBUG("UIManager: Conference State Hold"); - int pos = 1; + pos = 1; gtk_action_set_sensitive(hangUpAction_, TRUE); gtk_widget_set_sensitive(offHoldToolbar_, TRUE); gtk_action_set_sensitive(recordAction_, TRUE); @@ -1065,23 +1090,82 @@ static const GtkActionEntry menu_entries[] = { N_("About this application"), G_CALLBACK(help_about) } }; +static void register_custom_stock_icon(void) { + + static gboolean registered = FALSE; + + if (!registered) { + GdkPixbuf *pixbuf; + GtkIconFactory *factory; + + static GtkStockItem items[] = { + { "SFLPHONE_MUTE_CALL", + "_GTK!", + 0, 0, NULL } + }; + + registered = TRUE; + + /* Register our stock items */ + gtk_stock_add (items, G_N_ELEMENTS (items)); + + /* Add our custom icon factory to the list of defaults */ + factory = gtk_icon_factory_new (); + gtk_icon_factory_add_default (factory); + + /* demo_find_file() looks in the current directory first, + * so you can run gtk-demo without installing GTK, then looks + * in the location where the file is installed. + */ + pixbuf = NULL; + pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/mic.svg", NULL); + if(pixbuf == NULL) { + DEBUG("Error could not create mic.svg pixbuf"); + } + + /* Register icon to accompany stock item */ + if (pixbuf != NULL) { + GtkIconSet *icon_set; + GdkPixbuf *transparent; + + /* The gtk-logo-rgb icon has a white background, make it transparent */ + transparent = gdk_pixbuf_add_alpha (pixbuf, TRUE, 0xff, 0xff, 0xff); + + icon_set = gtk_icon_set_new_from_pixbuf (transparent); + gtk_icon_factory_add (factory, "SFLPHONE_MUTE_CALL", icon_set); + gtk_icon_set_unref (icon_set); + g_object_unref (pixbuf); + g_object_unref (transparent); + } + else + g_warning ("failed to load GTK logo for toolbar"); + + /* Drop our reference to the factory, GTK will hold a reference. */ + g_object_unref (factory); + } +} + static const GtkToggleActionEntry toggle_menu_entries[] = { { "Transfer", GTK_STOCK_TRANSFER, N_("_Transfer"), "<control>T", N_("Transfer the call"), NULL, TRUE }, { "Record", GTK_STOCK_MEDIA_RECORD, N_("_Record"), "<control>R", N_("Record the current conversation"), NULL, TRUE }, + { "Mute", "SFLPHONE_MUTE_CALL", N_("_Mute"), "<control>M", N_("Mute microphone for this call"), G_CALLBACK(call_mute), FALSE }, { "Toolbar", NULL, N_("_Show toolbar"), "<control>T", N_("Show the toolbar"), NULL, TRUE }, { "Dialpad", NULL, N_("_Dialpad"), "<control>D", N_("Show the dialpad"), G_CALLBACK(dialpad_bar_cb), TRUE }, { "VolumeControls", NULL, N_("_Volume controls"), "<control>V", N_("Show the volume controls"), G_CALLBACK(volume_bar_cb), TRUE }, { "History", "appointment-soon", N_("_History"), NULL, N_("Calls history"), G_CALLBACK(toggle_history_cb), FALSE }, - { "Addressbook", GTK_STOCK_ADDRESSBOOK, N_("_Address book"), NULL, N_("Address book"), G_CALLBACK(toggle_addressbook_cb), FALSE } + { "Addressbook", GTK_STOCK_ADDRESSBOOK, N_("_Address book"), NULL, N_("Address book"), G_CALLBACK(toggle_addressbook_cb), FALSE }, }; GtkUIManager *uimanager_new(void) { - gint nb_entries = addrbook ? 7 : 6; + gint nb_entries = addrbook ? 8 : 7; GtkWidget *window = get_main_window(); GtkUIManager *ui_manager = gtk_ui_manager_new(); + /* Register new icons as GTK_STOCK_ITEMS */ + register_custom_stock_icon(); + /* Create an accel group for window's shortcuts */ gchar *path = g_build_filename(SFLPHONE_UIDIR_UNINSTALLED, "./ui.xml", NULL); guint manager_id; @@ -1193,7 +1277,7 @@ void show_popup_menu(GtkWidget *my_widget, GdkEventButton *event) { // TODO update the selection to make sure the call under the mouse is the call selected - gboolean pickup = FALSE, hangup = FALSE, hold = FALSE, copy = FALSE, record = FALSE, im = FALSE; + gboolean pickup = FALSE, hangup = FALSE, hold = FALSE, copy = FALSE, record = FALSE, im = FALSE, mute = FALSE; gboolean accounts = FALSE; // conference type boolean @@ -1232,6 +1316,7 @@ show_popup_menu(GtkWidget *my_widget, GdkEventButton *event) hold = TRUE; record = TRUE; im = TRUE; + mute = TRUE; break; case CALL_STATE_BUSY: case CALL_STATE_FAILURE: @@ -1340,6 +1425,20 @@ show_popup_menu(GtkWidget *my_widget, GdkEventButton *event) gtk_widget_show(menu_items); } + if (mute) { + GtkWidget *menu_items = gtk_image_menu_item_new_with_mnemonic(_("_Mute")); + GtkWidget *image = gtk_image_new_from_stock(GTK_STOCK_MEDIA_RECORD, + GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_items), image); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_items); + g_signal_connect(G_OBJECT(menu_items), "activate", + G_CALLBACK(call_mute), + NULL); + gtk_widget_show(menu_items); + } + + + if (im) { // do not display message if instant messaging is disabled gboolean instant_messaging_enabled = TRUE; @@ -1564,15 +1663,59 @@ GtkWidget * create_menus(GtkUIManager *ui_manager) { GtkWidget *menu_bar = gtk_ui_manager_get_widget(ui_manager, "/MenuBar"); + if(menu_bar == NULL) { + ERROR("Could not create menu bar"); + } + pickUpAction_ = gtk_ui_manager_get_action(ui_manager, "/MenuBar/CallMenu/PickUp"); + if(pickUpAction_ == NULL) { + ERROR("Could not create pick up action"); + } + newCallAction_ = gtk_ui_manager_get_action(ui_manager, "/MenuBar/CallMenu/NewCall"); + if(newCallAction_ == NULL) { + ERROR("Could not create new call action"); + } + hangUpAction_ = gtk_ui_manager_get_action(ui_manager, "/MenuBar/CallMenu/HangUp"); + if(hangUpAction_ == NULL) { + ERROR("Could not create hangup action"); + } + holdMenu_ = gtk_ui_manager_get_widget(ui_manager, "/MenuBar/CallMenu/OnHoldMenu"); + if(holdMenu_ == NULL) { + ERROR("Could not create hold menu widget"); + } + recordAction_ = gtk_ui_manager_get_action(ui_manager, "/MenuBar/CallMenu/Record"); + if(recordAction_ == NULL) { + ERROR("Could not create record action"); + } + + muteAction_ = gtk_ui_manager_get_action(ui_manager, "/MenuBar/CallMenu/Mute"); + if(muteAction_ == NULL) { + ERROR("Could not create mute call action"); + } + imAction_ = gtk_ui_manager_get_action(ui_manager, "/MenuBar/CallMenu/InstantMessaging"); + if(imAction_ == NULL) { + ERROR("Could not create instant messaging action"); + } + copyAction_ = gtk_ui_manager_get_action(ui_manager, "/MenuBar/EditMenu/Copy"); + if(copyAction_ == NULL) { + ERROR("Could not create copy action"); + } + pasteAction_ = gtk_ui_manager_get_action(ui_manager, "/MenuBar/EditMenu/Paste"); + if(pasteAction_ == NULL) { + ERROR("Could not create paste action"); + } + volumeToggle_ = gtk_ui_manager_get_action(ui_manager, "/MenuBar/ViewMenu/VolumeControls"); + if(volumeToggle_ == NULL) { + ERROR("Could not create volume toggle action"); + } // Set the toggle buttons gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(ui_manager, "/MenuBar/ViewMenu/Dialpad")), eel_gconf_get_boolean(CONF_SHOW_DIALPAD)); @@ -1592,32 +1735,75 @@ create_toolbar_actions(GtkUIManager *ui_manager) { toolbar_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions"); - holdToolbar_ = gtk_ui_manager_get_widget(ui_manager, - "/ToolbarActions/OnHoldToolbar"); - offHoldToolbar_ = gtk_ui_manager_get_widget(ui_manager, - "/ToolbarActions/OffHoldToolbar"); - transferToolbar_ = gtk_ui_manager_get_widget(ui_manager, - "/ToolbarActions/TransferToolbar"); - voicemailAction_ = gtk_ui_manager_get_action(ui_manager, - "/ToolbarActions/Voicemail"); - voicemailToolbar_ = gtk_ui_manager_get_widget(ui_manager, - "/ToolbarActions/VoicemailToolbar"); - newCallWidget_ = gtk_ui_manager_get_widget(ui_manager, - "/ToolbarActions/NewCallToolbar"); - pickUpWidget_ = gtk_ui_manager_get_widget(ui_manager, - "/ToolbarActions/PickUpToolbar"); - hangUpWidget_ = gtk_ui_manager_get_widget(ui_manager, - "/ToolbarActions/HangUpToolbar"); - recordWidget_ = gtk_ui_manager_get_widget(ui_manager, - "/ToolbarActions/RecordToolbar"); - imToolbar_ = gtk_ui_manager_get_widget(ui_manager, - "/ToolbarActions/InstantMessagingToolbar"); - historyButton_ = gtk_ui_manager_get_widget(ui_manager, - "/ToolbarActions/HistoryToolbar"); - playRecordWidget_ = gtk_ui_manager_get_widget(ui_manager, - "/ToolbarActions/StartPlaybackRecordToolbar"); - stopRecordWidget_ = gtk_ui_manager_get_widget(ui_manager, - "/ToolbarActions/StopPlaybackRecordToolbar"); + holdToolbar_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/OnHoldToolbar"); + if(holdToolbar_ == NULL) { + ERROR("Could not create on hold toolbar widget"); + } + + offHoldToolbar_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/OffHoldToolbar"); + if(offHoldToolbar_ == NULL) { + ERROR("Could not create off hold toolbar widget"); + } + + transferToolbar_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/TransferToolbar"); + if(transferToolbar_ == NULL) { + ERROR("Could not create transfer toolbar widget"); + } + + voicemailAction_ = gtk_ui_manager_get_action(ui_manager, "/ToolbarActions/Voicemail"); + if(voicemailAction_ == NULL) { + ERROR("Could not create voicemail action"); + } + + voicemailToolbar_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/VoicemailToolbar"); + if(voicemailToolbar_ == NULL) { + ERROR("Could not create voicemail toolbar widget"); + } + + newCallWidget_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/NewCallToolbar"); + if(newCallWidget_ == NULL) { + ERROR("Could not create new call widget"); + } + + pickUpWidget_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/PickUpToolbar"); + if(pickUpWidget_ == NULL) { + ERROR("Could not create pick up toolbar widget"); + } + + hangUpWidget_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/HangUpToolbar"); + if(hangUpWidget_ == NULL) { + ERROR("Could not create hang up toolbar widget"); + } + + recordWidget_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/RecordToolbar"); + if(recordWidget_ == NULL) { + ERROR("Could not create record toolbar widget"); + } + + muteWidget_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/MuteToolbar"); + if(muteWidget_ == NULL) { + ERROR("Could not create mute call widget"); + } + + imToolbar_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/InstantMessagingToolbar"); + if(imToolbar_ == NULL) { + ERROR("Could not create instant messaging widget"); + } + + historyButton_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/HistoryToolbar"); + if(historyButton_ == NULL) { + ERROR("Could not create history button widget"); + } + + playRecordWidget_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/StartPlaybackRecordToolbar"); + if(playRecordWidget_ == NULL) { + ERROR("Could not create play record widget"); + } + + stopRecordWidget_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/StopPlaybackRecordToolbar"); + if(stopRecordWidget_ == NULL) { + ERROR("Could not create stop record widget"); + } if (addrbook) contactButton_ = gtk_ui_manager_get_widget(ui_manager, "/ToolbarActions/AddressbookToolbar"); @@ -1625,6 +1811,7 @@ create_toolbar_actions(GtkUIManager *ui_manager) // Set the handler ID for the transfer transferButtonConnId_ = g_signal_connect(G_OBJECT(transferToolbar_), "toggled", G_CALLBACK(call_transfer_cb), NULL); recordButtonConnId_ = g_signal_connect(G_OBJECT(recordWidget_), "toggled", G_CALLBACK(call_record), NULL); + // muteCallButtonId_ = g_signal_connect(G_OBJECT(muteWidget_), "toggled", G_CALLBACK(call_mute), NULL); active_calltree_tab = current_calls_tab; return toolbar_;