diff --git a/daemon/configure.ac b/daemon/configure.ac index 8d184bc4eec8692f123aa9c90ba0773d199f9575..21cdff7e5ff1e6bc8b5a5d844378d8a77e0bf649 100644 --- a/daemon/configure.ac +++ b/daemon/configure.ac @@ -256,7 +256,7 @@ AC_DEFUN([BB_ENABLE_DOXYGEN], # Acutally perform the doxygen check BB_ENABLE_DOXYGEN -CXXFLAGS="${CXXFLAGS} -g -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -Weffc++ -Wfatal-errors" +CXXFLAGS="${CXXFLAGS} -g -Wno-return-type -Wall -Wextra -Wnon-virtual-dtor -Weffc++" dnl What to generate AC_CONFIG_FILES([Makefile]) diff --git a/daemon/man/Makefile.am b/daemon/man/Makefile.am index 3a3edf5b86c8cbc8d753220258832bbc6e61ea23..155e4427c1f7efb8c38706069982f10f7f8aefcf 100644 --- a/daemon/man/Makefile.am +++ b/daemon/man/Makefile.am @@ -1,15 +1,14 @@ SECTION="1" -TEMPLATES=\ - sflphoned.pod +TEMPLATES=sflphoned.pod -man_MANS = sflphoned.1 +man_MANS=sflphoned.1 POD2MAN=pod2man -EXTRA_DIST= $(man_MANS) +EXTRA_DIST=$(man_MANS) $(TEMPLATES) -all: $(MANPAGES) +all: $(MANPAGES) SUFFIXES=.pod .1 diff --git a/daemon/src/Makefile.am b/daemon/src/Makefile.am index 2f83fe6bf6525bde85331934ee8627770df4743b..708bfcb1eef29ce9f9b33d0e87f18aec50af986d 100644 --- a/daemon/src/Makefile.am +++ b/daemon/src/Makefile.am @@ -54,7 +54,9 @@ noinst_HEADERS = \ cc_thread.h \ cc_config.h \ sfl_types.h \ - array_size.h + array_size.h \ + account_schema.h \ + registration_states.h if SFL_VIDEO SFL_VIDEO_LIB=./video/libvideo.la diff --git a/daemon/src/account.cpp b/daemon/src/account.cpp index ff8abfbd5ebe2287745156e365cbb594302bb69c..6ff8cc0c64908a2705c5f7ea046d7c3f7ea3343d 100644 --- a/daemon/src/account.cpp +++ b/daemon/src/account.cpp @@ -29,26 +29,53 @@ * shall include the source code for the parts of OpenSSL used as well * as that of the covered work. */ - +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif #include "account.h" -#include "manager.h" -#include "dbus/configurationmanager.h" +#include <algorithm> #ifdef SFL_VIDEO -#include "video/video_endpoint.h" +#include "video/libav_utils.h" #endif -Account::Account(const std::string &accountID, const std::string &type) : +#include "logger.h" +#include "manager.h" +#include "dbus/configurationmanager.h" + +const char * const Account::AUDIO_CODECS_KEY = "audioCodecs"; // 0/9/110/111/112/ +const char * const Account::VIDEO_CODECS_KEY = "videoCodecs"; +const char * const Account::VIDEO_CODEC_ENABLED = "enabled"; +const char * const Account::VIDEO_CODEC_NAME = "name"; +const char * const Account::VIDEO_CODEC_BITRATE = "bitrate"; +const char * const Account::RINGTONE_PATH_KEY = "ringtonePath"; +const char * const Account::RINGTONE_ENABLED_KEY = "ringtoneEnabled"; +const char * const Account::DISPLAY_NAME_KEY = "displayName"; +const char * const Account::ALIAS_KEY = "alias"; +const char * const Account::TYPE_KEY = "type"; +const char * const Account::ID_KEY = "id"; +const char * const Account::USERNAME_KEY = "username"; +const char * const Account::AUTHENTICATION_USERNAME_KEY = "authenticationUsername"; +const char * const Account::PASSWORD_KEY = "password"; +const char * const Account::HOSTNAME_KEY = "hostname"; +const char * const Account::ACCOUNT_ENABLE_KEY = "enable"; +const char * const Account::MAILBOX_KEY = "mailbox"; + +using std::map; +using std::string; +using std::vector; + + +Account::Account(const string &accountID, const string &type) : accountID_(accountID) , username_() , hostname_() , alias_() , enabled_(true) , type_(type) - , registrationState_(Unregistered) + , registrationState_(UNREGISTERED) , audioCodecList_() , videoCodecList_() , audioCodecStr_() - , videoCodecStr_() , ringtonePath_("/usr/share/sflphone/ringtones/konga.ul") , ringtoneEnabled_(true) , displayName_("") @@ -79,7 +106,7 @@ void Account::loadDefaultCodecs() // CodecMap codecMap = Manager::instance ().getCodecDescriptorMap ().getCodecsMap(); // Initialize codec - std::vector<std::string> result; + vector<string> result; result.push_back("0"); result.push_back("3"); result.push_back("8"); @@ -90,31 +117,90 @@ void Account::loadDefaultCodecs() setActiveAudioCodecs(result); #ifdef SFL_VIDEO - setActiveVideoCodecs(sfl_video::getCodecList()); + // we don't need to validate via setVideoCodecs, since these are defaults + videoCodecList_ = libav_utils::getDefaultCodecs(); #endif } -void Account::setActiveVideoCodecs(const std::vector<std::string> &list) +#ifdef SFL_VIDEO +namespace { + bool isPositiveInteger(const string &s) + { + string::const_iterator it = s.begin(); + while (it != s.end() and std::isdigit(*it)) + ++it; + return not s.empty() and it == s.end(); + } + + bool isBoolean(const string &s) + { + return s == "true" or s == "false"; + } + + template <typename Predicate> + bool isFieldValid(const map<string, string> &codec, const char *field, Predicate p) + { + map<string, string>::const_iterator key(codec.find(field)); + return key != codec.end() and p(key->second); + } + + bool isCodecValid(const map<string, string> &codec, const vector<map<string, string> > &defaults) + { + map<string, string>::const_iterator name(codec.find(Account::VIDEO_CODEC_NAME)); + if (name == codec.end()) { + ERROR("Field \"name\" missing in codec specification"); + return false; + } + + // check that it's in the list of valid codecs and that it has all the required fields + for (vector<map<string, string> >::const_iterator i = defaults.begin(); i != defaults.end(); ++i) { + map<string, string>::const_iterator defaultName = i->find(Account::VIDEO_CODEC_NAME); + if (defaultName->second == name->second) { + return isFieldValid(codec, Account::VIDEO_CODEC_BITRATE, isPositiveInteger) + and isFieldValid(codec, Account::VIDEO_CODEC_ENABLED, isBoolean); + } + } + ERROR("Codec %s not supported", name->second.c_str()); + return false; + } + + bool isCodecListValid(const vector<map<string, string> > &list) + { + const vector<map<string, string> > defaults(libav_utils::getDefaultCodecs()); + if (list.size() != defaults.size()) { + ERROR("New codec list has a different length than the list of supported codecs"); + return false; + } + + // make sure that all codecs are present + for (vector<map<string, string> >::const_iterator i = list.begin(); + i != list.end(); ++i) { + if (not isCodecValid(*i, defaults)) + return false; + } + return true; + } +} +#endif + +void Account::setVideoCodecs(const vector<map<string, string> > &list) { #ifdef SFL_VIDEO - // first clear the previously stored codecs - videoCodecList_.clear(); - videoCodecList_ = !list.empty() ? list : sfl_video::getCodecList(); - // update the codec string according to new codec selection - videoCodecStr_ = ManagerImpl::join_string(list); + if (isCodecListValid(list)) + videoCodecList_ = list; #else (void) list; #endif } -void Account::setActiveAudioCodecs(const std::vector<std::string> &list) +void Account::setActiveAudioCodecs(const vector<string> &list) { // first clear the previously stored codecs audioCodecList_.clear(); // list contains the ordered payload of active codecs picked by the user for this account // we used the CodecOrder vector to save the order. - for (std::vector<std::string>::const_iterator iter = list.begin(); iter != list.end(); + for (vector<string>::const_iterator iter = list.begin(); iter != list.end(); ++iter) { int payload = std::atoi(iter->c_str()); audioCodecList_.push_back(payload); @@ -124,7 +210,7 @@ void Account::setActiveAudioCodecs(const std::vector<std::string> &list) audioCodecStr_ = ManagerImpl::join_string(list); } -std::string Account::mapStateNumberToString(RegistrationState state) +string Account::mapStateNumberToString(RegistrationState state) { static const char * mapStateToChar[] = { "UNREGISTERED", @@ -138,8 +224,31 @@ std::string Account::mapStateNumberToString(RegistrationState state) "ERRORCONFSTUN" }; - if (state > NumberOfStates) + if (state > NUMBER_OF_STATES) return "ERROR"; return mapStateToChar[state]; } + +vector<map<string, string> > +Account::getAllVideoCodecs() const +{ + return videoCodecList_; +} + +namespace { + bool is_inactive(const map<string, string> &codec) + { + map<string, string>::const_iterator iter = codec.find(Account::VIDEO_CODEC_ENABLED); + return iter == codec.end() or iter->second != "true"; + } +} + +vector<map<string, string> > +Account::getActiveVideoCodecs() const +{ + // FIXME: validate video codec details first + vector<map<string, string> > result(videoCodecList_); + result.erase(std::remove_if(result.begin(), result.end(), is_inactive), result.end()); + return result; +} diff --git a/daemon/src/account.h b/daemon/src/account.h index d0b86ed413928d149ca9a32e26b6ea75076f5837..ee42256d8d5b110ce96c659132b122a063a6609d 100644 --- a/daemon/src/account.h +++ b/daemon/src/account.h @@ -35,10 +35,10 @@ #include <string> #include <vector> -#include "global.h" #include "noncopyable.h" #include "config/sfl_config.h" #include "config/serializable.h" +#include "registration_states.h" class VoIPLink; @@ -49,102 +49,6 @@ class VoIPLink; * It contains account, configuration, VoIP Link and Calls (inside the VoIPLink) */ -/** Contains all the state an Voip can be in */ -enum RegistrationState { - Unregistered, - Trying, - Registered, - Error, - ErrorAuth , - ErrorNetwork , - ErrorHost, - ErrorExistStun, - ErrorNotAcceptable, - NumberOfStates -}; - -// Account identifier -static const char *const CONFIG_ACCOUNT_ID = "Account.id"; - -// Common account parameters -static const char *const CONFIG_ACCOUNT_TYPE = "Account.type"; -static const char *const CONFIG_ACCOUNT_ALIAS = "Account.alias"; -static const char *const CONFIG_ACCOUNT_MAILBOX = "Account.mailbox"; -static const char *const CONFIG_ACCOUNT_ENABLE = "Account.enable"; -static const char *const CONFIG_ACCOUNT_REGISTRATION_EXPIRE = "Account.registrationExpire"; -static const char *const CONFIG_ACCOUNT_REGISTRATION_STATUS = "Account.registrationStatus"; -static const char *const CONFIG_ACCOUNT_REGISTRATION_STATE_CODE = "Account.registrationCode"; -static const char *const CONFIG_ACCOUNT_REGISTRATION_STATE_DESC = "Account.registrationDescription"; -static const char *const CONFIG_CREDENTIAL_NUMBER = "Credential.count"; -static const char *const CONFIG_ACCOUNT_DTMF_TYPE = "Account.dtmfType"; -static const char *const CONFIG_RINGTONE_PATH = "Account.ringtonePath"; -static const char *const CONFIG_RINGTONE_ENABLED = "Account.ringtoneEnabled"; -static const char *const CONFIG_KEEP_ALIVE_ENABLED = "Account.keepAliveEnabled"; - -static const char *const CONFIG_ACCOUNT_HOSTNAME = "Account.hostname"; -static const char *const CONFIG_ACCOUNT_USERNAME = "Account.username"; -static const char *const CONFIG_ACCOUNT_ROUTESET = "Account.routeset"; -static const char *const CONFIG_ACCOUNT_PASSWORD = "Account.password"; -static const char *const CONFIG_ACCOUNT_REALM = "Account.realm"; -static const char *const CONFIG_ACCOUNT_DEFAULT_REALM = "*"; -static const char *const CONFIG_ACCOUNT_USERAGENT = "Account.useragent"; - -static const char *const CONFIG_LOCAL_INTERFACE = "Account.localInterface"; -static const char *const CONFIG_PUBLISHED_SAMEAS_LOCAL = "Account.publishedSameAsLocal"; -static const char *const CONFIG_LOCAL_PORT = "Account.localPort"; -static const char *const CONFIG_PUBLISHED_PORT = "Account.publishedPort"; -static const char *const CONFIG_PUBLISHED_ADDRESS = "Account.publishedAddress"; - -static const char *const CONFIG_DISPLAY_NAME = "Account.displayName"; -static const char *const CONFIG_DEFAULT_ADDRESS = "0.0.0.0"; - -// SIP specific parameters -static const char *const CONFIG_SIP_PROXY = "SIP.proxy"; -static const char *const CONFIG_STUN_SERVER = "STUN.server"; -static const char *const CONFIG_STUN_ENABLE = "STUN.enable"; - -// SRTP specific parameters -static const char *const CONFIG_SRTP_ENABLE = "SRTP.enable"; -static const char *const CONFIG_SRTP_KEY_EXCHANGE = "SRTP.keyExchange"; -static const char *const CONFIG_SRTP_ENCRYPTION_ALGO = "SRTP.encryptionAlgorithm"; // Provided by ccRTP,0=NULL,1=AESCM,2=AESF8 -static const char *const CONFIG_SRTP_RTP_FALLBACK = "SRTP.rtpFallback"; -static const char *const CONFIG_ZRTP_HELLO_HASH = "ZRTP.helloHashEnable"; -static const char *const CONFIG_ZRTP_DISPLAY_SAS = "ZRTP.displaySAS"; -static const char *const CONFIG_ZRTP_NOT_SUPP_WARNING = "ZRTP.notSuppWarning"; -static const char *const CONFIG_ZRTP_DISPLAY_SAS_ONCE = "ZRTP.displaySasOnce"; - -static const char *const CONFIG_TLS_LISTENER_PORT = "TLS.listenerPort"; -static const char *const CONFIG_TLS_ENABLE = "TLS.enable"; -static const char *const CONFIG_TLS_CA_LIST_FILE = "TLS.certificateListFile"; -static const char *const CONFIG_TLS_CERTIFICATE_FILE = "TLS.certificateFile"; -static const char *const CONFIG_TLS_PRIVATE_KEY_FILE = "TLS.privateKeyFile"; -static const char *const CONFIG_TLS_PASSWORD = "TLS.password"; -static const char *const CONFIG_TLS_METHOD = "TLS.method"; -static const char *const CONFIG_TLS_CIPHERS = "TLS.ciphers"; -static const char *const CONFIG_TLS_SERVER_NAME = "TLS.serverName"; -static const char *const CONFIG_TLS_VERIFY_SERVER = "TLS.verifyServer"; -static const char *const CONFIG_TLS_VERIFY_CLIENT = "TLS.verifyClient"; -static const char *const CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE = "TLS.requireClientCertificate"; -static const char *const CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC = "TLS.negotiationTimeoutSec"; -static const char *const CONFIG_TLS_NEGOTIATION_TIMEOUT_MSEC = "TLS.negotiationTimemoutMsec"; - -// General configuration keys for accounts -static const char * const ALIAS_KEY = "alias"; -static const char * const TYPE_KEY = "type"; -static const char * const ID_KEY = "id"; -static const char * const USERNAME_KEY = "username"; -static const char * const AUTHENTICATION_USERNAME_KEY = "authenticationUsername"; -static const char * const PASSWORD_KEY = "password"; -static const char * const HOSTNAME_KEY = "hostname"; -static const char * const ACCOUNT_ENABLE_KEY = "enable"; -static const char * const MAILBOX_KEY = "mailbox"; - -static const char * const AUDIO_CODECS_KEY = "audioCodecs"; // 0/9/110/111/112/ -static const char * const VIDEO_CODECS_KEY = "videoCodecs"; -static const char * const RINGTONE_PATH_KEY = "ringtonePath"; -static const char * const RINGTONE_ENABLED_KEY = "ringtoneEnabled"; -static const char * const DISPLAY_NAME_KEY = "displayName"; - class Account : public Serializable { public: @@ -225,17 +129,16 @@ class Account : public Serializable { std::string getAlias() const { return alias_; } + void setAlias(const std::string &alias) { alias_ = alias; } - /** - * Accessor to data structures - * @return std::vector<std::string>& The list that reflects the user's choice - */ - std::vector<std::string> getActiveVideoCodecs() const { - return videoCodecList_; - } + std::vector<std::map<std::string, std::string> > + getAllVideoCodecs() const; + + std::vector<std::map<std::string, std::string> > + getActiveVideoCodecs() const; /* Accessor to data structures * @return CodecOrder& The list that reflects the user's choice @@ -249,7 +152,7 @@ class Account : public Serializable { * SDP offer and configuration respectively */ void setActiveAudioCodecs(const std::vector<std::string>& list); - void setActiveVideoCodecs(const std::vector<std::string>& list); + void setVideoCodecs(const std::vector<std::map<std::string, std::string> > &codecs); std::string getRingtonePath() const { return ringtonePath_; @@ -280,6 +183,9 @@ class Account : public Serializable { mailBox_ = mb; } + static const char * const VIDEO_CODEC_ENABLED; + static const char * const VIDEO_CODEC_NAME; + static const char * const VIDEO_CODEC_BITRATE; private: NON_COPYABLE(Account); @@ -289,6 +195,23 @@ class Account : public Serializable { void loadDefaultCodecs(); protected: + friend class ConfigurationTest; + // General configuration keys for accounts + static const char * const AUDIO_CODECS_KEY; + static const char * const VIDEO_CODECS_KEY; + static const char * const RINGTONE_PATH_KEY; + static const char * const RINGTONE_ENABLED_KEY; + static const char * const DISPLAY_NAME_KEY; + static const char * const ALIAS_KEY; + static const char * const TYPE_KEY; + static const char * const ID_KEY; + static const char * const USERNAME_KEY; + static const char * const AUTHENTICATION_USERNAME_KEY; + static const char * const PASSWORD_KEY; + static const char * const HOSTNAME_KEY; + static const char * const ACCOUNT_ENABLE_KEY; + static const char * const MAILBOX_KEY; + static std::string mapStateNumberToString(RegistrationState state); /** @@ -336,9 +259,9 @@ class Account : public Serializable { std::vector<int> audioCodecList_; /** - * Vector containing the order of the video codecs + * Vector containing the video codecs in order */ - std::vector<std::string> videoCodecList_; + std::vector<std::map<std::string, std::string> > videoCodecList_; /** * List of audio codecs obtained when parsing configuration and used @@ -346,12 +269,6 @@ class Account : public Serializable { */ std::string audioCodecStr_; - /** - * List of video codecs obtained when parsing configuration and used - * to generate codec order list - */ - std::string videoCodecStr_; - /** * Ringtone .au file used for this account */ diff --git a/daemon/src/account_schema.h b/daemon/src/account_schema.h new file mode 100644 index 0000000000000000000000000000000000000000..1e769cdb75a77894656c879f7c0ec91177ca2a02 --- /dev/null +++ b/daemon/src/account_schema.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * Author: Yan Morin <yan.morin@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#ifndef ACCOUNT_SCHEMA_H +#define ACCOUNT_SCHEMA_H_ + +/** + * @file account_schema.h + * @brief Account specfic keys/constants that must be shared in daemon and clients. + */ + +// Account identifier +static const char *const CONFIG_ACCOUNT_ID = "Account.id"; + +// Common account parameters +static const char *const CONFIG_ACCOUNT_TYPE = "Account.type"; +static const char *const CONFIG_ACCOUNT_ALIAS = "Account.alias"; +static const char *const CONFIG_ACCOUNT_MAILBOX = "Account.mailbox"; +static const char *const CONFIG_ACCOUNT_ENABLE = "Account.enable"; +static const char *const CONFIG_ACCOUNT_REGISTRATION_EXPIRE = "Account.registrationExpire"; +static const char *const CONFIG_ACCOUNT_REGISTRATION_STATUS = "Account.registrationStatus"; +static const char *const CONFIG_ACCOUNT_REGISTRATION_STATE_CODE = "Account.registrationCode"; +static const char *const CONFIG_ACCOUNT_REGISTRATION_STATE_DESC = "Account.registrationDescription"; +static const char *const CONFIG_CREDENTIAL_NUMBER = "Credential.count"; +static const char *const CONFIG_ACCOUNT_DTMF_TYPE = "Account.dtmfType"; +static const char *const CONFIG_RINGTONE_PATH = "Account.ringtonePath"; +static const char *const CONFIG_RINGTONE_ENABLED = "Account.ringtoneEnabled"; +static const char *const CONFIG_KEEP_ALIVE_ENABLED = "Account.keepAliveEnabled"; + +static const char *const CONFIG_ACCOUNT_HOSTNAME = "Account.hostname"; +static const char *const CONFIG_ACCOUNT_USERNAME = "Account.username"; +static const char *const CONFIG_ACCOUNT_ROUTESET = "Account.routeset"; +static const char *const CONFIG_ACCOUNT_PASSWORD = "Account.password"; +static const char *const CONFIG_ACCOUNT_REALM = "Account.realm"; +static const char *const CONFIG_ACCOUNT_DEFAULT_REALM = "*"; +static const char *const CONFIG_ACCOUNT_USERAGENT = "Account.useragent"; + +static const char *const CONFIG_LOCAL_INTERFACE = "Account.localInterface"; +static const char *const CONFIG_PUBLISHED_SAMEAS_LOCAL = "Account.publishedSameAsLocal"; +static const char *const CONFIG_LOCAL_PORT = "Account.localPort"; +static const char *const CONFIG_PUBLISHED_PORT = "Account.publishedPort"; +static const char *const CONFIG_PUBLISHED_ADDRESS = "Account.publishedAddress"; + +static const char *const CONFIG_DISPLAY_NAME = "Account.displayName"; +static const char *const CONFIG_DEFAULT_ADDRESS = "0.0.0.0"; + +// SIP specific parameters +static const char *const CONFIG_SIP_PROXY = "SIP.proxy"; +static const char *const CONFIG_STUN_SERVER = "STUN.server"; +static const char *const CONFIG_STUN_ENABLE = "STUN.enable"; + +// SRTP specific parameters +static const char *const CONFIG_SRTP_ENABLE = "SRTP.enable"; +static const char *const CONFIG_SRTP_KEY_EXCHANGE = "SRTP.keyExchange"; +static const char *const CONFIG_SRTP_ENCRYPTION_ALGO = "SRTP.encryptionAlgorithm"; // Provided by ccRTP,0=NULL,1=AESCM,2=AESF8 +static const char *const CONFIG_SRTP_RTP_FALLBACK = "SRTP.rtpFallback"; +static const char *const CONFIG_ZRTP_HELLO_HASH = "ZRTP.helloHashEnable"; +static const char *const CONFIG_ZRTP_DISPLAY_SAS = "ZRTP.displaySAS"; +static const char *const CONFIG_ZRTP_NOT_SUPP_WARNING = "ZRTP.notSuppWarning"; +static const char *const CONFIG_ZRTP_DISPLAY_SAS_ONCE = "ZRTP.displaySasOnce"; + +static const char *const CONFIG_TLS_LISTENER_PORT = "TLS.listenerPort"; +static const char *const CONFIG_TLS_ENABLE = "TLS.enable"; +static const char *const CONFIG_TLS_CA_LIST_FILE = "TLS.certificateListFile"; +static const char *const CONFIG_TLS_CERTIFICATE_FILE = "TLS.certificateFile"; +static const char *const CONFIG_TLS_PRIVATE_KEY_FILE = "TLS.privateKeyFile"; +static const char *const CONFIG_TLS_PASSWORD = "TLS.password"; +static const char *const CONFIG_TLS_METHOD = "TLS.method"; +static const char *const CONFIG_TLS_CIPHERS = "TLS.ciphers"; +static const char *const CONFIG_TLS_SERVER_NAME = "TLS.serverName"; +static const char *const CONFIG_TLS_VERIFY_SERVER = "TLS.verifyServer"; +static const char *const CONFIG_TLS_VERIFY_CLIENT = "TLS.verifyClient"; +static const char *const CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE = "TLS.requireClientCertificate"; +static const char *const CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC = "TLS.negotiationTimeoutSec"; +static const char *const CONFIG_TLS_NEGOTIATION_TIMEOUT_MSEC = "TLS.negotiationTimemoutMsec"; + +#endif diff --git a/daemon/src/audio/mainbuffer.cpp b/daemon/src/audio/mainbuffer.cpp index 31a18b14c4f07b6a79ce94279c7bdd282d4fc080..ebe1c14a508021f8596a0bb131ed4776240bce3b 100644 --- a/daemon/src/audio/mainbuffer.cpp +++ b/daemon/src/audio/mainbuffer.cpp @@ -383,10 +383,10 @@ void MainBuffer::flushAllBuffers() iter->second->flushAll(); } -void MainBuffer::stateInfo() +void MainBuffer::dumpInfo() const { // print each call and bound call ids - for (CallIDMap::iterator iter_call = callIDMap_.begin(); iter_call != callIDMap_.end(); ++iter_call) { + for (CallIDMap::const_iterator iter_call = callIDMap_.begin(); iter_call != callIDMap_.end(); ++iter_call) { std::string dbg_str(" Call: \t"); dbg_str.append(iter_call->first); dbg_str.append(" is bound to: \t"); @@ -402,7 +402,7 @@ void MainBuffer::stateInfo() } // Print ringbuffers ids and readpointers - for (RingBufferMap::iterator iter_buffer = ringBufferMap_.begin(); iter_buffer != ringBufferMap_.end(); ++iter_buffer) { + for (RingBufferMap::const_iterator iter_buffer = ringBufferMap_.begin(); iter_buffer != ringBufferMap_.end(); ++iter_buffer) { std::string dbg_str(" Buffer: \t"); dbg_str.append(iter_buffer->first); diff --git a/daemon/src/audio/mainbuffer.h b/daemon/src/audio/mainbuffer.h index 652f3bf5a90738e197908371cce12ca5966da65c..5097975138055b988bfb9fd5148fe1bf4b484cc8 100644 --- a/daemon/src/audio/mainbuffer.h +++ b/daemon/src/audio/mainbuffer.h @@ -94,7 +94,7 @@ class MainBuffer { void flushAllBuffers(); - void stateInfo(); + void dumpInfo() const; private: diff --git a/daemon/src/audio/pulseaudio/audiostream.cpp b/daemon/src/audio/pulseaudio/audiostream.cpp index 2d33356f447ddd72d7421a675a98e4decc42d2a5..1755a677bc8012659f159e0b80ce5edabdc87f91 100644 --- a/daemon/src/audio/pulseaudio/audiostream.cpp +++ b/daemon/src/audio/pulseaudio/audiostream.cpp @@ -33,7 +33,12 @@ #include "logger.h" #include <stdexcept> -AudioStream::AudioStream(pa_context *c, pa_threaded_mainloop *m, const char *desc, int type, unsigned samplrate, std::string& deviceName) +AudioStream::AudioStream(pa_context *c, + pa_threaded_mainloop *m, + const char *desc, + int type, + unsigned samplrate, + const std::string &deviceName) : audiostream_(0), mainloop_(m) { static const pa_channel_map channel_map = { @@ -65,13 +70,21 @@ AudioStream::AudioStream(pa_context *c, pa_threaded_mainloop *m, const char *des attributes.minreq = (uint32_t) -1; pa_threaded_mainloop_lock(mainloop_); - - if (type == PLAYBACK_STREAM || type == RINGTONE_STREAM) - pa_stream_connect_playback(audiostream_, deviceName == "" ? NULL : deviceName.c_str(), &attributes, - (pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL); - else if (type == CAPTURE_STREAM) - pa_stream_connect_record(audiostream_, deviceName == "" ? NULL : deviceName.c_str(), &attributes, - (pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY|PA_STREAM_AUTO_TIMING_UPDATE)); + const pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(PA_STREAM_ADJUST_LATENCY | + PA_STREAM_AUTO_TIMING_UPDATE); + + if (type == PLAYBACK_STREAM || type == RINGTONE_STREAM) { + pa_stream_connect_playback(audiostream_, + deviceName.empty() ? NULL : deviceName.c_str(), + &attributes, + flags, + NULL, NULL); + } else if (type == CAPTURE_STREAM) { + pa_stream_connect_record(audiostream_, + deviceName.empty() ? NULL : deviceName.c_str(), + &attributes, + flags); + } pa_threaded_mainloop_unlock(mainloop_); diff --git a/daemon/src/audio/pulseaudio/audiostream.h b/daemon/src/audio/pulseaudio/audiostream.h index 59576cc6157badc144953c47513a0e40df80b7a5..6253e7be62975f2391293e22933ff55a595bc378 100644 --- a/daemon/src/audio/pulseaudio/audiostream.h +++ b/daemon/src/audio/pulseaudio/audiostream.h @@ -55,7 +55,7 @@ class AudioStream { * @param audio sampling rate * @param device name */ - AudioStream(pa_context *, pa_threaded_mainloop *, const char *, int, unsigned, std::string&); + AudioStream(pa_context *, pa_threaded_mainloop *, const char *, int, unsigned, const std::string&); ~AudioStream(); diff --git a/daemon/src/audio/pulseaudio/pulselayer.cpp b/daemon/src/audio/pulseaudio/pulselayer.cpp index 72db20f95a431ff9f79b8118766cc8435d728b32..1bf6909d8a63f93608e30d97bd85171346e44882 100644 --- a/daemon/src/audio/pulseaudio/pulselayer.cpp +++ b/daemon/src/audio/pulseaudio/pulselayer.cpp @@ -190,16 +190,16 @@ void PulseLayer::updateSourceList() bool PulseLayer::inSinkList(const std::string &deviceName) const { - bool found = std::find(sinkList_.begin(), sinkList_.end(), deviceName) != sinkList_.end(); - DEBUG("seeking for %s in sinks. %s found", deviceName.c_str(), found?"":"NOT"); + const bool found = std::find(sinkList_.begin(), sinkList_.end(), deviceName) != sinkList_.end(); + DEBUG("seeking for %s in sinks. %s found", deviceName.c_str(), found ? "" : "NOT"); return found; } bool PulseLayer::inSourceList(const std::string &deviceName) const { - bool found = std::find(sourceList_.begin(), sourceList_.end(), deviceName) != sourceList_.end(); - DEBUG("seeking for %s in sources. %s found", deviceName.c_str(), found?"":"NOT"); + const bool found = std::find(sourceList_.begin(), sourceList_.end(), deviceName) != sourceList_.end(); + DEBUG("seeking for %s in sources. %s found", deviceName.c_str(), found ? "" : "NOT"); return found; } @@ -273,44 +273,22 @@ void PulseLayer::createStreams(pa_context* c) flushUrgent(); } - -void PulseLayer::disconnectAudioStream() -{ - if (playback_) { - if (playback_->pulseStream()) { - const char *name = pa_stream_get_device_name(playback_->pulseStream()); - - if (name && *name) - preference_.setPulseDevicePlayback(name); - } - - delete playback_; - playback_ = NULL; - } - - if (ringtone_) { - if (ringtone_->pulseStream()) { - const char *name = pa_stream_get_device_name(ringtone_->pulseStream()); - - if (name && *name) - preference_.setPulseDeviceRingtone(name); - } - - delete ringtone_; - ringtone_ = NULL; +namespace { + // Delete stream and zero out its pointer + void + cleanupStream(AudioStream *&stream) + { + delete stream; + stream = 0; } +} - if (record_) { - if (record_->pulseStream()) { - const char *name = pa_stream_get_device_name(record_->pulseStream()); - if (name && *name) - preference_.setPulseDeviceRecord(name); - } - - delete record_; - record_ = NULL; - } +void PulseLayer::disconnectAudioStream() +{ + cleanupStream(playback_); + cleanupStream(ringtone_); + cleanupStream(record_); } void PulseLayer::startStream() diff --git a/daemon/src/audio/sound/tone.cpp b/daemon/src/audio/sound/tone.cpp index 49d09766a510d32a0cdd31538c260a361d6a319e..1ff9ed3ad68f70f9a7483f6c246efd900f4c54bd 100644 --- a/daemon/src/audio/sound/tone.cpp +++ b/daemon/src/audio/sound/tone.cpp @@ -112,7 +112,6 @@ Tone::genBuffer(const std::string& definition) count = (sampleRate_ * time) / 1000; // Generate SAMPLING_RATE samples of sinus, buffer is the result - DEBUG("genSin(%d, %d)", freq1, freq2); genSin(bufferPos, freq1, freq2, count); // To concatenate the different buffers for each section. diff --git a/daemon/src/call.cpp b/daemon/src/call.cpp index 1ce208f8aeed2f662fd83054d702f57979a208b4..f549a2f5e4e67e08ef781d7e870a3d79a1428818 100644 --- a/daemon/src/call.cpp +++ b/daemon/src/call.cpp @@ -93,7 +93,7 @@ Call::getStateStr() return isIncoming() ? "INCOMING" : "RINGING"; case CONNECTED: default: - return isRecording() ? "RECORD" : "CURRENT"; + return "CURRENT"; } case HOLD: @@ -161,7 +161,7 @@ Call::setRecording() mbuffer->unBindHalfDuplexOut(process_id, MainBuffer::DEFAULT_ID); } - Manager::instance().getMainBuffer()->stateInfo(); + Manager::instance().getMainBuffer()->dumpInfo(); return recordStatus; } diff --git a/daemon/src/call.h b/daemon/src/call.h index 04fc8ae4bf64c88da2396f43f0635fd8f4c93b48..7cdf74d1b46b3558bf0379edf5f09a8f43f7528a 100644 --- a/daemon/src/call.h +++ b/daemon/src/call.h @@ -195,7 +195,7 @@ class Call : public Recordable { * Set local video port, as seen by me [not protected] * @param port The local video port */ - void setLocalVideoPort (unsigned int port) { + void setLocalVideoPort(unsigned int port) { localVideoPort_ = port; } diff --git a/daemon/src/config/yamlemitter.cpp b/daemon/src/config/yamlemitter.cpp index 9713bba467a7c137547623662566c6f522cd9a0c..bea1a9b3c83a1438c7d3011078426f8aa5484df3 100644 --- a/daemon/src/config/yamlemitter.cpp +++ b/daemon/src/config/yamlemitter.cpp @@ -97,7 +97,6 @@ void YamlEmitter::serializeAccount(MappingNode *map) if (isFirstAccount_) { int accountid; - DEBUG("Create account sequence"); // accountSequence_ need to be static outside this scope since reused each time an account is written if ((accountid = yaml_document_add_scalar(&document_, NULL, (yaml_char_t *) "accounts", -1, YAML_PLAIN_SCALAR_STYLE)) == 0) diff --git a/daemon/src/dbus/callmanager-introspec.xml b/daemon/src/dbus/callmanager-introspec.xml index a85e77732a1c548221b6d5d19eca3e0d06eb667a..07630ac456e632f1bc898bd6f8fb6a94bf714681 100644 --- a/daemon/src/dbus/callmanager-introspec.xml +++ b/daemon/src/dbus/callmanager-introspec.xml @@ -514,8 +514,7 @@ <li>BUSY</li> <li>FAILURE: Error when processing a call</li> <li>HOLD</li> - <li>UNHOLD_CURRENT</li> - <li>UNHOLD_RECORD</li> + <li>UNHOLD</li> </ul> </tp:docstring> </arg> diff --git a/daemon/src/dbus/callmanager.cpp b/daemon/src/dbus/callmanager.cpp index 8338313127a517a9f0a22ae981460372efc0bea3..537907e6cd56301c0b37f81fa05ce50fda693444 100644 --- a/daemon/src/dbus/callmanager.cpp +++ b/daemon/src/dbus/callmanager.cpp @@ -270,7 +270,7 @@ CallManager::getIsRecording(const std::string& callID) std::string CallManager::getCurrentAudioCodecName(const std::string& callID) { - return Manager::instance().getCurrentCodecName(callID).c_str(); + return Manager::instance().getCurrentAudioCodecName(callID); } std::map<std::string, std::string> diff --git a/daemon/src/dbus/configurationmanager.cpp b/daemon/src/dbus/configurationmanager.cpp index e561f7bba1643f3f7daea044cef5cbeabed2b41f..76362a79ef8ed8dea807eac735e6b73f0094b8bf 100644 --- a/daemon/src/dbus/configurationmanager.cpp +++ b/daemon/src/dbus/configurationmanager.cpp @@ -35,6 +35,7 @@ #endif #include "configurationmanager.h" +#include "account_schema.h" #include <cerrno> #include <sstream> #include "../manager.h" diff --git a/daemon/src/dbus/video_controls-introspec.xml b/daemon/src/dbus/video_controls-introspec.xml index 6ff150582983ee04b309e840221b231a7c69b051..73f6d7e8d893eb5a7dc0f2c84748810dfcc21c06 100644 --- a/daemon/src/dbus/video_controls-introspec.xml +++ b/daemon/src/dbus/video_controls-introspec.xml @@ -1,33 +1,37 @@ <?xml version="1.0" ?> <node name="/video_controls-introspec" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> <interface name="org.sflphone.SFLphone.VideoControls"> - <!-- Video device methods --> + <!-- Video device methods --> - <method name="getInputDeviceList" tp:name-for-bindings="getInputDeviceList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="getDeviceList" tp:name-for-bindings="getDeviceList"> + <tp:docstring>Returns a list of the detected v4l2 devices</tp:docstring> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> + <arg type="as" name="list" direction="out"> + </arg> + </method> - <method name="getInputDeviceChannelList" tp:name-for-bindings="getInputDeviceChannelList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="s" name="device" direction="in"> - </arg> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="getDeviceChannelList" tp:name-for-bindings="getDeviceChannelList"> + <tp:docstring>Returns a list of the channels available for a given v4l2 device</tp:docstring> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> + <arg type="s" name="device" direction="in"> + </arg> + <arg type="as" name="list" direction="out"> + </arg> + </method> - <method name="getInputDeviceSizeList" tp:name-for-bindings="getInputDeviceSizeList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="s" name="device" direction="in"> - </arg> - <arg type="s" name="channel" direction="in"> - </arg> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="getDeviceSizeList" tp:name-for-bindings="getDeviceSizeList"> + <tp:docstring>Returns a list of the resolutions available for a given channel of a given v4l2 device</tp:docstring> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> + <arg type="s" name="device" direction="in"> + </arg> + <arg type="s" name="channel" direction="in"> + </arg> + <arg type="as" name="list" direction="out"> + </arg> + </method> - <method name="getInputDeviceRateList" tp:name-for-bindings="getInputDeviceRateList"> + <method name="getDeviceRateList" tp:name-for-bindings="getDeviceRateList"> + <tp:docstring>Returns a list of the framerates available for a given resolution of a given channel of a given v4l2 device</tp:docstring> <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> <arg type="s" name="device" direction="in"> </arg> @@ -37,122 +41,115 @@ </arg> <arg type="as" name="list" direction="out"> </arg> - </method> - - <method name="getInputDevice" tp:name-for-bindings="getInputDevice"> - <arg type="s" name="device" direction="out"> - </arg> - </method> - - <method name="getInputDeviceChannel" tp:name-for-bindings="getInputDeviceChannel"> - <arg type="s" name="channel" direction="out"> - </arg> - </method> + </method> - <method name="getInputDeviceSize" tp:name-for-bindings="getInputDeviceSize"> - <arg type="s" name="size" direction="out"> - </arg> - </method> + <method name="getActiveDevice" tp:name-for-bindings="getActiveDevice"> + <arg type="s" name="device" direction="out"> + </arg> + </method> - <method name="getInputDeviceRate" tp:name-for-bindings="getInputDeviceRate"> - <arg type="s" name="rate" direction="out"> - </arg> - </method> + <method name="getActiveDeviceChannel" tp:name-for-bindings="getActiveDeviceChannel"> + <arg type="s" name="channel" direction="out"> + </arg> + </method> - <method name="setInputDevice" tp:name-for-bindings="setInputDevice"> - <arg type="s" name="device" direction="in"> - </arg> - </method> + <method name="getActiveDeviceSize" tp:name-for-bindings="getActiveDeviceSize"> + <arg type="s" name="size" direction="out"> + </arg> + </method> - <method name="setInputDeviceChannel" tp:name-for-bindings="setInputDeviceChannel"> - <arg type="s" name="channel" direction="in"> - </arg> - </method> + <method name="getActiveDeviceRate" tp:name-for-bindings="getActiveDeviceRate"> + <arg type="s" name="rate" direction="out"> + </arg> + </method> - <method name="setInputDeviceSize" tp:name-for-bindings="setInputDeviceSize"> - <arg type="s" name="size" direction="in"> - </arg> - </method> + <method name="setActiveDevice" tp:name-for-bindings="setActiveDevice"> + <arg type="s" name="device" direction="in"> + </arg> + </method> - <method name="setInputDeviceRate" tp:name-for-bindings="setInputDeviceRate"> - <arg type="s" name="rate" direction="in"> - </arg> - </method> + <method name="setActiveDeviceChannel" tp:name-for-bindings="setActiveDeviceChannel"> + <arg type="s" name="channel" direction="in"> + </arg> + </method> - <!-- Video Codec related methods --> + <method name="setActiveDeviceSize" tp:name-for-bindings="setActiveDeviceSize"> + <arg type="s" name="size" direction="in"> + </arg> + </method> - <method name="getCodecList" tp:name-for-bindings="getCodecList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="setActiveDeviceRate" tp:name-for-bindings="setActiveDeviceRate"> + <arg type="s" name="rate" direction="in"> + </arg> + </method> - <method name="getCodecDetails" tp:name-for-bindings="getCodecDetails"> - <arg type="s" name="codec" direction="in"> - </arg> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="MapStringString"/> - <arg type="a{ss}" name="details" direction="out" tp:type="String_String_Map"> - </arg> - </method> + <!-- Video Codec related methods --> - <method name="getActiveCodecList" tp:name-for-bindings="getActiveCodecList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="s" name="accountID" direction="in"> - </arg> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="getCodecs" tp:name-for-bindings="getCodecs"> + <tp:docstring>Gets the hashtable describing all the codecs and their parameters for a given account</tp:docstring> + <arg type="s" name="accountID" direction="in"> + </arg> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorMapStringString"/> + <arg type="aa{ss}" name="details" direction="out"> + </arg> + </method> - <method name="setActiveCodecList" tp:name-for-bindings="setActiveCodecList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="VectorString"/> - <arg type="as" name="list" direction="in"> - </arg> + <method name="setCodecs" tp:name-for-bindings="setCodecs"> + <tp:docstring>Sets a vector of hashtables describing codecs and their parameters for a given account, one hashtable per codec</tp:docstring> <arg type="s" name="accountID" direction="in"> </arg> - </method> - - <method name="startPreview" tp:name-for-bindings="startPreview"> - <arg type="i" name="width" direction="out"> - </arg> - <arg type="i" name="height" direction="out"> - </arg> - <arg type="i" name="shmKey" direction="out"> - </arg> - <arg type="i" name="semKey" direction="out"> + <annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="VectorMapStringString"/> + <arg type="aa{ss}" name="details" direction="in"> </arg> - <arg type="i" name="videoBufferSize" direction="out"> - </arg> - </method> + </method> - <method name="stopPreview" tp:name-for-bindings="stopPreview"> - </method> + <method name="startPreview" tp:name-for-bindings="startPreview"> + <tp:docstring> Starts the video preview, which renders the active v4l2 device's video to shared memory. Useful for testing/debugging camera settings</tp:docstring> + </method> - <signal name="deviceEvent" tp:name-for-bindings="deviceEvent"> - </signal> + <method name="stopPreview" tp:name-for-bindings="stopPreview"> + </method> - <signal name="receivingEvent" tp:name-for-bindings="receivingEvent"> - <arg type="i" name="shmKey"> - </arg> - <arg type="i" name="semKey"> - </arg> - <arg type="i" name="videoBufferSize"> - </arg> - <arg type="i" name="destWidth"> - </arg> - <arg type="i" name="destHeight"> - </arg> - </signal> + <method name="hasPreviewStarted" tp:name-for-bindings="hasPreviewStarted"> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="Bool"/> + <arg type="b" name="started" direction="out"> + <tp:docstring>Returns true if the preview has already started, false otherwise</tp:docstring> + </arg> + </method> - <signal name="stoppedReceivingEvent" tp:name-for-bindings="stoppedReceivingEvent"> - <arg type="i" name="shmKey"> - </arg> - <arg type="i" name="semKey"> - </arg> - </signal> + <signal name="deviceEvent" tp:name-for-bindings="deviceEvent"> + <tp:docstring>Signal triggered by changes in the detected v4l2 devices, e.g. a camera being unplugged.</tp:docstring> + </signal> + + <signal name="startedDecoding" tp:name-for-bindings="startedDecoding"> + <tp:docstring>Signal triggered when video is available in a shared memory buffer.</tp:docstring> + <arg type="s" name="id"> + <tp:docstring>The ID of the call associated with the video, or "local" in the case of local video</tp:docstring> + </arg> + <arg type="s" name="shmPath"> + <tp:docstring>The path of the newly created shared memory</tp:docstring> + </arg> + <arg type="i" name="width"> + <tp:docstring>The width of the video in the shared memory</tp:docstring> + </arg> + <arg type="i" name="height"> + <tp:docstring>The height of the video in the shared memory</tp:docstring> + </arg> + </signal> + + <signal name="stoppedDecoding" tp:name-for-bindings="stoppedDecoding"> + <tp:docstring>Signal triggered when video is no longer available in a shared memory buffer.</tp:docstring> + <arg type="s" name="id"> + <tp:docstring>The ID of the call associated with the video, or "local" in the case of local video</tp:docstring> + </arg> + <arg type="s" name="shmPath"> + <tp:docstring>The path of the newly created shared memory</tp:docstring> + </arg> + </signal> <method name="getCurrentCodecName" tp:name-for-bindings="getCurrentCodecName"> <arg type="s" name="callID" direction="in"/> <arg type="s" name="codecName" direction="out"/> </method> - </interface> + </interface> </node> diff --git a/daemon/src/dbus/video_controls.cpp b/daemon/src/dbus/video_controls.cpp index d1a8e4a31881ab4b2a0e0c7ee75db731df81b34c..e4d2255f8f9937babe676cf0255a7b10e8c3e88e 100644 --- a/daemon/src/dbus/video_controls.cpp +++ b/daemon/src/dbus/video_controls.cpp @@ -32,7 +32,6 @@ #include "video_controls.h" #include "video/libav_utils.h" -#include "video/video_endpoint.h" #include "video/video_preview.h" #include "account.h" #include "logger.h" @@ -55,102 +54,97 @@ VideoControls::getVideoPreferences() return videoPreference_; } -/** - * Send the list of all codecs loaded to the client through DBus. - * Can stay global, as only the active codecs will be set per accounts - */ -std::vector<std::string> VideoControls::getCodecList() -{ - return sfl_video::getCodecList(); -} - -std::map<std::string, std::string> VideoControls::getCodecDetails(const std::string& name) -{ - return sfl_video::getCodecSpecifications(name); -} - -std::vector<std::string> -VideoControls::getActiveCodecList(const std::string& accountID) +std::vector<std::map<std::string, std::string> > +VideoControls::getCodecs(const std::string &accountID) { - std::vector<std::string> v; Account *acc = Manager::instance().getAccount(accountID); if (acc != NULL) - v = acc->getActiveVideoCodecs(); - - return v; - + return acc->getAllVideoCodecs(); + else + return std::vector<std::map<std::string, std::string> >(); } void -VideoControls::setActiveCodecList(const std::vector<std::string>& list, const std::string& accountID) +VideoControls::setCodecs(const std::string& accountID, + const std::vector<std::map<std::string, std::string> > &details) { Account *acc = Manager::instance().getAccount(accountID); - if (acc != NULL) { - acc->setActiveVideoCodecs(list); + acc->setVideoCodecs(details); Manager::instance().saveConfig(); } } -std::vector<std::string> VideoControls::getInputDeviceList() +std::vector<std::string> +VideoControls::getDeviceList() { return videoPreference_.getDeviceList(); } -std::vector<std::string> VideoControls::getInputDeviceChannelList(const std::string &dev) +std::vector<std::string> +VideoControls::getDeviceChannelList(const std::string &dev) { return videoPreference_.getChannelList(dev); } -std::vector<std::string> VideoControls::getInputDeviceSizeList(const std::string &dev, const std::string &channel) +std::vector<std::string> +VideoControls::getDeviceSizeList(const std::string &dev, const std::string &channel) { return videoPreference_.getSizeList(dev, channel); } std::vector<std::string> -VideoControls::getInputDeviceRateList(const std::string &dev, const std::string &channel, const std::string &size) +VideoControls::getDeviceRateList(const std::string &dev, const std::string &channel, const std::string &size) { return videoPreference_.getRateList(dev, channel, size); } -std::string VideoControls::getInputDevice() +std::string +VideoControls::getActiveDevice() { return videoPreference_.getDevice(); } -std::string VideoControls::getInputDeviceChannel() +std::string +VideoControls::getActiveDeviceChannel() { return videoPreference_.getChannel(); } -std::string VideoControls::getInputDeviceSize() +std::string +VideoControls::getActiveDeviceSize() { return videoPreference_.getSize(); } -std::string VideoControls::getInputDeviceRate() +std::string +VideoControls::getActiveDeviceRate() { return videoPreference_.getRate(); } -void VideoControls::setInputDevice(const std::string& device) +void +VideoControls::setActiveDevice(const std::string &device) { DEBUG("Setting device to %s", device.c_str()); videoPreference_.setDevice(device); } -void VideoControls::setInputDeviceChannel(const std::string& channel) +void +VideoControls::setActiveDeviceChannel(const std::string &channel) { videoPreference_.setChannel(channel); } -void VideoControls::setInputDeviceSize(const std::string& size) +void +VideoControls::setActiveDeviceSize(const std::string &size) { videoPreference_.setSize(size); } -void VideoControls::setInputDeviceRate(const std::string& rate) +void +VideoControls::setActiveDeviceRate(const std::string &rate) { videoPreference_.setRate(rate); } @@ -160,15 +154,11 @@ VideoControls::getSettings() { return videoPreference_.getSettings(); } -void VideoControls::startPreview(int32_t &width, int32_t &height, - int32_t &shmKey, int32_t &semKey, - int32_t &videoBufferSize) +void +VideoControls::startPreview() { if (preview_.get()) { ERROR("Video preview was already started!"); - shmKey = -1; - semKey = -1; - videoBufferSize = -1; return; } @@ -177,23 +167,28 @@ void VideoControls::startPreview(int32_t &width, int32_t &height, map<string, string> args(videoPreference_.getSettings()); preview_.reset(new sfl_video::VideoPreview(args)); - - width = atoi(args["width"].c_str()); - height = atoi(args["height"].c_str()); - preview_->getShmInfo(shmKey, semKey, videoBufferSize); } -void VideoControls::stopPreview() +void +VideoControls::stopPreview() { if (preview_.get()) { DEBUG("Stopping video preview"); preview_.reset(); + } else { + ERROR("Video preview was already stopped"); } } +bool +VideoControls::hasPreviewStarted() +{ + return preview_.get() != 0; +} + std::string -VideoControls::getCurrentCodecName(const std::string& callID) +VideoControls::getCurrentCodecName(const std::string &callID) { - return Manager::instance().getCurrentCodecName(callID); + return Manager::instance().getCurrentVideoCodecName(callID); } diff --git a/daemon/src/dbus/video_controls.h b/daemon/src/dbus/video_controls.h index fe1fc653f59afdb22be67f078223a4fda0875c79..99800263855405a552e437552003ca2a3268b073 100644 --- a/daemon/src/dbus/video_controls.h +++ b/daemon/src/dbus/video_controls.h @@ -67,40 +67,58 @@ class VideoControls : public org::sflphone::SFLphone::VideoControls_adaptor, VideoControls(DBus::Connection& connection); VideoPreference &getVideoPreferences(); - std::vector<std::string> getCodecList(); - std::map<std::string, std::string> getCodecDetails(const std::string& name); - std::vector<std::string> - getActiveCodecList(const std::string& accountID); - void setActiveCodecList(const std::vector<std::string>& list, - const std::string& accountID); + std::vector<std::map<std::string, std::string> > + getCodecs(const std::string& accountID); + + void + setCodecs(const std::string& accountID, + const std::vector<std::map<std::string, std::string> > &details); - std::vector<std::string> getInputDeviceList(); + std::vector<std::string> + getDeviceList(); std::vector<std::string> - getInputDeviceChannelList(const std::string &dev); + getDeviceChannelList(const std::string &dev); std::vector<std::string> - getInputDeviceSizeList(const std::string &dev, - const std::string &channel); + getDeviceSizeList(const std::string &dev, const std::string &channel); std::vector<std::string> - getInputDeviceRateList(const std::string &dev, - const std::string &channel, - const std::string &size); - std::map<std::string, std::string> getSettings(); - void setInputDevice(const std::string& api); - void setInputDeviceChannel(const std::string& api); - void setInputDeviceSize(const std::string& api); - void setInputDeviceRate(const std::string& api); - std::string getInputDevice(); - std::string getInputDeviceChannel(); - std::string getInputDeviceSize(); - std::string getInputDeviceRate(); - std::string getCurrentCodecName(const std::string &callID); - - void startPreview(int32_t &width, int32_t &height, int32_t &shmKey, - int32_t &semKey, int32_t &bufferSize); + getDeviceRateList(const std::string &dev, const std::string &channel, const std::string &size); + + std::map<std::string, std::string> + getSettings(); + + void + setActiveDevice(const std::string &dev); + + void + setActiveDeviceChannel(const std::string &channel); + + void + setActiveDeviceSize(const std::string &size); + + void + setActiveDeviceRate(const std::string &rate); + + std::string + getActiveDevice(); + + std::string + getActiveDeviceChannel(); + + std::string + getActiveDeviceSize(); + + std::string + getActiveDeviceRate(); + + std::string + getCurrentCodecName(const std::string &callID); + + void startPreview(); void stopPreview(); + bool hasPreviewStarted(); }; #endif // VIDEO_CONTROLS_H_ diff --git a/daemon/src/iax/iaxaccount.cpp b/daemon/src/iax/iaxaccount.cpp index 61ba80a26b236f0d10f7ab9963dea7d1cefd7b48..ee2040180c4f27c2d82eee912eca8aa385e5b2e5 100644 --- a/daemon/src/iax/iaxaccount.cpp +++ b/daemon/src/iax/iaxaccount.cpp @@ -36,6 +36,7 @@ #endif #include "iaxaccount.h" +#include "account_schema.h" #include "iaxvoiplink.h" #include "logger.h" #include "manager.h" diff --git a/daemon/src/iax/iaxvoiplink.cpp b/daemon/src/iax/iaxvoiplink.cpp index 85e529b7b55f12d846beec3a87ddfd23fa2de51b..2bbbe581878924ac94ba3bc94f007a68d9413859 100644 --- a/daemon/src/iax/iaxvoiplink.cpp +++ b/daemon/src/iax/iaxvoiplink.cpp @@ -229,7 +229,7 @@ IAXVoIPLink::sendRegister(Account *a) if (regSession_) { iax_register(regSession_, account->getHostname().data(), account->getUsername().data(), account->getPassword().data(), 120); nextRefreshStamp_ = time(NULL) + 10; - account->setRegistrationState(Trying); + account->setRegistrationState(TRYING); } } @@ -244,7 +244,7 @@ IAXVoIPLink::sendUnregister(Account *a) nextRefreshStamp_ = 0; - dynamic_cast<IAXAccount*>(a)->setRegistrationState(Unregistered); + dynamic_cast<IAXAccount*>(a)->setRegistrationState(UNREGISTERED); } Call* @@ -412,16 +412,15 @@ IAXVoIPLink::sendTextMessage(const std::string& callID, } } -#ifdef SFL_VIDEO std::string -IAXVoIPLink::getCurrentVideoCodecName(const std::string& /*id*/) +IAXVoIPLink::getCurrentVideoCodecName(Call * /*call*/) const { + // FIXME: Video not supported for IAX yet return ""; } -#endif std::string -IAXVoIPLink::getCurrentCodecName(Call *c) const +IAXVoIPLink::getCurrentAudioCodecName(Call *c) const { IAXCall *call = dynamic_cast<IAXCall*>(c); sfl::Codec *audioCodec = Manager::instance().audioCodecFactory.getCodec(call->getAudioCodec()); @@ -601,7 +600,7 @@ void IAXVoIPLink::iaxHandleRegReply(iax_event* event) iax_destroy(regSession_); regSession_ = NULL; - account->setRegistrationState((event->etype == IAX_EVENT_REGREJ) ? ErrorAuth : Registered); + account->setRegistrationState((event->etype == IAX_EVENT_REGREJ) ? ERROR_AUTH : REGISTERED); if (event->etype == IAX_EVENT_REGACK) nextRefreshStamp_ = time(NULL) + (event->ies.refresh ? event->ies.refresh : 60); diff --git a/daemon/src/iax/iaxvoiplink.h b/daemon/src/iax/iaxvoiplink.h index 4e016b4aa6bf1cff0158319169b6f996e8addbea..804d4c629634deac1f71f58ae9505e34367bd67e 100644 --- a/daemon/src/iax/iaxvoiplink.h +++ b/daemon/src/iax/iaxvoiplink.h @@ -173,10 +173,8 @@ class IAXVoIPLink : public VoIPLink { * Return the codec protocol used for this call * @param id The call identifier */ -#ifdef SFL_VIDEO - virtual std::string getCurrentVideoCodecName(const std::string& id); -#endif - virtual std::string getCurrentCodecName(Call *c) const; + virtual std::string getCurrentVideoCodecName(Call *c) const; + virtual std::string getCurrentAudioCodecName(Call *c) const; private: NON_COPYABLE(IAXVoIPLink); diff --git a/daemon/src/main.cpp b/daemon/src/main.cpp index 2891ff76aca2b03a9b2989977c127c335621855c..54a7b9b1c7d410ec217628764cbd526ce072079d 100644 --- a/daemon/src/main.cpp +++ b/daemon/src/main.cpp @@ -137,7 +137,14 @@ int main(int argc, char *argv []) signal(SIGHUP, signal_handler); signal(SIGTERM, signal_handler); - fileutils::set_program_dir(argv[0]); + // make a copy as we don't want to modify argv[0], copy it to a vector to + // guarantee that memory is correctly managed/exception safe + std::string programName(argv[0]); + std::vector<char> writable(programName.size() + 1); + std::copy(programName.begin(), programName.end(), writable.begin()); + + fileutils::set_program_dir(&*writable.begin()); + print_title(); if (parse_args(argc, argv)) return 0; @@ -156,6 +163,10 @@ int main(int argc, char *argv []) return 1; } +#ifdef SFL_VIDEO + WARN("Built with video support"); +#endif + Manager::instance().run(); Manager::instance().saveHistory(); diff --git a/daemon/src/managerimpl.cpp b/daemon/src/managerimpl.cpp index bc1a0716695c6479065f82501676e3236e33a55e..b9984b85c4126221e9c97b8c1f44d87364761f8b 100644 --- a/daemon/src/managerimpl.cpp +++ b/daemon/src/managerimpl.cpp @@ -38,6 +38,7 @@ #include "logger.h" #include "managerimpl.h" +#include "account_schema.h" #include "account.h" #include "dbus/callmanager.h" @@ -266,7 +267,7 @@ bool ManagerImpl::outgoingCall(const std::string& account_id, return false; } - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); return true; } @@ -321,18 +322,14 @@ bool ManagerImpl::answerCall(const std::string& call_id) // Connect streams addStream(call_id); - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); // Start recording if set in preference if (audioPreference.getIsAlwaysRecording()) setRecordingCall(call_id); // update call state on client side - if (audioPreference.getIsAlwaysRecording()) - dbus_.getCallManager()->callStateChanged(call_id, "RECORD"); - else - dbus_.getCallManager()->callStateChanged(call_id, "CURRENT"); - + dbus_.getCallManager()->callStateChanged(call_id, "CURRENT"); return true; } @@ -370,23 +367,27 @@ void ManagerImpl::hangupCall(const std::string& callId) /* Direct IP to IP call */ try { Call * call = SIPVoIPLink::instance()->getCall(callId); - history_.addCall(call, preferences.getHistoryLimit()); - SIPVoIPLink::instance()->hangup(callId); - saveHistory(); + if (call) { + history_.addCall(call, preferences.getHistoryLimit()); + SIPVoIPLink::instance()->hangup(callId); + saveHistory(); + } } catch (const VoipLinkException &e) { ERROR("%s", e.what()); } } else { std::string accountId(getAccountFromCall(callId)); Call * call = getCallFromCallID(callId); - history_.addCall(call, preferences.getHistoryLimit()); - VoIPLink *link = getAccountLink(accountId); - link->hangup(callId); - removeCallAccount(callId); - saveHistory(); + if (call) { + history_.addCall(call, preferences.getHistoryLimit()); + VoIPLink *link = getAccountLink(accountId); + link->hangup(callId); + removeCallAccount(callId); + saveHistory(); + } } - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); } bool ManagerImpl::hangupConference(const std::string& id) @@ -412,7 +413,7 @@ bool ManagerImpl::hangupConference(const std::string& id) unsetCurrentCall(); - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); return true; } @@ -458,7 +459,7 @@ void ManagerImpl::onHoldCall(const std::string& callId) dbus_.getCallManager()->callStateChanged(callId, "HOLD"); - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); } //THREAD=Main @@ -483,8 +484,6 @@ void ManagerImpl::offHoldCall(const std::string& callId) detachParticipant(MainBuffer::DEFAULT_ID, currentCallId); } - bool isRec = false; - if (isIPToIP(callId)) SIPVoIPLink::instance()->offhold(callId); else { @@ -493,13 +492,11 @@ void ManagerImpl::offHoldCall(const std::string& callId) DEBUG("Setting offhold, Account %s, callid %s", accountId.c_str(), callId.c_str()); Call * call = getAccountLink(accountId)->getCall(callId); - if (call) { - isRec = call->isRecording(); + if (call) getAccountLink(accountId)->offhold(callId); - } } - dbus_.getCallManager()->callStateChanged(callId, isRec ? "UNHOLD_RECORD" : "UNHOLD_CURRENT"); + dbus_.getCallManager()->callStateChanged(callId, "UNHOLD"); if (isConferenceParticipant(callId)) { Call *call = getCallFromCallID(callId); @@ -511,7 +508,7 @@ void ManagerImpl::offHoldCall(const std::string& callId) addStream(callId); - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); } //THREAD=Main @@ -538,7 +535,7 @@ bool ManagerImpl::transferCall(const std::string& callId, const std::string& to) // remove waiting call in case we make transfer without even answer removeWaitingCall(callId); - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); return true; } @@ -599,7 +596,7 @@ void ManagerImpl::refuseCall(const std::string& id) // Disconnect streams removeStream(id); - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); } Conference* @@ -924,11 +921,9 @@ void ManagerImpl::joinParticipant(const std::string& callId1, const std::string& } else if (call1_state_str == "INCOMING") { conf->bindParticipant(callId1); answerCall(callId1); - } else if (call1_state_str == "CURRENT") - conf->bindParticipant(callId1); - else if (call1_state_str == "RECORD") + } else if (call1_state_str == "CURRENT") { conf->bindParticipant(callId1); - else if (call1_state_str == "INACTIVE") { + } else if (call1_state_str == "INACTIVE") { conf->bindParticipant(callId1); answerCall(callId1); } else @@ -944,11 +939,9 @@ void ManagerImpl::joinParticipant(const std::string& callId1, const std::string& } else if (call2_state_str == "INCOMING") { conf->bindParticipant(callId2); answerCall(callId2); - } else if (call2_state_str == "CURRENT") + } else if (call2_state_str == "CURRENT") { conf->bindParticipant(callId2); - else if (call2_state_str == "RECORD") - conf->bindParticipant(callId2); - else if (call2_state_str == "INACTIVE") { + } else if (call2_state_str == "INACTIVE") { conf->bindParticipant(callId2); answerCall(callId2); } else @@ -965,7 +958,7 @@ void ManagerImpl::joinParticipant(const std::string& callId1, const std::string& conf->setRecordingSmplRate(audiodriver_->getSampleRate()); } - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); } void ManagerImpl::createConfFromParticipantList(const std::vector< std::string > &participantList) @@ -1017,7 +1010,7 @@ void ManagerImpl::createConfFromParticipantList(const std::vector< std::string > conf->setRecordingSmplRate(audiodriver_->getSampleRate()); } - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); } else { delete conf; } @@ -1115,7 +1108,7 @@ void ManagerImpl::removeParticipant(const std::string& call_id) call->setConfId(""); removeStream(call_id); - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); dbus_.getCallManager()->conferenceChanged(conf->getConfID(), conf->getStateStr()); processRemainingParticipants(*conf); } @@ -1221,14 +1214,14 @@ void ManagerImpl::addStream(const std::string& call_id) audiodriver_->flushMain(); } - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); } void ManagerImpl::removeStream(const std::string& call_id) { DEBUG("Remove audio stream %s", call_id.c_str()); getMainBuffer()->unBindAll(call_id); - getMainBuffer()->stateInfo(); + getMainBuffer()->dumpInfo(); } //THREAD=Main @@ -1253,8 +1246,7 @@ void ManagerImpl::saveConfig() hookPreference.serialize(emitter); audioPreference.serialize(emitter); #ifdef SFL_VIDEO - VideoControls *controls(Manager::instance().getDbusManager()->getVideoControls()); - controls->getVideoPreferences().serialize(emitter); + getVideoControls()->getVideoPreferences().serialize(emitter); #endif shortcutPreferences.serialize(emitter); @@ -1516,11 +1508,10 @@ void ManagerImpl::peerAnsweredCall(const std::string& id) audiodriver_->flushUrgent(); } - if (audioPreference.getIsAlwaysRecording()) { + if (audioPreference.getIsAlwaysRecording()) setRecordingCall(id); - dbus_.getCallManager()->callStateChanged(id, "RECORD"); - } else - dbus_.getCallManager()->callStateChanged(id, "CURRENT"); + + dbus_.getCallManager()->callStateChanged(id, "CURRENT"); } //THREAD=VoIP Call=Outgoing @@ -1828,7 +1819,7 @@ std::string ManagerImpl::join_string(const std::vector<std::string> &v) return os.str(); } -std::string ManagerImpl::getCurrentCodecName(const std::string& id) +std::string ManagerImpl::getCurrentAudioCodecName(const std::string& id) { std::string accountid = getAccountFromCall(id); VoIPLink* link = getAccountLink(accountid); @@ -1839,20 +1830,23 @@ std::string ManagerImpl::getCurrentCodecName(const std::string& id) Call::CallState state = call->getState(); if (state == Call::ACTIVE or state == Call::CONFERENCING) - codecName = link->getCurrentCodecName(call); + codecName = link->getCurrentAudioCodecName(call); } return codecName; } -#ifdef SFL_VIDEO -std::string ManagerImpl::getCurrentVideoCodecName(const std::string& ID) +std::string +ManagerImpl::getCurrentVideoCodecName(const std::string& ID) { std::string accountID = getAccountFromCall(ID); VoIPLink* link = getAccountLink(accountID); - return link->getCurrentVideoCodecName(ID); + Call *call(getCallFromCallID(ID)); + if (call) + return link->getCurrentVideoCodecName(call); + else + return ""; } -#endif /** * Set input audio plugin @@ -2513,14 +2507,6 @@ std::vector<std::string> ManagerImpl::loadAccountOrder() const return split_string(preferences.getAccountOrder()); } -void ManagerImpl::loadDefaultAccountMap() -{ - // build a default IP2IP account with default parameters - accountMap_[SIPAccount::IP2IP_PROFILE] = new SIPAccount(SIPAccount::IP2IP_PROFILE); - SIPVoIPLink::instance()->sipTransport.createDefaultSipUdpTransport(); - accountMap_[SIPAccount::IP2IP_PROFILE]->registerVoIPLink(); -} - namespace { bool isIP2IP(const Conf::YamlNode *node) { @@ -2582,13 +2568,31 @@ namespace { } } + SIPAccount *createIP2IPAccount() + { + SIPAccount *ip2ip = new SIPAccount(SIPAccount::IP2IP_PROFILE); + try { + SIPVoIPLink::instance()->sipTransport.createSipTransport(*ip2ip); + } catch (const std::runtime_error &e) { + ERROR("%s", e.what()); + } + return ip2ip; + } + } // end anonymous namespace +void ManagerImpl::loadDefaultAccountMap() +{ + // build a default IP2IP account with default parameters + accountMap_[SIPAccount::IP2IP_PROFILE] = createIP2IPAccount(); + accountMap_[SIPAccount::IP2IP_PROFILE]->registerVoIPLink(); +} + void ManagerImpl::loadAccountMap(Conf::YamlParser &parser) { using namespace Conf; // build a default IP2IP account with default parameters - accountMap_[SIPAccount::IP2IP_PROFILE] = new SIPAccount(SIPAccount::IP2IP_PROFILE); + accountMap_[SIPAccount::IP2IP_PROFILE] = createIP2IPAccount(); // load saved preferences for IP2IP account from configuration file Sequence *seq = parser.getAccountSequence()->getSequence(); @@ -2600,11 +2604,7 @@ void ManagerImpl::loadAccountMap(Conf::YamlParser &parser) accountMap_[SIPAccount::IP2IP_PROFILE]->unserialize(*node); } - // Initialize default UDP transport according to - // IP to IP settings (most likely using port 5060) - SIPVoIPLink::instance()->sipTransport.createDefaultSipUdpTransport(); - - // Force IP2IP settings to be loaded to be loaded + // Force IP2IP settings to be loaded // No registration in the sense of the REGISTER method is performed. accountMap_[SIPAccount::IP2IP_PROFILE]->registerVoIPLink(); @@ -2616,7 +2616,7 @@ void ManagerImpl::loadAccountMap(Conf::YamlParser &parser) audioPreference.unserialize(*parser.getAudioNode()); shortcutPreferences.unserialize(*parser.getShortcutNode()); #ifdef SFL_VIDEO - VideoControls *controls(Manager::instance().getDbusManager()->getVideoControls()); + VideoControls *controls(getVideoControls()); try { MappingNode *videoNode = parser.getVideoNode(); if (videoNode) diff --git a/daemon/src/managerimpl.h b/daemon/src/managerimpl.h index e6674d746080c8899c85f47bc397b8ed1fc7ac03..6ef104c05a7db1b61c88df85fbaf7ff190e07b6d 100644 --- a/daemon/src/managerimpl.h +++ b/daemon/src/managerimpl.h @@ -35,6 +35,10 @@ #ifndef MANAGER_IMPL_H_ #define MANAGER_IMPL_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include <string> #include <vector> #include <set> @@ -510,10 +514,8 @@ class ManagerImpl { * @param call id * @return std::string The codec name */ - std::string getCurrentCodecName(const std::string& id); -#ifdef SFL_VIDEO + std::string getCurrentAudioCodecName(const std::string& id); std::string getCurrentVideoCodecName(const std::string& id); -#endif /** * Set input audio plugin @@ -1065,6 +1067,12 @@ class ManagerImpl { return &dbus_; } +#ifdef SFL_VIDEO + VideoControls * getVideoControls() { + return dbus_.getVideoControls(); + } +#endif + /** * Tell if an account exists * @param accountID account ID check diff --git a/daemon/src/numbercleaner.cpp b/daemon/src/numbercleaner.cpp index c49c2240ed345b6b732b36327171ba52971817b0..3f9273df644d701fe0c33a7b7c692ccaa14fc1ea 100644 --- a/daemon/src/numbercleaner.cpp +++ b/daemon/src/numbercleaner.cpp @@ -32,6 +32,8 @@ #include "numbercleaner.h" #include <algorithm> +#define INVALID_CHAR " -()" + namespace { void strip_chars(const std::string &to_strip, std::string &num) { @@ -43,6 +45,16 @@ void strip_chars(const std::string &to_strip, std::string &num) std::string NumberCleaner::clean(std::string to_clean, const std::string &prefix) { - strip_chars(" -()", to_clean); - return to_clean.insert(0, prefix); + int pos; + //Hostname and DNS can have '-' + if ((pos = to_clean.find("@")) == std::string::npos) { + strip_chars(INVALID_CHAR, to_clean); + return to_clean.insert(0, prefix); + } + else { + std::string high = to_clean.substr(0,pos+1); + strip_chars(INVALID_CHAR, high); + return high+to_clean.substr(pos+1); + } + } diff --git a/daemon/src/registration_states.h b/daemon/src/registration_states.h new file mode 100644 index 0000000000000000000000000000000000000000..f8fd96948bfd70e8e63631a4880e2343bec41533 --- /dev/null +++ b/daemon/src/registration_states.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * Author: Yan Morin <yan.morin@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#ifndef REGISTRATION_STATES_H_ +#define REGISTRATION_STATES_H_ + +/** Contains all the Registration states for an account can be in */ +typedef enum { + UNREGISTERED, + TRYING, + REGISTERED, + ERROR_GENERIC, + ERROR_AUTH , + ERROR_NETWORK , + ERROR_HOST, + ERROR_EXIST_STUN, + ERROR_NOT_ACCEPTABLE, + NUMBER_OF_STATES +} RegistrationState; + +#endif // REGISTRATION_STATES_H_ diff --git a/daemon/src/sip/sdp.cpp b/daemon/src/sip/sdp.cpp index be44dd6b1367460cee0a2583d626123ccc40285c..4226ab988c5d4e0b9157bf22253240634bb7fa49 100644 --- a/daemon/src/sip/sdp.cpp +++ b/daemon/src/sip/sdp.cpp @@ -38,10 +38,12 @@ #include "logger.h" #include "manager.h" -#ifdef SFL_VIDEO #include <algorithm> -#include "video/video_endpoint.h" -#endif + +using std::string; +using std::map; +using std::vector; +using std::stringstream; Sdp::Sdp(pj_pool_t *pool) : memPool_(pool) @@ -51,23 +53,15 @@ Sdp::Sdp(pj_pool_t *pool) , activeLocalSession_(NULL) , activeRemoteSession_(NULL) , audio_codec_list_() -#ifdef SFL_VIDEO , video_codec_list_() -#endif , sessionAudioMedia_() -#ifdef SFL_VIDEO , sessionVideoMedia_() -#endif , localIpAddr_() , remoteIpAddr_() , localAudioPort_(0) -#ifdef SFL_VIDEO , localVideoPort_(0) -#endif , remoteAudioPort_(0) -#ifdef SFL_VIDEO , remoteVideoPort_(0) -#endif , zrtpHelloHash_() , srtpCrypto_() , telephoneEventPayload_(101) // same as asterisk @@ -80,37 +74,34 @@ void Sdp::setActiveLocalSdpSession(const pjmedia_sdp_session *sdp) if (activeLocalSession_->media_count < 1) return; - pjmedia_sdp_media *current = activeLocalSession_->media[0]; + for (unsigned media = 0; media < activeLocalSession_->media_count; ++media) { + pjmedia_sdp_media *current = activeLocalSession_->media[media]; - for (unsigned j = 0; j < current->desc.fmt_count; j++) { - static const pj_str_t STR_RTPMAP = { (char*) "rtpmap", 6 }; - pjmedia_sdp_attr *attribute = pjmedia_sdp_media_find_attr(current, &STR_RTPMAP, NULL); + for (unsigned fmt = 0; fmt < current->desc.fmt_count; ++fmt) { + static const pj_str_t STR_RTPMAP = { (char*) "rtpmap", 6 }; + pjmedia_sdp_attr *attribute = pjmedia_sdp_media_find_attr(current, &STR_RTPMAP, NULL); - if (!attribute) { - sessionAudioMedia_.clear(); - return; - } - - pjmedia_sdp_rtpmap *rtpmap; - pjmedia_sdp_attr_to_rtpmap(memPool_, attribute, &rtpmap); - - std::string type(current->desc.media.ptr, current->desc.media.slen); - if (type == "audio") { - const int pt = pj_strtoul(&rtpmap->pt); - sfl::Codec *codec = Manager::instance().audioCodecFactory.getCodec(pt); - if (codec) - sessionAudioMedia_.push_back(codec); - else { - DEBUG("Could not get codec for payload type %lu", pt); - sessionAudioMedia_.clear(); - return; + if (!attribute) { + ERROR("Could not find rtpmap attribute"); + break; } + + pjmedia_sdp_rtpmap *rtpmap; + pjmedia_sdp_attr_to_rtpmap(memPool_, attribute, &rtpmap); + + string type(current->desc.media.ptr, current->desc.media.slen); + if (type == "audio") { + const int pt = pj_strtoul(&rtpmap->pt); + sfl::Codec *codec = Manager::instance().audioCodecFactory.getCodec(pt); + if (codec) + sessionAudioMedia_.push_back(codec); + else { + DEBUG("Could not get codec for payload type %lu", pt); + break; + } + } else if (type == "video") + sessionVideoMedia_.push_back(string(rtpmap->enc_name.ptr, rtpmap->enc_name.slen)); } -#ifdef SFL_VIDEO - else if (type == "video") { - sessionVideoMedia_.push_back(std::string(rtpmap->enc_name.ptr, rtpmap->enc_name.slen)); - } -#endif } } @@ -144,16 +135,16 @@ void Sdp::setActiveRemoteSdpSession(const pjmedia_sdp_session *sdp) ERROR("Could not found dtmf event from remote sdp"); } -#ifdef SFL_VIDEO -std::string Sdp::getSessionVideoCodec() const +string Sdp::getSessionVideoCodec() const { - if (sessionVideoMedia_.empty()) + if (sessionVideoMedia_.empty()) { + DEBUG("Session video media is empty"); return ""; + } return sessionVideoMedia_[0]; } -#endif -std::string Sdp::getAudioCodecName() const +string Sdp::getAudioCodecName() const { try { sfl::AudioCodec *codec = getSessionAudioMedia(); @@ -172,7 +163,6 @@ sfl::AudioCodec* Sdp::getSessionAudioMedia() const } -#ifdef SFL_VIDEO pjmedia_sdp_media *Sdp::setMediaDescriptorLine(bool audio) { pjmedia_sdp_media *med = PJ_POOL_ZALLOC_T(memPool_, pjmedia_sdp_media); @@ -188,7 +178,7 @@ pjmedia_sdp_media *Sdp::setMediaDescriptorLine(bool audio) med->desc.fmt_count = audio ? audio_codec_list_.size() : video_codec_list_.size(); for (unsigned i = 0; i < med->desc.fmt_count; ++i) { unsigned clock_rate; - std::string enc_name; + string enc_name; int payload; if (audio) { @@ -200,7 +190,8 @@ pjmedia_sdp_media *Sdp::setMediaDescriptorLine(bool audio) if (codec->getPayloadType () == 9) clock_rate = 8000; } else { - enc_name = video_codec_list_[i]; + // FIXME: get this key from header + enc_name = video_codec_list_[i]["name"]; clock_rate = 90000; payload = dynamic_payload; } @@ -245,61 +236,6 @@ pjmedia_sdp_media *Sdp::setMediaDescriptorLine(bool audio) return med; } -#else -pjmedia_sdp_media *Sdp::setMediaDescriptorLine(bool /*audio*/) -{ - pjmedia_sdp_media *med = PJ_POOL_ZALLOC_T(memPool_, pjmedia_sdp_media); - - med->desc.media = pj_str((char*) "audio"); - med->desc.port_count = 1; - med->desc.port = localAudioPort_; - // in case of sdes, media are tagged as "RTP/SAVP", RTP/AVP elsewhere - med->desc.transport = pj_str(srtpCrypto_.empty() ? (char*) "RTP/AVP" : (char*) "RTP/SAVP"); - - med->desc.fmt_count = audio_codec_list_.size(); - - for (unsigned i = 0; i < med->desc.fmt_count; ++i) { - sfl::Codec *codec = audio_codec_list_[i]; - - std::ostringstream result; - result << static_cast<int>(codec->getPayloadType()); - - pj_strdup2(memPool_, &med->desc.fmt[i], result.str().c_str()); - - // Add a rtpmap field for each codec - // We could add one only for dynamic payloads because the codecs with static RTP payloads - // are entirely defined in the RFC 3351, but if we want to add other attributes like an asymmetric - // connection, the rtpmap attribute will be useful to specify for which codec it is applicable - pjmedia_sdp_rtpmap rtpmap; - rtpmap.pt = med->desc.fmt[i]; - rtpmap.enc_name = pj_str((char*) codec->getMimeSubtype().c_str()); - - // G722 require G722/8000 media description even if it is 16000 codec - if (codec->getPayloadType() == 9) { - rtpmap.clock_rate = 8000; - } else { - rtpmap.clock_rate = codec->getClockRate(); - } - - rtpmap.param.ptr = ((char* const)""); - rtpmap.param.slen = 0; - - pjmedia_sdp_attr *attr; - pjmedia_sdp_rtpmap_to_attr(memPool_, &rtpmap, &attr); - - med->attr[med->attr_count++] = attr; - } - - med->attr[ med->attr_count++] = pjmedia_sdp_attr_create(memPool_, "sendrecv", NULL); - - if (!zrtpHelloHash_.empty()) - addZrtpAttribute(med, zrtpHelloHash_); - - setTelephoneEventRtpmap(med); - - return med; -} -#endif void Sdp::setTelephoneEventRtpmap(pjmedia_sdp_media *med) { @@ -316,27 +252,26 @@ void Sdp::setTelephoneEventRtpmap(pjmedia_sdp_media *med) med->attr[med->attr_count++] = attr_fmtp; } -#ifdef SFL_VIDEO -void Sdp::setLocalMediaVideoCapabilities(const std::vector<std::string> &selectedCodecs) +void Sdp::setLocalMediaVideoCapabilities(const vector<map<string, string> > &codecs) { - if (selectedCodecs.empty()) - throw SdpException("No selected video codec while building local SDP offer"); - video_codec_list_.clear(); - const std::vector<std::string> &codecs_list = sfl_video::getCodecList(); - for (std::vector<std::string>::const_iterator i = selectedCodecs.begin(); i != selectedCodecs.end(); ++i) - if (std::find(codecs_list.begin(), codecs_list.end(), *i) != codecs_list.end()) - video_codec_list_.push_back(*i); -} +#ifdef SFL_VIDEO + if (codecs.empty()) + WARN("No selected video codec while building local SDP offer"); + else + video_codec_list_ = codecs; +#else + (void) codecs; #endif +} -void Sdp::setLocalMediaAudioCapabilities(const std::vector<int> &selectedCodecs) +void Sdp::setLocalMediaAudioCapabilities(const vector<int> &selectedCodecs) { if (selectedCodecs.empty()) WARN("No selected codec while building local SDP offer"); audio_codec_list_.clear(); - for (std::vector<int>::const_iterator i = selectedCodecs.begin(); i != selectedCodecs.end(); ++i) { + for (vector<int>::const_iterator i = selectedCodecs.begin(); i != selectedCodecs.end(); ++i) { sfl::Codec *codec = Manager::instance().audioCodecFactory.getCodec(*i); if (codec) @@ -351,13 +286,12 @@ namespace { { char buffer[2048]; size_t size = pjmedia_sdp_print(session, buffer, sizeof(buffer)); - std::string sessionStr(buffer, std::min(size, sizeof(buffer))); + string sessionStr(buffer, std::min(size, sizeof(buffer))); DEBUG("%s", sessionStr.c_str()); } } -#ifdef SFL_VIDEO -int Sdp::createLocalSession(const std::vector<int> &selectedAudioCodecs, const std::vector<std::string> &selectedVideoCodecs) +int Sdp::createLocalSession(const vector<int> &selectedAudioCodecs, const vector<map<string, string> > &selectedVideoCodecs) { setLocalMediaAudioCapabilities(selectedAudioCodecs); setLocalMediaVideoCapabilities(selectedVideoCodecs); @@ -390,93 +324,34 @@ int Sdp::createLocalSession(const std::vector<int> &selectedAudioCodecs, const s localSession_->time.stop = 0; // For DTMF RTP events - localSession_->media_count = 2; const bool audio = true; - localSession_->media[0] = setMediaDescriptorLine(audio); - localSession_->media[1] = setMediaDescriptorLine(!audio); - - if (!srtpCrypto_.empty()) - addSdesAttribute(srtpCrypto_); - - DEBUG("SDP: Local SDP Session:"); - printSession(localSession_); - - return pjmedia_sdp_validate(localSession_); -} -#else -int Sdp::createLocalSession(const std::vector<int> &selectedCodecs) -{ - setLocalMediaAudioCapabilities(selectedCodecs); - - localSession_ = PJ_POOL_ZALLOC_T(memPool_, pjmedia_sdp_session); - if (!localSession_) { - ERROR("Could not create local SDP session"); - return !PJ_SUCCESS; - } - - localSession_->conn = PJ_POOL_ZALLOC_T(memPool_, pjmedia_sdp_conn); - - /* Initialize the fields of the struct */ - localSession_->origin.version = 0; - pj_time_val tv; - pj_gettimeofday(&tv); - - localSession_->origin.user = pj_str(pj_gethostname()->ptr); - // Use Network Time Protocol format timestamp to ensure uniqueness. - localSession_->origin.id = tv.sec + 2208988800UL; - localSession_->origin.net_type = pj_str((char*) "IN"); - localSession_->origin.addr_type = pj_str((char*) "IP4"); - localSession_->origin.addr = pj_str((char*) localIpAddr_.c_str()); - - localSession_->name = pj_str((char*) PACKAGE); - - localSession_->conn->net_type = localSession_->origin.net_type; - localSession_->conn->addr_type = localSession_->origin.addr_type; - localSession_->conn->addr = localSession_->origin.addr; - - // RFC 3264: An offer/answer model session description protocol - // As the session is created and destroyed through an external signaling mean (SIP), the line - // should have a value of "0 0". - localSession_->time.start = 0; - localSession_->time.stop = 0; - - // For DTMF RTP events localSession_->media_count = 1; - const bool audio = true; localSession_->media[0] = setMediaDescriptorLine(audio); + if (not selectedVideoCodecs.empty()) { + localSession_->media[1] = setMediaDescriptorLine(!audio); + ++localSession_->media_count; + } if (!srtpCrypto_.empty()) addSdesAttribute(srtpCrypto_); - DEBUG("Local SDP Session:"); + DEBUG("SDP: Local SDP Session:"); printSession(localSession_); return pjmedia_sdp_validate(localSession_); } -#endif -#ifdef SFL_VIDEO -void Sdp::createOffer(const std::vector<int> &selectedCodecs, const std::vector<std::string> &videoCodecs) +void Sdp::createOffer(const vector<int> &selectedCodecs, const vector<map<string, string> > &videoCodecs) { if (createLocalSession(selectedCodecs, videoCodecs) != PJ_SUCCESS) ERROR("Failed to create initial offer"); else if (pjmedia_sdp_neg_create_w_local_offer(memPool_, localSession_, &negotiator_) != PJ_SUCCESS) ERROR("Failed to create an initial SDP negotiator"); } -#else -void Sdp::createOffer(const std::vector<int> &selectedCodecs) -{ - if (createLocalSession(selectedCodecs) != PJ_SUCCESS) - ERROR("Failed to create initial offer"); - else if (pjmedia_sdp_neg_create_w_local_offer(memPool_, localSession_, &negotiator_) != PJ_SUCCESS) - ERROR("Failed to create an initial SDP negotiator"); -} -#endif -#ifdef SFL_VIDEO void Sdp::receiveOffer(const pjmedia_sdp_session* remote, - const std::vector<int> &selectedCodecs, - const std::vector<std::string> &videoCodecs) + const vector<int> &selectedCodecs, + const vector<map<string, string> > &videoCodecs) { if (!remote) { ERROR("Remote session is NULL"); @@ -496,32 +371,6 @@ void Sdp::receiveOffer(const pjmedia_sdp_session* remote, pjmedia_sdp_neg_create_w_remote_offer(memPool_, localSession_, remoteSession_, &negotiator_); } -#else -void Sdp::receiveOffer(const pjmedia_sdp_session* remote, - const std::vector<int> &selectedCodecs) -{ - if (!remote) { - ERROR("Remote session is NULL"); - return; - } - - DEBUG("Remote SDP Session:"); - printSession(remote); - - if (localSession_ == NULL and createLocalSession(selectedCodecs) != PJ_SUCCESS) { - ERROR("Failed to create initial offer"); - return; - } - - remoteSession_ = pjmedia_sdp_session_clone(memPool_, remote); - - if (pjmedia_sdp_neg_create_w_remote_offer(memPool_, localSession_, - remoteSession_, &negotiator_) != PJ_SUCCESS) { - ERROR("Could not create negotiator with remote offer"); - negotiator_ = NULL; - } -} -#endif void Sdp::startNegotiation() { @@ -552,10 +401,6 @@ void Sdp::startNegotiation() namespace { - using std::string; - using std::vector; - using std::stringstream; - vector<string> split(const string &s, char delim) { vector<string> elems; @@ -567,51 +412,44 @@ namespace } } // end anonymous namespace -std::string Sdp::getLineFromLocalSDP(const std::string &keyword) const +string Sdp::getLineFromSession(const pjmedia_sdp_session *sess, const string &keyword) const { char buffer[2048]; - int size = pjmedia_sdp_print(activeLocalSession_, buffer, sizeof buffer); - std::string sdp(buffer, size); - const std::vector<std::string> tokens(split(sdp, '\n')); - for (std::vector<std::string>::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter) + int size = pjmedia_sdp_print(sess, buffer, sizeof buffer); + string sdp(buffer, size); + const vector<string> tokens(split(sdp, '\n')); + for (vector<string>::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter) if ((*iter).find(keyword) != string::npos) return *iter; return ""; } -#ifdef SFL_VIDEO -std::vector<std::string> Sdp::getActiveVideoDescription() const +string Sdp::getActiveIncomingVideoDescription() const { - std::stringstream ss; + stringstream ss; ss << "v=0" << std::endl; ss << "o=- 0 0 IN IP4 " << localIpAddr_ << std::endl; ss << "s=sflphone" << std::endl; ss << "c=IN IP4 " << remoteIpAddr_ << std::endl; ss << "t=0 0" << std::endl; - std::string videoLine(getLineFromLocalSDP("m=video")); + std::string videoLine(getLineFromSession(activeLocalSession_, "m=video")); ss << videoLine << std::endl; - int payload; - if (sscanf(videoLine.c_str(), "m=video %*d %*s %d", &payload) != 1) - payload = 0; + int payload_num; + if (sscanf(videoLine.c_str(), "m=video %*d %*s %d", &payload_num) != 1) + payload_num = 0; std::ostringstream s; s << "a=rtpmap:"; - s << payload; + s << payload_num; - std::string vCodecLine(getLineFromLocalSDP(s.str())); + std::string vCodecLine(getLineFromSession(activeLocalSession_, s.str())); ss << vCodecLine << std::endl; - char codec[32]; - codec[0] = '\0'; - sscanf(vCodecLine.c_str(), "a=rtpmap:%*d %31[^/]", codec); - - std::vector<std::string> v; - - unsigned videoIdx = 0; - while (pj_stricmp2(&activeLocalSession_->media[videoIdx]->desc.media, "video") != 0) - ++videoIdx; + unsigned videoIdx; + for (videoIdx = 0; videoIdx < activeLocalSession_->media_count and pj_stricmp2(&activeLocalSession_->media[videoIdx]->desc.media, "video") != 0; ++videoIdx) + ; // get direction string static const pj_str_t DIRECTIONS[] = { @@ -630,20 +468,47 @@ std::vector<std::string> Sdp::getActiveVideoDescription() const if (direction) ss << "a=" + std::string(direction->name.ptr, direction->name.slen) << std::endl; - v.push_back(ss.str()); - v.push_back(codec); + return ss.str(); +} - ss.str(""); - ss << payload; +std::string Sdp::getActiveOutgoingVideoCodec() const +{ + string str("a=rtpmap:"); + str += getActiveOutgoingVideoPayload(); + string vCodecLine(getLineFromSession(activeRemoteSession_, str)); + char codec_buf[32]; + codec_buf[0] = '\0'; + sscanf(vCodecLine.c_str(), "a=rtpmap:%*d %31[^/]", codec_buf); + return string(codec_buf); +} - v.push_back(ss.str()); - return v; +std::string Sdp::getActiveOutgoingVideoBitrate(const std::string &codec) const +{ + for (vector<map<string, string> >::const_iterator i = video_codec_list_.begin(); i != video_codec_list_.end(); ++i) { + map<string, string>::const_iterator name = i->find("name"); + if (name != i->end() and (codec == name->second)) { + map<string, string>::const_iterator bitrate = i->find("bitrate"); + if (bitrate != i->end()) + return bitrate->second; + } + } + return "0"; } -#endif -void Sdp::addSdesAttribute(const std::vector<std::string>& crypto) +std::string Sdp::getActiveOutgoingVideoPayload() const { - for (std::vector<std::string>::const_iterator iter = crypto.begin(); + string videoLine(getLineFromSession(activeRemoteSession_, "m=video")); + int payload_num; + if (sscanf(videoLine.c_str(), "m=video %*d %*s %d", &payload_num) != 1) + payload_num = 0; + std::ostringstream os; + os << payload_num; + return os.str(); +} + +void Sdp::addSdesAttribute(const vector<std::string>& crypto) +{ + for (vector<std::string>::const_iterator iter = crypto.begin(); iter != crypto.end(); ++iter) { pj_str_t val = { (char*)(*iter).c_str(), static_cast<pj_ssize_t>((*iter).size()) }; pjmedia_sdp_attr *attr = pjmedia_sdp_attr_create(memPool_, "crypto", &val); @@ -691,7 +556,6 @@ void Sdp::removeAttributeFromLocalAudioMedia(const char *attr) pjmedia_sdp_media_remove_all_attr(localSession_->media[i], attr); } -#ifdef SFL_VIDEO void Sdp::removeAttributeFromLocalVideoMedia(const char *attr) { int i = 0; @@ -709,7 +573,6 @@ void Sdp::addAttributeToLocalVideoMedia(const char *attr) pjmedia_sdp_attr *attribute = pjmedia_sdp_attr_create(memPool_, attr, NULL); pjmedia_sdp_media_add_attr(localSession_->media[i], attribute); } -#endif void Sdp::setMediaTransportInfoFromRemoteSdp() { @@ -722,14 +585,10 @@ void Sdp::setMediaTransportInfoFromRemoteSdp() if (pj_stricmp2(&activeRemoteSession_->media[i]->desc.media, "audio") == 0) { remoteAudioPort_ = activeRemoteSession_->media[i]->desc.port; remoteIpAddr_ = std::string(activeRemoteSession_->conn->addr.ptr, activeRemoteSession_->conn->addr.slen); - } - -#ifdef SFL_VIDEO - else if (pj_stricmp2(&activeRemoteSession_->media[i]->desc.media, "video") == 0) { + } else if (pj_stricmp2(&activeRemoteSession_->media[i]->desc.media, "video") == 0) { remoteVideoPort_ = activeRemoteSession_->media[i]->desc.port; remoteIpAddr_ = std::string(activeRemoteSession_->conn->addr.ptr, activeRemoteSession_->conn->addr.slen); } -#endif } } diff --git a/daemon/src/sip/sdp.h b/daemon/src/sip/sdp.h index 93c9f9d9c8da32813f98707c2ace8863754aad73..fb0f9d2eedba1f02f56733d4f550718ef5cf4a73 100644 --- a/daemon/src/sip/sdp.h +++ b/daemon/src/sip/sdp.h @@ -32,10 +32,6 @@ #ifndef SDP_H_ #define SDP_H_ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include <pjmedia/sdp.h> #include <pjmedia/sdp_neg.h> #include <pjsip/sip_transport.h> @@ -113,24 +109,20 @@ class Sdp { */ void setActiveRemoteSdpSession(const pjmedia_sdp_session *sdp); -#ifdef SFL_VIDEO /** * Returns a string version of the negotiated SDP fields which pertain * to video. - * Second member of the vector is the video codec rtp name */ - std::vector<std::string> getActiveVideoDescription() const; -#endif + std::string getActiveIncomingVideoDescription() const; + std::string getActiveOutgoingVideoCodec() const; + std::string getActiveOutgoingVideoBitrate(const std::string &codec) const; + std::string getActiveOutgoingVideoPayload() const; /* * On building an invite outside a dialog, build the local offer and create the * SDP negotiator instance with it. */ -#ifdef SFL_VIDEO - void createOffer(const std::vector<int> &selectedCodecs, const std::vector<std::string> &videoCodecs); -#else - void createOffer(const std::vector<int> &selectedCodecs); -#endif + void createOffer(const std::vector<int> &selectedCodecs, const std::vector<std::map<std::string, std::string> > &videoCodecs); /* * On receiving an invite outside a dialog, build the local offer and create the @@ -138,14 +130,9 @@ class Sdp { * * @param remote The remote offer */ -#ifdef SFL_VIDEO void receiveOffer(const pjmedia_sdp_session* remote, const std::vector<int> &selectedCodecs, - const std::vector<std::string> &videoCodecs); -#else - void receiveOffer(const pjmedia_sdp_session* remote, - const std::vector<int> &selectedCodecs); -#endif + const std::vector<std::map<std::string, std::string> > &videoCodecs); /** * Start the sdp negotiation. @@ -180,11 +167,9 @@ class Sdp { localAudioPort_ = port; } -#ifdef SFL_VIDEO void setLocalPublishedVideoPort (int port) { localVideoPort_ = port; } -#endif /** * Return IP of destination @@ -210,7 +195,6 @@ class Sdp { return remoteAudioPort_; } -#ifdef SFL_VIDEO /** * Return video port at destination * @return unsigned int The remote video port @@ -218,14 +202,11 @@ class Sdp { unsigned int getRemoteVideoPort() const { return remoteVideoPort_; } -#endif void addAttributeToLocalAudioMedia(const char *attr); void removeAttributeFromLocalAudioMedia(const char *attr); -#ifdef SFL_VIDEO void addAttributeToLocalVideoMedia(const char *attr); void removeAttributeFromLocalVideoMedia(const char *attr); -#endif /** * Get SRTP master key @@ -259,16 +240,14 @@ class Sdp { void setMediaTransportInfoFromRemoteSdp(); std::string getAudioCodecName() const; -#ifdef SFL_VIDEO std::string getSessionVideoCodec() const; -#endif sfl::AudioCodec* getSessionAudioMedia() const; private: NON_COPYABLE(Sdp); friend class SDPTest; - std::string getLineFromLocalSDP(const std::string &keyword) const; + std::string getLineFromSession(const pjmedia_sdp_session *sess, const std::string &keyword) const; /** * The pool to allocate memory, ownership to SipCall @@ -307,29 +286,21 @@ class Sdp { * Codec Map used for offer */ std::vector<sfl::Codec *> audio_codec_list_; -#ifdef SFL_VIDEO - std::vector<std::string> video_codec_list_; -#endif + std::vector<std::map<std::string, std::string> > video_codec_list_; /** * The codecs that will be used by the session (after the SDP negotiation) */ std::vector<sfl::Codec *> sessionAudioMedia_; -#ifdef SFL_VIDEO std::vector<std::string> sessionVideoMedia_; -#endif std::string localIpAddr_; std::string remoteIpAddr_; int localAudioPort_; -#ifdef SFL_VIDEO int localVideoPort_; -#endif unsigned int remoteAudioPort_; -#ifdef SFL_VIDEO unsigned int remoteVideoPort_; -#endif std::string zrtpHelloHash_; @@ -353,18 +324,12 @@ class Sdp { * @param List of codec in preference order */ void setLocalMediaAudioCapabilities(const std::vector<int> &selected); -#ifdef SFL_VIDEO - void setLocalMediaVideoCapabilities(const std::vector<std::string> &selected); -#endif - + void setLocalMediaVideoCapabilities(const std::vector<std::map<std::string, std::string> > &codecs); /* * Build the local SDP offer */ -#ifdef SFL_VIDEO - int createLocalSession(const std::vector<int> &selectedAudio, const std::vector<std::string> &selectedVideo); -#else - int createLocalSession(const std::vector<int> &selectedAudio); -#endif + int createLocalSession(const std::vector<int> &selectedAudio, + const std::vector<std::map<std::string, std::string> > &selectedVideo); /* * Adds a sdes attribute to the given media section. * diff --git a/daemon/src/sip/sipaccount.cpp b/daemon/src/sip/sipaccount.cpp index d568e027b1be42b6dac8a34324ad4cdc37951f76..03d02cbd5b206fe096796b44e552bcb88f7889ac 100644 --- a/daemon/src/sip/sipaccount.cpp +++ b/daemon/src/sip/sipaccount.cpp @@ -34,6 +34,7 @@ #include "config.h" #endif +#include "account_schema.h" #include "sipaccount.h" #include "sipvoiplink.h" #include "config/yamlnode.h" @@ -44,6 +45,10 @@ #include <sstream> #include <stdlib.h> +#ifdef SFL_VIDEO +#include "video/libav_utils.h" +#endif + const char * const SIPAccount::IP2IP_PROFILE = "IP2IP"; const char * const SIPAccount::OVERRTP_STR = "overrtp"; const char * const SIPAccount::SIPINFO_STR = "sipinfo"; @@ -102,7 +107,7 @@ SIPAccount::SIPAccount(const std::string& accountID) , keepAliveTimer_() , keepAliveTimerActive_(false) , link_(SIPVoIPLink::instance()) - , receivedParameter_() + , receivedParameter_("") , rPort_(-1) { if (isIP2IP()) @@ -114,6 +119,7 @@ void SIPAccount::serialize(Conf::YamlEmitter &emitter) using namespace Conf; using std::vector; using std::string; + using std::map; MappingNode accountmap(NULL); MappingNode srtpmap(NULL); MappingNode zrtpmap(NULL); @@ -144,11 +150,18 @@ void SIPAccount::serialize(Conf::YamlEmitter &emitter) ScalarNode publishPort(publicportstr.str()); ScalarNode sameasLocal(publishedSameasLocal_); - DEBUG("%s", audioCodecStr_.c_str()); ScalarNode audioCodecs(audioCodecStr_); #ifdef SFL_VIDEO - DEBUG("%s", videoCodecStr_.c_str()); - ScalarNode videoCodecs(videoCodecStr_); + SequenceNode videoCodecs(NULL); + accountmap.setKeyValue(VIDEO_CODECS_KEY, &videoCodecs); + for (vector<map<string, string> >::iterator i = videoCodecList_.begin(); i != videoCodecList_.end(); ++i) { + map<string, string> &codec = *i; + MappingNode *mapNode = new MappingNode(NULL); + mapNode->setKeyValue(VIDEO_CODEC_NAME, new ScalarNode(codec[VIDEO_CODEC_NAME])); + mapNode->setKeyValue(VIDEO_CODEC_BITRATE, new ScalarNode(codec[VIDEO_CODEC_BITRATE])); + mapNode->setKeyValue(VIDEO_CODEC_ENABLED, new ScalarNode(codec[VIDEO_CODEC_ENABLED])); + videoCodecs.addNode(mapNode); + } #endif ScalarNode ringtonePath(ringtonePath_); @@ -207,9 +220,6 @@ void SIPAccount::serialize(Conf::YamlEmitter &emitter) accountmap.setKeyValue(DTMF_TYPE_KEY, &dtmfType); accountmap.setKeyValue(DISPLAY_NAME_KEY, &displayName); accountmap.setKeyValue(AUDIO_CODECS_KEY, &audioCodecs); -#ifdef SFL_VIDEO - accountmap.setKeyValue(VIDEO_CODECS_KEY, &videoCodecs); -#endif accountmap.setKeyValue(RINGTONE_PATH_KEY, &ringtonePath); accountmap.setKeyValue(RINGTONE_ENABLED_KEY, &ringtoneEnabled); accountmap.setKeyValue(KEEP_ALIVE_ENABLED, &keepAliveEnabled); @@ -260,68 +270,107 @@ void SIPAccount::serialize(Conf::YamlEmitter &emitter) ERROR("%s", e.what()); } - Sequence *seq = credentialseq.getSequence(); - Sequence::iterator seqit; - - for (seqit = seq->begin(); seqit != seq->end(); ++seqit) { + // Cleanup + Sequence *credSeq = credentialseq.getSequence(); + for (Sequence::iterator seqit = credSeq->begin(); seqit != credSeq->end(); ++seqit) { MappingNode *node = static_cast<MappingNode*>(*seqit); delete node->getValue(CONFIG_ACCOUNT_USERNAME); delete node->getValue(CONFIG_ACCOUNT_PASSWORD); delete node->getValue(CONFIG_ACCOUNT_REALM); delete node; } + +#ifdef SFL_VIDEO + Sequence *videoCodecSeq = videoCodecs.getSequence(); + for (Sequence::iterator i = videoCodecSeq->begin(); i != videoCodecSeq->end(); ++i) { + MappingNode *node = static_cast<MappingNode*>(*i); + delete node->getValue(VIDEO_CODEC_NAME); + delete node->getValue(VIDEO_CODEC_BITRATE); + delete node->getValue(VIDEO_CODEC_ENABLED); + delete node; + } +#endif } -void SIPAccount::unserialize(const Conf::MappingNode &map) +void SIPAccount::unserialize(const Conf::MappingNode &mapNode) { using namespace Conf; + using std::vector; + using std::map; + using std::string; - map.getValue(ALIAS_KEY, &alias_); - map.getValue(TYPE_KEY, &type_); - map.getValue(USERNAME_KEY, &username_); - if (not isIP2IP()) map.getValue(HOSTNAME_KEY, &hostname_); - map.getValue(ACCOUNT_ENABLE_KEY, &enabled_); - if (not isIP2IP()) map.getValue(MAILBOX_KEY, &mailBox_); - map.getValue(AUDIO_CODECS_KEY, &audioCodecStr_); + mapNode.getValue(ALIAS_KEY, &alias_); + mapNode.getValue(TYPE_KEY, &type_); + mapNode.getValue(USERNAME_KEY, &username_); + if (not isIP2IP()) mapNode.getValue(HOSTNAME_KEY, &hostname_); + mapNode.getValue(ACCOUNT_ENABLE_KEY, &enabled_); + if (not isIP2IP()) mapNode.getValue(MAILBOX_KEY, &mailBox_); + mapNode.getValue(AUDIO_CODECS_KEY, &audioCodecStr_); // Update codec list which one is used for SDP offer setActiveAudioCodecs(ManagerImpl::split_string(audioCodecStr_)); #ifdef SFL_VIDEO - map.getValue(VIDEO_CODECS_KEY, &videoCodecStr_); - setActiveVideoCodecs(ManagerImpl::split_string(videoCodecStr_)); + YamlNode *videoCodecsNode(mapNode.getValue(VIDEO_CODECS_KEY)); + + if (videoCodecsNode and videoCodecsNode->getType() == SEQUENCE) { + SequenceNode *videoCodecs = static_cast<SequenceNode *>(videoCodecsNode); + Sequence *seq = videoCodecs->getSequence(); + if (seq->empty()) { + // Video codecs are an empty list + WARN("Loading default video codecs"); + videoCodecList_ = libav_utils::getDefaultCodecs(); + } else { + vector<map<string, string> > videoCodecDetails; + for (Sequence::iterator it = seq->begin(); it != seq->end(); ++it) { + MappingNode *codec = static_cast<MappingNode *>(*it); + map<string, string> codecMap; + codec->getValue(VIDEO_CODEC_NAME, &codecMap[VIDEO_CODEC_NAME]); + codec->getValue(VIDEO_CODEC_BITRATE, &codecMap[VIDEO_CODEC_BITRATE]); + codec->getValue(VIDEO_CODEC_ENABLED, &codecMap[VIDEO_CODEC_ENABLED]); + videoCodecDetails.push_back(codecMap); + } + // these must be validated + setVideoCodecs(videoCodecDetails); + } + } else { + // either this is an older config file which had videoCodecs as a scalar node, + // or it had no video codecs at all + WARN("Loading default video codecs"); + videoCodecList_ = libav_utils::getDefaultCodecs(); + } #endif - map.getValue(RINGTONE_PATH_KEY, &ringtonePath_); - map.getValue(RINGTONE_ENABLED_KEY, &ringtoneEnabled_); - if (not isIP2IP()) map.getValue(Preferences::REGISTRATION_EXPIRE_KEY, ®istrationExpire_); - map.getValue(INTERFACE_KEY, &interface_); + mapNode.getValue(RINGTONE_PATH_KEY, &ringtonePath_); + mapNode.getValue(RINGTONE_ENABLED_KEY, &ringtoneEnabled_); + if (not isIP2IP()) mapNode.getValue(Preferences::REGISTRATION_EXPIRE_KEY, ®istrationExpire_); + mapNode.getValue(INTERFACE_KEY, &interface_); int port = DEFAULT_SIP_PORT; - map.getValue(PORT_KEY, &port); + mapNode.getValue(PORT_KEY, &port); localPort_ = port; - map.getValue(PUBLISH_ADDR_KEY, &publishedIpAddress_); - map.getValue(PUBLISH_PORT_KEY, &port); + mapNode.getValue(PUBLISH_ADDR_KEY, &publishedIpAddress_); + mapNode.getValue(PUBLISH_PORT_KEY, &port); publishedPort_ = port; - map.getValue(SAME_AS_LOCAL_KEY, &publishedSameasLocal_); - if (not isIP2IP()) map.getValue(KEEP_ALIVE_ENABLED, &keepAliveEnabled_); + mapNode.getValue(SAME_AS_LOCAL_KEY, &publishedSameasLocal_); + if (not isIP2IP()) mapNode.getValue(KEEP_ALIVE_ENABLED, &keepAliveEnabled_); std::string dtmfType; - map.getValue(DTMF_TYPE_KEY, &dtmfType); + mapNode.getValue(DTMF_TYPE_KEY, &dtmfType); dtmfType_ = dtmfType; - if (not isIP2IP()) map.getValue(SERVICE_ROUTE_KEY, &serviceRoute_); - map.getValue(UPDATE_CONTACT_HEADER_KEY, &contactUpdateEnabled_); + if (not isIP2IP()) mapNode.getValue(SERVICE_ROUTE_KEY, &serviceRoute_); + mapNode.getValue(UPDATE_CONTACT_HEADER_KEY, &contactUpdateEnabled_); // stun enabled - if (not isIP2IP()) map.getValue(STUN_ENABLED_KEY, &stunEnabled_); - if (not isIP2IP()) map.getValue(STUN_SERVER_KEY, &stunServer_); + if (not isIP2IP()) mapNode.getValue(STUN_ENABLED_KEY, &stunEnabled_); + if (not isIP2IP()) mapNode.getValue(STUN_SERVER_KEY, &stunServer_); // Init stun server name with default server name stunServerName_ = pj_str((char*) stunServer_.data()); - map.getValue(DISPLAY_NAME_KEY, &displayName_); + mapNode.getValue(DISPLAY_NAME_KEY, &displayName_); std::vector<std::map<std::string, std::string> > creds; - YamlNode *credNode = map.getValue(CRED_KEY); + YamlNode *credNode = mapNode.getValue(CRED_KEY); /* We check if the credential key is a sequence * because it was a mapping in a previous version of @@ -352,7 +401,7 @@ void SIPAccount::unserialize(const Conf::MappingNode &map) // migration from old file format std::map<std::string, std::string> credmap; std::string password; - map.getValue(PASSWORD_KEY, &password); + if (not isIP2IP()) mapNode.getValue(PASSWORD_KEY, &password); credmap[CONFIG_ACCOUNT_USERNAME] = username_; credmap[CONFIG_ACCOUNT_PASSWORD] = password; @@ -363,7 +412,7 @@ void SIPAccount::unserialize(const Conf::MappingNode &map) setCredentials(creds); // get srtp submap - MappingNode *srtpMap = static_cast<MappingNode *>(map.getValue(SRTP_KEY)); + MappingNode *srtpMap = static_cast<MappingNode *>(mapNode.getValue(SRTP_KEY)); if (srtpMap) { srtpMap->getValue(SRTP_ENABLE_KEY, &srtpEnabled_); @@ -372,7 +421,7 @@ void SIPAccount::unserialize(const Conf::MappingNode &map) } // get zrtp submap - MappingNode *zrtpMap = static_cast<MappingNode *>(map.getValue(ZRTP_KEY)); + MappingNode *zrtpMap = static_cast<MappingNode *>(mapNode.getValue(ZRTP_KEY)); if (zrtpMap) { zrtpMap->getValue(DISPLAY_SAS_KEY, &zrtpDisplaySas_); @@ -382,7 +431,7 @@ void SIPAccount::unserialize(const Conf::MappingNode &map) } // get tls submap - MappingNode *tlsMap = static_cast<MappingNode *>(map.getValue(TLS_KEY)); + MappingNode *tlsMap = static_cast<MappingNode *>(mapNode.getValue(TLS_KEY)); if (tlsMap) { tlsMap->getValue(TLS_ENABLE_KEY, &tlsEnable_); @@ -495,7 +544,7 @@ std::map<std::string, std::string> SIPAccount::getAccountDetails() const a[CONFIG_RINGTONE_ENABLED] = ringtoneEnabled_ ? "true" : "false"; a[CONFIG_ACCOUNT_MAILBOX] = mailBox_; - RegistrationState state = Unregistered; + RegistrationState state = UNREGISTERED; std::string registrationStateCode; std::string registrationStateDescription; @@ -618,7 +667,7 @@ void SIPAccount::startKeepAliveTimer() { if (isIP2IP()) return; - if(keepAliveTimerActive_) + if (keepAliveTimerActive_) return; DEBUG("Start keep alive timer for account %s", getAccountID().c_str()); @@ -635,8 +684,7 @@ void SIPAccount::startKeepAliveTimer() { if (registrationExpire_ == 0) { DEBUG("Registration Expire: 0, taking 60 instead"); keepAliveDelay_.sec = 3600; - } - else { + } else { DEBUG("Registration Expire: %d", registrationExpire_); keepAliveDelay_.sec = registrationExpire_ + MIN_REGISTRATION_TIME; } diff --git a/daemon/src/sip/sipaccount.h b/daemon/src/sip/sipaccount.h index df258f6fe7ed0dc142460ed7177e57930df022f7..cb4292f3d0f334f4e8712fce4ed86dc114b9189c 100644 --- a/daemon/src/sip/sipaccount.h +++ b/daemon/src/sip/sipaccount.h @@ -205,6 +205,10 @@ class SIPAccount : public Account { return credentials_.size(); } + bool hasCredentials() const { + return not credentials_.empty(); + } + void setCredentials(const std::vector<std::map<std::string, std::string> >& details); const std::vector<std::map<std::string, std::string> > & @@ -418,7 +422,7 @@ class SIPAccount : public Account { * @return pj_uint16 The port used for that account */ pj_uint16_t getLocalPort() const { - return (pj_uint16_t) localPort_; + return localPort_; } /** diff --git a/daemon/src/sip/sipcall.cpp b/daemon/src/sip/sipcall.cpp index e6f63e8f8d9c305bee78ae5f961c0612b138f71f..34c56000ec714e3798516e088fae61dcc6e5a0c5 100644 --- a/daemon/src/sip/sipcall.cpp +++ b/daemon/src/sip/sipcall.cpp @@ -33,13 +33,10 @@ #include "sipcall.h" #include "logger.h" // for _debug -#include "audio/audiortp/audio_rtp_factory.h" #include "sdp.h" #include "manager.h" #ifdef SFL_VIDEO #include "dbus/video_controls.h" - -#include "video/video_rtp_session.h" #endif namespace { @@ -52,7 +49,8 @@ SIPCall::SIPCall(const std::string& id, Call::CallType type, , inv(NULL) , audiortp_(this) #ifdef SFL_VIDEO - , videortp_(new sfl_video::VideoRtpSession(Manager::instance().getDbusManager()->getVideoControls()->getSettings())) + // The ID is used to associate video streams to calls + , videortp_(id, Manager::instance().getDbusManager()->getVideoControls()->getSettings()) #endif , pool_(pj_pool_create(&caching_pool->factory, id.c_str(), INITIAL_SIZE, INCREMENT_SIZE, NULL)) , local_sdp_(new Sdp(pool_)) diff --git a/daemon/src/sip/sipcall.h b/daemon/src/sip/sipcall.h index 5523b5131562e4070d14c253e9a0041e19764f5a..a5884f953da66b7fc27dee6abc4180372194dd78 100644 --- a/daemon/src/sip/sipcall.h +++ b/daemon/src/sip/sipcall.h @@ -37,8 +37,11 @@ #endif #include "call.h" -#include <tr1/memory> #include "audio/audiortp/audio_rtp_factory.h" +#ifdef SFL_VIDEO +#include "video/video_rtp_session.h" +#endif + #include "noncopyable.h" class pjsip_evsub; @@ -47,13 +50,6 @@ class pj_pool_t; class pjsip_inv_session; class Sdp; -#ifdef SFL_VIDEO -namespace sfl_video -{ -class VideoRtpSession; -} -#endif - /** * @file sipcall.h * @brief SIPCall are SIP implementation of a normal Call @@ -92,8 +88,8 @@ class SIPCall : public Call { /** * Returns a pointer to the VideoRtp object */ - sfl_video::VideoRtpSession * getVideoRtp () { - return videortp_.get(); + sfl_video::VideoRtpSession &getVideoRtp () { + return videortp_; } #endif @@ -123,7 +119,7 @@ class SIPCall : public Call { /** * Video Rtp Session factory */ - std::tr1::shared_ptr<sfl_video::VideoRtpSession> videortp_; + sfl_video::VideoRtpSession videortp_; #endif /** diff --git a/daemon/src/sip/siptransport.cpp b/daemon/src/sip/siptransport.cpp index e33545637b325640079e62fe9c54ae16cf31fd30..e149283efa46b2bd44f1d8da18a32e3d6debc4ce 100644 --- a/daemon/src/sip/siptransport.cpp +++ b/daemon/src/sip/siptransport.cpp @@ -59,19 +59,17 @@ #include "dbus/configurationmanager.h" static const char * const DEFAULT_INTERFACE = "default"; +static const char * const ANY_HOSTS = "0.0.0.0"; -static pjsip_transport *localUDPTransport_ = NULL; /** The default transport (5060) */ +#define RETURN_IF_FAIL(A, VAL, M, ...) if (!(A)) { ERROR(M, ##__VA_ARGS__); return (VAL); } std::string SipTransport::getSIPLocalIP() { pj_sockaddr ip_addr; - if (pj_gethostip(pj_AF_INET(), &ip_addr) == PJ_SUCCESS) - return pj_inet_ntoa(ip_addr.ipv4.sin_addr); - else { - ERROR("Could not get local IP"); - return ""; - } + const pj_status_t status = pj_gethostip(pj_AF_INET(), &ip_addr); + RETURN_IF_FAIL(status == PJ_SUCCESS, "", "Could not get local IP"); + return pj_inet_ntoa(ip_addr.ipv4.sin_addr); } std::vector<std::string> SipTransport::getAllIpInterfaceByName() @@ -100,12 +98,11 @@ std::vector<std::string> SipTransport::getAllIpInterfaceByName() std::string SipTransport::getInterfaceAddrFromName(const std::string &ifaceName) { - int fd = socket(AF_INET, SOCK_DGRAM,0); + if (ifaceName == DEFAULT_INTERFACE) + return getSIPLocalIP(); - if (fd < 0) { - ERROR("Error: could not open socket: %m"); - return ""; - } + int fd = socket(AF_INET, SOCK_DGRAM,0); + RETURN_IF_FAIL(fd >= 0, "", "Could not open socket: %m"); ifreq ifr; strcpy(ifr.ifr_name, ifaceName.c_str()); @@ -116,7 +113,10 @@ std::string SipTransport::getInterfaceAddrFromName(const std::string &ifaceName) close(fd); sockaddr_in *saddr_in = (sockaddr_in *) &ifr.ifr_addr; - return inet_ntoa(saddr_in->sin_addr); + std::string result(inet_ntoa(saddr_in->sin_addr)); + if (result == ANY_HOSTS) + result = getSIPLocalIP(); + return result; } std::vector<std::string> SipTransport::getAllIpInterface() @@ -162,11 +162,10 @@ stun_sock_on_status_cb(pj_stun_sock * /*stun_sock*/, pj_stun_sock_op op, break; } - if (status == PJ_SUCCESS) { + if (status == PJ_SUCCESS) DEBUG("STUN operation success"); - } else { + else ERROR("STUN operation failure"); - } // Always return true so the stun transport registration retry even on failure return true; @@ -223,7 +222,7 @@ pj_status_t SipTransport::createStunResolver(pj_str_t serverName, pj_uint16_t po char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); DEBUG("Error starting STUN socket for %.*s: %s", - (int) serverName.slen, serverName.ptr, errmsg); + (int) serverName.slen, serverName.ptr, errmsg); pj_stun_sock_destroy(stun_sock); } @@ -254,10 +253,7 @@ pjsip_tpfactory* SipTransport::createTlsListener(SIPAccount &account) pj_sockaddr_in_init(&local_addr, 0, 0); local_addr.sin_port = pj_htons(account.getTlsListenerPort()); - if (account.getTlsSetting() == NULL) { - ERROR("Error TLS settings not specified"); - return NULL; - } + RETURN_IF_FAIL(account.getTlsSetting() != NULL, NULL, "TLS settings not specified"); std::string interface(account.getLocalInterface()); std::string listeningAddress; @@ -275,11 +271,8 @@ pjsip_tpfactory* SipTransport::createTlsListener(SIPAccount &account) pj_sockaddr_in_set_port(&local_addr, account.getTlsListenerPort()); pjsip_tpfactory *listener = NULL; - if (pjsip_tls_transport_start(endpt_, account.getTlsSetting(), &local_addr, - NULL, 1, &listener) != PJ_SUCCESS) { - ERROR("Failed to start tls listener"); - listener = NULL; - } + const pj_status_t status = pjsip_tls_transport_start(endpt_, account.getTlsSetting(), &local_addr, NULL, 1, &listener); + RETURN_IF_FAIL(status == PJ_SUCCESS, NULL, "Failed to start TLS listener"); return listener; } @@ -300,9 +293,8 @@ SipTransport::createTlsTransport(SIPAccount &account) if (pos != std::string::npos) { ipAddr = remoteAddr.substr(0, pos); port = atoi(remoteAddr.substr(pos + 1, remoteAddr.length() - pos).c_str()); - } else { + } else ipAddr = remoteAddr; - } pj_str_t remote; pj_cstr(&remote, ipAddr.c_str()); @@ -320,12 +312,19 @@ SipTransport::createTlsTransport(SIPAccount &account) pjsip_transport *transport = NULL; pjsip_endpt_acquire_transport(endpt_, PJSIP_TRANSPORT_TLS, &rem_addr, sizeof rem_addr, NULL, &transport); - if (transport == NULL) - ERROR("Could not create new TLS transport\n"); - + RETURN_IF_FAIL(transport != NULL, NULL, "Could not create new TLS transport"); return transport; } +namespace { +std::string transportMapKey(const std::string &interface, int port) +{ + std::ostringstream os; + os << interface << ":" << port; + return os.str(); +} +} + void SipTransport::createSipTransport(SIPAccount &account) { shutdownSipTransport(account); @@ -339,60 +338,23 @@ void SipTransport::createSipTransport(SIPAccount &account) account.transport_ = createUdpTransport(account.getLocalInterface(), account.getLocalPort()); } } else { - account.transport_ = createUdpTransport(account.getLocalInterface(), account.getLocalPort()); - } + // if this transport already exists, reuse it + std::string key(transportMapKey(account.getLocalInterface(), account.getLocalPort())); + std::map<std::string, pjsip_transport *>::iterator iter = transportMap_.find(key); - if (!account.transport_) { - std::ostringstream key; - key << account.getLocalInterface(); - key << ":"; - key << account.getLocalPort(); - DEBUG("Looking into previously created transport map for" - " %s", key.str().c_str()); - // Could not create new transport, this transport may already exists - pjsip_transport *cachedTransport = transportMap_[key.str()]; - - if (cachedTransport) { - account.transport_ = cachedTransport; + if (iter != transportMap_.end()) { + account.transport_ = iter->second; pjsip_transport_add_ref(account.transport_); - } else { - if (account.isTlsEnabled()) - throw std::runtime_error("SipTransport: Could not create TLS connection"); - assert(localUDPTransport_); - account.transport_ = localUDPTransport_; - account.setLocalPort(localUDPTransport_->local_name.port); - } - } -} - -void SipTransport::createDefaultSipUdpTransport() -{ - DEBUG("Create default sip udp transport"); - - SIPAccount *account = Manager::instance().getIP2IPAccount(); - - pjsip_transport *transport = NULL; - pj_uint16_t port = 0; - static const int DEFAULT_TRANSPORT_ATTEMPTS = 5; - for (int counter = 0; transport == NULL and counter < DEFAULT_TRANSPORT_ATTEMPTS; ++counter) { - // 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); + } else + account.transport_ = createUdpTransport(account.getLocalInterface(), account.getLocalPort()); } - if (transport == NULL) { - ERROR("Could not create UDP transport"); - return; + if (!account.transport_) { + if (account.isTlsEnabled()) + throw std::runtime_error("Could not create TLS connection"); + else + throw std::runtime_error("Could not create new UDP transport"); } - - DEBUG("Created default sip transport on %d", port); - - // set transport for this account - account->transport_ = transport; - - // set local udp transport - localUDPTransport_ = account->transport_; } pjsip_transport * @@ -401,24 +363,15 @@ SipTransport::createUdpTransport(const std::string &interface, unsigned int port // init socket to bind this transport to pj_uint16_t listeningPort = (pj_uint16_t) port; - DEBUG("Create UDP transport on %s:%d", interface.c_str(), port); - - // determine the ip address for this transport + // determine the IP address for this transport std::string listeningAddress; if (interface == DEFAULT_INTERFACE) listeningAddress = getSIPLocalIP(); else listeningAddress = getInterfaceAddrFromName(interface); - if (listeningAddress.empty()) { - ERROR("Could not determine ip address for this transport"); - return NULL; - } - - if (listeningPort == 0) { - ERROR("Could not determine port for this transport"); - return NULL; - } + RETURN_IF_FAIL(not listeningAddress.empty(), NULL, "Could not determine ip address for this transport"); + RETURN_IF_FAIL(listeningPort != 0, NULL, "Could not determine port for this transport"); std::ostringstream fullAddress; fullAddress << listeningAddress << ":" << listeningPort; @@ -430,24 +383,19 @@ SipTransport::createUdpTransport(const std::string &interface, unsigned int port pj_status_t status; pjsip_transport *transport = NULL; - - if (boundAddr.addr.sa_family == pj_AF_INET()) { status = pjsip_udp_transport_start(endpt_, &boundAddr.ipv4, NULL, 1, &transport); - if (status != PJ_SUCCESS) { - return NULL; - } + RETURN_IF_FAIL(status == PJ_SUCCESS, NULL, "UDP IPV4 Transport did not start"); } else if (boundAddr.addr.sa_family == pj_AF_INET6()) { status = pjsip_udp_transport_start6(endpt_, &boundAddr.ipv6, NULL, 1, &transport); - if (status != PJ_SUCCESS) { - return NULL; - } + RETURN_IF_FAIL(status == PJ_SUCCESS, NULL, "UDP IPV6 Transport did not start"); } + DEBUG("Created UDP transport on %s:%d", interface.c_str(), port); DEBUG("Listening address %s", fullAddressStr.c_str()); // dump debug information to stdout pjsip_tpmgr_dump_transports(pjsip_endpt_get_tpmgr(endpt_)); - transportMap_[fullAddressStr] = transport; + transportMap_[transportMapKey(interface, port)] = transport; return transport; } @@ -463,16 +411,9 @@ SipTransport::createUdpTransport(const std::string &interface, unsigned int port interface.c_str(), port, publicAddr.c_str(), publicPort); // determine the ip address for this transport - std::string listeningAddress; - if (interface == DEFAULT_INTERFACE) - listeningAddress = getSIPLocalIP(); - else - listeningAddress = getInterfaceAddrFromName(interface); + std::string listeningAddress(getInterfaceAddrFromName(interface)); - if (listeningAddress.empty()) { - ERROR("Could not determine ip address for this transport"); - return NULL; - } + RETURN_IF_FAIL(not listeningAddress.empty(), NULL, "Could not determine ip address for this transport"); std::ostringstream fullAddress; fullAddress << listeningAddress << ":" << listeningPort; @@ -487,12 +428,9 @@ SipTransport::createUdpTransport(const std::string &interface, unsigned int port hostPort.host = public_addr; hostPort.port = publicPort; - pj_status_t status; - // status = pjsip_udp_transport_restart(transport, PJSIP_UDP_TRANSPORT_DESTROY_SOCKET, PJ_INVALID_SOCKET, &boundAddr.ipv4, &hostPort); - status = pjsip_udp_transport_start(endpt_, &boundAddr.ipv4, &hostPort, 1, &transport); - if (status != PJ_SUCCESS) { - ERROR("Could not start new transport with address %s:%d, error code %d", publicAddr.c_str(), publicPort, status); - } + pj_status_t status = pjsip_udp_transport_start(endpt_, &boundAddr.ipv4, &hostPort, 1, &transport); + RETURN_IF_FAIL(status == PJ_SUCCESS, NULL, + "Could not start new transport with address %s:%d, error code %d", publicAddr.c_str(), publicPort, status); // dump debug information to stdout pjsip_tpmgr_dump_transports(pjsip_endpt_get_tpmgr(endpt_)); @@ -502,10 +440,7 @@ SipTransport::createUdpTransport(const std::string &interface, unsigned int port pjsip_tpselector *SipTransport::initTransportSelector(pjsip_transport *transport, pj_pool_t *tp_pool) const { - if (!transport) { - ERROR("Transport is not initialized"); - return NULL; - } + RETURN_IF_FAIL(transport != NULL, NULL, "Transport is not initialized"); pjsip_tpselector *tp = (pjsip_tpselector *) pj_pool_zalloc(tp_pool, sizeof(pjsip_tpselector)); tp->type = PJSIP_TPSELECTOR_TRANSPORT; tp->u.transport = transport; @@ -514,31 +449,27 @@ pjsip_tpselector *SipTransport::initTransportSelector(pjsip_transport *transport pjsip_transport *SipTransport::createStunTransport(SIPAccount &account) { +#define RETURN_IF_STUN_FAIL(A, M, ...) \ + if (!(A)) { \ + ERROR(M, ##__VA_ARGS__); \ + Manager::instance().getDbusManager()->getConfigurationManager()->stunStatusFailure(account.getAccountID()); \ + return NULL; } + pj_str_t serverName = account.getStunServerName(); pj_uint16_t port = account.getStunPort(); DEBUG("Create STUN transport server name: %s, port: %d", serverName, port); - if (createStunResolver(serverName, port) != PJ_SUCCESS) { - ERROR("Can't resolve STUN server"); - Manager::instance().getDbusManager()->getConfigurationManager()->stunStatusFailure(account.getAccountID()); - return NULL; - } + RETURN_IF_STUN_FAIL(createStunResolver(serverName, port) == PJ_SUCCESS, "Can't resolve STUN server"); pj_sock_t sock = PJ_INVALID_SOCKET; pj_sockaddr_in boundAddr; - if (pj_sockaddr_in_init(&boundAddr, &serverName, 0) != PJ_SUCCESS) { - ERROR("Can't initialize IPv4 socket on %*s:%i", serverName.slen, serverName.ptr, port); - Manager::instance().getDbusManager()->getConfigurationManager()->stunStatusFailure(account.getAccountID()); - return NULL; - } + RETURN_IF_STUN_FAIL(pj_sockaddr_in_init(&boundAddr, &serverName, 0) == PJ_SUCCESS, + "Can't initialize IPv4 socket on %*s:%i", serverName.slen, serverName.ptr, port); - if (pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock) != PJ_SUCCESS) { - ERROR("Can't create or bind socket"); - Manager::instance().getDbusManager()->getConfigurationManager()->stunStatusFailure(account.getAccountID()); - return NULL; - } + RETURN_IF_STUN_FAIL(pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock) == PJ_SUCCESS, + "Can't create or bind socket"); // Query the mapped IP address and port on the 'outside' of the NAT pj_sockaddr_in pub_addr; @@ -562,6 +493,7 @@ pjsip_transport *SipTransport::createStunTransport(SIPAccount &account) pjsip_tpmgr_dump_transports(pjsip_endpt_get_tpmgr(endpt_)); return transport; +#undef RETURN_IF_STUN_FAIL } void SipTransport::shutdownSipTransport(SIPAccount &account) @@ -580,6 +512,8 @@ void SipTransport::shutdownSipTransport(SIPAccount &account) void SipTransport::findLocalAddressFromTransport(pjsip_transport *transport, pjsip_transport_type_e transportType, std::string &addr, std::string &port) const { +#define RETURN_IF_NULL(A, M, ...) if ((A) == NULL) { ERROR(M, ##__VA_ARGS__); return; } + // Initialize the sip port with the default SIP port std::stringstream ss; ss << DEFAULT_SIP_PORT; @@ -590,26 +524,17 @@ void SipTransport::findLocalAddressFromTransport(pjsip_transport *transport, pjs addr = std::string(pjMachineName->ptr, pjMachineName->slen); // Update address and port with active transport - if (!transport) { - ERROR("Transport is NULL in findLocalAddress, using local address %s:%s", addr.c_str(), port.c_str()); - return; - } + RETURN_IF_NULL(transport, "Transport is NULL in findLocalAddress, using local address %s:%s", addr.c_str(), port.c_str()); // get the transport manager associated with the SIP enpoint pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt_); - if (!tpmgr) { - ERROR("Transport manager is NULL in findLocalAddress, using local address %s:%s", addr.c_str(), port.c_str()); - return; - } + RETURN_IF_NULL(tpmgr, "Transport manager is NULL in findLocalAddress, using local address %s:%s", addr.c_str(), port.c_str()); // initialize a transport selector // TODO Need to determine why we exclude TLS here... // if (transportType == PJSIP_TRANSPORT_UDP and transport_) pjsip_tpselector *tp_sel = initTransportSelector(transport, pool_); - if (!tp_sel) { - ERROR("Could not initialize transport selector, using local address %s:%s", addr.c_str(), port.c_str()); - return; - } + RETURN_IF_NULL(tp_sel, "Could not initialize transport selector, using local address %s:%s", addr.c_str(), port.c_str()); pj_str_t localAddress = {0,0}; int i_port = 0; @@ -624,11 +549,13 @@ void SipTransport::findLocalAddressFromTransport(pjsip_transport *transport, pjs addr = std::string(localAddress.ptr, localAddress.slen); // Fallback on local ip provided by pj_gethostip() - if (addr == "0.0.0.0") + if (addr == ANY_HOSTS) addr = getSIPLocalIP(); // Determine the local port based on transport information ss.str(""); ss << i_port; port = ss.str(); + +#undef RETURN_IF_FAIL } diff --git a/daemon/src/sip/siptransport.h b/daemon/src/sip/siptransport.h index e37db61ae79a5178dd6147ed7074d3539fdc6ed2..7ca9ff1df293cd3f52bae58c1cf8ec1075be7c52 100644 --- a/daemon/src/sip/siptransport.h +++ b/daemon/src/sip/siptransport.h @@ -50,6 +50,11 @@ class SipTransport { SipTransport(pjsip_endpoint *endpt, pj_caching_pool *cp, pj_pool_t *pool); static std::string getSIPLocalIP(); + /** + * Get the IP for the network interface named ifaceName + */ + static std::string getInterfaceAddrFromName(const std::string &ifaceName); + /** * List all the interfaces on the system and return * a vector list containing their name (eth0, eth0:1 ...). @@ -60,16 +65,6 @@ class SipTransport { */ static std::vector<std::string> getAllIpInterfaceByName(); - /** - * List all the interfaces on the system and return - * a vector list containing their name (eth0, eth0:1 ...). - * @param void - * @return std::vector<std::string> A std::string vector - * of interface name available on all of the interfaces on - * the system. - */ - static std::string getInterfaceAddrFromName(const std::string &ifaceName); - /** * List all the interfaces on the system and return * a vector list containing their IPV4 address. @@ -101,12 +96,6 @@ class SipTransport { */ void createSipTransport(SIPAccount &account); - /** - * Create the default sip transport on 5060. In case this port is already used - * increme - */ - void createDefaultSipUdpTransport(); - /** * Initialize the transport selector * @param transport A transport associated with an account diff --git a/daemon/src/sip/sipvoiplink.cpp b/daemon/src/sip/sipvoiplink.cpp index 5e4d25e62e6db12af65b560875b2194e8af19296..4dd2020d2fe94e57864667c8ebeb52d046f9c45f 100644 --- a/daemon/src/sip/sipvoiplink.cpp +++ b/daemon/src/sip/sipvoiplink.cpp @@ -59,6 +59,7 @@ #ifdef SFL_VIDEO #include "video/video_rtp_session.h" +#include "dbus/video_controls.h" #endif #include "pjsip/sip_endpoint.h" @@ -151,18 +152,20 @@ void handleIncomingOptions(pjsip_rx_data *rdata) pjsip_tx_data_dec_ref(tdata); } -// Always return PJ_TRUE since we are the only module that will handle these requests +// return PJ_FALSE so that eventuall other modules will handle these requests +// TODO: move Voicemail to separate module +// TODO: add Buddy presence in separate module pj_bool_t transaction_response_cb(pjsip_rx_data *rdata) { pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); if (!dlg) - return PJ_TRUE; + return PJ_FALSE; pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); if (!tsx or tsx->method.id != PJSIP_INVITE_METHOD) - return PJ_TRUE; + return PJ_FALSE; if (tsx->status_code / 100 == 2) { /** @@ -176,34 +179,33 @@ pj_bool_t transaction_response_cb(pjsip_rx_data *rdata) } } - return PJ_TRUE; + return PJ_FALSE; } -// Always return PJ_TRUE since we are the only module that will handle these requests pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) { if (!rdata or !rdata->msg_info.msg) { ERROR("rx_data is NULL"); - return PJ_TRUE; + return PJ_FALSE; } pjsip_method *method = &rdata->msg_info.msg->line.req.method; if (!method) { ERROR("method is NULL"); - return PJ_TRUE; + return PJ_FALSE; } if (method->id == PJSIP_ACK_METHOD && pjsip_rdata_get_dlg(rdata)) - return PJ_TRUE; + return PJ_FALSE; if (!rdata->msg_info.to or !rdata->msg_info.from) { ERROR("NULL from/to fields"); - return PJ_TRUE; + return PJ_FALSE; } pjsip_sip_uri *sip_to_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(rdata->msg_info.to->uri); pjsip_sip_uri *sip_from_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(rdata->msg_info.from->uri); if (!sip_to_uri or !sip_from_uri) { ERROR("NULL uri"); - return PJ_TRUE; + return PJ_FALSE; } std::string userName(sip_to_uri->user.ptr, sip_to_uri->user.slen); std::string server(sip_from_uri->host.ptr, sip_from_uri->host.slen); @@ -226,19 +228,19 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) } pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_OK, NULL, NULL, NULL); - return PJ_TRUE; + return PJ_FALSE; } else if (method->id == PJSIP_OPTIONS_METHOD) { handleIncomingOptions(rdata); - return PJ_TRUE; + return PJ_FALSE; } else if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) { pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, NULL, NULL, NULL); - return PJ_TRUE; + return PJ_FALSE; } SIPAccount *account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(account_id)); if (!account) { ERROR("Could not find account %s", account_id.c_str()); - return PJ_TRUE; + return PJ_FALSE; } pjmedia_sdp_session *r_sdp; @@ -250,7 +252,7 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, NULL, NULL); - return PJ_TRUE; + return PJ_FALSE; } // Verify that we can handle the request @@ -258,7 +260,7 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) if (pjsip_inv_verify_request(rdata, &options, NULL, NULL, endpt_, NULL) != PJ_SUCCESS) { pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, NULL, NULL, NULL); - return PJ_TRUE; + return PJ_FALSE; } Manager::instance().hookPreference.runHook(rdata->msg_info.msg); @@ -274,17 +276,17 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) pjsip_tpselector *tp = SIPVoIPLink::instance()->sipTransport.initTransportSelector(account->transport_, call->getMemoryPool()); - if (addrToUse == "0.0.0.0") - addrToUse = SipTransport::getSIPLocalIP(); - - if (addrSdp == "0.0.0.0") - addrSdp = addrToUse; - char tmp[PJSIP_MAX_URL_SIZE]; size_t length = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, sip_from_uri, tmp, PJSIP_MAX_URL_SIZE); std::string peerNumber(tmp, std::min(length, sizeof tmp)); sip_utils::stripSipUriPrefix(peerNumber); + std::string remote_user(sip_from_uri->user.ptr, sip_from_uri->user.slen); + std::string remove_hostname(sip_from_uri->host.ptr, sip_from_uri->host.slen); + if (remote_user.size() > 0 && remove_hostname.size() > 0) { + peerNumber = remote_user+"@"+remove_hostname; + } + call->setConnectionState(Call::PROGRESSING); call->setPeerNumber(peerNumber); call->setDisplayName(displayName); @@ -321,17 +323,13 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) } } -#ifdef SFL_VIDEO call->getLocalSDP()->receiveOffer(r_sdp, account->getActiveAudioCodecs(), account->getActiveVideoCodecs()); -#else - call->getLocalSDP()->receiveOffer(r_sdp, account->getActiveAudioCodecs()); -#endif sfl::AudioCodec* ac = dynamic_cast<sfl::AudioCodec*>(Manager::instance().audioCodecFactory.instantiateCodec(PAYLOAD_CODEC_ULAW)); if (!ac) { ERROR("Could not instantiate codec"); delete call; - return PJ_TRUE; + return PJ_FALSE; } call->getAudioRtp().start(ac); @@ -340,7 +338,7 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) if (pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, NULL, &dialog) != PJ_SUCCESS) { delete call; pjsip_endpt_respond_stateless(endpt_, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL, NULL); - return PJ_TRUE; + return PJ_FALSE; } pjsip_inv_create_uas(dialog, rdata, call->getLocalSDP()->getLocalSdpSession(), 0, &call->inv); @@ -348,13 +346,13 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) if (pjsip_dlg_set_transport(dialog, tp) != PJ_SUCCESS) { ERROR("Could not set transport for dialog"); delete call; - return PJ_TRUE; + return PJ_FALSE; } if (!call->inv) { ERROR("Call invite is not initialized"); delete call; - return PJ_TRUE; + return PJ_FALSE; } call->inv->mod_data[mod_ua_.id] = call; @@ -374,9 +372,8 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) // If Replace header present if (replaced_dlg) { - // Always answer the new INVITE with 200, regardless whether - // the replaced call is in early or confirmed state. - if (pjsip_inv_answer(call->inv, 200, NULL, NULL, &response) == PJ_SUCCESS) + // Always answer the new INVITE with 200 if the replaced call is in early or confirmed state. + if (pjsip_inv_answer(call->inv, PJSIP_SC_OK, NULL, NULL, &response) == PJ_SUCCESS) pjsip_inv_send_msg(call->inv, response); // Get the INVITE session associated with the replaced dialog. @@ -389,12 +386,12 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) if (pjsip_inv_initial_answer(call->inv, rdata, PJSIP_SC_RINGING, NULL, NULL, &tdata) != PJ_SUCCESS) { ERROR("Could not answer invite"); delete call; - return PJ_TRUE; + return PJ_FALSE; } if (pjsip_inv_send_msg(call->inv, tdata) != PJ_SUCCESS) { ERROR("Could not send msg for invite"); delete call; - return PJ_TRUE; + return PJ_FALSE; } call->setConnectionState(Call::RINGING); @@ -403,7 +400,7 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) Manager::instance().getAccountLink(account_id)->addCall(call); } - return PJ_TRUE; + return PJ_FALSE; } } // end anonymous namespace @@ -425,7 +422,7 @@ SIPVoIPLink::SIPVoIPLink() : sipTransport(endpt_, cp_, pool_), evThread_(this) TRY(pjnath_init()); pj_caching_pool_init(cp_, &pj_pool_factory_default_policy, 0); - pool_ = pj_pool_create(&cp_->factory, "sflphone", 4000, 4000, NULL); + pool_ = pj_pool_create(&cp_->factory, PACKAGE, 4000, 4000, NULL); if (!pool_) throw VoipLinkException("UserAgent: Could not initialize memory pool"); @@ -546,10 +543,14 @@ void SIPVoIPLink::sendRegister(Account *a) if (!account) throw VoipLinkException("SipVoipLink: Account is not SIPAccount"); - sipTransport.createSipTransport(*account); + try { + sipTransport.createSipTransport(*account); + } catch (const std::runtime_error &e) { + ERROR("%s", e.what()); + } account->setRegister(true); - account->setRegistrationState(Trying); + account->setRegistrationState(TRYING); pjsip_regc *regc = account->getRegistrationInfo(); @@ -573,15 +574,15 @@ void SIPVoIPLink::sendRegister(Account *a) std::string contact = account->getContactHeader(); pj_str_t pjContact = pj_str((char*) contact.c_str()); - if (!received.empty()) { + if (not received.empty() and received != account->getPublishedAddress()) { // Set received parameter string to empty in order to avoid creating new transport for each register account->setReceivedParameter(""); - // Explicitely set the bound address port to 0 so that pjsip determine a random port by itself + DEBUG("Creating transport on random port because we have rx param %s", received.c_str()); + // Explicitly set the bound address port to 0 so that pjsip determines a random port by itself account->transport_= sipTransport.createUdpTransport(account->getLocalInterface(), 0, received, account->getRPort()); account->setRPort(-1); - if(account->transport_ == NULL) { + if (account->transport_ == NULL) ERROR("Could not create new udp transport with public address: %s:%d", received.c_str(), account->getLocalPort()); - } } if (pjsip_regc_init(regc, &pjSrv, &pjFrom, &pjFrom, 1, &pjContact, account->getRegistrationExpire()) != PJ_SUCCESS) @@ -626,7 +627,7 @@ void SIPVoIPLink::sendRegister(Account *a) // start the periodic registration request based on Expire header // account determines itself if a keep alive is required - if(account->isKeepAliveEnabled()) + if (account->isKeepAliveEnabled()) account->startKeepAliveTimer(); } @@ -636,7 +637,7 @@ void SIPVoIPLink::sendUnregister(Account *a) // This may occurs if account failed to register and is in state INVALID if (!account->isRegistered()) { - account->setRegistrationState(Unregistered); + account->setRegistrationState(UNREGISTERED); return; } @@ -699,10 +700,44 @@ bool isValidIpAddress(const std::string &address) return result != 0; } +/** + * This function look for '@' and replace the second part with the corresponding ip address (when possible) + */ +std::string resolvDns(const std::string& url) +{ + size_t pos; + if ((pos = url.find("@")) == std::string::npos) { + return url; + } + std::string hostname = url.substr(pos+1); + + int i; + struct hostent *he; + struct in_addr **addr_list; + + if ((he = gethostbyname(hostname.c_str())) == NULL) { + return url; + } + + addr_list = (struct in_addr **)he->h_addr_list; + std::list<std::string> ipList; + + for(i = 0; addr_list[i] != NULL; i++) { + ipList.push_back(inet_ntoa(*addr_list[i])); + } + + if (ipList.size() > 0 && ipList.front().size() > 7 ) + return url.substr(0,pos+1)+ipList.front(); + else + return hostname; +} + Call *SIPVoIPLink::newOutgoingCall(const std::string& id, const std::string& toUrl) { DEBUG("New outgoing call to %s", toUrl.c_str()); std::string toCpy = toUrl; + std::string resolvedUrl = resolvDns(toUrl); + DEBUG("URL resolved to %s", resolvedUrl.c_str()); sip_utils::stripSipUriPrefix(toCpy); @@ -711,9 +746,9 @@ Call *SIPVoIPLink::newOutgoingCall(const std::string& id, const std::string& toU if (IPToIP) { Manager::instance().associateCallToAccount(id, SIPAccount::IP2IP_PROFILE); - return SIPNewIpToIpCall(id, toUrl); + return SIPNewIpToIpCall(id, resolvedUrl); } else { - return newRegisteredAccountCall(id, toUrl); + return newRegisteredAccountCall(id, resolvedUrl); } } @@ -733,9 +768,6 @@ Call *SIPVoIPLink::SIPNewIpToIpCall(const std::string& id, const std::string& to std::string localAddress(SipTransport::getInterfaceAddrFromName(account->getLocalInterface())); - if (localAddress == "0.0.0.0") - localAddress = SipTransport::getSIPLocalIP(); - setCallMediaLocal(call, localAddress); std::string toUri = account->getToUri(to); @@ -756,11 +788,7 @@ Call *SIPVoIPLink::SIPNewIpToIpCall(const std::string& id, const std::string& to // Building the local SDP offer call->getLocalSDP()->setLocalIP(localAddress); -#ifdef SFL_VIDEO call->getLocalSDP()->createOffer(account->getActiveAudioCodecs(), account->getActiveVideoCodecs()); -#else - call->getLocalSDP()->createOffer(account->getActiveAudioCodecs()); -#endif if (!SIPStartCall(call)) { delete call; @@ -792,10 +820,6 @@ Call *SIPVoIPLink::newRegisteredAccountCall(const std::string& id, const std::st call->setPeerNumber(toUri); std::string localAddr(SipTransport::getInterfaceAddrFromName(account->getLocalInterface())); - - if (localAddr == "0.0.0.0") - localAddr = SipTransport::getSIPLocalIP(); - setCallMediaLocal(call, localAddr); // May use the published address as well @@ -803,9 +827,6 @@ Call *SIPVoIPLink::newRegisteredAccountCall(const std::string& id, const std::st account->getPublishedAddress() : SipTransport::getInterfaceAddrFromName(account->getLocalInterface()); - if (addrSdp == "0.0.0.0") - addrSdp = SipTransport::getSIPLocalIP(); - // Initialize the session using ULAW as default codec in case of early media // The session should be ready to receive media once the first INVITE is sent, before // the session initialization is completed @@ -829,11 +850,7 @@ Call *SIPVoIPLink::newRegisteredAccountCall(const std::string& id, const std::st call->initRecFilename(toUrl); call->getLocalSDP()->setLocalIP(addrSdp); -#ifdef SFL_VIDEO call->getLocalSDP()->createOffer(account->getActiveAudioCodecs(), account->getActiveVideoCodecs()); -#else - call->getLocalSDP()->createOffer(account->getActiveAudioCodecs()); -#endif if (!SIPStartCall(call)) { delete call; @@ -1117,22 +1134,14 @@ SIPVoIPLink::refuse(const std::string& id) removeCall(id); } -#ifdef SFL_VIDEO std::string -SIPVoIPLink::getCurrentVideoCodecName(const std::string& id) +SIPVoIPLink::getCurrentVideoCodecName(Call *call) const { - SIPCall *call = getSIPCall(id); - if (call) { - Call::CallState state = call->getState(); - if (state == Call::ACTIVE or state == Call::CONFERENCING) - return call->getLocalSDP()->getSessionVideoCodec(); - } - return ""; + return dynamic_cast<SIPCall*>(call)->getLocalSDP()->getSessionVideoCodec(); } -#endif std::string -SIPVoIPLink::getCurrentCodecName(Call *call) const +SIPVoIPLink::getCurrentAudioCodecName(Call *call) const { return dynamic_cast<SIPCall*>(call)->getLocalSDP()->getAudioCodecName(); } @@ -1229,8 +1238,8 @@ SIPVoIPLink::SIPStartCall(SIPCall *call) if (not account->getServiceRoute().empty()) pjsip_dlg_set_route_set(dialog, sip_utils::createRouteSet(account->getServiceRoute(), call->inv->pool)); - if (pjsip_auth_clt_set_credentials(&dialog->auth_sess, account->getCredentialCount(), account->getCredInfo()) != PJ_SUCCESS) { - ERROR("Could not initialize credential for invite session authentication"); + if (account->hasCredentials() and pjsip_auth_clt_set_credentials(&dialog->auth_sess, account->getCredentialCount(), account->getCredInfo()) != PJ_SUCCESS) { + ERROR("Could not initialize credentials for invite session authentication"); return false; } @@ -1392,11 +1401,7 @@ void sdp_request_offer_cb(pjsip_inv_session *inv, const pjmedia_sdp_session *off if (!account) return; -#ifdef SFL_VIDEO call->getLocalSDP()->receiveOffer(offer, account->getActiveAudioCodecs(), account->getActiveVideoCodecs()); -#else - call->getLocalSDP()->receiveOffer(offer, account->getActiveAudioCodecs()); -#endif call->getLocalSDP()->startNegotiation(); pjsip_inv_set_sdp_answer(call->inv, call->getLocalSDP()->getLocalSdpSession()); @@ -1416,20 +1421,10 @@ void sdp_create_offer_cb(pjsip_inv_session *inv, pjmedia_sdp_session **p_offer) std::string localAddress(SipTransport::getInterfaceAddrFromName(account->getLocalInterface())); std::string addrSdp(localAddress); - if (localAddress == "0.0.0.0") - localAddress = SipTransport::getSIPLocalIP(); - - if (addrSdp == "0.0.0.0") - addrSdp = localAddress; - setCallMediaLocal(call, localAddress); call->getLocalSDP()->setLocalIP(addrSdp); -#ifdef SFL_VIDEO call->getLocalSDP()->createOffer(account->getActiveAudioCodecs(), account->getActiveVideoCodecs()); -#else - call->getLocalSDP()->createOffer(account->getActiveAudioCodecs()); -#endif *p_offer = call->getLocalSDP()->getLocalSdpSession(); } @@ -1506,9 +1501,10 @@ void sdp_media_update_cb(pjsip_inv_session *inv, pj_status_t status) call->getAudioRtp().updateDestinationIpAddress(); call->getAudioRtp().setDtmfPayloadType(sdpSession->getTelephoneEventType()); #ifdef SFL_VIDEO - call->getVideoRtp()->updateSDP(*call->getLocalSDP()); - call->getVideoRtp()->updateDestination(call->getLocalSDP()->getRemoteIP(), call->getLocalSDP()->getRemoteVideoPort()); - call->getVideoRtp()->start(); + Manager::instance().getVideoControls()->stopPreview(); + call->getVideoRtp().updateSDP(*call->getLocalSDP()); + call->getVideoRtp().updateDestination(call->getLocalSDP()->getRemoteIP(), call->getLocalSDP()->getRemoteVideoPort()); + call->getVideoRtp().start(); #endif // Get the crypto attribute containing srtp's cryptographic context (keys, cipher) @@ -1583,6 +1579,46 @@ void sdp_media_update_cb(pjsip_inv_session *inv, pj_status_t status) void outgoing_request_forked_cb(pjsip_inv_session * /*inv*/, pjsip_event * /*e*/) {} +bool handle_media_control(pjsip_inv_session * inv, pjsip_transaction *tsx, pjsip_event *event) +{ + /* + * Incoming INFO request for media control. + */ + const pj_str_t STR_APPLICATION = { (char *) "application", 11}; + const pj_str_t STR_MEDIA_CONTROL_XML = { (char *) "media_control+xml", 17}; + pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; + pjsip_msg_body *body = rdata->msg_info.msg->body; + + if (body and body->len and pj_stricmp(&body->content_type.type, &STR_APPLICATION) == 0 and + pj_stricmp(&body->content_type.subtype, &STR_MEDIA_CONTROL_XML) == 0) { + pj_str_t control_st; + + /* Apply and answer the INFO request */ + pj_strset(&control_st, (char *) body->data, body->len); + const pj_str_t PICT_FAST_UPDATE = {(char *) "picture_fast_update", 19}; + + if (pj_strstr(&control_st, &PICT_FAST_UPDATE)) { +#ifdef SFL_VIDEO + DEBUG("handling picture fast update"); + SIPCall *call = static_cast<SIPCall *>(inv->mod_data[mod_ua_.id]); + if (call) + call->getVideoRtp().forceKeyFrame(); + pjsip_tx_data *tdata; + pj_status_t status = pjsip_endpt_create_response(tsx->endpt, rdata, + PJSIP_SC_OK, NULL, &tdata); + if (status == PJ_SUCCESS) { + status = pjsip_tsx_send_msg(tsx, tdata); + return true; + } +#else + (void) inv; + (void) tsx; +#endif + } + } + return false; +} + void transaction_state_changed_cb(pjsip_inv_session * inv, pjsip_transaction *tsx, pjsip_event *event) { @@ -1597,15 +1633,20 @@ void transaction_state_changed_cb(pjsip_inv_session * inv, } pjsip_tx_data* t_data; + if (tsx->role == PJSIP_ROLE_UAS and tsx->state == PJSIP_TSX_STATE_TRYING) { + if (handle_media_control(inv, tsx, event)) + return; + } if (event->body.rx_msg.rdata) { pjsip_rx_data *r_data = event->body.rx_msg.rdata; if (r_data && r_data->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD) { - std::string request = pjsip_rx_data_get_info(r_data); + std::string request(pjsip_rx_data_get_info(r_data)); DEBUG("%s", request.c_str()); - if (request.find("NOTIFY") == std::string::npos && request.find("INFO") != std::string::npos) { + if (request.find("NOTIFY") == std::string::npos and + request.find("INFO") != std::string::npos) { pjsip_dlg_create_response(inv->dlg, r_data, PJSIP_SC_OK, NULL, &t_data); pjsip_dlg_send_response(inv->dlg, tsx, t_data); return; @@ -1787,7 +1828,7 @@ void registration_cb(pjsip_regc_cbparam *param) if (param->status != PJ_SUCCESS) { FAILURE_MESSAGE(); - processRegistrationError(*account, ErrorAuth); + processRegistrationError(*account, ERROR_AUTH); return; } @@ -1799,11 +1840,11 @@ void registration_cb(pjsip_regc_cbparam *param) case PJSIP_SC_USE_PROXY: // 305 case PJSIP_SC_ALTERNATIVE_SERVICE: // 380 FAILURE_MESSAGE(); - processRegistrationError(*account, Error); + processRegistrationError(*account, ERROR_GENERIC); break; case PJSIP_SC_SERVICE_UNAVAILABLE: // 503 FAILURE_MESSAGE(); - processRegistrationError(*account, ErrorHost); + processRegistrationError(*account, ERROR_HOST); break; case PJSIP_SC_UNAUTHORIZED: // 401 // Automatically answered by PJSIP @@ -1812,11 +1853,11 @@ void registration_cb(pjsip_regc_cbparam *param) case PJSIP_SC_FORBIDDEN: // 403 case PJSIP_SC_NOT_FOUND: // 404 FAILURE_MESSAGE(); - processRegistrationError(*account, ErrorAuth); + processRegistrationError(*account, ERROR_AUTH); break; case PJSIP_SC_REQUEST_TIMEOUT: // 408 FAILURE_MESSAGE(); - processRegistrationError(*account, ErrorHost); + processRegistrationError(*account, ERROR_HOST); break; case PJSIP_SC_INTERVAL_TOO_BRIEF: // 423 // Expiration Interval Too Brief @@ -1826,21 +1867,21 @@ void registration_cb(pjsip_regc_cbparam *param) break; case PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE: // 606 lookForReceivedParameter(*param, *account); - account->setRegistrationState(ErrorNotAcceptable); + account->setRegistrationState(ERROR_NOT_ACCEPTABLE); account->registerVoIPLink(); break; default: FAILURE_MESSAGE(); - processRegistrationError(*account, Error); + processRegistrationError(*account, ERROR_GENERIC); break; } } else { lookForReceivedParameter(*param, *account); if (account->isRegistered()) - account->setRegistrationState(Registered); + account->setRegistrationState(REGISTERED); else { - account->setRegistrationState(Unregistered); + account->setRegistrationState(UNREGISTERED); SIPVoIPLink::instance()->sipTransport.shutdownSipTransport(*account); } } diff --git a/daemon/src/sip/sipvoiplink.h b/daemon/src/sip/sipvoiplink.h index 681bfacc00264cd8511c6b978b43fc14b074e49e..1b8617cdddea35e60c7494f800a8dbaf02e1e8cf 100644 --- a/daemon/src/sip/sipvoiplink.h +++ b/daemon/src/sip/sipvoiplink.h @@ -218,10 +218,8 @@ class SIPVoIPLink : public VoIPLink { * Return the codec protocol used for this call * @param c The call identifier */ -#ifdef SFL_VIDEO - std::string getCurrentVideoCodecName(const std::string& id); -#endif - std::string getCurrentCodecName(Call *c) const; + std::string getCurrentVideoCodecName(Call *c) const; + std::string getCurrentAudioCodecName(Call *c) const; /** * Retrive useragent name from account diff --git a/daemon/src/video/Makefile.am b/daemon/src/video/Makefile.am index c9e753f6363cadadb0abac95c072d6e18909c771..c88b86b9df3d879651e5a73a88a3bda1e739a0ac 100644 --- a/daemon/src/video/Makefile.am +++ b/daemon/src/video/Makefile.am @@ -3,15 +3,14 @@ include $(top_srcdir)/globals.mak SUBDIRS=test noinst_LTLIBRARIES = libvideo.la -libvideo_la_SOURCES = video_endpoint.cpp video_endpoint.h libav_utils.cpp \ - libav_utils.h video_rtp_session.cpp video_rtp_session.h \ - video_send_thread.h video_send_thread.cpp \ - video_receive_thread.h video_receive_thread.cpp \ - video_preview.h video_preview.cpp video_v4l2.cpp \ - video_v4l2_list.cpp video_v4l2.h video_v4l2_list.h \ - video_preferences.h video_preferences.cpp \ - packet_handle.h packet_handle.cpp shared_memory.cpp \ - shared_memory.h check.h +libvideo_la_SOURCES = libav_utils.cpp libav_utils.h video_rtp_session.cpp \ + video_rtp_session.h video_send_thread.h video_send_thread.cpp \ + video_receive_thread.h video_receive_thread.cpp \ + video_preview.h video_preview.cpp video_v4l2.cpp \ + video_v4l2_list.cpp video_v4l2.h video_v4l2_list.h \ + video_preferences.h video_preferences.cpp \ + packet_handle.h packet_handle.cpp check.h shm_header.h \ + shm_sink.cpp shm_sink.h libvideo_la_LIBADD = @LIBAVCODEC_LIBS@ @LIBAVFORMAT_LIBS@ @LIBAVDEVICE_LIBS@ @LIBSWSCALE_LIBS@ @LIBAVUTIL_LIBS@ @CCRTP_LIBS@ @UDEV_LIBS@ diff --git a/daemon/src/video/check.h b/daemon/src/video/check.h index 81134fa47f8445a6dbd93262893fa125215cca49..7f3b2d752df136f40b8ab4f4e7ec62d092d816a9 100644 --- a/daemon/src/video/check.h +++ b/daemon/src/video/check.h @@ -34,6 +34,6 @@ #include "logger.h" // If condition A is false, print the error message in M and exit thread -#define RETURN_IF_FAIL(A, M, ...) if (!(A)) { ERROR(M, ##__VA_ARGS__); ost::Thread::exit(); } +#define EXIT_IF_FAIL(A, M, ...) if (!(A)) { ERROR(M, ##__VA_ARGS__); ost::Thread::exit(); } #endif // CHECK_H_ diff --git a/daemon/src/video/libav_utils.cpp b/daemon/src/video/libav_utils.cpp index 9257f849360b4fe07a5749d0abf898e09e6ff5c5..52d842e86c77824b1c5187367b16e8bfffdba850 100644 --- a/daemon/src/video/libav_utils.cpp +++ b/daemon/src/video/libav_utils.cpp @@ -141,4 +141,22 @@ void sfl_avcodec_init() findInstalledVideoCodecs(); } +std::vector<std::map<std::string, std::string> > +getDefaultCodecs() +{ + const char * const DEFAULT_BITRATE = "400"; + sfl_avcodec_init(); + std::vector<std::map<std::string, std::string> > result; + for (std::vector<std::string>::const_iterator iter = installed_video_codecs_.begin(); + iter != installed_video_codecs_.end(); ++iter) { + std::map<std::string, std::string> codec; + // FIXME: get these keys from proper place + codec["name"] = *iter; + codec["bitrate"] = DEFAULT_BITRATE; + codec["enabled"] = "true"; + result.push_back(codec); + } + return result; +} + } // end namespace libav_utils diff --git a/daemon/src/video/libav_utils.h b/daemon/src/video/libav_utils.h index b812d4fe5781d5d47e1f7966037e0ba0fd26dbc3..a18714ec1bedffacc31fbe32ddb34ac5057b3fca 100644 --- a/daemon/src/video/libav_utils.h +++ b/daemon/src/video/libav_utils.h @@ -36,9 +36,17 @@ #include <string> namespace libav_utils { - void sfl_avcodec_init(); - std::map<std::string, std::string> encodersMap(); - std::vector<std::string> getVideoCodecList(); + void + sfl_avcodec_init(); + + std::map<std::string, std::string> + encodersMap(); + + std::vector<std::string> + getVideoCodecList(); + + std::vector<std::map<std::string, std::string> > + getDefaultCodecs(); } #endif // __LIBAV_UTILS_H__ diff --git a/daemon/src/video/libx264-ultrafast.ffpreset.h b/daemon/src/video/libx264-ultrafast.ffpreset.h deleted file mode 100644 index f9bbc23d5491c6667b8336aa3dfdbf104f28d25b..0000000000000000000000000000000000000000 --- a/daemon/src/video/libx264-ultrafast.ffpreset.h +++ /dev/null @@ -1,24 +0,0 @@ -static const char *x264_preset_ultrafast = "coder=0\n" \ -"flags=-loop\n" \ -"cmp=+chroma\n" \ -"partitions=-parti8x8-parti4x4-partp8x8-partb8x8\n" \ -"me_method=dia\n" \ -"subq=0\n" \ -"me_range=16\n" \ -"g=250\n" \ -"keyint_min=25\n" \ -"sc_threshold=0\n" \ -"i_qfactor=0.71\n" \ -"b_strategy=0\n" \ -"qcomp=0.6\n" \ -"qmin=0\n" \ -"qmax=69\n" \ -"qdiff=4\n" \ -"bf=0\n" \ -"refs=1\n" \ -"directpred=1\n" \ -"trellis=0\n" \ -"flags2=-bpyramid-mixed_refs-wpred-dct8x8+fastpskip-mbtree\n" \ -"wpredp=0\n" \ -"aq_mode=0\n" \ -"rc_lookahead=0"; diff --git a/daemon/src/video/shared_memory.cpp b/daemon/src/video/shared_memory.cpp deleted file mode 100644 index 86c913f6747567254990db0fcedd9a83754a2a8a..0000000000000000000000000000000000000000 --- a/daemon/src/video/shared_memory.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) 2004-2012 Savoir-Faire Linux Inc. - * - * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Additional permission under GNU GPL version 3 section 7: - * - * If you modify this program, or any covered work, by linking or - * combining it with the OpenSSL project's OpenSSL library (or a - * modified version of that library), containing parts covered by the - * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. - * grants you additional permission to convey the resulting work. - * Corresponding Source for a non-source form of such a combination - * shall include the source code for the parts of OpenSSL used as well - * as that of the covered work. - */ - -#include "shared_memory.h" - -// shm includes -#include <sys/types.h> -#include <sys/ipc.h> -#include <sys/sem.h> /* semaphore functions and structs. */ -#include <sys/shm.h> - -#include <cstdlib> -#include <stdexcept> - -#include "manager.h" -#include "logger.h" -#include "dbus/video_controls.h" -#include "fileutils.h" - -namespace sfl_video { - -namespace { // anonymous namespace - -#if _SEM_SEMUN_UNDEFINED -union semun { - int val; /* value for SETVAL */ - struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ - unsigned short int *array; /* array for GETALL & SETALL */ - struct seminfo *__buf; /* buffer for IPC_INFO */ -}; -#endif - -void cleanupSemaphore(int semaphoreSetID) -{ - semctl(semaphoreSetID, 0, IPC_RMID); -} - -/* - * function: sem_signal. signals the process that a frame is ready. - * input: semaphore set ID. - * output: none. - */ -void sem_signal(int semaphoreSetID) -{ - /* structure for semaphore operations. */ - sembuf sem_op; - - /* signal the semaphore - increase its value by one. */ - sem_op.sem_num = 0; - sem_op.sem_op = 1; - sem_op.sem_flg = 0; - semop(semaphoreSetID, &sem_op, 1); -} - -/* join and/or create a shared memory segment */ -int createShmKey() -{ - /* connect to and possibly create a segment with 644 permissions - (rw-r--r--) */ - srand(time(NULL)); - int proj_id = rand(); - return ftok(fileutils::get_program_dir(), proj_id); -} - -int createShmID(int key, int numBytes) -{ - int shm_id = shmget(key, numBytes, 0644 | IPC_CREAT); - - if (shm_id == -1) - ERROR("shmget:%m"); - - return shm_id; -} - -/* attach a shared memory segment */ -uint8_t *attachShm(int shm_id) -{ - /* attach to the segment and get a pointer to it */ - uint8_t *data = reinterpret_cast<uint8_t*>(shmat(shm_id, (void *) 0, 0)); - if (data == reinterpret_cast<uint8_t *>(-1)) { - ERROR("shmat:%m"); - data = NULL; - } - return data; -} - -void detachShm(uint8_t *data) -{ - /* detach from the segment: */ - if (data and shmdt(data) == -1) - ERROR("shmdt:%m"); -} - -void destroyShm(int shm_id) -{ - /* destroy it */ - shmctl(shm_id, IPC_RMID, NULL); -} - -void cleanupShm(int shm_id, uint8_t *data) -{ - detachShm(data); - destroyShm(shm_id); -} - -int createSemaphoreKey(int shmKey) -{ - key_t key; - do - key = ftok(fileutils::get_program_dir(), rand()); - while (key == shmKey); - return key; -} - -int createSemaphoreSetID(int semaphoreKey) -{ - /* first we create a semaphore set with a single semaphore, - whose counter is initialized to '0'. */ - int semaphoreSetID = semget(semaphoreKey, 1, 0600 | IPC_CREAT); - if (semaphoreSetID == -1) { - ERROR("semget:%m"); - throw std::runtime_error("Could not create semaphore set"); - } - - /* semaphore value, for semctl(). */ - union semun sem_val; - sem_val.val = 0; - semctl(semaphoreSetID, 0, SETVAL, sem_val); - return semaphoreSetID; -} -} // end anonymous namespace - -SharedMemory::SharedMemory(VideoControls &controls) : - videoControls_(controls), - shmKey_(0), - shmID_(0), - shmBuffer_(0), - semaphoreSetID_(0), - semaphoreKey_(0), - dstWidth_(0), - dstHeight_(0), - bufferSize_(0), - shmReady_() -{} - -void SharedMemory::allocateBuffer(int width, int height, int size) -{ - dstWidth_ = width; - dstHeight_ = height; - bufferSize_ = size; - shmKey_ = createShmKey(); - shmID_ = createShmID(shmKey_, bufferSize_); - shmBuffer_ = attachShm(shmID_); - semaphoreKey_ = createSemaphoreKey(shmKey_); - semaphoreSetID_ = createSemaphoreSetID(semaphoreKey_); - shmReady_.signal(); -} - -void SharedMemory::publishShm() -{ - DEBUG("Publishing shm: %d sem: %d size: %d", shmKey_, semaphoreKey_, - bufferSize_); - videoControls_.receivingEvent(shmKey_, semaphoreKey_, bufferSize_, - dstWidth_, dstHeight_); -} - -void SharedMemory::waitForShm() -{ - shmReady_.wait(); -} - -void SharedMemory::frameUpdatedCallback() -{ - // signal the semaphore that a new frame is ready - sem_signal(semaphoreSetID_); -} - -SharedMemory::~SharedMemory() -{ - // free shared memory resources - videoControls_.stoppedReceivingEvent(shmKey_, semaphoreKey_); - - // make sure no one is waiting for the SHM event which will never come if we've error'd out - shmReady_.signal(); - - cleanupSemaphore(semaphoreSetID_); - cleanupShm(shmID_, shmBuffer_); -} -} // end namespace sfl_video diff --git a/daemon/src/video/video_endpoint.h b/daemon/src/video/shm_header.h similarity index 74% rename from daemon/src/video/video_endpoint.h rename to daemon/src/video/shm_header.h index fc50ab8a7aba26e60f430062b048e0272a5ee6ca..ccf168939dce4b27f08402f5200aa5643d5af4b7 100644 --- a/daemon/src/video/video_endpoint.h +++ b/daemon/src/video/shm_header.h @@ -1,7 +1,12 @@ /* - * Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc. + * Copyright (C) 2012 Savoir-Faire Linux Inc. * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> * + * Portions derived from GStreamer: + * Copyright (C) <2009> Collabora Ltd + * @author: Olivier Crete <olivier.crete@collabora.co.uk + * Copyright (C) <2009> Nokia Inc + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or @@ -28,19 +33,19 @@ * as that of the covered work. */ -#ifndef VIDEO_ENDPOINT_H__ -#define VIDEO_ENDPOINT_H__ +#ifndef SHM_HEADER_H_ +#define SHM_HEADER_H_ + +#include <semaphore.h> + +struct SHMHeader { + sem_t notification; + sem_t mutex; -#include <vector> -#include <map> -#include <string> + unsigned buffer_gen; + int buffer_size; -namespace sfl_video { - /** - * Returns the list of codecs installed at runtime and that we support - */ - std::vector<std::string> getCodecList(); - std::map<std::string, std::string> getCodecSpecifications(const std::string &codec); -} + char data[0]; +}; -#endif // VIDEO_ENDPOINT_H__ +#endif diff --git a/daemon/src/video/shm_sink.cpp b/daemon/src/video/shm_sink.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b2a042c86844a67d4978598a71840bce7a4c640 --- /dev/null +++ b/daemon/src/video/shm_sink.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2012 Savoir-Faire Linux Inc. + * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * + * Portions derived from GStreamer: + * Copyright (C) <2009> Collabora Ltd + * @author: Olivier Crete <olivier.crete@collabora.co.uk + * Copyright (C) <2009> Nokia Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "shm_sink.h" +#include "shm_header.h" +#include "logger.h" +#include <sys/mman.h> +#include <fcntl.h> +#include <cstdio> +#include <sstream> +#include <unistd.h> +#include <cerrno> +#include <cstring> + +SHMSink::SHMSink(const std::string &shm_name) : + shm_name_(shm_name), + fd_(-1), + shm_area_(static_cast<SHMHeader*>(MAP_FAILED)), + shm_area_len_(0), + opened_name_(), + perms_(S_IRUSR | S_IWUSR | S_IRGRP) + {} + +SHMSink::~SHMSink() +{ + stop(); +} + +bool +SHMSink::start() +{ + if (fd_ != -1) { + ERROR("fd must be -1"); + return false; + } + + const int flags = O_RDWR | O_CREAT | O_TRUNC; + if (not shm_name_.empty()) { + fd_ = shm_open(shm_name_.c_str(), flags, perms_); + if (fd_ < 0) { + ERROR("could not open shm area \"%s\", shm_open failed:%s", shm_name_.c_str(), strerror(errno)); + perror(strerror(errno)); + return false; + } + } else { + for (int i = 0; fd_ < 0; ++i) { + std::ostringstream name; + name << PACKAGE_NAME << "_shm_" << getpid() << "_" << i; + shm_name_ = name.str(); + fd_ = shm_open(shm_name_.c_str(), flags, perms_); + if (fd_ < 0 and errno != EEXIST) { + ERROR("%s", strerror(errno)); + return false; + } + } + } + + DEBUG("Using name %s", shm_name_.c_str()); + opened_name_ = shm_name_; + + shm_area_len_ = sizeof(SHMHeader); + + if (ftruncate(fd_, shm_area_len_)) { + ERROR("Could not make shm area large enough for header"); + perror(strerror(errno)); + return false; + } + + shm_area_ = static_cast<SHMHeader*>(mmap(NULL, shm_area_len_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0)); + + if (shm_area_ == MAP_FAILED) { + ERROR("Could not map shm area, mmap failed"); + return false; + } + + memset(shm_area_, 0, shm_area_len_); + if (sem_init(&shm_area_->notification, 1, 0) != 0) { + ERROR("sem_init: notification initialization failed"); + return false; + } + if (sem_init(&shm_area_->mutex, 1, 1) != 0) { + ERROR("sem_init: mutex initialization failed"); + return false; + } + return true; +} + +bool +SHMSink::stop() +{ + if (fd_ >= 0) + close(fd_); + fd_ = -1; + + if (not opened_name_.empty()) { + shm_unlink(opened_name_.c_str()); + opened_name_ = ""; + } + + if (shm_area_ != MAP_FAILED) + munmap(shm_area_, shm_area_len_); + shm_area_len_ = 0; + shm_area_ = static_cast<SHMHeader*>(MAP_FAILED); + + return true; +} + +bool +SHMSink::resize_area(size_t desired_length) +{ + if (desired_length < shm_area_len_) + return true; + + shm_unlock(); + + if (munmap(shm_area_, shm_area_len_)) { + ERROR("Could not unmap shared area"); + perror(strerror(errno)); + return false; + } + + if (ftruncate(fd_, desired_length)) { + ERROR("Could not resize shared area"); + perror(strerror(errno)); + return false; + } + + shm_area_ = static_cast<SHMHeader*>(mmap(NULL, desired_length, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0)); + shm_area_len_ = desired_length; + + if (shm_area_ == MAP_FAILED) { + shm_area_ = 0; + ERROR("Could not remap shared area"); + return false; + } + + shm_lock(); + return true; +} + +void SHMSink::render(const std::vector<unsigned char> &data) +{ + shm_lock(); + + if (!resize_area(sizeof(SHMHeader) + data.size())) + return; + + memcpy(shm_area_->data, &(*data.begin()), data.size()); + shm_area_->buffer_size = data.size(); + shm_area_->buffer_gen++; + sem_post(&shm_area_->notification); + shm_unlock(); +} + +// Note: this doesn't depend on VideoReceiveThread's implementation since it's forward declared. +void SHMSink::render_callback(sfl_video::VideoReceiveThread * const th, const Callback &callback, size_t bytes) +{ + shm_lock(); + + if (!resize_area(sizeof(SHMHeader) + bytes)) { + ERROR("Could not resize area"); + return; + } + + callback(th, static_cast<void*>(shm_area_->data)); + shm_area_->buffer_size = bytes; + shm_area_->buffer_gen++; + sem_post(&shm_area_->notification); + shm_unlock(); +} + +void SHMSink::shm_lock() +{ + sem_wait(&shm_area_->mutex); +} + +void SHMSink::shm_unlock() +{ + sem_post(&shm_area_->mutex); +} diff --git a/daemon/src/video/shared_memory.h b/daemon/src/video/shm_sink.h similarity index 50% rename from daemon/src/video/shared_memory.h rename to daemon/src/video/shm_sink.h index 0db52ab04b8640cbb5a9868ace1f2e1ebd28aec3..e54595f074330f5233e2fe86bdb4e8760b4fa0fb 100644 --- a/daemon/src/video/shared_memory.h +++ b/daemon/src/video/shm_sink.h @@ -1,7 +1,12 @@ /* - * Copyright (C) 2011, 2012 Savoir-Faire Linux Inc. + * Copyright (C) 2012 Savoir-Faire Linux Inc. * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> * + * Portions derived from GStreamer: + * Copyright (C) <2009> Collabora Ltd + * @author: Olivier Crete <olivier.crete@collabora.co.uk + * Copyright (C) <2009> Nokia Inc + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or @@ -14,7 +19,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Additional permission under GNU GPL version 3 section 7: * @@ -28,47 +33,49 @@ * as that of the covered work. */ -#ifndef SHARED_MEMORY_H_ -#define SHARED_MEMORY_H_ +#ifndef SHM_SINK_H_ +#define SHM_SINK_H_ -#include "cc_thread.h" +#include <semaphore.h> +#include <string> +#include <vector> +#include <tr1/functional> #include "noncopyable.h" -class VideoControls; - +class SHMHeader; namespace sfl_video { -class SharedMemory { - private: - NON_COPYABLE(SharedMemory); - - /*-------------------------------------------------------------*/ - /* These variables should be used in thread (i.e. run()) only! */ - /*-------------------------------------------------------------*/ - VideoControls &videoControls_; - int shmKey_; - int shmID_; - uint8_t *shmBuffer_; - int semaphoreSetID_; - int semaphoreKey_; + class VideoReceiveThread; +} - int dstWidth_; - int dstHeight_; - int bufferSize_; - ost::Event shmReady_; +// A VideoReceiveThread member function handle, where the member function +// takes a (void *) as an argument +typedef std::tr1::function<void (sfl_video::VideoReceiveThread * const, void *)> Callback; +class SHMSink { public: - SharedMemory(VideoControls &controls); - ~SharedMemory(); - void frameUpdatedCallback(); - void waitForShm(); - // Returns a pointer to the memory where frames should be copied - uint8_t *getTargetBuffer() { return shmBuffer_; } - int getShmKey() const { return shmKey_; } - int getSemaphoreKey() const { return semaphoreKey_; } - int getBufferSize() const { return bufferSize_; } - void allocateBuffer(int width, int height, int size); - void publishShm(); + SHMSink(const std::string &shm_name = ""); + std::string openedName() const { return opened_name_; } + ~SHMSink(); + + bool start(); + bool stop(); + + bool resize_area(size_t desired_length); + + void render(const std::vector<unsigned char> &data); + void render_callback(sfl_video::VideoReceiveThread * const th, const Callback &callback, size_t bytes); + + private: + NON_COPYABLE(SHMSink); + + void shm_lock(); + void shm_unlock(); + std::string shm_name_; + int fd_; + SHMHeader *shm_area_; + size_t shm_area_len_; + std::string opened_name_; + unsigned perms_; }; -} -#endif // SHARED_MEMORY_H_ +#endif // SHM_SINK_H_ diff --git a/daemon/src/video/test/Makefile.am b/daemon/src/video/test/Makefile.am index 2c03bca1c45bb3312f88dfc767b313764ab69788..e0d8400b14b14dd910d6f6d164529217ac754f3e 100644 --- a/daemon/src/video/test/Makefile.am +++ b/daemon/src/video/test/Makefile.am @@ -1,7 +1,7 @@ include ../../../globals.mak -TESTS=test_video_endpoint test_thread test_v4l2 -check_PROGRAMS=test_video_endpoint test_video_rtp test_thread test_video_preview test_v4l2 +TESTS=test_video_endpoint test_thread test_v4l2 test_shm +check_PROGRAMS=test_video_endpoint test_video_rtp test_thread test_video_preview test_v4l2 test_shm test_video_endpoint_SOURCES=test_video_endpoint.cpp test_video_endpoint.h test_video_endpoint_LDADD=$(top_builddir)/src/libsflphone.la $(top_builddir)/src/video/libvideo.la $(YAML_LIBS) @@ -18,4 +18,8 @@ test_thread_LDADD=@CCRTP_LIBS@ test_v4l2_SOURCES=test_v4l2.cpp $(top_srcdir)/src/logger.cpp test_v4l2_LDADD=$(top_builddir)/src/libsflphone.la $(top_builddir)/src/video/libvideo.la $(YAML_LIBS) +test_shm_SOURCES=test_shm.cpp shm_src.cpp shm_src.h +test_shm_LDADD=$(top_builddir)/src/libsflphone.la $(top_builddir)/src/video/libvideo.la $(YAML_LIBS) +test_shm_CXXFLAGS=$(AM_CXXFLAGS) -std=c++0x + AM_CXXFLAGS=-I$(top_builddir)/src/video -I$(top_builddir)/src diff --git a/daemon/src/video/test/README b/daemon/src/video/test/README new file mode 100644 index 0000000000000000000000000000000000000000..9ef0c4f1e81db2960330be48b5138435024a50d5 --- /dev/null +++ b/daemon/src/video/test/README @@ -0,0 +1 @@ +c++ shm_src.cpp shmclient.cpp -o shmclient `pkg-config --cflags --libs clutter-1.0` -lrt -pthread -O2 diff --git a/daemon/src/video/test/make_rtp_stream.sh b/daemon/src/video/test/make_rtp_stream.sh index 5e632cbd107bd15c0fcf4cc42f55b22408999399..dfb4759b5bde85a8fce02915beb7642754a51eb8 100755 --- a/daemon/src/video/test/make_rtp_stream.sh +++ b/daemon/src/video/test/make_rtp_stream.sh @@ -1,2 +1,2 @@ # disables audio -ffmpeg -f video4linux2 -i /dev/video0 -an -r 30 -vb 10000 -vcodec mpeg4 -f rtp rtp://127.0.0.1:5000/ +ffmpeg -f video4linux2 -i /dev/video0 -srcw 320 -srch 240 -an -r 30 -vprofile baseline -level 13 -vb 400000 -vcodec libx264 -payload_type 109 -preset veryfast -tune zerolatency -f rtp rtp://192.168.50.116:2228 diff --git a/daemon/src/video/test/shm_src.cpp b/daemon/src/video/test/shm_src.cpp new file mode 100644 index 0000000000000000000000000000000000000000..023bb647653ced10831d728d2cbefea03566ab54 --- /dev/null +++ b/daemon/src/video/test/shm_src.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2012 Savoir-Faire Linux Inc. + * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * + * Portions derived from GStreamer: + * Copyright (C) <2009> Collabora Ltd + * @author: Olivier Crete <olivier.crete@collabora.co.uk + * Copyright (C) <2009> Nokia Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#include "shm_src.h" +#include "../shm_header.h" +#include <sys/mman.h> +#include <fcntl.h> +#include <cstdio> +#include <iostream> +#include <unistd.h> +#include <cerrno> +#include <cstring> +#include <cassert> + +SHMSrc::SHMSrc(const std::string &shm_name) : + shm_name_(shm_name), + fd_(-1), + shm_area_(static_cast<SHMHeader*>(MAP_FAILED)), + shm_area_len_(0), + buffer_gen_(0) + {} + +bool +SHMSrc::start() +{ + if (fd_ != -1) { + std::cerr << "fd must be -1" << std::endl; + return false; + } + + fd_ = shm_open(shm_name_.c_str(), O_RDWR, 0); + if (fd_ < 0) { + std::cerr << "could not open shm area \"" << shm_name_ << "\", shm_open failed" << std::endl; + perror(strerror(errno)); + return false; + } + shm_area_len_ = sizeof(SHMHeader); + + shm_area_ = static_cast<SHMHeader*>(mmap(NULL, shm_area_len_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0)); + + if (shm_area_ == MAP_FAILED) { + std::cerr << "Could not map shm area, mmap failed" << std::endl; + return false; + } + + return true; +} + +bool +SHMSrc::stop() +{ + if (fd_ >= 0) + close(fd_); + fd_ = -1; + + if (shm_area_ != MAP_FAILED) + munmap(shm_area_, shm_area_len_); + shm_area_len_ = 0; + shm_area_ = static_cast<SHMHeader*>(MAP_FAILED); + + return true; +} + +bool +SHMSrc::resize_area() +{ + while ((sizeof(SHMHeader) + shm_area_->buffer_size) > shm_area_len_) { + size_t new_size = sizeof(SHMHeader) + shm_area_->buffer_size; + + shm_unlock(); + if (munmap(shm_area_, shm_area_len_)) { + std::cerr << "Could not unmap shared area" << std::endl; + perror(strerror(errno)); + return false; + } + + shm_area_ = static_cast<SHMHeader*>(mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0)); + shm_area_len_ = new_size; + + if (!shm_area_) { + shm_area_ = 0; + std::cerr << "Could not remap shared area" << std::endl; + return false; + } + + shm_area_len_ = new_size; + shm_lock(); + } + return true; +} + +void SHMSrc::render(char *dest, size_t len) +{ + shm_lock(); + + while (buffer_gen_ == shm_area_->buffer_gen) { + shm_unlock(); + std::cerr << "Waiting for next buffer" << std::endl;; + sem_wait(&shm_area_->notification); + + shm_lock(); + } + + if (!resize_area()) + return; + + std::cerr << "Reading from buffer!" << std::endl; + memcpy(dest, shm_area_->data, len); + buffer_gen_ = shm_area_->buffer_gen; + shm_unlock(); +} + +void SHMSrc::shm_lock() +{ + sem_wait(&shm_area_->mutex); +} + +void SHMSrc::shm_unlock() +{ + sem_post(&shm_area_->mutex); +} diff --git a/daemon/src/video/video_endpoint.cpp b/daemon/src/video/test/shm_src.h similarity index 57% rename from daemon/src/video/video_endpoint.cpp rename to daemon/src/video/test/shm_src.h index 8b4953678baca3e1ddefb31b56516a142c8b67f0..9ae216aa17478dd305bfd167069e689f9558f4e0 100644 --- a/daemon/src/video/video_endpoint.cpp +++ b/daemon/src/video/test/shm_src.h @@ -1,7 +1,12 @@ /* - * Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc. + * Copyright (C) 2012 Savoir-Faire Linux Inc. * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> * + * Portions derived from GStreamer: + * Copyright (C) <2009> Collabora Ltd + * @author: Olivier Crete <olivier.crete@collabora.co.uk + * Copyright (C) <2009> Nokia Inc + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or @@ -14,7 +19,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * * Additional permission under GNU GPL version 3 section 7: * * If you modify this program, or any covered work, by linking or @@ -27,46 +33,39 @@ * as that of the covered work. */ -#include "video_endpoint.h" +#ifndef SHM_SRC_H_ +#define SHM_SRC_H_ -#include <sstream> -#include <vector> -#include "libav_utils.h" +#include <string> +#include "../../noncopyable.h" -namespace sfl_video { +class SHMHeader; +// Example Shared memory source, only useful for testing +// as far as the daemon is concerned -/* anonymous namespace */ -namespace { -int FAKE_BITRATE() -{ - return 1000; -} +class SHMSrc { + public: + SHMSrc(const std::string &shm_name); + virtual ~SHMSrc() {}; -/* FIXME: use real bitrates */ -int getBitRate(const std::string & /*codec*/) -{ - return FAKE_BITRATE(); -} -} // end anonymous namespace + bool start(); + bool stop(); -std::vector<std::string> getCodecList() -{ - return libav_utils::getVideoCodecList(); -} + bool resize_area(); -std::map<std::string, std::string> getCodecSpecifications(const std::string &codec) -{ - std::map<std::string, std::string> specs; - const char * const NAME_KEY = "name"; - const char * const BITRATE_KEY = "bitrate"; + void render(char *data, size_t len); - // Add the bit rate - specs[NAME_KEY] = codec; - std::stringstream ss; - ss << getBitRate(codec); - specs[BITRATE_KEY] = ss.str(); + protected: + void shm_lock(); + void shm_unlock(); + std::string shm_name_; + int fd_; + SHMHeader *shm_area_; + size_t shm_area_len_; + unsigned buffer_gen_; - return specs; -} + private: + NON_COPYABLE(SHMSrc); +}; -} // end namespace sfl_video +#endif // SHM_SRC_H_ diff --git a/daemon/src/video/test/shmclient.c b/daemon/src/video/test/shmclient.c deleted file mode 100644 index 267bd7fe04ebcecd2021d8bc74d568e33f7de6c7..0000000000000000000000000000000000000000 --- a/daemon/src/video/test/shmclient.c +++ /dev/null @@ -1,192 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/ipc.h> -#include <sys/sem.h> /* semaphore functions and structs. */ -#include <sys/shm.h> - -#include <clutter/clutter.h> -#define TEMPFILE "/tmp/frame.txt" - -#if _SEM_SEMUN_UNDEFINED - union semun - { - int val; /* value for SETVAL */ - struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ - unsigned short int *array; /* array for GETALL & SETALL */ - struct seminfo *__buf; /* buffer for IPC_INFO */ - }; -#endif - -typedef struct { - unsigned size; - unsigned width; - unsigned height; -} FrameInfo; - -struct AppData { - unsigned width; - unsigned height; - char *shm_buffer; - int sem_set_id; - ClutterActor *texture; -}; - -FrameInfo getFrameSize() -{ - FrameInfo info; - - /* get message out of the file */ - FILE *tmp = fopen(TEMPFILE, "r"); - fscanf(tmp, "%u\n%u\n%u\n", &info.size, &info.width, &info.height); - printf("Size is %u\n", info.size); - printf("Width is %u\n", info.width); - printf("Height is %u\n", info.height); - fclose(tmp); - return info; -} - -int get_sem_set() -{ - /* this variable will contain the semaphore set. */ - int sem_set_id; - key_t key = ftok("/tmp", 'b'); - - /* semaphore value, for semctl(). */ - union semun sem_val; - - /* first we get a semaphore set with a single semaphore, */ - /* whose counter is initialized to '0'. */ - sem_set_id = semget(key, 1, 0600); - if (sem_set_id == -1) { - perror("semget"); - exit(1); - } - sem_val.val = 0; - semctl(sem_set_id, 0, SETVAL, sem_val); - return sem_set_id; -} - -/* - * function: sem_wait. wait for frame from other process - * input: semaphore set ID. - * output: none. - */ - void -sem_wait(int sem_set_id) -{ - /* structure for semaphore operations. */ - struct sembuf sem_op; - - /* wait on the semaphore, unless it's value is non-negative. */ - sem_op.sem_num = 0; - sem_op.sem_op = -1; - sem_op.sem_flg = 0; - semop(sem_set_id, &sem_op, 1); -} - -/* join and/or create a shared memory segment */ -int getShm(unsigned numBytes) -{ - key_t key; - int shm_id; - /* connect to a segment with 600 permissions - (r--r--r--) */ - key = ftok("/tmp", 'c'); - shm_id = shmget(key, numBytes, 0644); - - return shm_id; -} - -/* attach a shared memory segment */ -char *attachShm(int shm_id) -{ - char *data = NULL; - - /* attach to the segment and get a pointer to it */ - data = shmat(shm_id, (void *)0, 0); - if (data == (char *)(-1)) { - perror("shmat"); - data = NULL; - } - - return data; -} - -void detachShm(char *data) -{ - /* detach from the segment: */ - if (shmdt(data) == -1) { - perror("shmdt"); - } -} - -/* round integer value up to next multiple of 4 */ -int round_up_4(int value) -{ - return (value + 3) &~ 3; -} - -void readFrameFromShm(int width, int height, char *data, int sem_set_id, - ClutterActor *texture) -{ - sem_wait(sem_set_id); - clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE(texture), - (void*)data, - FALSE, - width, - height, - round_up_4(3 * width), - 3, - 0, - NULL); -} - -gboolean updateTexture(gpointer data) -{ - struct AppData *app = (struct AppData*) data; - readFrameFromShm(app->width, app->height, app->shm_buffer, app->sem_set_id, - app->texture); - return TRUE; -} - -int main(int argc, char *argv[]) -{ - /* Initialize Clutter */ - if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) - return 1; - FrameInfo info = getFrameSize(); - int shm_id = getShm(info.size); - char *shm_buffer = attachShm(shm_id); - if (shm_buffer == NULL) - return 1; - int sem_set_id = get_sem_set(); - - ClutterActor *stage, *texture; - - /* Get the default stage */ - stage = clutter_stage_get_default (); - clutter_actor_set_size(stage, - info.width, - info.height); - - texture = clutter_texture_new(); - - clutter_stage_set_title(CLUTTER_STAGE (stage), "Client"); - /* Add ClutterTexture to the stage */ - clutter_container_add(CLUTTER_CONTAINER (stage), texture, NULL); - - struct AppData app = {info.width, info.height, shm_buffer, sem_set_id, - texture}; - /* frames are read and saved here */ - g_idle_add(updateTexture, &app); - - clutter_actor_show_all(stage); - - /* main loop */ - clutter_main(); - - detachShm(shm_buffer); - - return 0; -} diff --git a/daemon/src/video/test/shmclient.cpp b/daemon/src/video/test/shmclient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba459d1bfed110e61d2933998fa204cadaf0a37e --- /dev/null +++ b/daemon/src/video/test/shmclient.cpp @@ -0,0 +1,116 @@ +#include <cstdio> +#include <cstdlib> +#include "shm_src.h" +#include "../shm_header.h" +#include "../../noncopyable.h" +#include <sys/mman.h> +#include <iostream> +#include <clutter/clutter.h> + +class ClutterSHMSrc : public SHMSrc { + public: + ClutterSHMSrc(const std::string &name, + unsigned width, + unsigned height, + ClutterActor *texture) : + SHMSrc(name), + width_(width), + height_(height), + texture_(texture) + { + printf("Creating source with name:%s width:%d height:%d texture:%p\n", name.c_str(), width, height, texture); + } + + void render_to_texture() + { + if (shm_area_ == MAP_FAILED) { + g_print("shm_area is MAP FAILED!\n"); + return; + } + + shm_lock(); + + while (buffer_gen_ == shm_area_->buffer_gen) { + shm_unlock(); + sem_wait(&shm_area_->notification); + + shm_lock(); + } + + if (!resize_area()) { + g_print("could not resize area\n"); + return; + } + + clutter_actor_set_size(texture_, width_, height_); + const int BPP = 4; + const int ROW_STRIDE = BPP * width_; + /* update the clutter texture */ + clutter_texture_set_from_rgb_data(CLUTTER_TEXTURE(texture_), + reinterpret_cast<const unsigned char *>(shm_area_->data), + TRUE, + width_, + height_, + ROW_STRIDE, + BPP, + CLUTTER_TEXTURE_RGB_FLAG_BGR, + NULL); + buffer_gen_ = shm_area_->buffer_gen; + shm_unlock(); + } + + private: + NON_COPYABLE(ClutterSHMSrc); + unsigned width_; + unsigned height_; + ClutterActor *texture_; +}; + +gboolean updateTexture(gpointer data) +{ + ClutterSHMSrc *src = static_cast<ClutterSHMSrc *>(data); + src->render_to_texture(); + return TRUE; +} + +int main(int argc, char *argv[]) +{ + if (argc < 4) { + printf("Usage: ./shmclient <shm_filename> <width> <height>\n"); + return 1; + } + + /* Initialize Clutter */ + if (clutter_init(NULL, NULL) != CLUTTER_INIT_SUCCESS) + return 1; + + /* Get the default stage */ + ClutterActor *stage = clutter_stage_get_default(); + + const int width = atoi(argv[2]); + const int height = atoi(argv[3]); + + clutter_actor_set_size(stage, width, height); + + ClutterActor *texture = clutter_texture_new(); + + clutter_stage_set_title(CLUTTER_STAGE(stage), "Client"); + /* Add ClutterTexture to the stage */ + clutter_container_add(CLUTTER_CONTAINER(stage), texture, NULL); + + ClutterSHMSrc src(argv[1], width, height, texture); + if (not src.start()) { + printf("Could not start SHM source\n"); + return 1; + } + /* frames are read and saved here */ + g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 30, updateTexture, &src, NULL); + + clutter_actor_show_all(stage); + + /* main loop */ + clutter_main(); + src.stop(); + + return 0; +} diff --git a/daemon/src/video/test/test_shm.cpp b/daemon/src/video/test/test_shm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d51bf5a2b0c1973c83c8be60815e061b9bfbabf1 --- /dev/null +++ b/daemon/src/video/test/test_shm.cpp @@ -0,0 +1,107 @@ +#include "shm_sink.h" +#include "shm_src.h" +#include <thread> +#include <signal.h> +#include <iostream> +#include <unistd.h> +#include <sys/wait.h> +#include <atomic> +#include <cstring> +#include <cassert> + +namespace { + +std::atomic<bool> done(false); + +void signal_handler(int /*sig*/) +{ + done = true; +} + +const char test_data[] = "abcdefghijklmnopqrstuvwxyz"; + +void sink_thread() +{ + SHMSink sink("bob");; + if (!sink.start()) + return; + std::vector<unsigned char> test_vec(test_data, test_data + sizeof(test_data) / sizeof(test_data[0])); + + while (!done) { + sink.render(test_vec); + usleep(1000); + } + sink.stop(); + std::cerr << std::endl; + std::cerr << "Exitting sink thread" << std::endl; +} + +void run_client() +{ + SHMSrc src("bob");; + bool started = false; + while (not done and not started) { + sleep(1); + if (src.start()) + started = true; + } + // we get here if the above loop was interupted by our signal handler + if (!started) + return; + + // initialize destination string to 0's + std::string dest(sizeof(test_data), 0); + const std::string test_data_str(test_data, sizeof(test_data)); + assert(test_data_str.size() == 27); + assert(dest.size() == test_data_str.size()); + while (not done and dest != test_data_str) { + src.render(&(*dest.begin()), dest.size()); + usleep(1000); + } + src.stop(); + std::cerr << "Got characters, exitting client process" << std::endl; +} + +void run_daemon() +{ + std::thread bob(sink_thread); + /* Wait for child process. */ + int status; + int pid; + if ((pid = wait(&status)) == -1) { + perror("wait error"); + } else { + // Check status. + if (WIFSIGNALED(status) != 0) + std::cout << "Child process ended because of signal " << + WTERMSIG(status) << std::endl; + else if (WIFEXITED(status) != 0) + std::cout << "Child process ended normally; status = " << + WEXITSTATUS(status) << std::endl; + else + std::cout << "Child process did not end normally" << std::endl; + } + std::cout << "Finished waiting for child" << std::endl; + done = true; + // wait for thread + bob.join(); +} + +} // end anonymous namespace + +int main() +{ + signal(SIGINT, signal_handler); + pid_t pid = fork(); + if (pid < 0) { + std::cerr << "Failed to fork" << std::endl; + return 1; + } else if (pid == 0) { + // child code only + run_client(); + } else { + // parent code only + run_daemon(); + } + return 0; +} diff --git a/daemon/src/video/test/test_video_endpoint.cpp b/daemon/src/video/test/test_video_endpoint.cpp index f19d51ce4f313652d4a43a0e2d76775ac3ac3f00..7eec1a1c17da72015b9d8bee2f760b8ab736f334 100644 --- a/daemon/src/video/test/test_video_endpoint.cpp +++ b/daemon/src/video/test/test_video_endpoint.cpp @@ -33,17 +33,16 @@ #include <memory> #include <iostream> #include <cassert> -#include "video_endpoint.h" #include "libav_utils.h" void VideoEndpointTest::testListInstalledCodecs() { /* This would list codecs */ - std::cout << "Installed codecs:" << std::endl; - std::vector<std::string> codecs = sfl_video::getCodecList(); + std::cout << "Installed codecs:" << std::endl; + std::vector<std::string> codecs(libav_utils::getVideoCodecList()); std::vector<std::string>::iterator it; - for (it = codecs.begin(); it != codecs.end(); ++it) - std::cout << '\t' << *it << std::endl; + for (it = codecs.begin(); it != codecs.end(); ++it) + std::cout << '\t' << *it << std::endl; } int main () diff --git a/daemon/src/video/test/test_video_rtp.cpp b/daemon/src/video/test/test_video_rtp.cpp index e113c7750bf36db4d17c28470a11c8b456d6463d..0cfaec39ec3e31a536d66f89f73df33e5e35113a 100644 --- a/daemon/src/video/test/test_video_rtp.cpp +++ b/daemon/src/video/test/test_video_rtp.cpp @@ -39,7 +39,7 @@ int main () { VideoPreference preference; - sfl_video::VideoRtpSession session(preference.getSettings()); + sfl_video::VideoRtpSession session("test", preference.getSettings()); session.start(); sleep(10); session.stop(); diff --git a/daemon/src/video/video_preview.cpp b/daemon/src/video/video_preview.cpp index 0417e0469ed3572491dd7d7a1572cee0c7d0ab46..bf2aa9d2ccb849fd07c50a49d9ac29e9e19aa20c 100644 --- a/daemon/src/video/video_preview.cpp +++ b/daemon/src/video/video_preview.cpp @@ -29,10 +29,9 @@ */ #include "video_preview.h" +#include "logger.h" #include <map> #include <string> -#include "manager.h" -#include "shared_memory.h" #include "video_receive_thread.h" class VideoControls; @@ -40,19 +39,17 @@ class VideoControls; namespace sfl_video { VideoPreview::VideoPreview(const std::map<std::string, std::string> &args) : - args_(args), sharedMemory_(), receiveThread_() + args_(args), receiveThread_() { - VideoControls *controls(Manager::instance().getDbusManager()->getVideoControls()); - sharedMemory_.reset(new SharedMemory(*controls)); - receiveThread_.reset(new VideoReceiveThread(args_, *sharedMemory_)); + const char * const LOCAL_ID = "local"; + receiveThread_.reset(new VideoReceiveThread(LOCAL_ID, args_)); receiveThread_->start(); - sharedMemory_->waitForShm(); } -void VideoPreview::getShmInfo(int &shmKey, int &semaphoreKey, int &bufferSize) +VideoPreview::~VideoPreview() { - shmKey = sharedMemory_->getShmKey(); - semaphoreKey = sharedMemory_->getSemaphoreKey(); - bufferSize = sharedMemory_->getBufferSize(); + // explicitly destroy the thread object + receiveThread_.reset(); } + } // end namspace sfl_video diff --git a/daemon/src/video/video_preview.h b/daemon/src/video/video_preview.h index 4dd2795a2e1be2505f0b9d7336a04777b9f71eeb..212d9bc3f6a480bde1df5c6bf6955c362524e661 100644 --- a/daemon/src/video/video_preview.h +++ b/daemon/src/video/video_preview.h @@ -37,17 +37,15 @@ namespace sfl_video { -class SharedMemory; class VideoReceiveThread; class VideoPreview { public: VideoPreview(const std::map<std::string, std::string> &args); - void getShmInfo(int &shmKey, int &semaphoreKey, int &bufferSize); + ~VideoPreview(); private: std::map<std::string, std::string> args_; - std::tr1::shared_ptr<SharedMemory> sharedMemory_; std::tr1::shared_ptr<VideoReceiveThread> receiveThread_; }; } diff --git a/daemon/src/video/video_receive_thread.cpp b/daemon/src/video/video_receive_thread.cpp index 2dd60c9053503d428bab0bfc8c1fd04f0eb6d549..9f91482fb2b022b651a9052d0b89fa23f1a16719 100644 --- a/daemon/src/video/video_receive_thread.cpp +++ b/daemon/src/video/video_receive_thread.cpp @@ -30,6 +30,7 @@ */ #include "video_receive_thread.h" +#include "dbus/video_controls.h" #include "packet_handle.h" #include "check.h" @@ -50,7 +51,6 @@ extern "C" { #include <fstream> #include "manager.h" -#include "shared_memory.h" static const enum PixelFormat VIDEO_RGB_FORMAT = PIX_FMT_BGRA; @@ -64,20 +64,23 @@ int getBufferSize(int width, int height, int format) { enum PixelFormat fmt = (enum PixelFormat) format; // determine required buffer size and allocate buffer - return sizeof(uint8_t) * avpicture_get_size(fmt, width, height); + return sizeof(unsigned char) * avpicture_get_size(fmt, width, height); } -string openTemp(string path, std::ofstream& f) +string openTemp(string path, std::ofstream& os) { - path += "/XXXXXX"; + path += "/"; + // POSIX the mktemp family of functions requires names to end with 6 x's + const char * const X_SUFFIX = "XXXXXX"; + + path += X_SUFFIX; std::vector<char> dst_path(path.begin(), path.end()); dst_path.push_back('\0'); - int fd = -1; - while (fd == -1) { + for (int fd = -1; fd == -1; ) { fd = mkstemp(&dst_path[0]); if (fd != -1) { path.assign(dst_path.begin(), dst_path.end() - 1); - f.open(path.c_str(), std::ios_base::trunc | std::ios_base::out); + os.open(path.c_str(), std::ios_base::trunc | std::ios_base::out); close(fd); } } @@ -87,7 +90,7 @@ string openTemp(string path, std::ofstream& f) void VideoReceiveThread::loadSDP() { - RETURN_IF_FAIL(not args_["receiving_sdp"].empty(), "Cannot load empty SDP"); + EXIT_IF_FAIL(not args_["receiving_sdp"].empty(), "Cannot load empty SDP"); std::ofstream os; sdpFilename_ = openTemp("/tmp", os); @@ -120,7 +123,7 @@ void VideoReceiveThread::setup() DEBUG("Using %s format", format_str.c_str()); AVInputFormat *file_iformat = av_find_input_format(format_str.c_str()); - RETURN_IF_FAIL(file_iformat, "Could not find format \"%s\"", format_str.c_str()); + EXIT_IF_FAIL(file_iformat, "Could not find format \"%s\"", format_str.c_str()); AVDictionary *options = NULL; if (!args_["framerate"].empty()) @@ -132,8 +135,10 @@ void VideoReceiveThread::setup() // Open video file DEBUG("Opening input"); + inputCtx_ = avformat_alloc_context(); + inputCtx_->interrupt_callback = interruptCb_; int ret = avformat_open_input(&inputCtx_, input.c_str(), file_iformat, options ? &options : NULL); - RETURN_IF_FAIL(ret == 0, "Could not open input \"%s\"", input.c_str()); + EXIT_IF_FAIL(ret == 0, "Could not open input \"%s\"", input.c_str()); DEBUG("Finding stream info"); #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 8, 0) @@ -141,7 +146,7 @@ void VideoReceiveThread::setup() #else ret = avformat_find_stream_info(inputCtx_, options ? &options : NULL); #endif - RETURN_IF_FAIL(ret >= 0, "Could not find stream info!"); + EXIT_IF_FAIL(ret >= 0, "Could not find stream info!"); // find the first video stream from the input streamIndex_ = -1; @@ -149,41 +154,38 @@ void VideoReceiveThread::setup() if (inputCtx_->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) streamIndex_ = i; - RETURN_IF_FAIL(streamIndex_ != -1, "Could not find video stream"); + EXIT_IF_FAIL(streamIndex_ != -1, "Could not find video stream"); // Get a pointer to the codec context for the video stream decoderCtx_ = inputCtx_->streams[streamIndex_]->codec; // find the decoder for the video stream AVCodec *inputDecoder = avcodec_find_decoder(decoderCtx_->codec_id); - RETURN_IF_FAIL(inputDecoder, "Unsupported codec"); + EXIT_IF_FAIL(inputDecoder, "Unsupported codec"); #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 6, 0) ret = avcodec_open(decoderCtx_, inputDecoder); #else ret = avcodec_open2(decoderCtx_, inputDecoder, NULL); #endif - RETURN_IF_FAIL(ret == 0, "Could not open codec"); + EXIT_IF_FAIL(ret == 0, "Could not open codec"); scaledPicture_ = avcodec_alloc_frame(); - RETURN_IF_FAIL(scaledPicture_, "Could not allocate output frame"); + EXIT_IF_FAIL(scaledPicture_, "Could not allocate output frame"); if (dstWidth_ == 0 and dstHeight_ == 0) { dstWidth_ = decoderCtx_->width; dstHeight_ = decoderCtx_->height; } - // determine required buffer size and allocate buffer - const int bufferSize = getBufferSize(dstWidth_, dstHeight_, VIDEO_RGB_FORMAT); - try { - sharedMemory_.allocateBuffer(dstWidth_, dstHeight_, bufferSize); - } catch (const std::runtime_error &e) { - ERROR("%s", e.what()); - ost::Thread::exit(); - } - // allocate video frame rawFrame_ = avcodec_alloc_frame(); + // determine required buffer size and allocate buffer + bufferSize_ = getBufferSize(dstWidth_, dstHeight_, VIDEO_RGB_FORMAT); + + EXIT_IF_FAIL(sink_.start(), "Cannot start shared memory sink"); + Manager::instance().getVideoControls()->startedDecoding(id_, sink_.openedName(), dstWidth_, dstHeight_); + DEBUG("shm sink started with size %d, width %d and height %d", bufferSize_, dstWidth_, dstHeight_); } void VideoReceiveThread::createScalingContext() @@ -194,25 +196,51 @@ void VideoReceiveThread::createScalingContext() decoderCtx_->pix_fmt, dstWidth_, dstHeight_, VIDEO_RGB_FORMAT, SWS_BICUBIC, NULL, NULL, NULL); - RETURN_IF_FAIL(imgConvertCtx_, "Cannot init the conversion context!"); + EXIT_IF_FAIL(imgConvertCtx_, "Cannot init the conversion context!"); +} + +// This callback is used by libav internally to break out of blocking calls +int VideoReceiveThread::interruptCb(void *ctx) +{ + VideoReceiveThread *context = static_cast<VideoReceiveThread*>(ctx); + return not context->receiving_; } -VideoReceiveThread::VideoReceiveThread(const std::map<string, string> &args, - sfl_video::SharedMemory &handle) : +VideoReceiveThread::VideoReceiveThread(const std::string &id, const std::map<string, string> &args) : args_(args), frameNumber_(0), decoderCtx_(0), rawFrame_(0), scaledPicture_(0), streamIndex_(-1), inputCtx_(0), imgConvertCtx_(0), - dstWidth_(0), dstHeight_(0), sharedMemory_(handle), receiving_(false), - sdpFilename_() -{} + dstWidth_(0), dstHeight_(0), sink_(), receiving_(false), sdpFilename_(), + bufferSize_(0), id_(id), interruptCb_() +{ + interruptCb_.callback = interruptCb; + interruptCb_.opaque = this; +} + +/// Copies and scales our rendered frame to the buffer pointed to by data +void VideoReceiveThread::fill_buffer(void *data) +{ + avpicture_fill(reinterpret_cast<AVPicture *>(scaledPicture_), + static_cast<uint8_t *>(data), + VIDEO_RGB_FORMAT, + dstWidth_, + dstHeight_); + + sws_scale(imgConvertCtx_, + rawFrame_->data, + rawFrame_->linesize, + 0, + decoderCtx_->height, + scaledPicture_->data, + scaledPicture_->linesize); +} void VideoReceiveThread::run() { + receiving_ = true; setup(); createScalingContext(); - receiving_ = true; - if (not args_["receiving_sdp"].empty()) - sharedMemory_.publishShm(); + const Callback cb(&VideoReceiveThread::fill_buffer); while (receiving_) { AVPacket inpacket; @@ -221,24 +249,19 @@ void VideoReceiveThread::run() ERROR("Couldn't read frame : %s\n", strerror(ret)); break; } + // Guarantee that we free the packet every iteration PacketHandle inpacket_handle(inpacket); // is this a packet from the video stream? if (inpacket.stream_index == streamIndex_) { - int frameFinished; + int frameFinished = 0; avcodec_decode_video2(decoderCtx_, rawFrame_, &frameFinished, &inpacket); - if (frameFinished) { - avpicture_fill(reinterpret_cast<AVPicture *>(scaledPicture_), - sharedMemory_.getTargetBuffer(), - VIDEO_RGB_FORMAT, dstWidth_, dstHeight_); - sws_scale(imgConvertCtx_, rawFrame_->data, rawFrame_->linesize, - 0, decoderCtx_->height, scaledPicture_->data, - scaledPicture_->linesize); - - sharedMemory_.frameUpdatedCallback(); - } + // we want our rendering code to be called by the shm_sink, + // because it manages the shared memory synchronization + if (frameFinished) + sink_.render_callback(this, cb, bufferSize_); } yield(); } @@ -247,6 +270,7 @@ void VideoReceiveThread::run() VideoReceiveThread::~VideoReceiveThread() { receiving_ = false; + Manager::instance().getVideoControls()->stoppedDecoding(id_, sink_.openedName()); ost::Thread::terminate(); if (imgConvertCtx_) @@ -261,11 +285,12 @@ VideoReceiveThread::~VideoReceiveThread() if (decoderCtx_) avcodec_close(decoderCtx_); - if (inputCtx_) + if (inputCtx_) { #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 8, 0) av_close_input_file(inputCtx_); #else avformat_close_input(&inputCtx_); #endif + } } } // end namespace sfl_video diff --git a/daemon/src/video/video_receive_thread.h b/daemon/src/video/video_receive_thread.h index 4e1d27de12f9743f15207cd2ef8531d4888bf044..f36126bec6df4d87bfcafe10994559fc17c53959 100644 --- a/daemon/src/video/video_receive_thread.h +++ b/daemon/src/video/video_receive_thread.h @@ -35,8 +35,13 @@ #include <map> #include <string> #include <climits> +#include "shm_sink.h" #include "noncopyable.h" +extern "C" { +#include <libavformat/avformat.h> +} + class SwsContext; class AVCodecContext; class AVStream; @@ -44,7 +49,6 @@ class AVFormatContext; class AVFrame; namespace sfl_video { -class SharedMemory; class VideoReceiveThread : public ost::Thread { private: @@ -66,16 +70,20 @@ class VideoReceiveThread : public ost::Thread { int dstWidth_; int dstHeight_; - SharedMemory &sharedMemory_; + SHMSink sink_; bool receiving_; std::string sdpFilename_; + size_t bufferSize_; + const std::string id_; void setup(); void createScalingContext(); void loadSDP(); + void fill_buffer(void *data); + static int interruptCb(void *ctx); + AVIOInterruptCB interruptCb_; public: - VideoReceiveThread(const std::map<std::string, std::string> &args, - SharedMemory &handle); + VideoReceiveThread(const std::string &id, const std::map<std::string, std::string> &args); virtual ~VideoReceiveThread(); virtual void run(); }; diff --git a/daemon/src/video/video_rtp_session.cpp b/daemon/src/video/video_rtp_session.cpp index d1c911a09968f7a25555ba7e21ec399849c42859..dc70d262dbdfe1eb12ca806f72f37d76c657e5e3 100644 --- a/daemon/src/video/video_rtp_session.cpp +++ b/daemon/src/video/video_rtp_session.cpp @@ -32,7 +32,6 @@ #include <sstream> #include <map> #include <string> -#include "shared_memory.h" #include "video_send_thread.h" #include "video_receive_thread.h" #include "sip/sdp.h" @@ -45,24 +44,14 @@ namespace sfl_video { using std::map; using std::string; -VideoRtpSession::VideoRtpSession(const map<string, string> &txArgs) : - sharedMemory_(), sendThread_(), receiveThread_(), txArgs_(txArgs), - rxArgs_(), sending_(true), receiving_(true) -{ - // FIXME: bitrate must be configurable - txArgs_["bitrate"] = "500000"; -} - -VideoRtpSession::VideoRtpSession(const map<string, string> &txArgs, - const map<string, string> &rxArgs) : - sharedMemory_(), sendThread_(), receiveThread_(), txArgs_(txArgs), - rxArgs_(rxArgs), sending_(true), receiving_(true) +VideoRtpSession::VideoRtpSession(const string &callID, const map<string, string> &txArgs) : + sendThread_(), receiveThread_(), txArgs_(txArgs), + rxArgs_(), sending_(false), receiving_(false), callID_(callID) {} void VideoRtpSession::updateSDP(const Sdp &sdp) { - const std::vector<string> v(sdp.getActiveVideoDescription()); - const string &desc = v[0]; + string desc(sdp.getActiveIncomingVideoDescription()); // if port has changed if (desc != rxArgs_["receiving_sdp"]) { rxArgs_["receiving_sdp"] = desc; @@ -94,19 +83,21 @@ void VideoRtpSession::updateSDP(const Sdp &sdp) receiving_ = false; } - if (not v[1].empty()) { - const string codec = libav_utils::encodersMap()[v[1]]; - if (codec.empty()) { - DEBUG("Couldn't find encoder for \"%s\"\n", v[1].c_str()); + string codec(sdp.getActiveOutgoingVideoCodec()); + if (not codec.empty()) { + const string encoder(libav_utils::encodersMap()[codec]); + if (encoder.empty()) { + DEBUG("Couldn't find encoder for \"%s\"\n", codec.c_str()); sending_ = false; } else { - txArgs_["codec"] = codec; + txArgs_["codec"] = encoder; + txArgs_["bitrate"] = sdp.getActiveOutgoingVideoBitrate(codec); } } else { sending_ = false; } - txArgs_["payload_type"] = v[2]; + txArgs_["payload_type"] = sdp.getActiveOutgoingVideoPayload();; } void VideoRtpSession::updateDestination(const string &destination, @@ -149,9 +140,7 @@ void VideoRtpSession::start() if (receiving_) { if (receiveThread_.get()) WARN("Restarting video receiver"); - VideoControls *controls(Manager::instance().getDbusManager()->getVideoControls()); - sharedMemory_.reset(new SharedMemory(*controls)); - receiveThread_.reset(new VideoReceiveThread(rxArgs_, *sharedMemory_)); + receiveThread_.reset(new VideoReceiveThread(callID_, rxArgs_)); receiveThread_->start(); } else @@ -163,4 +152,10 @@ void VideoRtpSession::stop() receiveThread_.reset(); sendThread_.reset(); } + +void VideoRtpSession::forceKeyFrame() +{ + sendThread_->forceKeyFrame(); +} + } // end namespace sfl_video diff --git a/daemon/src/video/video_rtp_session.h b/daemon/src/video/video_rtp_session.h index 08c0065a4a8b8e6a3a25b41708e43e7915d27c19..2b80928cd558ef27cc4980c3ed896714961046e8 100644 --- a/daemon/src/video/video_rtp_session.h +++ b/daemon/src/video/video_rtp_session.h @@ -39,30 +39,28 @@ class Sdp; namespace sfl_video { -class SharedMemory; class VideoSendThread; class VideoReceiveThread; class VideoRtpSession { public: - VideoRtpSession(const std::map<std::string, std::string> &txArgs); - VideoRtpSession(const std::map<std::string, std::string> &txArgs, - const std::map<std::string, std::string> &rxArgs); - + VideoRtpSession(const std::string &callID, + const std::map<std::string, std::string> &txArgs); void start(); void stop(); void updateDestination(const std::string &destination, unsigned int port); void updateSDP(const Sdp &sdp); + void forceKeyFrame(); private: - std::tr1::shared_ptr<SharedMemory> sharedMemory_; std::tr1::shared_ptr<VideoSendThread> sendThread_; std::tr1::shared_ptr<VideoReceiveThread> receiveThread_; std::map<std::string, std::string> txArgs_; std::map<std::string, std::string> rxArgs_; bool sending_; bool receiving_; + const std::string callID_; }; } diff --git a/daemon/src/video/video_send_thread.cpp b/daemon/src/video/video_send_thread.cpp index 58122675bc9bf7dca0e348227b57e360453b1b22..1848e4e3c30d9b8208bf659772cb919dfe933146 100644 --- a/daemon/src/video/video_send_thread.cpp +++ b/daemon/src/video/video_send_thread.cpp @@ -45,7 +45,6 @@ extern "C" { #include <map> #include "manager.h" -#include "libx264-ultrafast.ffpreset.h" namespace sfl_video { @@ -77,7 +76,12 @@ void VideoSendThread::waitForSDP() void VideoSendThread::forcePresetX264() { - av_set_options_string(encoderCtx_, x264_preset_ultrafast, "=", "\n"); + const char *speedPreset = "ultrafast"; + if (av_opt_set(encoderCtx_->priv_data, "preset", speedPreset, 0)) + WARN("Failed to set x264 preset '%s'", speedPreset); + const char *tune = "zerolatency"; + if (av_opt_set(encoderCtx_->priv_data, "tune", tune, 0)) + WARN("Failed to set x264 tune '%s'", tune); } void VideoSendThread::prepareEncoderContext(AVCodec *encoder) @@ -91,11 +95,8 @@ void VideoSendThread::prepareEncoderContext(AVCodec *encoder) #endif // set some encoder settings here - encoderCtx_->bit_rate = atoi(args_["bitrate"].c_str()); - encoderCtx_->bit_rate = 400000; - encoderCtx_->rc_max_rate = encoderCtx_->bit_rate; - encoderCtx_->rc_min_rate = 0; - encoderCtx_->rc_buffer_size = encoderCtx_->rc_max_rate; + encoderCtx_->bit_rate = 1000 * atoi(args_["bitrate"].c_str()); + DEBUG("Using bitrate %d", encoderCtx_->bit_rate); // resolution must be a multiple of two if (args_["width"].empty() and inputDecoderCtx_) @@ -112,10 +113,7 @@ void VideoSendThread::prepareEncoderContext(AVCodec *encoder) const int fps = args_["framerate"].empty() ? DEFAULT_FPS : atoi(args_["framerate"].c_str()); encoderCtx_->time_base = (AVRational) {1, fps}; // emit one intra frame every gop_size frames - encoderCtx_->gop_size = 10 * fps; encoderCtx_->max_b_frames = 0; - const int MTU = 1500; - encoderCtx_->rtp_payload_size = MTU / 2; // Target GOB length encoderCtx_->pix_fmt = PIX_FMT_YUV420P; // Fri Jul 22 11:37:59 EDT 2011:tmatth:XXX: DON'T set this, we want our // pps and sps to be sent in-band for RTP @@ -123,6 +121,7 @@ void VideoSendThread::prepareEncoderContext(AVCodec *encoder) // encoderCtx_->flags |= CODEC_FLAG_GLOBAL_HEADER; } + void VideoSendThread::setup() { AVInputFormat *file_iformat = 0; @@ -132,7 +131,7 @@ void VideoSendThread::setup() if (args_["input"].find(V4L_PATH) != std::string::npos) { DEBUG("Using v4l2 format"); file_iformat = av_find_input_format("video4linux2"); - RETURN_IF_FAIL(file_iformat, "Could not find format video4linux2"); + EXIT_IF_FAIL(file_iformat, "Could not find format video4linux2"); } AVDictionary *options = NULL; @@ -144,9 +143,11 @@ void VideoSendThread::setup() av_dict_set(&options, "channel", args_["channel"].c_str(), 0); // Open video file + inputCtx_ = avformat_alloc_context(); + inputCtx_->interrupt_callback = interruptCb_; int ret = avformat_open_input(&inputCtx_, args_["input"].c_str(), file_iformat, &options); - RETURN_IF_FAIL(ret == 0, "Could not open input file %s", args_["input"].c_str()); + EXIT_IF_FAIL(ret == 0, "Could not open input file %s", args_["input"].c_str()); // find the first video stream from the input streamIndex_ = -1; @@ -154,27 +155,28 @@ void VideoSendThread::setup() if (inputCtx_->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) streamIndex_ = i; - RETURN_IF_FAIL(streamIndex_ != -1, "Could not find video stream"); + EXIT_IF_FAIL(streamIndex_ != -1, "Could not find video stream"); // Get a pointer to the codec context for the video stream inputDecoderCtx_ = inputCtx_->streams[streamIndex_]->codec; - RETURN_IF_FAIL(inputDecoderCtx_, "Could not get input codec context"); + EXIT_IF_FAIL(inputDecoderCtx_, "Could not get input codec context"); // find the decoder for the video stream AVCodec *inputDecoder = avcodec_find_decoder(inputDecoderCtx_->codec_id); - RETURN_IF_FAIL(inputDecoder, "Could not decode video stream"); + EXIT_IF_FAIL(inputDecoder, "Could not decode video stream"); #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 6, 0) ret = avcodec_open(inputDecoderCtx_, inputDecoder); #else ret = avcodec_open2(inputDecoderCtx_, inputDecoder, NULL); #endif - RETURN_IF_FAIL(ret >= 0, "Could not open codec"); + EXIT_IF_FAIL(ret >= 0, "Could not open codec"); outputCtx_ = avformat_alloc_context(); + outputCtx_->interrupt_callback = interruptCb_; AVOutputFormat *file_oformat = av_guess_format("rtp", args_["destination"].c_str(), NULL); - RETURN_IF_FAIL(file_oformat, "Unable to find a suitable output format for %s", + EXIT_IF_FAIL(file_oformat, "Unable to find a suitable output format for %s", args_["destination"].c_str()); outputCtx_->oformat = file_oformat; @@ -183,7 +185,7 @@ void VideoSendThread::setup() /* find the video encoder */ AVCodec *encoder = avcodec_find_encoder_by_name(enc_name); - RETURN_IF_FAIL(encoder != 0, "Encoder \"%s\" not found!", enc_name); + EXIT_IF_FAIL(encoder != 0, "Encoder \"%s\" not found!", enc_name); prepareEncoderContext(encoder); @@ -202,9 +204,9 @@ void VideoSendThread::setup() // open encoder #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 6, 0) - RETURN_IF_FAIL(avcodec_open(encoderCtx_, encoder) >= 0, "Could not open encoder") + EXIT_IF_FAIL(avcodec_open(encoderCtx_, encoder) >= 0, "Could not open encoder") #else - RETURN_IF_FAIL(avcodec_open2(encoderCtx_, encoder, NULL) >= 0, "Could not open " + EXIT_IF_FAIL(avcodec_open2(encoderCtx_, encoder, NULL) >= 0, "Could not open " "encoder") #endif @@ -214,26 +216,26 @@ void VideoSendThread::setup() #else stream_ = avformat_new_stream(outputCtx_, 0); #endif - RETURN_IF_FAIL(stream_ != 0, "Could not allocate stream."); + EXIT_IF_FAIL(stream_ != 0, "Could not allocate stream."); stream_->codec = encoderCtx_; // open the output file, if needed if (!(file_oformat->flags & AVFMT_NOFILE)) { - ret = avio_open(&outputCtx_->pb, outputCtx_->filename, AVIO_FLAG_WRITE); - RETURN_IF_FAIL(ret >= 0, "Could not open \"%s\"!", outputCtx_->filename); + ret = avio_open2(&outputCtx_->pb, outputCtx_->filename, AVIO_FLAG_WRITE, &interruptCb_, NULL); + EXIT_IF_FAIL(ret >= 0, "Could not open \"%s\"!", outputCtx_->filename); } else DEBUG("No need to open \"%s\"", outputCtx_->filename); + AVDictionary *outOptions = NULL; // write the stream header, if any - options = NULL; - if (!args_["payload_type"].empty()) - av_dict_set(&options, "payload_type", args_["payload_type"].c_str(), 0); - - RETURN_IF_FAIL(avformat_write_header(outputCtx_, &options) >= 0, "Could not write " - "header for output file...check codec parameters"); - - print_sdp(); + if (not args_["payload_type"].empty()) { + DEBUG("Writing stream header for payload type %s", args_["payload_type"].c_str()); + av_dict_set(&outOptions, "payload_type", args_["payload_type"].c_str(), 0); + } + EXIT_IF_FAIL(avformat_write_header(outputCtx_, outOptions ? &outOptions : NULL) >= 0, "Could not write " + "header for output file...check codec parameters") av_dump_format(outputCtx_, 0, outputCtx_->filename, 1); + print_sdp(); // allocate video frame rawFrame_ = avcodec_alloc_frame(); @@ -263,28 +265,44 @@ void VideoSendThread::createScalingContext() encoderCtx_->height, encoderCtx_->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL); - RETURN_IF_FAIL(imgConvertCtx_, "Cannot init the conversion context"); + EXIT_IF_FAIL(imgConvertCtx_, "Cannot init the conversion context"); +} + +// This callback is used by libav internally to break out of blocking calls +int VideoSendThread::interruptCb(void *ctx) +{ + VideoSendThread *context = static_cast<VideoSendThread*>(ctx); + return not context->sending_; } VideoSendThread::VideoSendThread(const std::map<string, string> &args) : sdpReady_(), args_(args), scaledPictureBuf_(0), outbuf_(0), inputDecoderCtx_(0), rawFrame_(0), scaledPicture_(0), streamIndex_(-1), outbufSize_(0), encoderCtx_(0), stream_(0), - inputCtx_(0), outputCtx_(0), imgConvertCtx_(0), sdp_(), sending_(false) -{} + inputCtx_(0), outputCtx_(0), imgConvertCtx_(0), sdp_(), interruptCb_(), + sending_(false), forceKeyFrame_(0) +{ + interruptCb_.callback = interruptCb; + interruptCb_.opaque = this; +} void VideoSendThread::run() { + sending_ = true; // We don't want setup() called in the main thread in case it exits or blocks setup(); createScalingContext(); int frameNumber = 0; - sending_ = true; while (sending_) { AVPacket inpacket; - if (av_read_frame(inputCtx_, &inpacket) < 0) - break; + { + int ret = av_read_frame(inputCtx_, &inpacket); + if (ret == AVERROR(EAGAIN)) + continue; + else + EXIT_IF_FAIL(ret >= 0, "Could not read frame"); + } /* Guarantees that we free the packet allocated by av_read_frame */ PacketHandle inpacket_handle(inpacket); @@ -307,6 +325,18 @@ void VideoSendThread::run() // Set presentation timestamp on our scaled frame before encoding it scaledPicture_->pts = frameNumber++; +#ifdef CCPP_PREFIX + if ((int) forceKeyFrame_ > 0) { +#else + if (*forceKeyFrame_ > 0) { +#endif +#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(53, 20, 0) + scaledPicture_->pict_type = AV_PICTURE_TYPE_I; +#else + scaledPicture_->pict_type = FF_I_TYPE; +#endif + --forceKeyFrame_; + } const int encodedSize = avcodec_encode_video(encoderCtx_, outbuf_, outbufSize_, scaledPicture_); @@ -336,8 +366,10 @@ void VideoSendThread::run() opkt.stream_index = stream_->index; // write the compressed frame in the media file - int ret = av_interleaved_write_frame(outputCtx_, &opkt); - RETURN_IF_FAIL(ret >= 0, "av_interleaved_write_frame() error"); + { + int ret = av_interleaved_write_frame(outputCtx_, &opkt); + EXIT_IF_FAIL(ret >= 0, "av_interleaved_write_frame() error"); + } yield(); } } @@ -393,4 +425,10 @@ VideoSendThread::~VideoSendThread() avformat_close_input(&inputCtx_); #endif } + +void VideoSendThread::forceKeyFrame() +{ + ++forceKeyFrame_; +} + } // end namespace sfl_video diff --git a/daemon/src/video/video_send_thread.h b/daemon/src/video/video_send_thread.h index a0c51c2e6cb4c979855d1e876496dfbcfcd4521b..c7d63efeb8571974ab9422b23240024a840da3b0 100644 --- a/daemon/src/video/video_send_thread.h +++ b/daemon/src/video/video_send_thread.h @@ -36,6 +36,10 @@ #include <string> #include "noncopyable.h" +extern "C" { +#include <libavformat/avformat.h> +} + class SwsContext; class AVCodecContext; class AVStream; @@ -53,6 +57,7 @@ class VideoSendThread : public ost::Thread { void setup(); void prepareEncoderContext(AVCodec *encoder); void createScalingContext(); + static int interruptCb(void *ctx); ost::Event sdpReady_; std::map<std::string, std::string> args_; @@ -72,7 +77,13 @@ class VideoSendThread : public ost::Thread { AVFormatContext *outputCtx_; SwsContext *imgConvertCtx_; std::string sdp_; + AVIOInterruptCB interruptCb_; bool sending_; +#ifdef CCPP_PREFIX + ost::AtomicCounter forceKeyFrame_; +#else + ucommon::atomic::counter forceKeyFrame_; +#endif public: explicit VideoSendThread(const std::map<std::string, std::string> &args); virtual ~VideoSendThread(); @@ -80,6 +91,7 @@ class VideoSendThread : public ost::Thread { void waitForSDP(); virtual void run(); std::string getSDP() const { return sdp_; } + void forceKeyFrame(); }; } diff --git a/daemon/src/video/video_v4l2_list.cpp b/daemon/src/video/video_v4l2_list.cpp index 4dfb5b637c073488db6bff15b19ee36c93109feb..d9b10f66dbf06ed256f65ad490aeee9d406e2547 100644 --- a/daemon/src/video/video_v4l2_list.cpp +++ b/daemon/src/video/video_v4l2_list.cpp @@ -235,7 +235,7 @@ void VideoV4l2ListThread::run() DEBUG("udev: adding %s", node); try { addDevice(node); - Manager::instance().getDbusManager()->getVideoControls()->deviceEvent(); + Manager::instance().getVideoControls()->deviceEvent(); } catch (const std::runtime_error &e) { ERROR("%s", e.what()); } @@ -272,7 +272,7 @@ void VideoV4l2ListThread::delDevice(const string &node) for (std::vector<VideoV4l2Device>::iterator itr = devices_.begin(); itr != devices_.end(); ++itr) { if (itr->device == node) { devices_.erase(itr); - Manager::instance().getDbusManager()->getVideoControls()->deviceEvent(); + Manager::instance().getVideoControls()->deviceEvent(); return; } } diff --git a/daemon/src/voiplink.h b/daemon/src/voiplink.h index a049d4a5cab32096abb5ebc2a56a4725ec989d5e..404225e4ca8771d59b4b82b76f6cc0db5c7aa40b 100644 --- a/daemon/src/voiplink.h +++ b/daemon/src/voiplink.h @@ -150,10 +150,8 @@ class VoIPLink { * Return the codec protocol used for this call * @param call The call */ -#ifdef SFL_VIDEO - virtual std::string getCurrentVideoCodecName(const std::string& id) = 0; -#endif - virtual std::string getCurrentCodecName(Call *call) const = 0; + virtual std::string getCurrentVideoCodecName(Call *call) const = 0; + virtual std::string getCurrentAudioCodecName(Call *call) const = 0; /** * Send a message to a call identified by its callid diff --git a/daemon/test/accounttest.cpp b/daemon/test/accounttest.cpp index 57bd49dbf8c1e7c5481ac36ce4c86a87419abc84..6fcfb3415e49dfb14ded1ece5bcf5d45888295ab 100644 --- a/daemon/test/accounttest.cpp +++ b/daemon/test/accounttest.cpp @@ -31,6 +31,7 @@ #include <cppunit/extensions/HelperMacros.h> #include <map> #include "account.h" +#include "account_schema.h" #include "accounttest.h" #include "manager.h" #include "logger.h" @@ -43,6 +44,8 @@ void AccountTest::TestAddRemove() std::map<std::string, std::string> details; details[CONFIG_ACCOUNT_TYPE] = "SIP"; details[CONFIG_ACCOUNT_ENABLE] = "false"; + details[CONFIG_LOCAL_INTERFACE] = "default"; + details[CONFIG_LOCAL_PORT] = "5060"; std::string accountId = Manager::instance().addAccount(details); CPPUNIT_ASSERT(Validator::isNotNull(accountId)); diff --git a/daemon/test/configurationtest.cpp b/daemon/test/configurationtest.cpp index a8ee6b6d544d2336c44f6ed2ba6c7b63898dc8df..59136e9117bc47c0b370e64790313e543f4bafed 100644 --- a/daemon/test/configurationtest.cpp +++ b/daemon/test/configurationtest.cpp @@ -33,6 +33,7 @@ #include "config/yamlemitter.h" #include "config/yamlparser.h" #include "account.h" +#include "account_schema.h" #include "logger.h" #include "audio/alsa/alsalayer.h" #include "audio/pulseaudio/pulselayer.h" @@ -153,14 +154,14 @@ void ConfigurationTest::testYamlEmitter() ScalarNode verifyclient(true); ScalarNode verifyserver(true); - accountmap.setKeyValue(ALIAS_KEY, &alias); - accountmap.setKeyValue(TYPE_KEY, &type); - accountmap.setKeyValue(ID_KEY, &id); - accountmap.setKeyValue(USERNAME_KEY, &username); - accountmap.setKeyValue(PASSWORD_KEY, &password); - accountmap.setKeyValue(HOSTNAME_KEY, &hostname); - accountmap.setKeyValue(ACCOUNT_ENABLE_KEY, &enable); - accountmap.setKeyValue(MAILBOX_KEY, &mailbox); + accountmap.setKeyValue(Account::ALIAS_KEY, &alias); + accountmap.setKeyValue(Account::TYPE_KEY, &type); + accountmap.setKeyValue(Account::ID_KEY, &id); + accountmap.setKeyValue(Account::USERNAME_KEY, &username); + accountmap.setKeyValue(Account::PASSWORD_KEY, &password); + accountmap.setKeyValue(Account::HOSTNAME_KEY, &hostname); + accountmap.setKeyValue(Account::ACCOUNT_ENABLE_KEY, &enable); + accountmap.setKeyValue(Account::MAILBOX_KEY, &mailbox); accountmap.setKeyValue(Preferences::REGISTRATION_EXPIRE_KEY, &expire); accountmap.setKeyValue(INTERFACE_KEY, &interface); accountmap.setKeyValue(PORT_KEY, &port); @@ -168,7 +169,7 @@ void ConfigurationTest::testYamlEmitter() accountmap.setKeyValue(PUBLISH_PORT_KEY, &publishPort); accountmap.setKeyValue(SAME_AS_LOCAL_KEY, &sameasLocal); accountmap.setKeyValue(DTMF_TYPE_KEY, &dtmfType); - accountmap.setKeyValue(DISPLAY_NAME_KEY, &displayName); + accountmap.setKeyValue(Account::DISPLAY_NAME_KEY, &displayName); accountmap.setKeyValue(SRTP_KEY, &srtpmap); srtpmap.setKeyValue(SRTP_ENABLE_KEY, &srtpenabled); diff --git a/daemon/test/main.cpp b/daemon/test/main.cpp index 624c6531e33ab4299ea1b1c1c553cce1a498700f..f67d1b85ff3e47348eaf1cb8192eea811ad21284 100644 --- a/daemon/test/main.cpp +++ b/daemon/test/main.cpp @@ -53,8 +53,15 @@ namespace { } } +void cleanup() +{ + std::cerr << "Killing all sipp processes" << std::endl; + system("killall sipp"); +} + int main(int argc, char* argv[]) { + atexit(cleanup); printf("\nSFLphone Daemon Test Suite, by Savoir-Faire Linux 2004-2010\n\n"); Logger::setConsoleLog(true); Logger::setDebugMode(true); diff --git a/daemon/test/sdptest.cpp b/daemon/test/sdptest.cpp index f8afce5ec1883800e78d21e8aa7fe884a0c7b8b6..69e9ecc75fc12f5327ba3d1a2a076057a2064755 100644 --- a/daemon/test/sdptest.cpp +++ b/daemon/test/sdptest.cpp @@ -50,12 +50,8 @@ static const char *sdp_answer1 = "v=0\r\n" "t=0 0\r\n" "m=audio 49920 RTP/AVP 0\r\n" "a=rtpmap:0 PCMU/8000\r\n" -#ifdef SFL_VIDEO - "m=video 0 RTP/AVP 31\r\n" "m=video 53002 RTP/AVP 32\r\n" - "a=rtpmap:32 MPV/90000\r\n" -#endif - ; + "a=rtpmap:32 MPV/90000\r\n"; static const char *sdp_offer1 = "v=0\r\n" "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n" @@ -64,12 +60,8 @@ static const char *sdp_offer1 = "v=0\r\n" "t=0 0\r\n" "m=audio 49920 RTP/AVP 0\r\n" "a=rtpmap:0 PCMU/8000\r\n" -#ifdef SFL_VIDEO - "m=video 0 RTP/AVP 31\r\n" "m=video 53002 RTP/AVP 32\r\n" - "a=rtpmap:32 MPV/90000\r\n" -#endif - ; + "a=rtpmap:32 MPV/90000\r\n"; static const char *sdp_answer2 = "v=0\r\n" "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n" @@ -80,12 +72,8 @@ static const char *sdp_answer2 = "v=0\r\n" "a=rtpmap:3 GSM/8000\r\n" "a=rtpmap:97 iLBC/8000\r\n" "a=rtpmap:9 G722/8000\r\n" -#ifdef SFL_VIDEO - "m=video 0 RTP/AVP 31\r\n" "m=video 53002 RTP/AVP 32\r\n" - "a=rtpmap:32 MPV/90000\r\n" -#endif - ; + "a=rtpmap:32 MPV/90000\r\n"; static const char *sdp_offer2 = "v=0\r\n" "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n" @@ -96,12 +84,8 @@ static const char *sdp_offer2 = "v=0\r\n" "a=rtpmap:3 GSM/8000\r\n" "a=rtpmap:97 iLBC/8000\r\n" "a=rtpmap:9 G722/8000\r\n" -#ifdef SFL_VIDEO - "m=video 0 RTP/AVP 31\r\n" "m=video 53002 RTP/AVP 32\r\n" - "a=rtpmap:32 MPV/90000\r\n" -#endif - ; + "a=rtpmap:32 MPV/90000\r\n"; static const char *sdp_reinvite = "v=0\r\n" "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n" @@ -110,12 +94,8 @@ static const char *sdp_reinvite = "v=0\r\n" "t=0 0\r\n" "m=audio 42445 RTP/AVP 0\r\n" "a=rtpmap:0 PCMU/8000\r\n" -#ifdef SFL_VIDEO - "m=video 0 RTP/AVP 31\r\n" "m=video 53002 RTP/AVP 32\r\n" - "a=rtpmap:32 MPV/90000\r\n" -#endif - ; + "a=rtpmap:32 MPV/90000\r\n"; static const char *const LOCALHOST = "127.0.0.1"; @@ -141,6 +121,22 @@ void SDPTest::receiveAnswerAfterInitialOffer(const pjmedia_sdp_session* remote) CPPUNIT_ASSERT(pjmedia_sdp_neg_get_state(session_->negotiator_) == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO); } +namespace { +std::vector<std::map<std::string, std::string> > + createVideoCodecs() { + std::vector<std::map<std::string, std::string> > videoCodecs; +#ifdef SFL_VIDEO + std::map<std::string, std::string> codec; + codec["name"] = "H264"; + codec["enabled"] = "true"; + videoCodecs.push_back(codec); + codec["name"] = "H263"; + videoCodecs.push_back(codec); +#endif + return videoCodecs; + } +} + void SDPTest::testInitialOfferFirstCodec() { std::cout << "------------ SDPTest::testInitialOfferFirstCodec --------------" << std::endl; @@ -154,20 +150,12 @@ void SDPTest::testInitialOfferFirstCodec() codecSelection.push_back(PAYLOAD_CODEC_ALAW); codecSelection.push_back(PAYLOAD_CODEC_G722); -#ifdef SFL_VIDEO - std::vector<std::string> videoCodecs; - videoCodecs.push_back("H264"); - videoCodecs.push_back("H263"); -#endif + std::vector<std::map<std::string, std::string> > videoCodecs(createVideoCodecs()); session_->setLocalIP(LOCALHOST); session_->setLocalPublishedAudioPort(49567); -#ifdef SFL_VIDEO session_->createOffer(codecSelection, videoCodecs); -#else - session_->createOffer(codecSelection); -#endif pjmedia_sdp_session *remoteAnswer; pjmedia_sdp_parse(testPool_, (char*) sdp_answer1, strlen(sdp_answer1), &remoteAnswer); @@ -195,22 +183,12 @@ void SDPTest::testInitialAnswerFirstCodec() codecSelection.push_back(PAYLOAD_CODEC_ALAW); codecSelection.push_back(PAYLOAD_CODEC_G722); -#ifdef SFL_VIDEO - std::vector<std::string> videoCodecs; - videoCodecs.push_back("H264"); - videoCodecs.push_back("H263"); -#endif - pjmedia_sdp_parse(testPool_, (char*) sdp_offer1, strlen(sdp_offer1), &remoteOffer); session_->setLocalIP(LOCALHOST); session_->setLocalPublishedAudioPort(49567); -#ifdef SFL_VIDEO - session_->receiveOffer(remoteOffer, codecSelection, videoCodecs); -#else - session_->receiveOffer(remoteOffer, codecSelection); -#endif + session_->receiveOffer(remoteOffer, codecSelection, createVideoCodecs()); session_->startNegotiation(); @@ -233,20 +211,10 @@ void SDPTest::testInitialOfferLastCodec() codecSelection.push_back(PAYLOAD_CODEC_ALAW); codecSelection.push_back(PAYLOAD_CODEC_G722); -#ifdef SFL_VIDEO - std::vector<std::string> videoCodecs; - videoCodecs.push_back("H264"); - videoCodecs.push_back("H263"); -#endif - session_->setLocalIP(LOCALHOST); session_->setLocalPublishedAudioPort(49567); -#ifdef SFL_VIDEO - session_->createOffer(codecSelection, videoCodecs); -#else - session_->createOffer(codecSelection); -#endif + session_->createOffer(codecSelection, createVideoCodecs()); pjmedia_sdp_session *remoteAnswer; pjmedia_sdp_parse(testPool_, (char*) sdp_answer2, strlen(sdp_answer2), &remoteAnswer); @@ -274,22 +242,12 @@ void SDPTest::testInitialAnswerLastCodec() codecSelection.push_back(PAYLOAD_CODEC_ALAW); codecSelection.push_back(PAYLOAD_CODEC_G722); -#ifdef SFL_VIDEO - std::vector<std::string> videoCodecs; - videoCodecs.push_back("H264"); - videoCodecs.push_back("H263"); -#endif - pjmedia_sdp_parse(testPool_, (char*)sdp_offer2, strlen(sdp_offer2), &remoteOffer); session_->setLocalIP(LOCALHOST); session_->setLocalPublishedAudioPort(49567); -#ifdef SFL_VIDEO - session_->receiveOffer(remoteOffer, codecSelection, videoCodecs); -#else - session_->receiveOffer(remoteOffer, codecSelection); -#endif + session_->receiveOffer(remoteOffer, codecSelection, createVideoCodecs()); session_->startNegotiation(); @@ -312,20 +270,11 @@ void SDPTest::testReinvite() codecSelection.push_back(PAYLOAD_CODEC_ALAW); codecSelection.push_back(PAYLOAD_CODEC_G722); -#ifdef SFL_VIDEO - std::vector<std::string> videoCodecs; - videoCodecs.push_back("H264"); - videoCodecs.push_back("H263"); -#endif - session_->setLocalIP(LOCALHOST); session_->setLocalPublishedAudioPort(49567); -#ifdef SFL_VIDEO + std::vector<std::map<std::string, std::string> > videoCodecs(createVideoCodecs()); session_->createOffer(codecSelection, videoCodecs); -#else - session_->createOffer(codecSelection); -#endif pjmedia_sdp_session *remoteAnswer; // pjmedia_sdp_parse(testPool_, test[0].offer_answer[0].sdp2, strlen(test[0].offer_answer[0].sdp2), &remoteAnswer); @@ -342,11 +291,7 @@ void SDPTest::testReinvite() pjmedia_sdp_session *reinviteOffer; pjmedia_sdp_parse(testPool_, (char*) sdp_reinvite, strlen(sdp_reinvite), &reinviteOffer); -#ifdef SFL_VIDEO session_->receiveOffer(reinviteOffer, codecSelection, videoCodecs); -#else - session_->receiveOffer(reinviteOffer, codecSelection); -#endif session_->startNegotiation(); session_->setMediaTransportInfoFromRemoteSdp(); diff --git a/daemon/test/sflphoned-sample.yml b/daemon/test/sflphoned-sample.yml index d70b7ceb75882f82402c01a5f0aa70b914fb1f71..76bbb639d5104aa20a515858ac3d13834caf3875 100644 --- a/daemon/test/sflphoned-sample.yml +++ b/daemon/test/sflphoned-sample.yml @@ -93,6 +93,13 @@ accounts: type: SIP updateContact: false username: + videoCodecs: + - bitrate: 400 + enabled: true + name: H263-2000 + - bitrate: 400 + enabled: true + name: H264 zrtp: displaySas: true displaySasOnce: false diff --git a/gnome/Makefile.am b/gnome/Makefile.am index ca8bba4dff38d04e81e9e13af21ba4adb2e09298..cbb5d64c1ffe665302906277712b18b32ddb5deb 100644 --- a/gnome/Makefile.am +++ b/gnome/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = src pixmaps webkit tests man po doc +SUBDIRS = src pixmaps tests man po doc CFLAGS=-Wall -Werror -Wextra diff --git a/gnome/configure.ac b/gnome/configure.ac index 2a81f1c73313f36ae7076b2742773c65869fb23d..35fabd16db628b1ee0acb5340559727adf5306e5 100644 --- a/gnome/configure.ac +++ b/gnome/configure.ac @@ -44,9 +44,9 @@ AM_CONDITIONAL(SFL_VIDEO, test "x$enable_video" = "xyes") # Check for gtk+-3.0 and if found, check for webkitgtk-3.0, otherwise # check for gtk+-2.0 and if found, check for webkitgtk-1.0. -PKG_CHECK_MODULES(GTK, gtk+-3.0, [PKG_CHECK_MODULES(WEBKIT, webkitgtk-3.0)], +PKG_CHECK_MODULES(GTK, gtk+-3.0, [PKG_CHECK_MODULES(GTK, gtk+-2.0, - [PKG_CHECK_MODULES(WEBKIT, webkit-1.0) && PKG_CHECK_MODULES(GTHREAD, gthread-2.0)], + [PKG_CHECK_MODULES(GTHREAD, gthread-2.0)], [AC_MSG_ERROR(gtk-2 not found)])]) PKG_CHECK_MODULES(GCONF, gconf-2.0) @@ -67,8 +67,8 @@ src/contacts/Makefile src/widget/Makefile src/video/Makefile src/icons/Makefile +src/messaging/Makefile pixmaps/Makefile -webkit/Makefile sflphone.desktop tests/Makefile man/Makefile diff --git a/gnome/man/Makefile.am b/gnome/man/Makefile.am index 90a426f5beca3209a3ec5d86156128e769d130fd..60079b2656d8c7338bcb6297357d72c5c88977c9 100644 --- a/gnome/man/Makefile.am +++ b/gnome/man/Makefile.am @@ -1,17 +1,15 @@ SECTION="1" -TEMPLATES=\ - sflphone-client-gnome.pod +TEMPLATES=sflphone-client-gnome.pod -man_MANS = \ - sflphone-client-gnome.1 \ - sflphone.1 +man_MANS = sflphone-client-gnome.1 \ + sflphone.1 POD2MAN=pod2man -EXTRA_DIST= $(man_MANS) +EXTRA_DIST=$(man_MANS) $(TEMPLATES) -all: $(MANPAGES) +all: $(MANPAGES) SUFFIXES=.pod .1 diff --git a/gnome/src/Makefile.am b/gnome/src/Makefile.am index 9b7735d8b89d4782009a3f1d89e920638f564ae3..4918d519a8aa352b75bf1161d2e398f7bae7b251 100644 --- a/gnome/src/Makefile.am +++ b/gnome/src/Makefile.am @@ -1,27 +1,17 @@ include ../globals.mak -bin_PROGRAMS = sflphone-client-gnome - -SFLPHONEGTK_LIBS=./contacts/libcontacts.la ./config/libconfig.la \ - ./dbus/libdbus.la ./widget/libwidget.la ./icons/libicons.la +SUBDIRS = config contacts dbus widget icons video messaging -sflphone_client_gnome_SOURCES = main.c -sflphone_client_gnome_CFLAGS = @DBUSGLIB_CFLAGS@ @LIBNOTIFY_CFLAGS@ \ - @GTK_CFLAGS@ @WEBKIT_CFLAGS@ \ - @GCONF_CFLAGS@ @JAVASCRIPT_CORE_GTK_CFLAGS@ \ - @GTHREAD_CFLAGS@ -sflphone_client_gnome_LDADD = ./libsflphone_client.la $(SFLPHONEGTK_LIBS) $(LD_LIBS) +bin_PROGRAMS = sflphone-client-gnome -SUBDIRS = config contacts dbus widget icons -if SFL_VIDEO -SUBDIRS += video -endif +SFLPHONE_LIBS=./contacts/libcontacts.la ./config/libconfig.la \ + ./dbus/libdbus.la ./widget/libwidget.la ./icons/libicons.la \ + ./messaging/libmessaging.la -NOFIFY_LIBS = -lnotify -LD_LIBS = -ldl +DL_LIBS = -ldl if SFL_VIDEO -SFLPHONEGTK_LIBS+=./video/libvideo.la +SFLPHONE_LIBS+=./video/libvideo.la endif noinst_LTLIBRARIES = libsflphone_client.la @@ -31,7 +21,6 @@ libsflphone_client_la_SOURCES = \ uimanager.c \ sflnotify.c \ mainwindow.c \ - imwindow.c \ dialpad.c \ callable_obj.c \ conference_obj.c \ @@ -60,22 +49,30 @@ libsflphone_client_la_SOURCES = \ shortcuts.h \ eel-gconf-extensions.h \ logger.h \ - imwindow.h \ unused.h \ str_utils.h \ gtk2_wrappers.h \ - seekslider.h + seekslider.h \ + account_schema.h -libsflphone_client_la_LDFLAGS = @DBUSGLIB_LIBS@ @LIBNOTIFY_LIBS@ \ - @NOTIFY_LIBS@ $(SFLPHONEGTK_LIBS) @X11_LIBS@ \ - @GTK_LIBS@ @GLIB_LIBS@ @WEBKIT_LIBS@ \ - $(LD_LIBS) @GCONF_LIBS@ \ - @JAVASCRIPT_CORE_GTK_LIBS@ @GTHREAD_LIBS@ +libsflphone_client_la_LIBADD = @CLUTTER_LIBS@ @CLUTTERGTK_LIBS@ $(SFLPHONE_LIBS) +libsflphone_client_la_LDFLAGS = @CLUTTER_LDFLAGS@ @DBUSGLIB_LIBS@ @LIBNOTIFY_LIBS@ \ + @X11_LIBS@ @GTK_LIBS@ @GLIB_LIBS@ \ + $(DL_LIBS) @GCONF_LIBS@ @JAVASCRIPT_CORE_GTK_LIBS@ \ + @GTHREAD_LIBS@ @CLUTTERGTK_LDFLAGS@ + +libsflphone_client_la_CFLAGS = @CLUTTER_CFLAGS@ @DBUSGLIB_CFLAGS@ @LIBNOTIFY_CFLAGS@ \ + @GTK_CFLAGS@ \ + @GCONF_CFLAGS@ @JAVASCRIPT_CORE_GTK_CFLAGS@ \ + @GTHREAD_CFLAGS@ @CLUTTERGTK_CFLAGS@ -libsflphone_client_la_CFLAGS = @DBUSGLIB_CFLAGS@ @LIBNOTIFY_CFLAGS@ \ - @GTK_CFLAGS@ @WEBKIT_CFLAGS@ \ + +sflphone_client_gnome_SOURCES = main.c +sflphone_client_gnome_CFLAGS = @DBUSGLIB_CFLAGS@ @LIBNOTIFY_CFLAGS@ \ + @GTK_CFLAGS@ \ @GCONF_CFLAGS@ @JAVASCRIPT_CORE_GTK_CFLAGS@ \ @GTHREAD_CFLAGS@ +sflphone_client_gnome_LDADD = libsflphone_client.la # add symbolic link install-exec-local: diff --git a/gnome/src/account_schema.h b/gnome/src/account_schema.h new file mode 100644 index 0000000000000000000000000000000000000000..1e769cdb75a77894656c879f7c0ec91177ca2a02 --- /dev/null +++ b/gnome/src/account_schema.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc. + * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * Author: Yan Morin <yan.morin@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#ifndef ACCOUNT_SCHEMA_H +#define ACCOUNT_SCHEMA_H_ + +/** + * @file account_schema.h + * @brief Account specfic keys/constants that must be shared in daemon and clients. + */ + +// Account identifier +static const char *const CONFIG_ACCOUNT_ID = "Account.id"; + +// Common account parameters +static const char *const CONFIG_ACCOUNT_TYPE = "Account.type"; +static const char *const CONFIG_ACCOUNT_ALIAS = "Account.alias"; +static const char *const CONFIG_ACCOUNT_MAILBOX = "Account.mailbox"; +static const char *const CONFIG_ACCOUNT_ENABLE = "Account.enable"; +static const char *const CONFIG_ACCOUNT_REGISTRATION_EXPIRE = "Account.registrationExpire"; +static const char *const CONFIG_ACCOUNT_REGISTRATION_STATUS = "Account.registrationStatus"; +static const char *const CONFIG_ACCOUNT_REGISTRATION_STATE_CODE = "Account.registrationCode"; +static const char *const CONFIG_ACCOUNT_REGISTRATION_STATE_DESC = "Account.registrationDescription"; +static const char *const CONFIG_CREDENTIAL_NUMBER = "Credential.count"; +static const char *const CONFIG_ACCOUNT_DTMF_TYPE = "Account.dtmfType"; +static const char *const CONFIG_RINGTONE_PATH = "Account.ringtonePath"; +static const char *const CONFIG_RINGTONE_ENABLED = "Account.ringtoneEnabled"; +static const char *const CONFIG_KEEP_ALIVE_ENABLED = "Account.keepAliveEnabled"; + +static const char *const CONFIG_ACCOUNT_HOSTNAME = "Account.hostname"; +static const char *const CONFIG_ACCOUNT_USERNAME = "Account.username"; +static const char *const CONFIG_ACCOUNT_ROUTESET = "Account.routeset"; +static const char *const CONFIG_ACCOUNT_PASSWORD = "Account.password"; +static const char *const CONFIG_ACCOUNT_REALM = "Account.realm"; +static const char *const CONFIG_ACCOUNT_DEFAULT_REALM = "*"; +static const char *const CONFIG_ACCOUNT_USERAGENT = "Account.useragent"; + +static const char *const CONFIG_LOCAL_INTERFACE = "Account.localInterface"; +static const char *const CONFIG_PUBLISHED_SAMEAS_LOCAL = "Account.publishedSameAsLocal"; +static const char *const CONFIG_LOCAL_PORT = "Account.localPort"; +static const char *const CONFIG_PUBLISHED_PORT = "Account.publishedPort"; +static const char *const CONFIG_PUBLISHED_ADDRESS = "Account.publishedAddress"; + +static const char *const CONFIG_DISPLAY_NAME = "Account.displayName"; +static const char *const CONFIG_DEFAULT_ADDRESS = "0.0.0.0"; + +// SIP specific parameters +static const char *const CONFIG_SIP_PROXY = "SIP.proxy"; +static const char *const CONFIG_STUN_SERVER = "STUN.server"; +static const char *const CONFIG_STUN_ENABLE = "STUN.enable"; + +// SRTP specific parameters +static const char *const CONFIG_SRTP_ENABLE = "SRTP.enable"; +static const char *const CONFIG_SRTP_KEY_EXCHANGE = "SRTP.keyExchange"; +static const char *const CONFIG_SRTP_ENCRYPTION_ALGO = "SRTP.encryptionAlgorithm"; // Provided by ccRTP,0=NULL,1=AESCM,2=AESF8 +static const char *const CONFIG_SRTP_RTP_FALLBACK = "SRTP.rtpFallback"; +static const char *const CONFIG_ZRTP_HELLO_HASH = "ZRTP.helloHashEnable"; +static const char *const CONFIG_ZRTP_DISPLAY_SAS = "ZRTP.displaySAS"; +static const char *const CONFIG_ZRTP_NOT_SUPP_WARNING = "ZRTP.notSuppWarning"; +static const char *const CONFIG_ZRTP_DISPLAY_SAS_ONCE = "ZRTP.displaySasOnce"; + +static const char *const CONFIG_TLS_LISTENER_PORT = "TLS.listenerPort"; +static const char *const CONFIG_TLS_ENABLE = "TLS.enable"; +static const char *const CONFIG_TLS_CA_LIST_FILE = "TLS.certificateListFile"; +static const char *const CONFIG_TLS_CERTIFICATE_FILE = "TLS.certificateFile"; +static const char *const CONFIG_TLS_PRIVATE_KEY_FILE = "TLS.privateKeyFile"; +static const char *const CONFIG_TLS_PASSWORD = "TLS.password"; +static const char *const CONFIG_TLS_METHOD = "TLS.method"; +static const char *const CONFIG_TLS_CIPHERS = "TLS.ciphers"; +static const char *const CONFIG_TLS_SERVER_NAME = "TLS.serverName"; +static const char *const CONFIG_TLS_VERIFY_SERVER = "TLS.verifyServer"; +static const char *const CONFIG_TLS_VERIFY_CLIENT = "TLS.verifyClient"; +static const char *const CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE = "TLS.requireClientCertificate"; +static const char *const CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC = "TLS.negotiationTimeoutSec"; +static const char *const CONFIG_TLS_NEGOTIATION_TIMEOUT_MSEC = "TLS.negotiationTimemoutMsec"; + +#endif diff --git a/gnome/src/accountlist.c b/gnome/src/accountlist.c index c0356736d1789172af495ae1548b39201a97eae7..2f2901fc10eb189284f6240903cd791b51393368 100644 --- a/gnome/src/accountlist.c +++ b/gnome/src/accountlist.c @@ -33,6 +33,7 @@ #include "str_utils.h" #include "dbus.h" #include "accountlist.h" +#include "account_schema.h" #include "logger.h" #include "actions.h" #include "unused.h" @@ -100,7 +101,11 @@ account_list_get_by_state(account_state_t state) account_t * account_list_get_by_id(const gchar * const accountID) { - g_assert(accountID); + if (!accountID) { + DEBUG("AccountID is NULL"); + return NULL; + } + GList * c = g_queue_find_custom(accountQueue, accountID, is_accountID_struct); if (c) @@ -267,7 +272,7 @@ gboolean current_account_has_mailbox(void) account_t *current = account_list_get_current(); if (current) { - gchar * account_mailbox = account_lookup(current, ACCOUNT_MAILBOX); + gchar * account_mailbox = account_lookup(current, CONFIG_ACCOUNT_MAILBOX); if (account_mailbox && !utf8_case_equal(account_mailbox, "")) return TRUE; @@ -306,7 +311,7 @@ gboolean account_is_IP2IP(const account_t *account) static gboolean is_type(const account_t *account, const gchar *type) { - const gchar *account_type = account_lookup(account, ACCOUNT_TYPE); + const gchar *account_type = account_lookup(account, CONFIG_ACCOUNT_TYPE); return g_strcmp0(account_type, type) == 0; } @@ -345,9 +350,9 @@ void initialize_credential_information(account_t *account) if (!account->credential_information) { account->credential_information = g_ptr_array_sized_new(1); GHashTable * new_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - g_hash_table_insert(new_table, g_strdup(ACCOUNT_REALM), g_strdup("*")); - g_hash_table_insert(new_table, g_strdup(ACCOUNT_USERNAME), g_strdup("")); - g_hash_table_insert(new_table, g_strdup(ACCOUNT_PASSWORD), g_strdup("")); + g_hash_table_insert(new_table, g_strdup(CONFIG_ACCOUNT_REALM), g_strdup("*")); + g_hash_table_insert(new_table, g_strdup(CONFIG_ACCOUNT_USERNAME), g_strdup("")); + g_hash_table_insert(new_table, g_strdup(CONFIG_ACCOUNT_PASSWORD), g_strdup("")); g_ptr_array_add(account->credential_information, new_table); } } diff --git a/gnome/src/accountlist.h b/gnome/src/accountlist.h index b35386a9e5b4f6402468add1e9bea62cbad913e0..6395421303a63e6fd1cee33f2dc5455575caac7d 100644 --- a/gnome/src/accountlist.h +++ b/gnome/src/accountlist.h @@ -84,7 +84,6 @@ typedef struct { /* The codec lists */ GQueue *acodecs; - GQueue *vcodecs; guint _messages_number; } account_t; diff --git a/gnome/src/actions.c b/gnome/src/actions.c index 647bd1ac6630398397e8fb02b59d8250c0a8642c..5ba218cdd7074cf2a71db1c88bd4706054281473 100644 --- a/gnome/src/actions.c +++ b/gnome/src/actions.c @@ -61,15 +61,15 @@ #include "actions.h" #include "dbus/dbus.h" #include "logger.h" +#include "account_schema.h" #include "contacts/calltab.h" #include "contacts/searchbar.h" #include "contacts/addrbookfactory.h" #include "icons/icon_factory.h" -#include "imwindow.h" #include "statusicon.h" #include "unused.h" -#include "widget/imwidget.h" #include "sliders.h" +#include "messaging/message_tab.h" static GHashTable * ip2ip_profile; @@ -135,13 +135,13 @@ status_bar_display_account() if (acc) { msg = g_markup_printf_escaped("%s %s (%s)" , _("Using account"), - (gchar*) account_lookup(acc, ACCOUNT_ALIAS), - (gchar*) account_lookup(acc, ACCOUNT_TYPE)); + (gchar*) account_lookup(acc, CONFIG_ACCOUNT_ALIAS), + (gchar*) account_lookup(acc, CONFIG_ACCOUNT_TYPE)); } else { msg = g_markup_printf_escaped(_("No registered accounts")); } - statusbar_push_message(msg, NULL, __MSG_ACCOUNT_DEFAULT); + statusbar_push_message(msg, NULL, __MSG_ACCOUNT_DEFAULT); g_free(msg); } @@ -180,19 +180,13 @@ void sflphone_hung_up(callable_obj_t * c) { DEBUG("%s", __PRETTY_FUNCTION__); - + disable_messaging_tab(c->_callID); calllist_remove_call(current_calls_tab, c->_callID); calltree_remove_call(current_calls_tab, c->_callID); c->_state = CALL_STATE_DIALING; call_remove_all_errors(c); update_actions(); - // test whether the widget contains text, if not remove it - if ((im_window_get_nb_tabs() > 1) && c->_im_widget && !(IM_WIDGET(c->_im_widget)->containText)) - im_window_remove_tab(c->_im_widget); - else - im_widget_update_state(IM_WIDGET(c->_im_widget), FALSE); - status_tray_icon_blink(FALSE); statusbar_update_clock(""); @@ -213,7 +207,7 @@ void sflphone_fill_account_list(void) account_list_add(acc); /* Fill the actual array of credentials */ dbus_get_credentials(acc); - gchar * status = account_lookup(acc, ACCOUNT_REGISTRATION_STATUS); + gchar * status = account_lookup(acc, CONFIG_ACCOUNT_REGISTRATION_STATUS); if (g_strcmp0(status, "REGISTERED") == 0) acc->state = ACCOUNT_STATE_REGISTERED; @@ -238,10 +232,10 @@ void sflphone_fill_account_list(void) else acc->state = ACCOUNT_STATE_INVALID; - gchar * code = account_lookup(acc, ACCOUNT_REGISTRATION_STATE_CODE); + gchar * code = account_lookup(acc, CONFIG_ACCOUNT_REGISTRATION_STATE_CODE); if (code != NULL) acc->protocol_state_code = atoi(code); - acc->protocol_state_description = account_lookup(acc, ACCOUNT_REGISTRATION_STATE_DESC); + acc->protocol_state_description = account_lookup(acc, CONFIG_ACCOUNT_REGISTRATION_STATE_DESC); } g_strfreev(array); @@ -295,7 +289,7 @@ sflphone_hang_up() DEBUG("%s", __PRETTY_FUNCTION__); if (selectedConf) { - im_widget_update_state(IM_WIDGET(selectedConf->_im_widget), FALSE); + disable_messaging_tab(selectedConf->_confID); dbus_hang_up_conference(selectedConf); } else if (selectedCall) { switch (selectedCall->_state) { @@ -311,14 +305,11 @@ sflphone_hang_up() case CALL_STATE_CURRENT: case CALL_STATE_HOLD: case CALL_STATE_BUSY: - case CALL_STATE_RECORD: dbus_hang_up(selectedCall); call_remove_all_errors(selectedCall); selectedCall->_state = CALL_STATE_DIALING; time(&selectedCall->_time_stop); - im_widget_update_state(IM_WIDGET(selectedCall->_im_widget), FALSE); - break; case CALL_STATE_FAILURE: dbus_hang_up(selectedCall); @@ -371,21 +362,11 @@ sflphone_pick_up() case CALL_STATE_DIALING: sflphone_place_call(selectedCall); - // if instant messaging window is visible, create new tab (deleted automatically if not used) - if (im_window_is_visible()) - if (!selectedCall->_im_widget) - selectedCall->_im_widget = im_widget_display(selectedCall->_callID); - break; case CALL_STATE_INCOMING: selectedCall->_history_state = g_strdup(INCOMING_STRING); calltree_update_call(history_tab, selectedCall); - // if instant messaging window is visible, create new tab (deleted automatically if not used) - if (im_window_is_visible()) - if (!selectedCall->_im_widget) - selectedCall->_im_widget = im_widget_display(selectedCall->_callID); - dbus_accept(selectedCall); break; case CALL_STATE_TRANSFER: @@ -397,7 +378,6 @@ sflphone_pick_up() break; case CALL_STATE_CURRENT: case CALL_STATE_HOLD: - case CALL_STATE_RECORD: case CALL_STATE_RINGING: sflphone_new_call(); break; @@ -416,7 +396,6 @@ sflphone_on_hold() if (selectedCall) { switch (selectedCall->_state) { case CALL_STATE_CURRENT: - case CALL_STATE_RECORD: dbus_hold(selectedCall); break; default: @@ -475,17 +454,6 @@ sflphone_current(callable_obj_t * c) update_actions(); } -void -sflphone_record(callable_obj_t * c) -{ - if (c->_state != CALL_STATE_HOLD) - time(&c->_time_start); - - c->_state = CALL_STATE_RECORD; - calltree_update_call(current_calls_tab, c); - update_actions(); -} - void sflphone_set_transfer() { @@ -658,7 +626,6 @@ sflphone_keypad(guint keyval, gchar * key) case CALL_STATE_DIALING: // Currently dialing => edit number process_dialing(c, keyval, key); break; - case CALL_STATE_RECORD: case CALL_STATE_CURRENT: switch (keyval) { @@ -817,7 +784,7 @@ sflphone_detach_participant(const gchar* callID) DEBUG("Detach participant %s", selectedCall->_callID); - im_widget_update_state(IM_WIDGET(selectedCall->_im_widget), TRUE); + /*TODO elepage(2012) correct IM conversation*/ calltree_remove_call(current_calls_tab, selectedCall->_callID); calltree_add_call(current_calls_tab, selectedCall, NULL); dbus_detach_participant(selectedCall->_callID); @@ -854,19 +821,6 @@ sflphone_rec_call() if (selectedCall) { DEBUG("Set record for selected call"); dbus_set_record(selectedCall->_callID); - - switch (selectedCall->_state) { - case CALL_STATE_CURRENT: - selectedCall->_state = CALL_STATE_RECORD; - break; - case CALL_STATE_RECORD: - selectedCall->_state = CALL_STATE_CURRENT; - break; - default: - WARN("Should not happen in sflphone_off_hold ()!"); - break; - } - calltree_update_call(current_calls_tab, selectedCall); } else if (selectedConf) { DEBUG("Set record for selected conf"); @@ -906,41 +860,6 @@ sflphone_mute_call() toggle_slider_mute_microphone(); } -#ifdef SFL_VIDEO -static void -sflphone_fill_video_codec_list_per_account(account_t *account) -{ - if (!account->vcodecs) - account->vcodecs = g_queue_new(); - else - g_queue_clear(account->vcodecs); - - /* First add the active codecs for this account */ - GQueue* system_vcodecs = get_video_codecs_list(); - gchar **order = dbus_get_active_video_codec_list(account->accountID); - for (gchar **pl = order; *pl; pl++) { - codec_t *orig = codec_list_get_by_name(*pl, system_vcodecs); - codec_t *c = codec_create_new_from_caps(orig); - if (c) - g_queue_push_tail(account->vcodecs, c); - else - ERROR("Couldn't find codec %s %p", *pl, orig); - g_free(*pl); - } - g_free(order); - - /* Here we add installed codecs that aren't active for the account */ - guint caps_size = g_queue_get_length(system_vcodecs); - for (guint i = 0; i < caps_size; ++i) { - codec_t * vcodec = g_queue_peek_nth(system_vcodecs, i); - if (codec_list_get_by_name(vcodec->name, account->vcodecs) == NULL) { - vcodec->is_active = FALSE; - g_queue_push_tail(account->vcodecs, vcodec); - } - } -} -#endif - static void sflphone_fill_audio_codec_list_per_account(account_t *account) { @@ -978,9 +897,6 @@ sflphone_fill_audio_codec_list_per_account(account_t *account) void sflphone_fill_codec_list_per_account(account_t *account) { sflphone_fill_audio_codec_list_per_account(account); -#ifdef SFL_VIDEO - sflphone_fill_video_codec_list_per_account(account); -#endif } void sflphone_fill_call_list(void) diff --git a/gnome/src/actions.h b/gnome/src/actions.h index 4bb3cbdf1382a3a336a1cb2447026d8a5cf2891f..e0d466f792ec7aba530d717ee03eb06a9fce9c2b 100644 --- a/gnome/src/actions.h +++ b/gnome/src/actions.h @@ -192,8 +192,6 @@ void sflphone_fill_codec_list_per_account(account_t *); void sflphone_add_participant(); -void sflphone_record(callable_obj_t *c); - void sflphone_rec_call(void); void sflphone_mute_call(void); diff --git a/gnome/src/callable_obj.h b/gnome/src/callable_obj.h index b8cfaca0e70623fac7d13d01adf45ea2b68f98ed..5c9eeaec69907e038cf684b47998b0b76e4048ee 100644 --- a/gnome/src/callable_obj.h +++ b/gnome/src/callable_obj.h @@ -69,7 +69,6 @@ typedef enum { CALL_STATE_FAILURE, CALL_STATE_BUSY, CALL_STATE_TRANSFER, - CALL_STATE_RECORD, } call_state_t; static const char * const TIMESTAMP_START_KEY = "timestamp_start"; diff --git a/gnome/src/codeclist.c b/gnome/src/codeclist.c index 9dc34aba4f3d4d2dc201b560e4aec69928a15b32..65f5e02ad4d5726a1108a9f54c9d8b87b450f2df 100644 --- a/gnome/src/codeclist.c +++ b/gnome/src/codeclist.c @@ -43,7 +43,6 @@ #include "dbus.h" static GQueue audioCodecs = G_QUEUE_INIT; -static GQueue videoCodecs = G_QUEUE_INIT; /* * Instantiate a new codec @@ -113,60 +112,6 @@ static void codecs_audio_unload(void) g_queue_foreach(&audioCodecs, codec_free, NULL); } -#ifdef SFL_VIDEO -static -codec_t * -codec_create_from_hash_table(gint payload, GHashTable *details) -{ - codec_t *codec = g_new0(codec_t, 1); - - codec->payload = payload; - codec->name = g_strdup(g_hash_table_lookup(details, "name")); - codec->bitrate = g_strdup(g_hash_table_lookup(details, "bitrate")); - codec->is_active = TRUE; - - return codec; -} - -static gboolean codecs_video_load(void) -{ - gchar **codecs = dbus_video_codec_list(); - gchar **codecs_orig = codecs; - - if (!codecs) - return FALSE; - - int payload = 96; // dynamic payloads - // Add the codecs in the list - for (; codecs && *codecs; ++codecs) { - GHashTable *codec_details = dbus_video_codec_details(*codecs); - codec_t *c = codec_create_from_hash_table(payload++, codec_details); - g_queue_push_tail(&videoCodecs, c); - g_free(*codecs); - } - g_free(codecs_orig); - - // If we didn't load any codecs, problem ... - return g_queue_get_length(&videoCodecs) > 0; -} - -gboolean codecs_load(void) -{ - return codecs_audio_load() && codecs_video_load(); -} - -static void codecs_video_unload(void) -{ - g_queue_foreach(&videoCodecs, codec_free, NULL); -} - -void codecs_unload(void) -{ - codecs_audio_unload(); - codecs_video_unload(); -} - -#else gboolean codecs_load(void) { return codecs_audio_load(); @@ -176,7 +121,6 @@ void codecs_unload(void) { codecs_audio_unload(); } -#endif // SFL_VIDEO codec_t *codec_create_new_from_caps(codec_t *original) { @@ -260,22 +204,17 @@ void codec_list_move_codec_down(guint codec_index, GQueue **q) } *q = tmp; - } /* Returns a list of strings for just the active codecs in a given queue of codecs */ static GSList* -codec_list_get_active_codecs(GQueue *codecs, gboolean by_payload) +codec_list_get_active_codecs(GQueue *codecs) { GSList *active = NULL; for (guint i = 0; i < codecs->length; i++) { codec_t* currentCodec = g_queue_peek_nth(codecs, i); - if (currentCodec && currentCodec->is_active) { - if (by_payload) - active = g_slist_append(active, g_strdup_printf("%d", currentCodec->payload)); - else - active = g_slist_append(active, g_strdup(currentCodec->name)); - } + if (currentCodec && currentCodec->is_active) + active = g_slist_append(active, g_strdup_printf("%d", currentCodec->payload)); } return active; } @@ -297,7 +236,7 @@ get_items_from_list(GSList *codecs) static void codec_list_update_to_daemon_audio(const account_t *acc) { - GSList *activeCodecs = codec_list_get_active_codecs(acc->acodecs, TRUE); + GSList *activeCodecs = codec_list_get_active_codecs(acc->acodecs); gchar **activeCodecsStr = get_items_from_list(activeCodecs); // call dbus function with array of strings @@ -306,33 +245,12 @@ codec_list_update_to_daemon_audio(const account_t *acc) g_slist_free_full(activeCodecs, g_free); } -#ifdef SFL_VIDEO -static void codec_list_update_to_daemon_video(const account_t *acc) -{ - GSList *activeCodecs = codec_list_get_active_codecs(acc->vcodecs, FALSE); - gchar **activeCodecsStr = get_items_from_list(activeCodecs); - - // call dbus function with array of strings - dbus_set_active_video_codec_list((const gchar **) activeCodecsStr, acc->accountID); - g_free(activeCodecsStr); - g_slist_free_full(activeCodecs, g_free); -} -#endif - void codec_list_update_to_daemon(const account_t *acc) { codec_list_update_to_daemon_audio(acc); -#ifdef SFL_VIDEO - codec_list_update_to_daemon_video(acc); -#endif } GQueue* get_audio_codecs_list(void) { return &audioCodecs; } - -GQueue* get_video_codecs_list(void) -{ - return &videoCodecs; -} diff --git a/gnome/src/codeclist.h b/gnome/src/codeclist.h index 530345449184f798a9a02965c1b86e9998337e37..49f581fb29ed061b54b8e4c60b63caee16baf067 100644 --- a/gnome/src/codeclist.h +++ b/gnome/src/codeclist.h @@ -86,8 +86,6 @@ void codec_list_update_to_daemon(const account_t *acc); codec_t* codec_list_get_by_payload(gint payload, GQueue *q); -GQueue* get_video_codecs_list(void); - GQueue* get_audio_codecs_list(void); /* diff --git a/gnome/src/config/Makefile.am b/gnome/src/config/Makefile.am index c512303ea31f51fd96f2b10a620cfd8bac5531a1..7c4ceada74293a1b7539941c2ef76e04356ea461 100644 --- a/gnome/src/config/Makefile.am +++ b/gnome/src/config/Makefile.am @@ -37,7 +37,7 @@ SFL_VIDEO_LDFLAGS=@CLUTTER_LIBS@ @CLUTTERGTK_LIBS@ endif libconfig_la_LDFLAGS = $(DBUSGLIB_LDFLAGS) $(LIBNOTIFY_LDFLAGS) $(GCONF_LDFLAGS) \ - $(GTK_LDFLAGS) $(GLIB_LDFLAGS) $(WEBKIT_LDFLAGS) \ + $(GTK_LDFLAGS) $(GLIB_LDFLAGS) \ $(SFL_VIDEO_LDFLAGS) if SFL_VIDEO @@ -45,12 +45,12 @@ SFL_VIDEO_LIBADD=@CLUTTER_LIBS@ @CLUTTERGTK_LIBS@ endif libconfig_la_LIBADD = $(DBUSGLIB_LIBS) $(LIBNOTIFY_LIBS) $(GCONF_LIBS) \ - $(GTK_LIBS) $(GLIB_LIBS) $(WEBKIT_LIBS) $(SFL_VIDEO_LIBADD) + $(GTK_LIBS) $(GLIB_LIBS) $(SFL_VIDEO_LIBADD) if SFL_VIDEO SFL_VIDEO_CFLAGS=@CLUTTER_CFLAGS@ @CLUTTERGTK_CFLAGS@ endif libconfig_la_CFLAGS = $(DBUSGLIB_CFLAGS) $(LIBNOTIFY_CFLAGS) $(GCONF_CFLAGS) \ - $(GTK_CFLAGS) $(GLIB_CFLAGS) $(WEBKIT_CFLAGS) \ + $(GTK_CFLAGS) $(GLIB_CFLAGS) \ $(SFL_VIDEO_CFLAGS) diff --git a/gnome/src/config/accountconfigdialog.c b/gnome/src/config/accountconfigdialog.c index 57a07b3466f74c88d1e58a219f61b0e3a0ee9817..5434dc165bfca311b882eb0093d6c2895b682df5 100644 --- a/gnome/src/config/accountconfigdialog.c +++ b/gnome/src/config/accountconfigdialog.c @@ -53,6 +53,7 @@ #include "audioconf.h" #include "videoconf.h" #include "accountconfigdialog.h" +#include "account_schema.h" #include "zrtpadvanceddialog.h" #include "tlsadvanceddialog.h" #include "dbus/dbus.h" @@ -181,9 +182,9 @@ static GPtrArray* get_new_credential(void) GHashTable * new_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - g_hash_table_insert(new_table, g_strdup(ACCOUNT_REALM), realm); - g_hash_table_insert(new_table, g_strdup(ACCOUNT_USERNAME), username); - g_hash_table_insert(new_table, g_strdup(ACCOUNT_PASSWORD), password); + g_hash_table_insert(new_table, g_strdup(CONFIG_ACCOUNT_REALM), realm); + g_hash_table_insert(new_table, g_strdup(CONFIG_ACCOUNT_USERNAME), username); + g_hash_table_insert(new_table, g_strdup(CONFIG_ACCOUNT_PASSWORD), password); g_ptr_array_add(credential_array, new_table); } @@ -209,10 +210,10 @@ static GtkWidget* create_basic_tab(const account_t *account) /* get password from credentials list */ if (account->credential_information) { GHashTable * element = g_ptr_array_index(account->credential_information, 0); - password = g_hash_table_lookup(element, ACCOUNT_PASSWORD); + password = g_hash_table_lookup(element, CONFIG_ACCOUNT_PASSWORD); } } else - password = account_lookup(account, ACCOUNT_PASSWORD); + password = account_lookup(account, CONFIG_ACCOUNT_PASSWORD); GtkWidget *frame = gnome_main_section_new(_("Account Parameters")); gtk_widget_show(frame); @@ -240,7 +241,7 @@ static GtkWidget* create_basic_tab(const account_t *account) gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); entry_alias = gtk_entry_new(); gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_alias); - gchar *alias = account_lookup(account, ACCOUNT_ALIAS); + gchar *alias = account_lookup(account, CONFIG_ACCOUNT_ALIAS); gtk_entry_set_text(GTK_ENTRY(entry_alias), alias); gtk_table_attach(GTK_TABLE(table), entry_alias, 1, 2, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); @@ -282,7 +283,7 @@ static GtkWidget* create_basic_tab(const account_t *account) gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); entry_hostname = gtk_entry_new(); gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_hostname); - const gchar *hostname = account_lookup(account, ACCOUNT_HOSTNAME); + const gchar *hostname = account_lookup(account, CONFIG_ACCOUNT_HOSTNAME); gtk_entry_set_text(GTK_ENTRY(entry_hostname), hostname); gtk_table_attach(GTK_TABLE(table), entry_hostname, 1, 2, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); @@ -298,7 +299,7 @@ static GtkWidget* create_basic_tab(const account_t *account) GTK_ENTRY_ICON_PRIMARY, gdk_pixbuf_new_from_file(PERSON_IMG, NULL)); gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_username); - gchar *username = account_lookup(account, ACCOUNT_USERNAME); + gchar *username = account_lookup(account, CONFIG_ACCOUNT_USERNAME); gtk_entry_set_text(GTK_ENTRY(entry_username), username); gtk_table_attach(GTK_TABLE(table), entry_username, 1, 2, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); @@ -342,7 +343,7 @@ static GtkWidget* create_basic_tab(const account_t *account) gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); entry_route_set = gtk_entry_new(); gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_route_set); - gchar *route_set = account_lookup(account, ACCOUNT_ROUTE); + gchar *route_set = account_lookup(account, CONFIG_ACCOUNT_ROUTESET); gtk_entry_set_text(GTK_ENTRY(entry_route_set), route_set); gtk_table_attach(GTK_TABLE(table), entry_route_set, 1, 2, row, row+1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); @@ -352,7 +353,7 @@ static GtkWidget* create_basic_tab(const account_t *account) gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); entry_mailbox = gtk_entry_new(); gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_mailbox); - gchar *mailbox = account_lookup(account, ACCOUNT_MAILBOX); + gchar *mailbox = account_lookup(account, CONFIG_ACCOUNT_MAILBOX); mailbox = mailbox ? mailbox : ""; gtk_entry_set_text(GTK_ENTRY(entry_mailbox), mailbox); gtk_table_attach(GTK_TABLE(table), entry_mailbox, 1, 2, row, row+1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); @@ -363,7 +364,7 @@ static GtkWidget* create_basic_tab(const account_t *account) gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); entry_user_agent = gtk_entry_new(); gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_user_agent); - gchar *user_agent = account_lookup(account, ACCOUNT_USERAGENT); + gchar *user_agent = account_lookup(account, CONFIG_ACCOUNT_USERAGENT); gtk_entry_set_text(GTK_ENTRY(entry_user_agent), user_agent); gtk_table_attach(GTK_TABLE(table), entry_user_agent, 1, 2, row, row+1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); @@ -381,9 +382,9 @@ static void fill_treeview_with_credential(const account_t * account) for (unsigned i = 0; i < account->credential_information->len; i++) { GHashTable * element = g_ptr_array_index(account->credential_information, i); gtk_list_store_append(credential_store, &iter); - gtk_list_store_set(credential_store, &iter, COLUMN_CREDENTIAL_REALM, g_hash_table_lookup(element, ACCOUNT_REALM), - COLUMN_CREDENTIAL_USERNAME, g_hash_table_lookup(element, ACCOUNT_USERNAME), - COLUMN_CREDENTIAL_PASSWORD, g_hash_table_lookup(element, ACCOUNT_PASSWORD), + gtk_list_store_set(credential_store, &iter, COLUMN_CREDENTIAL_REALM, g_hash_table_lookup(element, CONFIG_ACCOUNT_REALM), + COLUMN_CREDENTIAL_USERNAME, g_hash_table_lookup(element, CONFIG_ACCOUNT_USERNAME), + COLUMN_CREDENTIAL_PASSWORD, g_hash_table_lookup(element, CONFIG_ACCOUNT_PASSWORD), COLUMN_CREDENTIAL_DATA, element, -1); } } @@ -734,16 +735,16 @@ create_security_widget(const account_t *account) // Load from SIP/IAX/Unknown ? if (account && account->properties) { - curKeyExchange = account_lookup(account, ACCOUNT_KEY_EXCHANGE); + curKeyExchange = account_lookup(account, CONFIG_SRTP_KEY_EXCHANGE); if (curKeyExchange == NULL) curKeyExchange = "none"; - curSRTPEnabled = account_lookup(account, ACCOUNT_SRTP_ENABLED); + curSRTPEnabled = account_lookup(account, CONFIG_SRTP_ENABLE); if (curSRTPEnabled == NULL) curSRTPEnabled = "false"; - curTLSEnabled = account_lookup(account, TLS_ENABLE); + curTLSEnabled = account_lookup(account, CONFIG_TLS_ENABLE); if (curTLSEnabled == NULL) curTLSEnabled = "false"; @@ -833,10 +834,10 @@ static GtkWidget* create_registration_expire(const account_t *account) gchar *account_expire = NULL; void *orig_key = NULL; if (account && account->properties) - if (!g_hash_table_lookup_extended(account->properties, ACCOUNT_REGISTRATION_EXPIRE, + if (!g_hash_table_lookup_extended(account->properties, CONFIG_ACCOUNT_REGISTRATION_EXPIRE, &orig_key, (gpointer) &account_expire)) ERROR("Could not retrieve %s from account properties", - ACCOUNT_REGISTRATION_EXPIRE); + CONFIG_ACCOUNT_REGISTRATION_EXPIRE); GtkWidget *table, *frame; gnome_main_section_new_with_table(_("Registration"), &frame, &table, 2, 3); @@ -861,8 +862,8 @@ create_network(const account_t *account) gchar *local_port = NULL; if (account) { - local_interface = account_lookup(account, LOCAL_INTERFACE); - local_port = account_lookup(account, LOCAL_PORT); + local_interface = account_lookup(account, CONFIG_LOCAL_INTERFACE); + local_port = account_lookup(account, CONFIG_LOCAL_PORT); } GtkWidget *table, *frame; @@ -933,20 +934,20 @@ GtkWidget* create_published_address(const account_t *account) // Get the user configuration if (account) { - use_tls = account_lookup(account, TLS_ENABLE); - published_sameas_local = account_lookup(account, PUBLISHED_SAMEAS_LOCAL); + use_tls = account_lookup(account, CONFIG_TLS_ENABLE); + published_sameas_local = account_lookup(account, CONFIG_PUBLISHED_SAMEAS_LOCAL); if (utf8_case_equal(published_sameas_local, "true")) { - published_address = dbus_get_address_from_interface_name(account_lookup(account, LOCAL_INTERFACE)); - published_port = account_lookup(account, LOCAL_PORT); + published_address = dbus_get_address_from_interface_name(account_lookup(account, CONFIG_LOCAL_INTERFACE)); + published_port = account_lookup(account, CONFIG_LOCAL_PORT); } else { - published_address = account_lookup(account, PUBLISHED_ADDRESS); - published_port = account_lookup(account, PUBLISHED_PORT); + published_address = account_lookup(account, CONFIG_PUBLISHED_ADDRESS); + published_port = account_lookup(account, CONFIG_PUBLISHED_PORT); } - stun_enable = account_lookup(account, ACCOUNT_SIP_STUN_ENABLED); - stun_server = account_lookup(account, ACCOUNT_SIP_STUN_SERVER); - published_sameas_local = account_lookup(account, PUBLISHED_SAMEAS_LOCAL); + stun_enable = account_lookup(account, CONFIG_STUN_ENABLE); + stun_server = account_lookup(account, CONFIG_STUN_SERVER); + published_sameas_local = account_lookup(account, CONFIG_PUBLISHED_SAMEAS_LOCAL); } gnome_main_section_new_with_table(_("Published address"), &frame, &table, 2, 3); @@ -1071,7 +1072,7 @@ create_audiocodecs_configuration(const account_t *account) gtk_widget_show(dtmf); overrtp = gtk_radio_button_new_with_label(NULL, _("RTP")); - const gchar * const dtmf_type = account_lookup(account, ACCOUNT_DTMF_TYPE); + const gchar * const dtmf_type = account_lookup(account, CONFIG_ACCOUNT_DTMF_TYPE); const gboolean dtmf_are_rtp = utf8_case_equal(dtmf_type, OVERRTP); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(overrtp), dtmf_are_rtp); gtk_table_attach(GTK_TABLE(table), overrtp, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); @@ -1181,67 +1182,67 @@ static void update_account_from_basic_tab(account_t *account) if (g_strcmp0(proto, "SIP") == 0) { if (!account_is_IP2IP(account)) { - account_replace(account, ACCOUNT_REGISTRATION_EXPIRE, + account_replace(account, CONFIG_ACCOUNT_REGISTRATION_EXPIRE, gtk_entry_get_text(GTK_ENTRY(expire_spin_box))); - account_replace(account, ACCOUNT_ROUTE, + account_replace(account, CONFIG_ACCOUNT_ROUTESET, gtk_entry_get_text(GTK_ENTRY(entry_route_set))); - account_replace(account, ACCOUNT_USERAGENT, + account_replace(account, CONFIG_ACCOUNT_USERAGENT, gtk_entry_get_text(GTK_ENTRY(entry_user_agent))); gboolean v = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(use_stun_check_box)); - account_replace(account, ACCOUNT_SIP_STUN_ENABLED, + account_replace(account, CONFIG_STUN_ENABLE, bool_to_string(v)); - account_replace(account, ACCOUNT_SIP_STUN_SERVER, + account_replace(account, CONFIG_STUN_SERVER, gtk_entry_get_text(GTK_ENTRY(stun_server_entry))); v = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(same_as_local_radio_button)); - account_replace(account, PUBLISHED_SAMEAS_LOCAL, bool_to_string(v)); + account_replace(account, CONFIG_PUBLISHED_SAMEAS_LOCAL, bool_to_string(v)); if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(same_as_local_radio_button))) { - account_replace(account, PUBLISHED_PORT, + account_replace(account, CONFIG_PUBLISHED_PORT, gtk_entry_get_text(GTK_ENTRY(published_port_spin_box))); - account_replace(account, PUBLISHED_ADDRESS, + account_replace(account, CONFIG_PUBLISHED_ADDRESS, gtk_entry_get_text(GTK_ENTRY(published_address_entry))); } else { - account_replace(account, PUBLISHED_PORT, + account_replace(account, CONFIG_PUBLISHED_PORT, gtk_entry_get_text(GTK_ENTRY(local_port_spin_box))); gchar *local_interface = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(local_address_combo)); gchar *published_address = dbus_get_address_from_interface_name(local_interface); g_free(local_interface); - account_replace(account, PUBLISHED_ADDRESS, published_address); + account_replace(account, CONFIG_PUBLISHED_ADDRESS, published_address); } } if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(overrtp))) { DEBUG("Set dtmf over rtp"); - account_replace(account, ACCOUNT_DTMF_TYPE, OVERRTP); + account_replace(account, CONFIG_ACCOUNT_DTMF_TYPE, OVERRTP); } else { DEBUG("Set dtmf over sip"); - account_replace(account, ACCOUNT_DTMF_TYPE, SIPINFO); + account_replace(account, CONFIG_ACCOUNT_DTMF_TYPE, SIPINFO); } gchar* key_exchange = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(key_exchange_combo)); if (utf8_case_equal(key_exchange, "ZRTP")) { - account_replace(account, ACCOUNT_SRTP_ENABLED, "true"); - account_replace(account, ACCOUNT_KEY_EXCHANGE, ZRTP); + account_replace(account, CONFIG_SRTP_ENABLE, "true"); + account_replace(account, CONFIG_SRTP_KEY_EXCHANGE, ZRTP); } else if (utf8_case_equal(key_exchange, "SDES")) { - account_replace(account, ACCOUNT_SRTP_ENABLED, "true"); - account_replace(account, ACCOUNT_KEY_EXCHANGE, SDES); + account_replace(account, CONFIG_SRTP_ENABLE, "true"); + account_replace(account, CONFIG_SRTP_KEY_EXCHANGE, SDES); } else { - account_replace(account, ACCOUNT_SRTP_ENABLED, "false"); - account_replace(account, ACCOUNT_KEY_EXCHANGE, ""); + account_replace(account, CONFIG_SRTP_ENABLE, "false"); + account_replace(account, CONFIG_SRTP_KEY_EXCHANGE, ""); } g_free(key_exchange); const gboolean tls_enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(use_sip_tls_check_box)); - account_replace(account, TLS_ENABLE, bool_to_string(tls_enabled)); + account_replace(account, CONFIG_TLS_ENABLE, bool_to_string(tls_enabled)); const gboolean tone_enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(enable_tone)); account_replace(account, CONFIG_RINGTONE_ENABLED, bool_to_string(tone_enabled)); @@ -1251,19 +1252,19 @@ static void update_account_from_basic_tab(account_t *account) g_free(ringtone_path); gchar *address_combo_text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(local_address_combo)); - account_replace(account, LOCAL_INTERFACE, address_combo_text); + account_replace(account, CONFIG_LOCAL_INTERFACE, address_combo_text); g_free(address_combo_text); - account_replace(account, LOCAL_PORT, + account_replace(account, CONFIG_LOCAL_PORT, gtk_entry_get_text(GTK_ENTRY(local_port_spin_box))); } - account_replace(account, ACCOUNT_ALIAS, gtk_entry_get_text(GTK_ENTRY(entry_alias))); - account_replace(account, ACCOUNT_TYPE, proto); - account_replace(account, ACCOUNT_HOSTNAME, gtk_entry_get_text(GTK_ENTRY(entry_hostname))); - account_replace(account, ACCOUNT_USERNAME, gtk_entry_get_text(GTK_ENTRY(entry_username))); - account_replace(account, ACCOUNT_PASSWORD, gtk_entry_get_text(GTK_ENTRY(entry_password))); - account_replace(account, ACCOUNT_MAILBOX, gtk_entry_get_text(GTK_ENTRY(entry_mailbox))); + account_replace(account, CONFIG_ACCOUNT_ALIAS, gtk_entry_get_text(GTK_ENTRY(entry_alias))); + account_replace(account, CONFIG_ACCOUNT_TYPE, proto); + account_replace(account, CONFIG_ACCOUNT_HOSTNAME, gtk_entry_get_text(GTK_ENTRY(entry_hostname))); + account_replace(account, CONFIG_ACCOUNT_USERNAME, gtk_entry_get_text(GTK_ENTRY(entry_username))); + account_replace(account, CONFIG_ACCOUNT_PASSWORD, gtk_entry_get_text(GTK_ENTRY(entry_password))); + account_replace(account, CONFIG_ACCOUNT_MAILBOX, gtk_entry_get_text(GTK_ENTRY(entry_mailbox))); g_free(proto); } diff --git a/gnome/src/config/accountlistconfigdialog.c b/gnome/src/config/accountlistconfigdialog.c index 28aa48cb2af3531c493501c5f1d19884213c23ea..52a688e2d51dbc62c30e545e2f83f29dd2671fa0 100644 --- a/gnome/src/config/accountlistconfigdialog.c +++ b/gnome/src/config/accountlistconfigdialog.c @@ -33,6 +33,7 @@ #include "str_utils.h" #include "dbus/dbus.h" #include "accountconfigdialog.h" +#include "account_schema.h" #include "accountlist.h" #include "actions.h" #include "mainwindow.h" @@ -145,13 +146,13 @@ static void edit_account_cb(GtkButton *button UNUSED, gpointer data) static void account_store_add(GtkTreeIter *iter, account_t *account) { - const gchar *enabled = account_lookup(account, ACCOUNT_ENABLED); - const gchar *type = account_lookup(account, ACCOUNT_TYPE); + const gchar *enabled = account_lookup(account, CONFIG_ACCOUNT_ENABLE); + const gchar *type = account_lookup(account, CONFIG_ACCOUNT_TYPE); DEBUG("Account is enabled :%s", enabled); const gchar *state_name = account_state_name(account->state); gtk_list_store_set(account_store, iter, - COLUMN_ACCOUNT_ALIAS, account_lookup(account, ACCOUNT_ALIAS), + COLUMN_ACCOUNT_ALIAS, account_lookup(account, CONFIG_ACCOUNT_ALIAS), COLUMN_ACCOUNT_TYPE, type, COLUMN_ACCOUNT_STATUS, state_name, COLUMN_ACCOUNT_ACTIVE, utf8_case_equal(enabled, "true"), @@ -271,7 +272,7 @@ enable_account_cb(GtkCellRendererToggle *rend UNUSED, gchar* path, const gchar * enabled_str = enable ? "true" : "false"; DEBUG("Account is enabled: %s", enabled_str); - account_replace(account, ACCOUNT_ENABLED, enabled_str); + account_replace(account, CONFIG_ACCOUNT_ENABLE, enabled_str); dbus_send_register(account->accountID, enable); } diff --git a/gnome/src/config/assistant.c b/gnome/src/config/assistant.c index d25e882bd63050b624af210d2ce7c088ff616071..32b495177674e945d40034d6ac7f4b6ebede586e 100644 --- a/gnome/src/config/assistant.c +++ b/gnome/src/config/assistant.c @@ -36,6 +36,7 @@ #include "logger.h" #include "dbus.h" #include "reqaccount.h" +#include "account_schema.h" #define SFLPHONE_ORG_SERVER "sip.sflphone.org" @@ -144,23 +145,23 @@ static void sip_apply_callback(void) } if (account_type == _SIP) { - account_insert(current, ACCOUNT_ALIAS, gtk_entry_get_text(GTK_ENTRY(wiz->sip_alias))); - account_insert(current, ACCOUNT_ENABLED, "true"); - account_insert(current, ACCOUNT_MAILBOX, gtk_entry_get_text(GTK_ENTRY(wiz->sip_voicemail))); - account_insert(current, ACCOUNT_TYPE, "SIP"); - account_insert(current, ACCOUNT_HOSTNAME, gtk_entry_get_text(GTK_ENTRY(wiz->sip_server))); - account_insert(current, ACCOUNT_PASSWORD, gtk_entry_get_text(GTK_ENTRY(wiz->sip_password))); - account_insert(current, ACCOUNT_USERNAME, gtk_entry_get_text(GTK_ENTRY(wiz->sip_username))); - account_insert(current, ACCOUNT_SIP_STUN_ENABLED, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wiz->enable)) ? "true" : "false"); - account_insert(current, ACCOUNT_SIP_STUN_SERVER, gtk_entry_get_text(GTK_ENTRY(wiz->addr))); + account_insert(current, CONFIG_ACCOUNT_ALIAS, gtk_entry_get_text(GTK_ENTRY(wiz->sip_alias))); + account_insert(current, CONFIG_ACCOUNT_ENABLE, "true"); + account_insert(current, CONFIG_ACCOUNT_MAILBOX, gtk_entry_get_text(GTK_ENTRY(wiz->sip_voicemail))); + account_insert(current, CONFIG_ACCOUNT_TYPE, "SIP"); + account_insert(current, CONFIG_ACCOUNT_HOSTNAME, gtk_entry_get_text(GTK_ENTRY(wiz->sip_server))); + account_insert(current, CONFIG_ACCOUNT_PASSWORD, gtk_entry_get_text(GTK_ENTRY(wiz->sip_password))); + account_insert(current, CONFIG_ACCOUNT_USERNAME, gtk_entry_get_text(GTK_ENTRY(wiz->sip_username))); + account_insert(current, CONFIG_STUN_ENABLE, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wiz->enable)) ? "true" : "false"); + account_insert(current, CONFIG_STUN_SERVER, gtk_entry_get_text(GTK_ENTRY(wiz->addr))); if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wiz->zrtp_enable)) == TRUE) { - account_insert(current, ACCOUNT_SRTP_ENABLED, "true"); - account_insert(current, ACCOUNT_KEY_EXCHANGE, ZRTP); - account_insert(current, ACCOUNT_ZRTP_DISPLAY_SAS, "true"); - account_insert(current, ACCOUNT_ZRTP_NOT_SUPP_WARNING, "true"); - account_insert(current, ACCOUNT_ZRTP_HELLO_HASH, "true"); - account_insert(current, ACCOUNT_DISPLAY_SAS_ONCE, "false"); + account_insert(current, CONFIG_SRTP_ENABLE, "true"); + account_insert(current, CONFIG_SRTP_KEY_EXCHANGE, ZRTP); + account_insert(current, CONFIG_ZRTP_DISPLAY_SAS, "true"); + account_insert(current, CONFIG_ZRTP_NOT_SUPP_WARNING, "true"); + account_insert(current, CONFIG_ZRTP_HELLO_HASH, "true"); + account_insert(current, CONFIG_ZRTP_DISPLAY_SAS_ONCE, "false"); } // Add default interface info @@ -172,8 +173,8 @@ static void sip_apply_callback(void) iface = iface_list; DEBUG("Selected interface %s", *iface); - account_insert(current, LOCAL_INTERFACE, *iface); - account_insert(current, PUBLISHED_ADDRESS, *iface); + account_insert(current, CONFIG_LOCAL_INTERFACE, *iface); + account_insert(current, CONFIG_PUBLISHED_ADDRESS, *iface); dbus_add_account(current); getMessageSummary(gtk_entry_get_text(GTK_ENTRY(wiz->sip_alias)), @@ -193,13 +194,13 @@ static void sip_apply_callback(void) static void iax_apply_callback(void) { if (account_type == _IAX) { - account_insert(current, ACCOUNT_ALIAS, gtk_entry_get_text(GTK_ENTRY(wiz->iax_alias))); - account_insert(current, ACCOUNT_ENABLED, "true"); - account_insert(current, ACCOUNT_MAILBOX, gtk_entry_get_text(GTK_ENTRY(wiz->iax_voicemail))); - account_insert(current, ACCOUNT_TYPE, "IAX"); - account_insert(current, ACCOUNT_USERNAME, gtk_entry_get_text(GTK_ENTRY(wiz->iax_username))); - account_insert(current, ACCOUNT_HOSTNAME, gtk_entry_get_text(GTK_ENTRY(wiz->iax_server))); - account_insert(current, ACCOUNT_PASSWORD, gtk_entry_get_text(GTK_ENTRY(wiz->iax_password))); + account_insert(current, CONFIG_ACCOUNT_ALIAS, gtk_entry_get_text(GTK_ENTRY(wiz->iax_alias))); + account_insert(current, CONFIG_ACCOUNT_ENABLE, "true"); + account_insert(current, CONFIG_ACCOUNT_MAILBOX, gtk_entry_get_text(GTK_ENTRY(wiz->iax_voicemail))); + account_insert(current, CONFIG_ACCOUNT_TYPE, "IAX"); + account_insert(current, CONFIG_ACCOUNT_USERNAME, gtk_entry_get_text(GTK_ENTRY(wiz->iax_username))); + account_insert(current, CONFIG_ACCOUNT_HOSTNAME, gtk_entry_get_text(GTK_ENTRY(wiz->iax_server))); + account_insert(current, CONFIG_ACCOUNT_PASSWORD, gtk_entry_get_text(GTK_ENTRY(wiz->iax_password))); dbus_add_account(current); getMessageSummary(gtk_entry_get_text(GTK_ENTRY(wiz->iax_alias)), @@ -459,7 +460,7 @@ GtkWidget* build_iax_account_configuration(void) gtk_label_set_mnemonic_widget(GTK_LABEL(label), wiz->iax_voicemail); gtk_table_attach(GTK_TABLE(table), wiz->iax_voicemail, 1, 2, 5, 6, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); - current -> state = ACCOUNT_STATE_UNREGISTERED; + current->state = ACCOUNT_STATE_UNREGISTERED; g_signal_connect(G_OBJECT(wiz->assistant), "apply", G_CALLBACK(iax_apply_callback), NULL); diff --git a/gnome/src/config/preferencesdialog.c b/gnome/src/config/preferencesdialog.c index 65dfe5169ae4895e2199fe9dcb79eae3b0f79b67..f8965f1d37b4f8c39f4319c4a4f422b0b7eb06f1 100644 --- a/gnome/src/config/preferencesdialog.c +++ b/gnome/src/config/preferencesdialog.c @@ -465,6 +465,11 @@ show_preferences_dialog() gtk_icon_view_select_path(GTK_ICON_VIEW(iconview), gtk_tree_path_new_first()); guint result = gtk_dialog_run(dialog); +#ifdef SFL_VIDEO + // stop video preview if it's running + if (dbus_has_video_preview_started()) + dbus_stop_video_preview(); +#endif save_configuration_parameters(); update_actions(); diff --git a/gnome/src/config/tlsadvanceddialog.c b/gnome/src/config/tlsadvanceddialog.c index 2ae6f2b8c611a97cf9ccfcecc7a0f9bdc90d9937..c8636d00bf88cee1e3883c3fe7f31c11dcb51d8b 100644 --- a/gnome/src/config/tlsadvanceddialog.c +++ b/gnome/src/config/tlsadvanceddialog.c @@ -31,7 +31,7 @@ #include "tlsadvanceddialog.h" #include "gtk2_wrappers.h" #include "str_utils.h" -#include "sflphone_const.h" +#include "account_schema.h" #include "mainwindow.h" #include "utils.h" #include <dbus.h> @@ -101,19 +101,19 @@ void show_advanced_tls_options(account_t *account) gchar * negotiation_timeout_msec = NULL; if (account->properties != NULL) { - tls_listener_port = account_lookup(account, TLS_LISTENER_PORT); - tls_ca_list_file = account_lookup(account, TLS_CA_LIST_FILE); - tls_certificate_file = account_lookup(account, TLS_CERTIFICATE_FILE); - tls_private_key_file = account_lookup(account, TLS_PRIVATE_KEY_FILE); - tls_password = account_lookup(account, TLS_PASSWORD); - tls_method = account_lookup(account, TLS_METHOD); - tls_ciphers = account_lookup(account, TLS_CIPHERS); - tls_server_name = account_lookup(account, TLS_SERVER_NAME); - verify_server = account_lookup(account, TLS_VERIFY_SERVER); - verify_client = account_lookup(account, TLS_VERIFY_CLIENT); - require_client_certificate = account_lookup(account, TLS_REQUIRE_CLIENT_CERTIFICATE); - negotiation_timeout_sec = account_lookup(account, TLS_NEGOTIATION_TIMEOUT_SEC); - negotiation_timeout_msec = account_lookup(account, TLS_NEGOTIATION_TIMEOUT_MSEC); + tls_listener_port = account_lookup(account, CONFIG_TLS_LISTENER_PORT); + tls_ca_list_file = account_lookup(account, CONFIG_TLS_CA_LIST_FILE); + tls_certificate_file = account_lookup(account, CONFIG_TLS_CERTIFICATE_FILE); + tls_private_key_file = account_lookup(account, CONFIG_TLS_PRIVATE_KEY_FILE); + tls_password = account_lookup(account, CONFIG_TLS_PASSWORD); + tls_method = account_lookup(account, CONFIG_TLS_METHOD); + tls_ciphers = account_lookup(account, CONFIG_TLS_CIPHERS); + tls_server_name = account_lookup(account, CONFIG_TLS_SERVER_NAME); + verify_server = account_lookup(account, CONFIG_TLS_VERIFY_SERVER); + verify_client = account_lookup(account, CONFIG_TLS_VERIFY_CLIENT); + require_client_certificate = account_lookup(account, CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE); + negotiation_timeout_sec = account_lookup(account, CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC); + negotiation_timeout_msec = account_lookup(account, CONFIG_TLS_NEGOTIATION_TIMEOUT_MSEC); } @@ -274,35 +274,35 @@ void show_advanced_tls_options(account_t *account) gtk_widget_show_all(ret); if (gtk_dialog_run(GTK_DIALOG(tlsDialog)) == GTK_RESPONSE_ACCEPT) { - account_replace(account, TLS_LISTENER_PORT, + account_replace(account, CONFIG_TLS_LISTENER_PORT, gtk_entry_get_text(GTK_ENTRY(tlsListenerPort))); - account_replace(account, TLS_CA_LIST_FILE, get_filename(caListFileChooser)); + account_replace(account, CONFIG_TLS_CA_LIST_FILE, get_filename(caListFileChooser)); - account_replace(account, TLS_CERTIFICATE_FILE, + account_replace(account, CONFIG_TLS_CERTIFICATE_FILE, get_filename(certificateFileChooser)); - account_replace(account, TLS_PRIVATE_KEY_FILE, + account_replace(account, CONFIG_TLS_PRIVATE_KEY_FILE, get_filename(privateKeyFileChooser)); - account_replace(account, TLS_PASSWORD, gtk_entry_get_text(GTK_ENTRY(privateKeyPasswordEntry))); + account_replace(account, CONFIG_TLS_PASSWORD, gtk_entry_get_text(GTK_ENTRY(privateKeyPasswordEntry))); gchar *tls_text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(tlsProtocolMethodCombo)); - account_replace(account, TLS_METHOD, tls_text); + account_replace(account, CONFIG_TLS_METHOD, tls_text); g_free(tls_text); - account_replace(account, TLS_CIPHERS, gtk_entry_get_text(GTK_ENTRY(cipherListEntry))); + account_replace(account, CONFIG_TLS_CIPHERS, gtk_entry_get_text(GTK_ENTRY(cipherListEntry))); - account_replace(account, TLS_SERVER_NAME, gtk_entry_get_text(GTK_ENTRY(serverNameInstance))); + account_replace(account, CONFIG_TLS_SERVER_NAME, gtk_entry_get_text(GTK_ENTRY(serverNameInstance))); - account_replace(account, TLS_VERIFY_SERVER, toggle_to_string(verifyCertificateServer)); + account_replace(account, CONFIG_TLS_VERIFY_SERVER, toggle_to_string(verifyCertificateServer)); - account_replace(account, TLS_VERIFY_CLIENT, toggle_to_string(verifyCertificateClient)); + account_replace(account, CONFIG_TLS_VERIFY_CLIENT, toggle_to_string(verifyCertificateClient)); - account_replace(account, TLS_REQUIRE_CLIENT_CERTIFICATE, toggle_to_string(requireCertificate)); + account_replace(account, CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, toggle_to_string(requireCertificate)); - account_replace(account, TLS_NEGOTIATION_TIMEOUT_SEC, gtk_entry_get_text(GTK_ENTRY(tlsTimeOutSec))); + account_replace(account, CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, gtk_entry_get_text(GTK_ENTRY(tlsTimeOutSec))); - account_replace(account, TLS_NEGOTIATION_TIMEOUT_MSEC, gtk_entry_get_text(GTK_ENTRY(tlsTimeOutMSec))); + account_replace(account, CONFIG_TLS_NEGOTIATION_TIMEOUT_MSEC, gtk_entry_get_text(GTK_ENTRY(tlsTimeOutMSec))); } gtk_widget_destroy(GTK_WIDGET(tlsDialog)); diff --git a/gnome/src/config/videoconf.c b/gnome/src/config/videoconf.c index cb9fc712ccb912f428dc5791e8d7f344e8f47b93..3639403712b19f7e7559175d40976a3f12726ad0 100644 --- a/gnome/src/config/videoconf.c +++ b/gnome/src/config/videoconf.c @@ -36,13 +36,8 @@ #include "unused.h" #include "eel-gconf-extensions.h" #include "dbus.h" -#include "video/video_renderer.h" -#include "actions.h" #include "codeclist.h" -#include <clutter/clutter.h> -#include <clutter-gtk/clutter-gtk.h> - static GtkWidget *v4l2Device; static GtkWidget *v4l2Channel; static GtkWidget *v4l2Size; @@ -56,14 +51,9 @@ static GtkListStore *v4l2RateList; static GtkWidget *v4l2_hbox; static GtkWidget *v4l2_nodev; -static GtkWidget *preview_button = NULL; - -static GtkWidget *drawarea = NULL; -static int drawWidth = 352; -static int drawHeight = 288; -static VideoRenderer *preview = NULL; +static GtkWidget *preview_button; -static GtkWidget *codecTreeView; // View used instead of store to get access to selection +static GtkWidget *codecTreeView; // View used instead of store to get access to selection static GtkWidget *codecMoveUpButton; static GtkWidget *codecMoveDownButton; @@ -90,7 +80,7 @@ select_codec(GtkTreeSelection *selection, GtkTreeModel *model) gtk_widget_set_sensitive(GTK_WIDGET(codecMoveDownButton), TRUE); } } - + void active_is_always_recording() { gboolean enabled = FALSE; @@ -107,93 +97,82 @@ void active_is_always_recording() dbus_set_is_always_recording(enabled); } -/* This gets called when the video preview is stopped */ -static gboolean -preview_is_running_cb(GObject *obj, GParamSpec *pspec, gpointer user_data) +static const gchar *const PREVIEW_START_STR = "_Start"; +static const gchar *const PREVIEW_STOP_STR = "_Stop"; + +static void +preview_button_toggled(GtkButton *button, gpointer data UNUSED) { - (void) pspec; - gboolean running = FALSE; - g_object_get(obj, "running", &running, NULL); - GtkButton *button = GTK_BUTTON(user_data); - if (running) - gtk_button_set_label(button, _("_Stop")); - else { - gtk_button_set_label(button, _("_Start")); - preview = NULL; - } - return TRUE; + preview_button = GTK_WIDGET(button); + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) + dbus_start_video_preview(); + else + dbus_stop_video_preview(); + + update_preview_button_label(); } void -video_preview_started_cb(DBusGProxy *proxy, gint OUT_width, gint OUT_height, - gint OUT_shmId, gint OUT_semId, - gint OUT_videoBufferSize, GError *error, - gpointer userdata) +set_preview_button_sensitivity(gboolean sensitive) { - (void) proxy; - (void) error; - (void) userdata; - - if (OUT_shmId == -1 || OUT_semId == -1 || OUT_videoBufferSize == -1) + if (!preview_button || !GTK_IS_WIDGET(preview_button)) return; - - DEBUG("Preview started width:%d height:%d shm:%d sem:%d size:%d", - OUT_width, OUT_height, OUT_shmId, OUT_semId, OUT_videoBufferSize); - drawWidth = OUT_width; - drawHeight = OUT_height; - gtk_widget_set_size_request(drawarea, drawWidth, drawHeight); - preview = video_renderer_new(drawarea, drawWidth, drawHeight, OUT_shmId, OUT_semId, OUT_videoBufferSize); - g_signal_connect(preview, "notify::running", G_CALLBACK(preview_is_running_cb), preview_button); - if (video_renderer_run(preview)) { - ERROR("Video preview run returned an error, unreffing\n"); - g_object_unref(preview); - } + DEBUG("%ssetting preview button", sensitive ? "" : "Un"); + gtk_widget_set_sensitive(GTK_WIDGET(preview_button), sensitive); } -static void -preview_button_clicked(GtkButton *button, gpointer data UNUSED) +void +update_preview_button_label() { - preview_button = GTK_WIDGET(button); - if (g_strcmp0(gtk_button_get_label(button), _("_Start")) == 0) - dbus_start_video_preview(); - else { - /* user clicked stop */ - if (!preview) /* preview was not created yet on the server */ - return ; - video_renderer_stop(preview); - dbus_stop_video_preview(); - preview = NULL; + if (!preview_button || !GTK_IS_WIDGET(preview_button)) + return; + + GtkToggleButton *button = GTK_TOGGLE_BUTTON(preview_button); + if (dbus_has_video_preview_started()) { + /* We call g_object_set to avoid triggering the "toggled" signal */ + gtk_button_set_label(GTK_BUTTON(button), _(PREVIEW_STOP_STR)); + g_object_set(button, "active", TRUE, NULL); + } else { + gtk_button_set_label(GTK_BUTTON(button), _(PREVIEW_START_STR)); + g_object_set(button, "active", FALSE, NULL); } } /** * Fills the tree list with supported codecs */ -static void preferences_dialog_fill_codec_list(account_t *a) +static void +preferences_dialog_fill_codec_list(account_t *acc) { - GtkTreeIter iter; - + if (!acc) { + ERROR("Account is NULL"); + return; + } // Get model of view and clear it GtkListStore *codecStore = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(codecTreeView))); gtk_list_store_clear(codecStore); - GQueue *list = a ? a->vcodecs : get_video_codecs_list(); + GPtrArray *vcodecs = dbus_get_video_codecs(acc->accountID); + if (!vcodecs) + return; // Add the codecs in the list - for (size_t i = 0; i < list->length; i++) { - codec_t *c = g_queue_peek_nth(list, i); + for (size_t i = 0; i < vcodecs->len; ++i) { + GHashTable *c = g_ptr_array_index(vcodecs, i); if (c) { - DEBUG("%s is %sactive", c->name, c->is_active ? "" : "not "); + GtkTreeIter iter; gtk_list_store_append(codecStore, &iter); - gchar *bitrate = g_strdup_printf("%s kbps", c->bitrate); + const gchar *bitrate = g_hash_table_lookup(c, "bitrate"); + const gboolean is_active = !g_strcmp0(g_hash_table_lookup(c, "enabled"), "true"); + const gchar *name = g_hash_table_lookup(c, "name"); gtk_list_store_set(codecStore, &iter, COLUMN_CODEC_ACTIVE, - c->is_active, COLUMN_CODEC_NAME, c->name, + is_active, COLUMN_CODEC_NAME, name, COLUMN_CODEC_BITRATE, bitrate, -1); - g_free(bitrate); } } + g_ptr_array_free(vcodecs, TRUE); } /** @@ -201,17 +180,39 @@ static void preferences_dialog_fill_codec_list(account_t *a) * and in configuration files */ +static gboolean +video_codec_has_name(GHashTable *codec, const gchar *name) +{ + return g_strcmp0(g_hash_table_lookup(codec, "name"), name) == 0; +} + +static void +video_codec_set_active(GHashTable *codec, gboolean active) +{ + g_hash_table_replace(codec, g_strdup("enabled"), active ? g_strdup("true") : g_strdup("false")); +} + +static void +video_codec_set_bitrate(GHashTable *codec, const gchar *bitrate) +{ + g_hash_table_replace(codec, g_strdup("bitrate"), g_strdup(bitrate)); +} + +static GHashTable * +video_codec_list_get_by_name(GPtrArray *vcodecs, const gchar *name) +{ + for (guint i = 0; i < vcodecs->len; ++i) { + GHashTable *codec = g_ptr_array_index(vcodecs, i); + if (video_codec_has_name(codec, name)) + return codec; + } + return NULL; +} + static void codec_active_toggled(GtkCellRendererToggle *renderer UNUSED, gchar *path, gpointer data) { - // Get path of clicked codec active toggle box - GtkTreePath *treePath = gtk_tree_path_new_from_string(path); - GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW (codecTreeView)); - GtkTreeIter iter; - gtk_tree_model_get_iter(model, &iter, treePath); - - // Retrieve userdata account_t *acc = (account_t*) data; if (!acc) { @@ -219,6 +220,13 @@ codec_active_toggled(GtkCellRendererToggle *renderer UNUSED, gchar *path, return; } + // Get path of clicked codec active toggle box + GtkTreePath *tree_path = gtk_tree_path_new_from_string(path); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW (codecTreeView)); + GtkTreeIter iter; + gtk_tree_model_get_iter(model, &iter, tree_path); + gtk_tree_path_free(tree_path); + // Get active value and name at iteration gboolean active = FALSE; gchar *name = NULL; @@ -226,9 +234,11 @@ codec_active_toggled(GtkCellRendererToggle *renderer UNUSED, gchar *path, COLUMN_CODEC_NAME, &name, -1); DEBUG("%s", name); - DEBUG("video codecs length %i", g_queue_get_length(acc->vcodecs)); + GPtrArray *vcodecs = dbus_get_video_codecs(acc->accountID); + if (!vcodecs) + return; - codec_t *codec = codec_list_get_by_name((gconstpointer) name, acc->vcodecs); + DEBUG("video codecs length %i", vcodecs->len); // Toggle active value active = !active; @@ -237,85 +247,164 @@ codec_active_toggled(GtkCellRendererToggle *renderer UNUSED, gchar *path, gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLUMN_CODEC_ACTIVE, active, -1); - gtk_tree_path_free(treePath); + GHashTable *codec = video_codec_list_get_by_name(vcodecs, name); + if (codec) { + video_codec_set_active(codec, active); + dbus_set_video_codecs(acc->accountID, vcodecs); + } +} + - // Modify codec queue to represent change - codec->is_active = active; +static GPtrArray * +swap_pointers(GPtrArray *array, guint old_pos, guint new_pos) +{ + GHashTable *src = g_ptr_array_index(array, old_pos); + GHashTable *dst = g_ptr_array_index(array, new_pos); + + GPtrArray *new_array = g_ptr_array_new(); + for (guint i = 0; i < array->len; ++i) { + if (i == new_pos) + g_ptr_array_add(new_array, src); + else if (i == old_pos) + g_ptr_array_add(new_array, dst); + else + g_ptr_array_add(new_array, g_ptr_array_index(array, i)); + } + + g_ptr_array_free(array, TRUE); + return new_array; } /** * Move codec in list depending on direction and selected codec and * update changes in the daemon list and the configuration files */ -static void codec_move(gboolean moveUp, gpointer data) +static void +codec_move(gboolean move_up, gpointer data) { - GtkTreeIter iter; - GtkTreeIter *iter2; - GtkTreeModel *model; - GtkTreeSelection *selection; - GtkTreePath *treePath; - gchar *path; - // Get view, model and selection of codec store - model = gtk_tree_view_get_model(GTK_TREE_VIEW(codecTreeView)); - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(codecTreeView)); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(codecTreeView)); + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(codecTreeView)); // Find selected iteration and create a copy + GtkTreeIter iter; gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection), &model, &iter); - iter2 = gtk_tree_iter_copy(&iter); + GtkTreeIter *iter_cpy = gtk_tree_iter_copy(&iter); // Find path of iteration - path = gtk_tree_model_get_string_from_iter(GTK_TREE_MODEL (model), &iter); - treePath = gtk_tree_path_new_from_string(path); - gint *indices = gtk_tree_path_get_indices(treePath); - gint pos = indices[0]; + gchar *path = gtk_tree_model_get_string_from_iter(GTK_TREE_MODEL(model), &iter); + GtkTreePath *tree_path = gtk_tree_path_new_from_string(path); + gint *indices = gtk_tree_path_get_indices(tree_path); + const gint pos = indices[0]; // Depending on button direction get new path - if (moveUp) - gtk_tree_path_prev(treePath); + if (move_up) + gtk_tree_path_prev(tree_path); else - gtk_tree_path_next(treePath); + gtk_tree_path_next(tree_path); - gtk_tree_model_get_iter(model, &iter, treePath); + gtk_tree_model_get_iter(model, &iter, tree_path); // Swap iterations if valid - if (gtk_list_store_iter_is_valid (GTK_LIST_STORE (model), &iter)) - gtk_list_store_swap(GTK_LIST_STORE (model), &iter, iter2); + GtkListStore *list_store = GTK_LIST_STORE(model); + if (gtk_list_store_iter_is_valid(list_store, &iter)) { + gtk_list_store_swap(list_store, &iter, iter_cpy); + + const gint dest_pos = move_up ? pos - 1 : pos + 1; + if (dest_pos >= 0 && + dest_pos < gtk_tree_model_iter_n_children(model, NULL)) { + account_t *acc = (account_t *) data; + GPtrArray *vcodecs = dbus_get_video_codecs(acc->accountID); + if (vcodecs) { + // Perpetuate changes in daemon + vcodecs = swap_pointers(vcodecs, pos, dest_pos); + // FIXME: only do this AFTER apply is clicked, not every time we move codecs! + dbus_set_video_codecs(acc->accountID, vcodecs); + g_ptr_array_free(vcodecs, TRUE); + } + } + } // Scroll to new position - gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(codecTreeView), treePath, NULL, FALSE, 0, 0); + gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(codecTreeView), tree_path, NULL, FALSE, 0, 0); // Free resources - gtk_tree_path_free(treePath); - gtk_tree_iter_free(iter2); + gtk_tree_path_free(tree_path); + gtk_tree_iter_free(iter_cpy); g_free(path); - - // Perpetuate changes in codec queue - if (moveUp) - codec_list_move_codec_up(pos, &((account_t*)data)->vcodecs); - else - codec_list_move_codec_down(pos, &((account_t*)data)->vcodecs); } /** * Called from move up codec button signal */ -static void codec_move_up (GtkButton *button UNUSED, gpointer data) +static void +codec_move_up(GtkButton *button UNUSED, gpointer data) { - // Change tree view ordering and get indice changed - codec_move (TRUE, data); + codec_move(TRUE, data); } /** * Called from move down codec button signal */ -static void codec_move_down (GtkButton *button UNUSED, gpointer data) +static void +codec_move_down(GtkButton *button UNUSED, gpointer data) { - // Change tree view ordering and get indice changed - codec_move (FALSE, data); + codec_move(FALSE, data); +} + +static void +bitrate_edited_cb(GtkCellRenderer *renderer UNUSED, gchar *path, gchar *new_text, gpointer data) +{ + // Retrieve userdata + account_t *acc = (account_t*) data; + + if (!acc) { + ERROR("No account selected"); + return; + } + DEBUG("updating bitrate for %s", acc->accountID); + // Get active value and name at iteration + const gint base = 10; + gchar *endptr; + const long long val = strtoll(new_text, &endptr, base); + /* Ignore if it's not a number */ + if (*endptr != '\0') { + WARN("Ignoring characters %s\n", val, endptr); + } else if (val < 0) { + WARN("Ignoring negative bitrate value"); + } else { + // Get path of edited codec + GtkTreePath *tree_path = gtk_tree_path_new_from_string(path); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(codecTreeView)); + GtkTreeIter iter; + gtk_tree_model_get_iter(model, &iter, tree_path); + gtk_tree_path_free(tree_path); + gchar *name = NULL; + gtk_tree_model_get(model, &iter, COLUMN_CODEC_NAME, &name, -1); + + GPtrArray *vcodecs = dbus_get_video_codecs(acc->accountID); + if (!vcodecs) + return; + + gchar *bitrate = g_strdup_printf("%llu", val); + gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLUMN_CODEC_BITRATE, bitrate, -1); + + GHashTable *codec = video_codec_list_get_by_name(vcodecs, name); + if (codec) { + DEBUG("Setting new bitrate %s for %s", bitrate, name); + video_codec_set_bitrate(codec, bitrate); + dbus_set_video_codecs(acc->accountID, vcodecs); + } else { + ERROR("Could not find codec %s", name); + } + g_free(bitrate); + g_ptr_array_free(vcodecs, TRUE); + } } -GtkWidget* videocodecs_box(account_t *a) + +GtkWidget * +videocodecs_box(account_t *acc) { GtkWidget *ret = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); gtk_container_set_border_width(GTK_CONTAINER(ret), 10); @@ -347,7 +436,7 @@ GtkWidget* videocodecs_box(account_t *a) gtk_tree_view_append_column(GTK_TREE_VIEW(codecTreeView), treeViewColumn); // Toggle codec active property on clicked - g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(codec_active_toggled), (gpointer) a); + g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(codec_active_toggled), (gpointer) acc); // Name column renderer = gtk_cell_renderer_text_new(); @@ -356,7 +445,9 @@ GtkWidget* videocodecs_box(account_t *a) // Bitrate column renderer = gtk_cell_renderer_text_new(); - treeViewColumn = gtk_tree_view_column_new_with_attributes(_("Bitrate"), renderer, "text", COLUMN_CODEC_BITRATE, NULL); + g_object_set(renderer, "editable", TRUE, NULL); + g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(bitrate_edited_cb), acc); + treeViewColumn = gtk_tree_view_column_new_with_attributes(_("Bitrate (kbps)"), renderer, "text", COLUMN_CODEC_BITRATE, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(codecTreeView), treeViewColumn); g_object_unref(G_OBJECT(codecStore)); @@ -370,21 +461,22 @@ GtkWidget* videocodecs_box(account_t *a) codecMoveUpButton = gtk_button_new_from_stock(GTK_STOCK_GO_UP); gtk_widget_set_sensitive(GTK_WIDGET(codecMoveUpButton), FALSE); gtk_box_pack_start(GTK_BOX(buttonBox), codecMoveUpButton, FALSE, FALSE, 0); - g_signal_connect(G_OBJECT(codecMoveUpButton), "clicked", G_CALLBACK(codec_move_up), a); + g_signal_connect(G_OBJECT(codecMoveUpButton), "clicked", G_CALLBACK(codec_move_up), acc); codecMoveDownButton = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN); gtk_widget_set_sensitive(GTK_WIDGET(codecMoveDownButton), FALSE); gtk_box_pack_start(GTK_BOX(buttonBox), codecMoveDownButton, FALSE, FALSE, 0); - g_signal_connect(G_OBJECT(codecMoveDownButton), "clicked", G_CALLBACK(codec_move_down), a); + g_signal_connect(G_OBJECT(codecMoveDownButton), "clicked", G_CALLBACK(codec_move_down), acc); - preferences_dialog_fill_codec_list(a); + preferences_dialog_fill_codec_list(acc); return ret; } /* Gets a newly allocated string with the active text, the caller must * free this string */ -static gchar *get_active_text(GtkComboBox *box) +static gchar * +get_active_text(GtkComboBox *box) { gchar *text = NULL; int comboBoxIndex = gtk_combo_box_get_active(box); @@ -397,7 +489,8 @@ static gchar *get_active_text(GtkComboBox *box) } /* Return 0 if string was found in the combo box, != 0 if the string was not found */ -static int set_combo_index_from_str(GtkComboBox *box, const gchar *str, size_t max) +static int +set_combo_index_from_str(GtkComboBox *box, const gchar *str, size_t max) { g_assert(str); @@ -406,7 +499,7 @@ static int set_combo_index_from_str(GtkComboBox *box, const gchar *str, size_t m unsigned idx = 0; gtk_tree_model_get_iter_first(model, &iter); do { - gchar *boxstr; + gchar *boxstr = 0; gtk_tree_model_get(model, &iter, 0, &boxstr, -1); if (boxstr && !g_strcmp0(boxstr, str)) break; @@ -438,10 +531,10 @@ preferences_dialog_fill_video_input_device_rate_list() // Call dbus to retreive list if (dev && chan && size) { - list = dbus_get_video_input_device_rate_list(dev, chan, size); - g_free(size); - g_free(chan); - g_free(dev); + list = dbus_get_video_device_rate_list(dev, chan, size); + g_free(size); + g_free(chan); + g_free(dev); } // For each device name included in list @@ -453,11 +546,11 @@ preferences_dialog_fill_video_input_device_rate_list() } g_strfreev(list); - gchar *rate = dbus_get_video_input_device_rate(); + gchar *rate = dbus_get_active_video_device_rate(); if (!rate || !*rate || set_combo_index_from_str(GTK_COMBO_BOX(v4l2Rate), rate, c)) { // if setting is invalid, choose first entry gtk_combo_box_set_active(GTK_COMBO_BOX(v4l2Rate), 0); - dbus_set_video_input_rate(get_active_text(GTK_COMBO_BOX(v4l2Rate))); + dbus_set_active_video_device_rate(get_active_text(GTK_COMBO_BOX(v4l2Rate))); } g_free(rate); } else @@ -473,7 +566,7 @@ select_video_input_device_rate_cb(GtkComboBox* comboBox, gpointer data UNUSED) { gchar *str = get_active_text(comboBox); if (str) - dbus_set_video_input_rate(str); + dbus_set_active_video_device_rate(str); g_free(str); } @@ -483,8 +576,6 @@ select_video_input_device_rate_cb(GtkComboBox* comboBox, gpointer data UNUSED) static void preferences_dialog_fill_video_input_device_size_list() { - GtkTreeIter iter; - if (v4l2SizeList) gtk_list_store_clear(v4l2SizeList); @@ -494,7 +585,7 @@ preferences_dialog_fill_video_input_device_size_list() gchar** list = NULL; // Call dbus to retrieve list if (dev && chan) { - list = dbus_get_video_input_device_size_list(dev, chan); + list = dbus_get_video_device_size_list(dev, chan); g_free(chan); g_free(dev); } @@ -503,15 +594,16 @@ preferences_dialog_fill_video_input_device_size_list() // For each device name included in list gint c = 0; for (gchar **tmp = list; *tmp; c++, tmp++) { + GtkTreeIter iter; gtk_list_store_append(v4l2SizeList, &iter); gtk_list_store_set(v4l2SizeList, &iter, 0, *tmp, 1, c, -1); } g_strfreev(list); - gchar *size = dbus_get_video_input_device_size(); + gchar *size = dbus_get_active_video_device_size(); if (!size || !*size || set_combo_index_from_str(GTK_COMBO_BOX(v4l2Size), size, c)) { // if setting is invalid, choose first entry gtk_combo_box_set_active(GTK_COMBO_BOX(v4l2Size), 0); - dbus_set_video_input_size(get_active_text(GTK_COMBO_BOX(v4l2Size))); + dbus_set_active_video_device_size(get_active_text(GTK_COMBO_BOX(v4l2Size))); } g_free(size); } else @@ -526,7 +618,7 @@ select_video_input_device_size_cb(GtkComboBox* comboBox, gpointer data UNUSED) { gchar *str = get_active_text(comboBox); if (str) { - dbus_set_video_input_size(str); + dbus_set_active_video_device_size(str); preferences_dialog_fill_video_input_device_rate_list(); g_free(str); } @@ -546,7 +638,7 @@ preferences_dialog_fill_video_input_device_channel_list() gchar **list = NULL; // Call dbus to retrieve list if (dev) { - list = dbus_get_video_input_device_channel_list(dev); + list = dbus_get_video_device_channel_list(dev); g_free(dev); } @@ -559,11 +651,11 @@ preferences_dialog_fill_video_input_device_channel_list() gtk_list_store_set(v4l2ChannelList, &iter, 0, *tmp, 1, c, -1); } g_strfreev(list); - gchar *channel = dbus_get_video_input_device_channel(); + gchar *channel = dbus_get_active_video_device_channel(); if (!channel || !*channel || set_combo_index_from_str(GTK_COMBO_BOX(v4l2Channel), channel, c)) { // if setting is invalid, choose first entry gtk_combo_box_set_active(GTK_COMBO_BOX(v4l2Channel), 0); - dbus_set_video_input_device_channel(get_active_text(GTK_COMBO_BOX(v4l2Channel))); + dbus_set_active_video_device_channel(get_active_text(GTK_COMBO_BOX(v4l2Channel))); } g_free(channel); } else @@ -578,7 +670,7 @@ select_video_input_device_channel_cb(GtkComboBox* comboBox, gpointer data UNUSED { gchar *str = get_active_text(comboBox); if (str) { - dbus_set_video_input_device_channel(str); + dbus_set_active_video_device_channel(str); preferences_dialog_fill_video_input_device_size_list(); g_free(str); } @@ -593,7 +685,7 @@ preferences_dialog_fill_video_input_device_list() gtk_list_store_clear(v4l2DeviceList); // Call dbus to retrieve list - gchar **list = dbus_get_video_input_device_list(); + gchar **list = dbus_get_video_device_list(); if (!list || !*list) { ERROR("No device list found"); return FALSE; @@ -606,11 +698,11 @@ preferences_dialog_fill_video_input_device_list() gtk_list_store_set(v4l2DeviceList, &iter, 0, *tmp, 1, c, -1); } g_strfreev(list); - gchar *dev = dbus_get_video_input_device(); + gchar *dev = dbus_get_active_video_device(); if (!dev || !*dev || set_combo_index_from_str(GTK_COMBO_BOX(v4l2Device), dev, c)) { // if setting is invalid, choose first entry gtk_combo_box_set_active(GTK_COMBO_BOX(v4l2Device), 0); - dbus_set_video_input_device(get_active_text(GTK_COMBO_BOX(v4l2Device))); + dbus_set_active_video_device(get_active_text(GTK_COMBO_BOX(v4l2Device))); } g_free(dev); return TRUE; @@ -626,13 +718,14 @@ select_video_input_device_cb(GtkComboBox* comboBox, gpointer data UNUSED) gchar *str = get_active_text(comboBox); if (str) { DEBUG("Setting video input device to %s", str); - dbus_set_video_input_device(str); + dbus_set_active_video_device(str); preferences_dialog_fill_video_input_device_channel_list(); g_free(str); } } -static void fill_devices(void) +static void +fill_devices() { if (preferences_dialog_fill_video_input_device_list()) { gtk_widget_show_all(v4l2_hbox); @@ -645,13 +738,15 @@ static void fill_devices(void) } } -void video_device_event_cb(DBusGProxy *proxy UNUSED, void * foo UNUSED) +void +video_device_event_cb(DBusGProxy *proxy UNUSED, void * foo UNUSED) { fill_devices(); } -static GtkWidget* v4l2_box() +static GtkWidget * +v4l2_box() { DEBUG("%s", __PRETTY_FUNCTION__); GtkWidget *ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); @@ -727,23 +822,8 @@ static GtkWidget* v4l2_box() } -static gint -on_drawarea_unrealize(GtkWidget *widget, gpointer data) -{ - (void) widget; - (void) data; - if (preview) { - gboolean running = FALSE; - g_object_get(preview, "running", &running, NULL); - if (running) { - video_renderer_stop(preview); - dbus_stop_video_preview(); - } - } - return FALSE; // call other handlers -} - -GtkWidget* create_video_configuration() +GtkWidget * +create_video_configuration() { // Main widget GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); @@ -764,12 +844,16 @@ GtkWidget* create_video_configuration() gnome_main_section_new_with_table(_("Preview"), &frame, &table, 1, 2); gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); - preview_button = gtk_button_new_with_mnemonic(_("_Start")); + const gboolean started = dbus_has_video_preview_started(); + + preview_button = gtk_toggle_button_new_with_mnemonic(started ? _(PREVIEW_STOP_STR) : _(PREVIEW_START_STR)); gtk_widget_set_size_request(preview_button, 80, 30); gtk_table_attach(GTK_TABLE(table), preview_button, 0, 1, 0, 1, 0, 0, 0, 6); - g_signal_connect(G_OBJECT(preview_button), "clicked", - G_CALLBACK(preview_button_clicked), NULL); gtk_widget_show(GTK_WIDGET(preview_button)); + if (started) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(preview_button), TRUE); + g_signal_connect(G_OBJECT(preview_button), "toggled", + G_CALLBACK(preview_button_toggled), NULL); gchar **list = dbus_get_call_list(); gboolean active_call; @@ -781,20 +865,6 @@ GtkWidget* create_video_configuration() if (active_call) gtk_widget_set_sensitive(GTK_WIDGET(preview_button), FALSE); - if (!try_clutter_init()) - return NULL; - - drawarea = gtk_clutter_embed_new(); - gtk_widget_set_size_request(drawarea, drawWidth, drawHeight); - gtk_table_attach(GTK_TABLE(table), drawarea, 0, 1, 1, 2, 0, 0, 0, 6); - if (!gtk_clutter_embed_get_stage(GTK_CLUTTER_EMBED(drawarea))) { - DEBUG("Could not get stage, destroying"); - gtk_widget_destroy(drawarea); - drawarea = NULL; - } - g_signal_connect(drawarea, "unrealize", G_CALLBACK(on_drawarea_unrealize), - NULL); - gtk_widget_show_all(vbox); // get devices list from daemon *after* showing all widgets diff --git a/gnome/src/config/videoconf.h b/gnome/src/config/videoconf.h index 25cda074499c3bfacefc69861f406dbe43f556e3..b8591c7842df094b1179ba9dbe4f47b722b3317d 100644 --- a/gnome/src/config/videoconf.h +++ b/gnome/src/config/videoconf.h @@ -36,10 +36,8 @@ GtkWidget* create_video_configuration(); GtkWidget* videocodecs_box(); -void video_preview_started_cb(DBusGProxy *proxy, gint OUT_width, - gint OUT_height, gint OUT_shmId, gint OUT_semId, - gint OUT_videoBufferSize, GError *error, - gpointer userdata); void video_device_event_cb(DBusGProxy *proxy, void * foo); +void update_preview_button_label(); +void set_preview_button_sensitivity(gboolean sensitive); #endif // __VIDEO_CONF_H__ diff --git a/gnome/src/config/zrtpadvanceddialog.c b/gnome/src/config/zrtpadvanceddialog.c index 8b1090655239eb32a19fac11c086d45babc3a6ad..be1b190d9634c55fcfd3e1c78b579863b801e947 100644 --- a/gnome/src/config/zrtpadvanceddialog.c +++ b/gnome/src/config/zrtpadvanceddialog.c @@ -33,7 +33,7 @@ #include "str_utils.h" #include "mainwindow.h" #include "zrtpadvanceddialog.h" -#include "sflphone_const.h" +#include "account_schema.h" #include "utils.h" void show_advanced_zrtp_options(account_t *account) @@ -44,10 +44,10 @@ void show_advanced_zrtp_options(account_t *account) gboolean curDisplaySasOnce = FALSE; if (account != NULL) { - curHelloEnabled = utf8_case_equal(account_lookup(account, ACCOUNT_ZRTP_HELLO_HASH), "true"); - curSasConfirm = utf8_case_equal(account_lookup(account, ACCOUNT_ZRTP_DISPLAY_SAS), "true"); - curZrtpNotSuppOther = utf8_case_equal(account_lookup(account, ACCOUNT_ZRTP_NOT_SUPP_WARNING), "true"); - curDisplaySasOnce = utf8_case_equal(account_lookup(account, ACCOUNT_DISPLAY_SAS_ONCE), "true"); + curHelloEnabled = utf8_case_equal(account_lookup(account, CONFIG_ZRTP_HELLO_HASH), "true"); + curSasConfirm = utf8_case_equal(account_lookup(account, CONFIG_ZRTP_DISPLAY_SAS), "true"); + curZrtpNotSuppOther = utf8_case_equal(account_lookup(account, CONFIG_ZRTP_NOT_SUPP_WARNING), "true"); + curDisplaySasOnce = utf8_case_equal(account_lookup(account, CONFIG_ZRTP_DISPLAY_SAS_ONCE), "true"); } GtkDialog *securityDialog = GTK_DIALOG(gtk_dialog_new_with_buttons(_("ZRTP Options"), @@ -92,16 +92,16 @@ void show_advanced_zrtp_options(account_t *account) gtk_container_set_border_width(GTK_CONTAINER(tableZrtp), 10); if (gtk_dialog_run(GTK_DIALOG(securityDialog)) == GTK_RESPONSE_ACCEPT) { - account_replace(account, ACCOUNT_ZRTP_DISPLAY_SAS, + account_replace(account, CONFIG_ZRTP_DISPLAY_SAS, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(enableSASConfirm)) ? "true": "false"); - account_replace(account, ACCOUNT_DISPLAY_SAS_ONCE, + account_replace(account, CONFIG_ZRTP_DISPLAY_SAS_ONCE, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(displaySasOnce)) ? "true": "false"); - account_replace(account, ACCOUNT_ZRTP_HELLO_HASH, + account_replace(account, CONFIG_ZRTP_HELLO_HASH, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(enableHelloHash)) ? "true": "false"); - account_replace(account, ACCOUNT_ZRTP_NOT_SUPP_WARNING, + account_replace(account, CONFIG_ZRTP_NOT_SUPP_WARNING, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(enableZrtpNotSuppOther)) ? "true": "false"); } @@ -114,7 +114,7 @@ void show_advanced_sdes_options(account_t *account) gboolean rtpFallback = FALSE; if (account != NULL) - rtpFallback = utf8_case_equal(account_lookup(account, ACCOUNT_SRTP_RTP_FALLBACK), "true"); + rtpFallback = utf8_case_equal(account_lookup(account, CONFIG_SRTP_RTP_FALLBACK), "true"); GtkDialog *securityDialog = GTK_DIALOG(gtk_dialog_new_with_buttons(_("SDES Options"), GTK_WINDOW(get_main_window()), @@ -142,7 +142,7 @@ void show_advanced_sdes_options(account_t *account) gtk_container_set_border_width(GTK_CONTAINER(sdesTable), 10); if (gtk_dialog_run(GTK_DIALOG(securityDialog)) == GTK_RESPONSE_ACCEPT) { - account_replace(account, ACCOUNT_SRTP_RTP_FALLBACK, + account_replace(account, CONFIG_SRTP_RTP_FALLBACK, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(enableRtpFallback)) ? "true": "false"); } diff --git a/gnome/src/contacts/Makefile.am b/gnome/src/contacts/Makefile.am index 6154033b906d9217f6a3b75965f8ce34cf4d5c3d..a026b0136dc9c0ab939b728232210473bbb31785 100644 --- a/gnome/src/contacts/Makefile.am +++ b/gnome/src/contacts/Makefile.am @@ -20,9 +20,9 @@ libcontacts_la_SOURCES = \ searchbar.h libcontacts_la_LIBS = $(DBUSGLIB_LIBS) $(LIBNOTIFY_LIBS) \ - $(GTK_LIBS) $(GLIB_LIBS) $(WEBKIT_LIBS) \ + $(GTK_LIBS) $(GLIB_LIBS) \ $(GCONF_LDFLAGS) -ldl libcontacts_la_CFLAGS = $(DBUSGLIB_CFLAGS) $(LIBNOTIFY_CFLAGS) \ - $(GTK_CFLAGS) $(GLIB_CFLAGS) $(WEBKIT_CFLAGS) \ + $(GTK_CFLAGS) $(GLIB_CFLAGS) \ $(GCONF_CFLAGS) diff --git a/gnome/src/contacts/calllist.h b/gnome/src/contacts/calllist.h index a16fd0d3928600ef556ecc694a634e28da54300e..b912ad3f8153720bb487d7519450a4e400612f05 100644 --- a/gnome/src/contacts/calllist.h +++ b/gnome/src/contacts/calllist.h @@ -48,6 +48,7 @@ typedef struct { GtkWidget* view; GtkWidget* tree; GtkWidget* searchbar; + GtkWidget* mainwidget; // Calllist vars GQueue* callQueue; diff --git a/gnome/src/contacts/calltab.c b/gnome/src/contacts/calltab.c index 2dea9a55a67c593e91c44c8ecdf75b023e9e972e..741830918a6a803643fbfe6463ba4911e22b9bb2 100644 --- a/gnome/src/contacts/calltab.c +++ b/gnome/src/contacts/calltab.c @@ -45,6 +45,7 @@ calltab_t* calltab_init(gboolean searchbar_type, const gchar * const name) ret->callQueue = g_queue_new(); ret->selectedCall = NULL; + ret->mainwidget = NULL; return ret; } diff --git a/gnome/src/contacts/calltree.c b/gnome/src/contacts/calltree.c index 7ecd81c58491d3aeab867b24713bc021018d9987..bc750a607357786b153a4670ef51ac59c9ae390a 100644 --- a/gnome/src/contacts/calltree.c +++ b/gnome/src/contacts/calltree.c @@ -30,9 +30,14 @@ * as that of the covered work. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "calllist.h" #include "calltree.h" #include "str_utils.h" +#include "account_schema.h" #include <string.h> #include <stdlib.h> #include <gtk/gtk.h> @@ -49,7 +54,6 @@ #include "calltree.h" #include "uimanager.h" #include "actions.h" -#include "imwindow.h" #include "searchbar.h" #if !GLIB_CHECK_VERSION(2, 30, 0) @@ -230,13 +234,13 @@ row_single_click(GtkTreeView *tree_view UNUSED, void * data UNUSED) DEBUG("AccountID %s", selectedCall->_accountID); if (account_details != NULL) { - displaySasOnce = g_hash_table_lookup(account_details->properties, ACCOUNT_DISPLAY_SAS_ONCE); + displaySasOnce = g_hash_table_lookup(account_details->properties, CONFIG_ZRTP_DISPLAY_SAS_ONCE); DEBUG("Display SAS once %s", displaySasOnce); } else { GHashTable *properties = sflphone_get_ip2ip_properties(); if (properties != NULL) { - displaySasOnce = g_hash_table_lookup(properties, ACCOUNT_DISPLAY_SAS_ONCE); + displaySasOnce = g_hash_table_lookup(properties, CONFIG_ZRTP_DISPLAY_SAS_ONCE); DEBUG("IP2IP displaysasonce %s", displaySasOnce); } } @@ -345,7 +349,7 @@ calltree_display_call_info(callable_obj_t * call, CallDisplayType display_type, break; case DISPLAY_TYPE_STATE_CODE : if (video_codec && *video_codec) - codec = g_strconcat(audio_codec, "/", video_codec, NULL); + codec = g_strconcat(audio_codec, " ", video_codec, NULL); else codec = g_strdup(audio_codec); @@ -595,13 +599,13 @@ update_call(GtkTreeModel *model, GtkTreePath *path UNUSED, GtkTreeIter *iter, gp account = account_list_get_by_id(call->_accountID); if (account != NULL) { - srtp_enabled = account_lookup(account, ACCOUNT_SRTP_ENABLED); - display_sas = utf8_case_equal(account_lookup(account, ACCOUNT_ZRTP_DISPLAY_SAS), "true"); + srtp_enabled = account_lookup(account, CONFIG_SRTP_ENABLE); + display_sas = utf8_case_equal(account_lookup(account, CONFIG_ZRTP_DISPLAY_SAS), "true"); } else { GHashTable * properties = sflphone_get_ip2ip_properties(); if (properties != NULL) { - srtp_enabled = g_hash_table_lookup(properties, ACCOUNT_SRTP_ENABLED); - display_sas = utf8_case_equal(g_hash_table_lookup(properties, ACCOUNT_ZRTP_DISPLAY_SAS), "true"); + srtp_enabled = g_hash_table_lookup(properties, CONFIG_SRTP_ENABLE); + display_sas = utf8_case_equal(g_hash_table_lookup(properties, CONFIG_ZRTP_DISPLAY_SAS), "true"); } } @@ -647,7 +651,10 @@ update_call(GtkTreeModel *model, GtkTreePath *path UNUSED, GtkTreeIter *iter, gp pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/ring.svg", NULL); break; case CALL_STATE_CURRENT: - pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL); + if (dbus_get_is_recording(call)) + pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/icon_rec.svg", NULL); + else + pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL); break; case CALL_STATE_DIALING: pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/dial.svg", NULL); @@ -661,9 +668,6 @@ update_call(GtkTreeModel *model, GtkTreePath *path UNUSED, GtkTreeIter *iter, gp case CALL_STATE_TRANSFER: pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/transfer.svg", NULL); break; - case CALL_STATE_RECORD: - pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/icon_rec.svg", NULL); - break; default: WARN("Update calltree - Should not happen!"); } @@ -762,8 +766,8 @@ void calltree_add_call(calltab_t* tab, callable_obj_t * call, GtkTreeIter *paren account_details = account_list_get_by_id(call->_accountID); if (account_details) { - srtp_enabled = g_hash_table_lookup(account_details->properties, ACCOUNT_SRTP_ENABLED); - key_exchange = g_hash_table_lookup(account_details->properties, ACCOUNT_KEY_EXCHANGE); + srtp_enabled = g_hash_table_lookup(account_details->properties, CONFIG_SRTP_ENABLE); + key_exchange = g_hash_table_lookup(account_details->properties, CONFIG_SRTP_KEY_EXCHANGE); } } @@ -782,15 +786,15 @@ void calltree_add_call(calltab_t* tab, callable_obj_t * call, GtkTreeIter *paren break; case CALL_STATE_CURRENT: // If the call has been initiated by a another client and, when we start, it is already current - pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL); + if (dbus_get_is_recording(call)) + pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/icon_rec.svg", NULL); + else + pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL); break; case CALL_STATE_HOLD: // If the call has been initiated by a another client and, when we start, it is already current pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/hold.svg", NULL); break; - case CALL_STATE_RECORD: - pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/icon_rec.svg", NULL); - break; case CALL_STATE_FAILURE: // If the call has been initiated by a another client and, when we start, it is already current pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/fail.svg", NULL); @@ -963,7 +967,7 @@ void calltree_add_conference_to_current_calls(conference_obj_t* conf) if (!account_details) ERROR("Could not find account %s in account list", call->_accountID); else - srtp_enabled = g_hash_table_lookup(account_details->properties, ACCOUNT_SRTP_ENABLED); + srtp_enabled = g_hash_table_lookup(account_details->properties, CONFIG_SRTP_ENABLE); if (utf8_case_equal(srtp_enabled, "true")) { DEBUG("SRTP enabled for participant %s", call_id); @@ -1124,9 +1128,15 @@ void calltree_display(calltab_t *tab) } else ERROR("Not a valid call tab (%d, %s)", __LINE__, __FILE__); - gtk_widget_hide(active_calltree_tab->tree); + if (active_calltree_tab->mainwidget) + gtk_widget_hide(active_calltree_tab->mainwidget); + else + gtk_widget_hide(active_calltree_tab->tree); active_calltree_tab = tab; - gtk_widget_show(active_calltree_tab->tree); + if (active_calltree_tab->mainwidget) + gtk_widget_show(active_calltree_tab->mainwidget); + else + gtk_widget_show(active_calltree_tab->tree); GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(active_calltree_tab->view)); g_signal_emit_by_name(sel, "changed"); diff --git a/gnome/src/contacts/searchbar.c b/gnome/src/contacts/searchbar.c index a19806425dcf9e7c79d7a44d1f6d890bd5b915dc..2cdb299ebea0490f7bdaf2fa21abba059c615fe3 100644 --- a/gnome/src/contacts/searchbar.c +++ b/gnome/src/contacts/searchbar.c @@ -76,7 +76,8 @@ void searchbar_entry_changed(GtkEntry* entry UNUSED, gchar* arg1 UNUSED, gpointe static gchar *get_combobox_active_text(GtkWidget *widget) { GtkTreeIter iter; - gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter); + if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter)) + return NULL; GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); gchar *string = NULL; /* this will return a strdup'd string of the text for the active @@ -128,7 +129,6 @@ void update_searchbar_addressbook_list() // store the current active text gchar *activeText = get_combobox_active_text(cbox); - if (activeText == NULL) activeText = g_strdup(""); diff --git a/gnome/src/dbus/Makefile.am b/gnome/src/dbus/Makefile.am index 354703141106594d5237163c61c3300452cfa4a5..7da5c9afa2537a6b799de63f625d6708b529fadb 100644 --- a/gnome/src/dbus/Makefile.am +++ b/gnome/src/dbus/Makefile.am @@ -32,11 +32,11 @@ libdbus_la_SOURCES= \ $(BUILT_SOURCES) libdbus_la_LDFLAGS= $(DBUSGLIB_LDFLAGS) $(LIBNOTIFY_LDFLAGS) \ - $(GTK_LDFLAGS) $(GLIB_LDFLAGS) $(WEBKIT_LDFLAGS) \ + $(GTK_LDFLAGS) $(GLIB_LDFLAGS) \ $(GCONF_LDFLAGS) libdbus_la_CFLAGS= $(DBUSGLIB_CFLAGS) $(LIBNOTIFY_CFLAGS) \ - $(GTK_CFLAGS) $(GLIB_CFLAGS) $(WEBKIT_CFLAGS) \ + $(GTK_CFLAGS) $(GLIB_CFLAGS) \ $(GCONF_CFLAGS) EXTRA_DIST= marshaller.list diff --git a/gnome/src/dbus/callmanager-introspec.xml b/gnome/src/dbus/callmanager-introspec.xml index a85e77732a1c548221b6d5d19eca3e0d06eb667a..07630ac456e632f1bc898bd6f8fb6a94bf714681 100644 --- a/gnome/src/dbus/callmanager-introspec.xml +++ b/gnome/src/dbus/callmanager-introspec.xml @@ -514,8 +514,7 @@ <li>BUSY</li> <li>FAILURE: Error when processing a call</li> <li>HOLD</li> - <li>UNHOLD_CURRENT</li> - <li>UNHOLD_RECORD</li> + <li>UNHOLD</li> </ul> </tp:docstring> </arg> diff --git a/gnome/src/dbus/configurationmanager-introspec.xml b/gnome/src/dbus/configurationmanager-introspec.xml index a3de1b205306e61f2023c81ee2d42365d0f62485..3f785278fe528000caa7aa29681d1abece1e77f9 100644 --- a/gnome/src/dbus/configurationmanager-introspec.xml +++ b/gnome/src/dbus/configurationmanager-introspec.xml @@ -334,6 +334,16 @@ </arg> </method> + <method name="getRingtoneList" tp:name-for-bindings="getRingtoneList"> + <tp:added version="1.1.1"/> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="MapStringString"/> + <tp:docstring> + Get a map [Path,Filename] of available ringtone. Note, set the ringtone with the complete path + </tp:docstring> + <arg type="a{ss}" name="list" direction="out"> + </arg> + </method> + <method name="setEchoCancelState" tp:name-for-bindings="setEchoCancelState"> <arg type="s" name="state" direction="in"> </arg> diff --git a/gnome/src/dbus/dbus.c b/gnome/src/dbus/dbus.c index 010784fe1ed3aa132205f89130c46d11ba7af388..5a838d1913448f97b8ffa48b4e527ee124b389b7 100644 --- a/gnome/src/dbus/dbus.c +++ b/gnome/src/dbus/dbus.c @@ -51,18 +51,18 @@ #include "assistant.h" #include "accountlist.h" #include "accountlistconfigdialog.h" +#include "messaging/message_tab.h" #include "dbus.h" #include "actions.h" #include "unused.h" -#include "widget/imwidget.h" - #ifdef SFL_VIDEO -#include "video/video_renderer.h" #include "config/videoconf.h" +#include "video/video_callbacks.h" #endif #include "eel-gconf-extensions.h" +#include "account_schema.h" #include "mainwindow.h" #ifdef SFL_VIDEO @@ -148,20 +148,17 @@ voice_mail_cb(DBusGProxy *proxy UNUSED, const gchar *accountID, guint nb, static void incoming_message_cb(DBusGProxy *proxy UNUSED, const gchar *callID UNUSED, - const gchar *from, const gchar *msg, void *foo UNUSED) + const gchar *from UNUSED, const gchar *msg, void *foo UNUSED) { // do not display message if instant messaging is disabled if (eel_gconf_key_exists(INSTANT_MESSAGING_ENABLED) && !eel_gconf_get_integer(INSTANT_MESSAGING_ENABLED)) return; - GtkWidget **widget; - gchar *id; callable_obj_t *call = calllist_get_call(current_calls_tab, callID); if (call) { - widget = &call->_im_widget; - id = call->_callID; + new_text_message(call,msg); } else { conference_obj_t *conf = conferencelist_get(current_calls_tab, callID); if (!conf) { @@ -169,14 +166,8 @@ incoming_message_cb(DBusGProxy *proxy UNUSED, const gchar *callID UNUSED, return; } - widget = &conf->_im_widget; - id = conf->_confID; + new_text_message_conf(conf,msg,from); } - - if (!*widget) - *widget = im_widget_display(id); - - im_widget_add_message(IM_WIDGET(*widget), from, msg, 0); } /** @@ -202,19 +193,12 @@ process_existing_call_state_change(callable_obj_t *c, const gchar *state) calltree_update_call(history_tab, c); status_bar_display_account(); sflphone_hung_up(c); - } - else if (g_strcmp0(state, "UNHOLD_CURRENT") == 0) + } else if (g_strcmp0(state, "UNHOLD") == 0 || g_strcmp0(state, "CURRENT") == 0) sflphone_current(c); - else if (g_strcmp0(state, "UNHOLD_RECORD") == 0) - sflphone_record(c); else if (g_strcmp0(state, "HOLD") == 0) sflphone_hold(c); else if (g_strcmp0(state, "RINGING") == 0) sflphone_ringing(c); - else if (g_strcmp0(state, "CURRENT") == 0) - sflphone_current(c); - else if (g_strcmp0(state, "RECORD") == 0) - sflphone_record(c); else if (g_strcmp0(state, "FAILURE") == 0) sflphone_fail(c); else if (g_strcmp0(state, "BUSY") == 0) @@ -241,9 +225,7 @@ process_nonexisting_call_state_change(const gchar *callID, const gchar *state) // The callID is unknown, treat it like a new call // If it were an incoming call, we won't be here // It means that a new call has been initiated with an other client (cli for instance) - if (g_strcmp0(state, "RINGING") == 0 || - g_strcmp0(state, "CURRENT") == 0 || - g_strcmp0(state, "RECORD")) { + if (g_strcmp0(state, "RINGING") == 0 || g_strcmp0(state, "CURRENT") == 0) { DEBUG("New ringing call! accountID: %s", callID); @@ -271,13 +253,12 @@ call_state_cb(DBusGProxy *proxy UNUSED, const gchar *callID, } static void -toggle_im(conference_obj_t *conf, gboolean activate) +toggle_im(conference_obj_t *conf, gboolean activate UNUSED) { for (GSList *p = conf->participant_list; p; p = g_slist_next(p)) { - callable_obj_t *call = calllist_get_call(current_calls_tab, p->data); + //callable_obj_t *call = calllist_get_call(current_calls_tab, p->data); - if (call) - im_widget_update_state(IM_WIDGET(call->_im_widget), activate); + /*TODO elepage(2012) Implement IM messaging toggle here*/ } } @@ -340,10 +321,10 @@ conference_created_cb(DBusGProxy *proxy UNUSED, const gchar *confID, void *foo U for (gchar **part = participants; part && *part; ++part) { callable_obj_t *call = calllist_get_call(current_calls_tab, *part); - im_widget_update_state(IM_WIDGET(call->_im_widget), FALSE); + /*TODO elepage (2012) implement merging IM conversation here*/ // if one of these participants is currently recording, the whole conference will be recorded - if (call->_state == CALL_STATE_RECORD) + if (dbus_get_is_recording(call)) new_conf->_state = CONFERENCE_STATE_ACTIVE_ATTACHED_RECORD; call->_historyConfID = g_strdup(confID); @@ -370,15 +351,12 @@ conference_removed_cb(DBusGProxy *proxy UNUSED, const gchar *confID, calltree_remove_conference(current_calls_tab, c); - im_widget_update_state(IM_WIDGET(c->_im_widget), FALSE); + /*TODO elepage(2012) implement unmerging of IM here*/ // remove all participants for this conference for (GSList *p = c->participant_list; p; p = g_slist_next(p)) { - callable_obj_t *call = calllist_get_call(current_calls_tab, p->data); - - if (call) { - im_widget_update_state(IM_WIDGET(call->_im_widget), TRUE); - } + //callable_obj_t *call = calllist_get_call(current_calls_tab, p->data); + /*TODO elepage(2012) implement unmerging of IM here*/ } conferencelist_remove(current_calls_tab, c->_confID); @@ -464,7 +442,7 @@ stun_status_failure_cb(DBusGProxy *proxy UNUSED, const gchar *accountID, void *f // Disable STUN for the account that tried to create the STUN transport account_t *account = account_list_get_by_id(accountID); if (account) { - account_replace(account, ACCOUNT_SIP_STUN_ENABLED, "false"); + account_replace(account, CONFIG_STUN_ENABLE, "false"); dbus_set_account_details(account); } } @@ -703,47 +681,52 @@ gboolean dbus_connect(GError **error) dbus_g_object_register_marshaller(g_cclosure_user_marshal_VOID__INT, G_TYPE_NONE, G_TYPE_INT, G_TYPE_INVALID); - /* Register STRING STRING STRING Marshaller */ - dbus_g_object_register_marshaller( - g_cclosure_user_marshal_VOID__STRING_STRING_STRING, G_TYPE_NONE, - G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); - - /* Register STRING STRING INT Marshaller */ - dbus_g_object_register_marshaller( - g_cclosure_user_marshal_VOID__STRING_STRING_INT, G_TYPE_NONE, - G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INVALID); + /* Register INT INT Marshaller */ + dbus_g_object_register_marshaller(g_cclosure_user_marshal_VOID__INT_INT, + G_TYPE_NONE, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID); - /* Register STRING STRING Marshaller */ - dbus_g_object_register_marshaller( - g_cclosure_user_marshal_VOID__STRING_STRING, G_TYPE_NONE, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_INVALID); + /* Register STRING Marshaller */ + dbus_g_object_register_marshaller(g_cclosure_user_marshal_VOID__STRING, + G_TYPE_NONE, G_TYPE_STRING, G_TYPE_INVALID); /* Register STRING INT Marshaller */ dbus_g_object_register_marshaller(g_cclosure_user_marshal_VOID__STRING_INT, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INVALID); - /* Register INT INT Marshaller */ - dbus_g_object_register_marshaller(g_cclosure_user_marshal_VOID__INT_INT, - G_TYPE_NONE, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID); - /* Register STRING DOUBLE Marshaller */ dbus_g_object_register_marshaller( g_cclosure_user_marshal_VOID__STRING_DOUBLE, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_INVALID); - /* Register STRING Marshaller */ - dbus_g_object_register_marshaller(g_cclosure_user_marshal_VOID__STRING, - G_TYPE_NONE, G_TYPE_STRING, G_TYPE_INVALID); + /* Register STRING STRING Marshaller */ + dbus_g_object_register_marshaller( + g_cclosure_user_marshal_VOID__STRING_STRING, G_TYPE_NONE, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_INVALID); + + /* Register STRING INT INT Marshaller */ + dbus_g_object_register_marshaller( + g_cclosure_user_marshal_VOID__STRING_INT_INT, G_TYPE_NONE, + G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID); /* Register STRING STRING BOOL Marshaller */ dbus_g_object_register_marshaller( g_cclosure_user_marshal_VOID__STRING_STRING_BOOL, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INVALID); - /* Register STRING Marshaller */ - dbus_g_object_register_marshaller(g_cclosure_user_marshal_VOID__STRING, - G_TYPE_NONE, G_TYPE_STRING, G_TYPE_INVALID); + /* Register STRING STRING INT Marshaller */ + dbus_g_object_register_marshaller( + g_cclosure_user_marshal_VOID__STRING_STRING_INT, G_TYPE_NONE, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INVALID); + + /* Register STRING STRING STRING Marshaller */ + dbus_g_object_register_marshaller( + g_cclosure_user_marshal_VOID__STRING_STRING_STRING, G_TYPE_NONE, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); + /* Register STRING STRING INT INT Marshaller */ + dbus_g_object_register_marshaller( + g_cclosure_user_marshal_VOID__STRING_STRING_INT_INT, G_TYPE_NONE, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID); DEBUG("Adding callmanager Dbus signals"); @@ -892,7 +875,6 @@ gboolean dbus_connect(GError **error) const gchar *videocontrols_interface = "org.sflphone.SFLphone.VideoControls"; video_proxy = dbus_g_proxy_new_for_name(connection, dbus_message_bus_name, videocontrols_object_instance, videocontrols_interface); - g_assert(video_proxy != NULL); if (video_proxy == NULL) { ERROR("Error: Failed to connect to %s", videocontrols_object_instance); return FALSE; @@ -902,27 +884,16 @@ gboolean dbus_connect(GError **error) dbus_g_proxy_connect_signal(video_proxy, "deviceEvent", G_CALLBACK(video_device_event_cb), NULL, NULL); - /* Marshaller for INT INT INT INT INT */ - dbus_g_object_register_marshaller( - g_cclosure_user_marshal_VOID__INT_INT_INT_INT_INT, G_TYPE_NONE, - G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID); - - dbus_g_proxy_add_signal(video_proxy, "receivingEvent", G_TYPE_INT, - G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, - G_TYPE_INVALID); - dbus_g_proxy_connect_signal(video_proxy, "receivingEvent", - G_CALLBACK(receiving_video_event_cb), NULL, + dbus_g_proxy_add_signal(video_proxy, "startedDecoding", G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID); + dbus_g_proxy_connect_signal(video_proxy, "startedDecoding", + G_CALLBACK(started_decoding_video_cb), NULL, NULL); - /* Marshaller for INT INT */ - dbus_g_object_register_marshaller(g_cclosure_user_marshal_VOID__INT_INT, - G_TYPE_NONE, G_TYPE_INT, G_TYPE_INT, - G_TYPE_INVALID); - - dbus_g_proxy_add_signal(video_proxy, "stoppedReceivingEvent", - G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID); - dbus_g_proxy_connect_signal(video_proxy, "stoppedReceivingEvent", - G_CALLBACK(stopped_receiving_video_event_cb), + dbus_g_proxy_add_signal(video_proxy, "stoppedDecoding", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_connect_signal(video_proxy, "stoppedDecoding", + G_CALLBACK(stopped_decoding_video_cb), NULL, NULL); #endif @@ -1006,20 +977,22 @@ dbus_stop_recorded_file_playback(const gchar *filepath) check_error(error); } +static void +hang_up_reply_cb(DBusGProxy *proxy UNUSED, GError *error, gpointer userdata UNUSED) +{ + check_error(error); +} + void dbus_hang_up(const callable_obj_t *c) { - GError *error = NULL; - org_sflphone_SFLphone_CallManager_hang_up(call_proxy, c->_callID, &error); - check_error(error); + org_sflphone_SFLphone_CallManager_hang_up_async(call_proxy, c->_callID, hang_up_reply_cb, NULL); } void dbus_hang_up_conference(const conference_obj_t *c) { - GError *error = NULL; - org_sflphone_SFLphone_CallManager_hang_up_conference(call_proxy, c->_confID, &error); - check_error(error); + org_sflphone_SFLphone_CallManager_hang_up_conference_async(call_proxy, c->_confID, hang_up_reply_cb, NULL); } void @@ -1256,33 +1229,21 @@ dbus_audio_codec_list() } #ifdef SFL_VIDEO -gchar ** -dbus_video_codec_list() -{ - GError *error = NULL; - gchar **array = NULL; - org_sflphone_SFLphone_VideoControls_get_codec_list(video_proxy, &array, &error); - check_error(error); - - return array; -} - -gchar ** -dbus_get_active_video_codec_list(const gchar *accountID) +GPtrArray * +dbus_get_video_codecs(const gchar *accountID) { - gchar **array = NULL; GError *error = NULL; - org_sflphone_SFLphone_VideoControls_get_active_codec_list(video_proxy, accountID, &array, &error); + GPtrArray *array = NULL; + org_sflphone_SFLphone_VideoControls_get_codecs(video_proxy, accountID, &array, &error); check_error(error); - return array; } void -dbus_set_active_video_codec_list(const gchar** list, const gchar *accountID) +dbus_set_video_codecs(const gchar *accountID, const GPtrArray *list) { GError *error = NULL; - org_sflphone_SFLphone_VideoControls_set_active_codec_list(video_proxy, list, accountID, &error); + org_sflphone_SFLphone_VideoControls_set_codecs(video_proxy, accountID, list, &error); check_error(error); } #endif @@ -1298,16 +1259,6 @@ dbus_audio_codec_details(int payload) } #ifdef SFL_VIDEO -GHashTable* -dbus_video_codec_details(const gchar *codec) -{ - GError *error = NULL; - GHashTable *details = NULL; - org_sflphone_SFLphone_VideoControls_get_codec_details(video_proxy, - codec, &details, &error); - check_error(error); - return details; -} gchar * dbus_get_current_video_codec_name(const callable_obj_t *c) @@ -1758,180 +1709,135 @@ dbus_get_audio_manager(void) #ifdef SFL_VIDEO gchar * -dbus_get_video_input_device_channel() +dbus_get_active_video_device_channel() { gchar *str = NULL; GError *error = NULL; - org_sflphone_SFLphone_VideoControls_get_input_device_channel(video_proxy, &str, &error); + org_sflphone_SFLphone_VideoControls_get_active_device_channel(video_proxy, &str, &error); check_error(error); return str; } gchar * -dbus_get_video_input_device_size() +dbus_get_active_video_device_size() { gchar *str = NULL; GError *error = NULL; - org_sflphone_SFLphone_VideoControls_get_input_device_size(video_proxy, &str, &error); + org_sflphone_SFLphone_VideoControls_get_active_device_size(video_proxy, &str, &error); check_error(error); return str; } gchar * -dbus_get_video_input_device_rate() +dbus_get_active_video_device_rate() { gchar *str = NULL; GError *error = NULL; - org_sflphone_SFLphone_VideoControls_get_input_device_rate(video_proxy, &str, &error); + org_sflphone_SFLphone_VideoControls_get_active_device_rate(video_proxy, &str, &error); check_error(error); return str; } gchar * -dbus_get_video_input_device() +dbus_get_active_video_device() { gchar *str = NULL; GError *error = NULL; - org_sflphone_SFLphone_VideoControls_get_input_device(video_proxy, &str, &error); + org_sflphone_SFLphone_VideoControls_get_active_device(video_proxy, &str, &error); check_error(error); return str; } -/** - * Set video input device - */ void -dbus_set_video_input_device(const gchar *device) +dbus_set_active_video_device(const gchar *device) { GError *error = NULL; - org_sflphone_SFLphone_VideoControls_set_input_device(video_proxy, device, &error); + org_sflphone_SFLphone_VideoControls_set_active_device(video_proxy, device, &error); check_error(error); } -/** - * Set video input device channel - */ void -dbus_set_video_input_device_channel(const gchar *channel) +dbus_set_active_video_device_channel(const gchar *channel) { GError *error = NULL; - org_sflphone_SFLphone_VideoControls_set_input_device_channel(video_proxy, channel, &error); + org_sflphone_SFLphone_VideoControls_set_active_device_channel(video_proxy, channel, &error); check_error(error); } -/** - * Set video input size - */ void -dbus_set_video_input_size(const gchar *size) +dbus_set_active_video_device_size(const gchar *size) { GError *error = NULL; - org_sflphone_SFLphone_VideoControls_set_input_device_size(video_proxy, size, &error); + org_sflphone_SFLphone_VideoControls_set_active_device_size(video_proxy, size, &error); check_error(error); } -/** - * Set video input rate - */ void -dbus_set_video_input_rate(const gchar *rate) +dbus_set_active_video_device_rate(const gchar *rate) { GError *error = NULL; - org_sflphone_SFLphone_VideoControls_set_input_device_rate(video_proxy, rate, &error); + org_sflphone_SFLphone_VideoControls_set_active_device_rate(video_proxy, rate, &error); check_error(error); } -/** - * Get a list of video input devices - */ gchar ** -dbus_get_video_input_device_list() +dbus_get_video_device_list() { gchar **array = NULL; GError *error = NULL; - if (!org_sflphone_SFLphone_VideoControls_get_input_device_list(video_proxy, &array, &error)) { - if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) - ERROR("Caught remote method (get_video_input_device_list) exception %s: %s", dbus_g_error_get_name(error), error->message); - else - ERROR("Error while calling get_video_input_device_list: %s", error->message); - - g_error_free (error); - } - + org_sflphone_SFLphone_VideoControls_get_device_list(video_proxy, &array, &error); + check_error(error); return array; } /** - * Get a list of inputs supported by the video input device + * Get the list of channels supported by the given device */ gchar ** -dbus_get_video_input_device_channel_list(const gchar *dev) +dbus_get_video_device_channel_list(const gchar *dev) { gchar **array = NULL; GError *error = NULL; - - if (!org_sflphone_SFLphone_VideoControls_get_input_device_channel_list( - video_proxy, dev, &array, &error)) { - if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) - ERROR("Caught remote method (get_video_input_device_channel_list) exception %s: %s", - dbus_g_error_get_name (error), error->message); - else - ERROR("Error while calling get_video_input_device_channel_list: %s", error->message); - - g_error_free(error); - } + org_sflphone_SFLphone_VideoControls_get_device_channel_list(video_proxy, dev, &array, &error); + check_error(error); return array; } /** - * Get a list of resolutions supported by the video input + * Get the list of resolutions supported by the given channel of the given device */ gchar ** -dbus_get_video_input_device_size_list(const gchar *dev, const gchar *channel) +dbus_get_video_device_size_list(const gchar *dev, const gchar *channel) { gchar **array = NULL; GError *error = NULL; - if (!org_sflphone_SFLphone_VideoControls_get_input_device_size_list(video_proxy, dev, channel, &array, &error)) { - if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) - ERROR("Caught remote method (get_video_input_device_size_list) exception %s: %s", dbus_g_error_get_name(error), error->message); - else - ERROR("Error while calling get_video_input_device_size_list: %s", error->message); - - g_error_free (error); - return NULL; - } else - return array; + org_sflphone_SFLphone_VideoControls_get_device_size_list(video_proxy, dev, channel, &array, &error); + check_error(error); + return array; } /** - * Get a list of frame rates supported by the video input resolution + * Get the list of frame rates supported by the given resolution of the given channel of the given device */ gchar ** -dbus_get_video_input_device_rate_list(const gchar *dev, const gchar *channel, const gchar *size) +dbus_get_video_device_rate_list(const gchar *dev, const gchar *channel, const gchar *size) { gchar **array = NULL; GError *error = NULL; - if (!org_sflphone_SFLphone_VideoControls_get_input_device_rate_list(video_proxy, dev, channel, size, &array, &error)) { - if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) - ERROR("Caught remote method (get_video_input_device_rate_list) exception %s: %s", - dbus_g_error_get_name(error), error->message); - else - ERROR("Error while calling get_video_input_device_rate_list: %s", error->message); - g_error_free(error); - return NULL; - } else - return array; + org_sflphone_SFLphone_VideoControls_get_device_rate_list(video_proxy, dev, channel, size, &array, &error); + check_error(error); + return array; } #endif @@ -2219,29 +2125,36 @@ dbus_send_text_message(const gchar *callID, const gchar *message) } #ifdef SFL_VIDEO -void -dbus_start_video_preview() +static void +video_preview_async_cb(DBusGProxy *proxy UNUSED, GError *error, gpointer userdata UNUSED) { - GError *error = NULL; - org_sflphone_SFLphone_VideoControls_start_preview_async(video_proxy, - video_preview_started_cb, - &error); check_error(error); + // Reactivate it now that we're done, D-Bus wise + set_preview_button_sensitivity(TRUE); } -static void preview_stopped_cb() +void +dbus_start_video_preview() { - DEBUG("Video preview has stopped"); + set_preview_button_sensitivity(FALSE); + org_sflphone_SFLphone_VideoControls_start_preview_async(video_proxy, video_preview_async_cb, NULL); } void dbus_stop_video_preview() +{ + set_preview_button_sensitivity(FALSE); + org_sflphone_SFLphone_VideoControls_stop_preview_async(video_proxy, video_preview_async_cb, NULL); +} + +gboolean +dbus_has_video_preview_started() { GError *error = NULL; - org_sflphone_SFLphone_VideoControls_stop_preview_async(video_proxy, - preview_stopped_cb, - &error); + gboolean started = FALSE; + org_sflphone_SFLphone_VideoControls_has_preview_started(video_proxy, &started, &error); check_error(error); + return started; } #endif diff --git a/gnome/src/dbus/dbus.h b/gnome/src/dbus/dbus.h index 588d6997dea261461c62aa53616c3cff571fd161..380f74f3a88e0056b12ef94e08fcba7dab439de1 100644 --- a/gnome/src/dbus/dbus.h +++ b/gnome/src/dbus/dbus.h @@ -194,12 +194,6 @@ void dbus_play_dtmf(const gchar *key); */ GArray *dbus_audio_codec_list(); -/** - * ConfigurationManager - Get the video codecs list - * @return gchar** The list of video codecs - */ -gchar** dbus_video_codec_list(); - /** * ConfigurationManager - Get the audio codec details * @param payload The payload of the audio codec @@ -237,13 +231,16 @@ void dbus_set_active_audio_codec_list(const gchar **list, const gchar *); * ConfigurationManager - Get the list of the audio codecs used for media negotiation * @return gchar** The list of audio codecs */ -gchar **dbus_get_active_video_codec_list(const gchar *accountID); +GPtrArray * +dbus_get_video_codecs(const gchar *accountID); /** * ConfigurationManager - Set the list of audio codecs used for media negociation - * @param list The list of audio codecs + * @param id The accountID + * @param list The list of codecs */ -void dbus_set_active_video_codec_list(const gchar **list, const gchar *); +void +dbus_set_video_codecs(const gchar *id, const GPtrArray *list); /** * CallManager - return the video codec name @@ -380,18 +377,18 @@ gchar *dbus_get_audio_manager(void); */ void dbus_set_audio_manager(const gchar *api); -void dbus_set_video_input_device(const gchar *dev); -void dbus_set_video_input_device_channel(const gchar *channel); -void dbus_set_video_input_size(const gchar *size); -void dbus_set_video_input_rate(const gchar *rate); -gchar *dbus_get_video_input_device(); -gchar *dbus_get_video_input_device_channel(); -gchar *dbus_get_video_input_device_size(); -gchar *dbus_get_video_input_device_rate(); -gchar **dbus_get_video_input_device_list(); -gchar **dbus_get_video_input_device_channel_list(const gchar *dev); -gchar **dbus_get_video_input_device_size_list(const gchar *dev, const gchar *channel); -gchar **dbus_get_video_input_device_rate_list(const gchar *dev, const gchar *channel, const gchar *size); +void dbus_set_active_video_device(const gchar *dev); +void dbus_set_active_video_device_channel(const gchar *channel); +void dbus_set_active_video_device_size(const gchar *size); +void dbus_set_active_video_device_rate(const gchar *rate); +gchar *dbus_get_active_video_device(); +gchar *dbus_get_active_video_device_channel(); +gchar *dbus_get_active_video_device_size(); +gchar *dbus_get_active_video_device_rate(); +gchar **dbus_get_video_device_list(); +gchar **dbus_get_video_device_channel_list(const gchar *dev); +gchar **dbus_get_video_device_size_list(const gchar *dev, const gchar *channel); +gchar **dbus_get_video_device_rate_list(const gchar *dev, const gchar *channel, const gchar *size); /** * ConfigurationManager - Start a tone when a new call is open and no numbers have been dialed @@ -623,6 +620,7 @@ void dbus_stop_recorded_file_playback(const gchar *); void dbus_start_video_preview(); void dbus_stop_video_preview(); +gboolean dbus_has_video_preview_started(); /** * Prevent Gnome Session Manager from entering in screen-saver mode diff --git a/gnome/src/dbus/marshaller.list b/gnome/src/dbus/marshaller.list index 65213afe747b14700814cb54c448f3a6fc7f09a7..84cc4cf1e10f082a39c4d8a4fc8a520c5f0a7cbf 100644 --- a/gnome/src/dbus/marshaller.list +++ b/gnome/src/dbus/marshaller.list @@ -1,11 +1,11 @@ -VOID:STRING,STRING,STRING -VOID:STRING,STRING VOID:INT VOID:INT,INT -VOID:INT,INT,INT,INT,INT +VOID:STRING VOID:STRING,INT VOID:STRING,DOUBLE -VOID:STRING +VOID:STRING,STRING +VOID:STRING,INT,INT VOID:STRING,STRING,BOOL -VOID:STRING, STRING, INT -VOID:INT,INT +VOID:STRING,STRING,INT +VOID:STRING,STRING,STRING +VOID:STRING,STRING,INT,INT diff --git a/gnome/src/dbus/video_controls-introspec.xml b/gnome/src/dbus/video_controls-introspec.xml index b7680f72de901c19fbac6f310f9a0bdb3e94c6e7..ef99d1df7aa39747a7d0fb08f8975a8d72fd5351 100644 --- a/gnome/src/dbus/video_controls-introspec.xml +++ b/gnome/src/dbus/video_controls-introspec.xml @@ -1,33 +1,37 @@ <?xml version="1.0" ?> <node name="/video_controls-introspec" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> <interface name="org.sflphone.SFLphone.VideoControls"> - <!-- Video device methods --> + <!-- Video device methods --> - <method name="getInputDeviceList" tp:name-for-bindings="getInputDeviceList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="getDeviceList" tp:name-for-bindings="getDeviceList"> + <tp:docstring>Returns a list of the detected v4l2 devices</tp:docstring> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> + <arg type="as" name="list" direction="out"> + </arg> + </method> - <method name="getInputDeviceChannelList" tp:name-for-bindings="getInputDeviceChannelList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="s" name="device" direction="in"> - </arg> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="getDeviceChannelList" tp:name-for-bindings="getDeviceChannelList"> + <tp:docstring>Returns a list of the channels available for a given v4l2 device</tp:docstring> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> + <arg type="s" name="device" direction="in"> + </arg> + <arg type="as" name="list" direction="out"> + </arg> + </method> - <method name="getInputDeviceSizeList" tp:name-for-bindings="getInputDeviceSizeList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="s" name="device" direction="in"> - </arg> - <arg type="s" name="channel" direction="in"> - </arg> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="getDeviceSizeList" tp:name-for-bindings="getDeviceSizeList"> + <tp:docstring>Returns a list of the resolutions available for a given channel of a given v4l2 device</tp:docstring> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> + <arg type="s" name="device" direction="in"> + </arg> + <arg type="s" name="channel" direction="in"> + </arg> + <arg type="as" name="list" direction="out"> + </arg> + </method> - <method name="getInputDeviceRateList" tp:name-for-bindings="getInputDeviceRateList"> + <method name="getDeviceRateList" tp:name-for-bindings="getDeviceRateList"> + <tp:docstring>Returns a list of the framerates available for a given resolution of a given channel of a given v4l2 device</tp:docstring> <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> <arg type="s" name="device" direction="in"> </arg> @@ -37,122 +41,114 @@ </arg> <arg type="as" name="list" direction="out"> </arg> - </method> - - <method name="getInputDevice" tp:name-for-bindings="getInputDevice"> - <arg type="s" name="device" direction="out"> - </arg> - </method> - - <method name="getInputDeviceChannel" tp:name-for-bindings="getInputDeviceChannel"> - <arg type="s" name="channel" direction="out"> - </arg> - </method> + </method> - <method name="getInputDeviceSize" tp:name-for-bindings="getInputDeviceSize"> - <arg type="s" name="size" direction="out"> - </arg> - </method> + <method name="getActiveDevice" tp:name-for-bindings="getActiveDevice"> + <arg type="s" name="device" direction="out"> + </arg> + </method> - <method name="getInputDeviceRate" tp:name-for-bindings="getInputDeviceRate"> - <arg type="s" name="rate" direction="out"> - </arg> - </method> + <method name="getActiveDeviceChannel" tp:name-for-bindings="getActiveDeviceChannel"> + <arg type="s" name="channel" direction="out"> + </arg> + </method> - <method name="setInputDevice" tp:name-for-bindings="setInputDevice"> - <arg type="s" name="device" direction="in"> - </arg> - </method> + <method name="getActiveDeviceSize" tp:name-for-bindings="getActiveDeviceSize"> + <arg type="s" name="size" direction="out"> + </arg> + </method> - <method name="setInputDeviceChannel" tp:name-for-bindings="setInputDeviceChannel"> - <arg type="s" name="channel" direction="in"> - </arg> - </method> + <method name="getActiveDeviceRate" tp:name-for-bindings="getActiveDeviceRate"> + <arg type="s" name="rate" direction="out"> + </arg> + </method> - <method name="setInputDeviceSize" tp:name-for-bindings="setInputDeviceSize"> - <arg type="s" name="size" direction="in"> - </arg> - </method> + <method name="setActiveDevice" tp:name-for-bindings="setActiveDevice"> + <arg type="s" name="device" direction="in"> + </arg> + </method> - <method name="setInputDeviceRate" tp:name-for-bindings="setInputDeviceRate"> - <arg type="s" name="rate" direction="in"> - </arg> - </method> + <method name="setActiveDeviceChannel" tp:name-for-bindings="setActiveDeviceChannel"> + <arg type="s" name="channel" direction="in"> + </arg> + </method> - <!-- Video Codec related methods --> + <method name="setActiveDeviceSize" tp:name-for-bindings="setActiveDeviceSize"> + <arg type="s" name="size" direction="in"> + </arg> + </method> - <method name="getCodecList" tp:name-for-bindings="getCodecList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="setActiveDeviceRate" tp:name-for-bindings="setActiveDeviceRate"> + <arg type="s" name="rate" direction="in"> + </arg> + </method> - <method name="getCodecDetails" tp:name-for-bindings="getCodecDetails"> - <arg type="s" name="codec" direction="in"> - </arg> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="MapStringString"/> - <arg type="a{ss}" name="details" direction="out"> - </arg> - </method> + <!-- Video Codec related methods --> - <method name="getActiveCodecList" tp:name-for-bindings="getActiveCodecList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="s" name="accountID" direction="in"> - </arg> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="getCodecs" tp:name-for-bindings="getCodecs"> + <tp:docstring>Gets the hashtable describing all the codecs and their parameters for a given account</tp:docstring> + <arg type="s" name="accountID" direction="in"> + </arg> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorMapStringString"/> + <arg type="aa{ss}" name="details" direction="out"> + </arg> + </method> - <method name="setActiveCodecList" tp:name-for-bindings="setActiveCodecList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="VectorString"/> - <arg type="as" name="list" direction="in"> - </arg> + <method name="setCodecs" tp:name-for-bindings="setCodecs"> + <tp:docstring>Sets a vector of hashtables describing codecs and their parameters for a given account, one hashtable per codec</tp:docstring> <arg type="s" name="accountID" direction="in"> </arg> - </method> - - <method name="startPreview" tp:name-for-bindings="startPreview"> - <arg type="i" name="width" direction="out"> - </arg> - <arg type="i" name="height" direction="out"> - </arg> - <arg type="i" name="shmKey" direction="out"> - </arg> - <arg type="i" name="semKey" direction="out"> + <arg type="aa{ss}" name="details" direction="in"> </arg> - <arg type="i" name="videoBufferSize" direction="out"> - </arg> - </method> + </method> - <method name="stopPreview" tp:name-for-bindings="stopPreview"> - </method> + <method name="startPreview" tp:name-for-bindings="startPreview"> + <tp:docstring> Starts the video preview, which renders the active v4l2 device's video to shared memory. Useful for testing/debugging camera settings</tp:docstring> + </method> - <signal name="deviceEvent" tp:name-for-bindings="deviceEvent"> - </signal> + <method name="stopPreview" tp:name-for-bindings="stopPreview"> + </method> - <signal name="receivingEvent" tp:name-for-bindings="receivingEvent"> - <arg type="i" name="shmKey"> - </arg> - <arg type="i" name="semKey"> - </arg> - <arg type="i" name="videoBufferSize"> - </arg> - <arg type="i" name="destWidth"> - </arg> - <arg type="i" name="destHeight"> - </arg> - </signal> + <method name="hasPreviewStarted" tp:name-for-bindings="hasPreviewStarted"> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="Bool"/> + <arg type="b" name="started" direction="out"> + <tp:docstring>Returns true if the preview has already started, false otherwise</tp:docstring> + </arg> + </method> - <signal name="stoppedReceivingEvent" tp:name-for-bindings="stoppedReceivingEvent"> - <arg type="i" name="shmKey"> - </arg> - <arg type="i" name="semKey"> - </arg> - </signal> + <signal name="deviceEvent" tp:name-for-bindings="deviceEvent"> + <tp:docstring>Signal triggered by changes in the detected v4l2 devices, e.g. a camera being unplugged.</tp:docstring> + </signal> + + <signal name="startedDecoding" tp:name-for-bindings="startedDecoding"> + <tp:docstring>Signal triggered when video is available in a shared memory buffer.</tp:docstring> + <arg type="s" name="id"> + <tp:docstring>The ID of the call associated with the video, or "local" in the case of local video</tp:docstring> + </arg> + <arg type="s" name="shmPath"> + <tp:docstring>The path of the newly created shared memory</tp:docstring> + </arg> + <arg type="i" name="width"> + <tp:docstring>The width of the video in the shared memory</tp:docstring> + </arg> + <arg type="i" name="height"> + <tp:docstring>The height of the video in the shared memory</tp:docstring> + </arg> + </signal> + + <signal name="stoppedDecoding" tp:name-for-bindings="stoppedDecoding"> + <tp:docstring>Signal triggered when video is no longer available in a shared memory buffer.</tp:docstring> + <arg type="s" name="id"> + <tp:docstring>The ID of the call associated with the video, or "local" in the case of local video</tp:docstring> + </arg> + <arg type="s" name="shmPath"> + <tp:docstring>The path of the newly created shared memory</tp:docstring> + </arg> + </signal> <method name="getCurrentCodecName" tp:name-for-bindings="getCurrentCodecName"> <arg type="s" name="callID" direction="in"/> <arg type="s" name="codecName" direction="out"/> </method> - </interface> + </interface> </node> diff --git a/gnome/src/eel-gconf-extensions.h b/gnome/src/eel-gconf-extensions.h index e9139477759e3388c84881688bf62edbca1b0001..40eab1ccfdbdbc294d00ce3170e64323297fd7b4 100644 --- a/gnome/src/eel-gconf-extensions.h +++ b/gnome/src/eel-gconf-extensions.h @@ -38,6 +38,7 @@ BEGIN_EXTERN_C #define CONF_PREFIX "/apps/sflphone-client-gnome" #define CONF_MAIN_WINDOW_WIDTH CONF_PREFIX "/state/window_width" #define CONF_MAIN_WINDOW_HEIGHT CONF_PREFIX "/state/window_height" +#define CONF_MESSAGING_HEIGHT CONF_PREFIX "/state/window_height" #define CONF_MAIN_WINDOW_POSITION_X CONF_PREFIX "/state/window_position_x" #define CONF_MAIN_WINDOW_POSITION_Y CONF_PREFIX "/state/window_position_y" #define CONF_IM_WINDOW_WIDTH CONF_PREFIX "/state/im_width" diff --git a/gnome/src/icons/Makefile.am b/gnome/src/icons/Makefile.am index ecb4274268bf03772f37b30e7f99547d4a3b7dbf..37efcb6036aa96892ee1c648df08da40b129b45f 100644 --- a/gnome/src/icons/Makefile.am +++ b/gnome/src/icons/Makefile.am @@ -7,7 +7,7 @@ libicons_la_SOURCES = icon_factory.c \ pixmap_data.h libicons_la_LDFLAGS = $(DBUSGLIB_LDFLAGS) $(LIBNOTIFY_LDFLAGS) \ - $(GTK_LDFLAGS) $(GLIB_LDFLAGS) $(WEBKIT_LDFLAGS) + $(GTK_LDFLAGS) $(GLIB_LDFLAGS) libicons_la_CFLAGS = $(DBUSGLIB_CFLAGS) $(LIBNOTIFY_CFLAGS) \ - $(GTK_CFLAGS) $(GLIB_CFLAGS) $(WEBKIT_CFLAGS) + $(GTK_CFLAGS) $(GLIB_CFLAGS) diff --git a/gnome/src/imwindow.c b/gnome/src/imwindow.c deleted file mode 100644 index 0ddd14d135815e1c8ef954ebcff511c3e0fd3406..0000000000000000000000000000000000000000 --- a/gnome/src/imwindow.c +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc. - * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> - * Author: Pierre-Luc Beaudoin <pierre-luc.beaudoin@savoirfairelinux.com> - * Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Additional permission under GNU GPL version 3 section 7: - * - * If you modify this program, or any covered work, by linking or - * combining it with the OpenSSL project's OpenSSL library (or a - * modified version of that library), containing parts covered by the - * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. - * grants you additional permission to convey the resulting work. - * Corresponding Source for a non-source form of such a combination - * shall include the source code for the parts of OpenSSL used as well - * as that of the covered work. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gtk2_wrappers.h" -#include "eel-gconf-extensions.h" -#include "logger.h" -#include "imwindow.h" -#include "unused.h" -#include "contacts/calltab.h" -#include "contacts/calltab.h" -#include "sflphone_const.h" -#include <sys/stat.h> - -/** Local variables */ -static GtkWidget *im_window; -static GtkWidget *im_notebook; - -static void im_window_init(); - -static GtkWidget *im_window_get() -{ - if (im_window == NULL) - im_window_init(); - - return im_window; -} - -static gboolean window_configure_cb(GtkWidget *wini UNUSED, GdkEventConfigure *event) -{ - int pos_x, pos_y; - - eel_gconf_set_integer(CONF_IM_WINDOW_WIDTH, event->width); - eel_gconf_set_integer(CONF_IM_WINDOW_HEIGHT, event->height); - - gtk_window_get_position(GTK_WINDOW(im_window_get()), &pos_x, &pos_y); - eel_gconf_set_integer(CONF_IM_WINDOW_POSITION_X, pos_x); - eel_gconf_set_integer(CONF_IM_WINDOW_POSITION_Y, pos_y); - - return FALSE; -} - -/** - * Minimize the main window. - */ -static gboolean -on_delete(GtkWidget * widget UNUSED, gpointer data UNUSED) -{ - /* Only hide the main window that contains all the instant messaging instances */ - gtk_widget_hide(im_window_get()); - return TRUE; -} - -static void -on_switch_page(GtkNotebook *notebook, gpointer page UNUSED, guint page_num, gpointer userdata UNUSED) -{ - GtkWidget *tab = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num); - - // show the current widget - gtk_widget_grab_focus(tab); - gtk_widget_show_now(tab); -} - -static void -im_window_init() -{ - const char *window_title = "SFLphone IM Client"; - int width, height, position_x, position_y; - - // Get configuration stored in gconf - width = eel_gconf_get_integer(CONF_IM_WINDOW_WIDTH); - - if (width <= 0) - width = 400; - - height = eel_gconf_get_integer(CONF_IM_WINDOW_HEIGHT); - - if (height <= 0) - height = 500; - - position_x = eel_gconf_get_integer(CONF_IM_WINDOW_POSITION_X); - position_y = eel_gconf_get_integer(CONF_IM_WINDOW_POSITION_Y); - - im_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_container_set_border_width(GTK_CONTAINER(im_window), 0); - gtk_window_set_title(GTK_WINDOW(im_window), window_title); - gtk_window_set_default_size(GTK_WINDOW(im_window), width, height); - struct stat st; - - if (!stat(LOGO, &st)) - gtk_window_set_default_icon_from_file(LOGO, NULL); - - gtk_window_set_position(GTK_WINDOW(im_window), GTK_WIN_POS_MOUSE); - - gtk_widget_set_name(im_window, "imwindow"); - - GtkWidget *im_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0 /*spacing*/); - im_notebook = gtk_notebook_new(); - - gtk_container_add(GTK_CONTAINER(im_window), im_box); - gtk_box_pack_start(GTK_BOX(im_box), im_notebook, TRUE, TRUE, 0); - gtk_widget_show(im_notebook); - - g_signal_connect(G_OBJECT(im_window), "delete-event", G_CALLBACK(on_delete), NULL); - g_signal_connect_object(G_OBJECT(im_window), "configure-event", G_CALLBACK(window_configure_cb), NULL, 0); - g_signal_connect(G_OBJECT(im_notebook), "switch-page", G_CALLBACK(on_switch_page), NULL); - - /* make sure that everything is visible */ - gtk_widget_show_all(im_window); - - // Restore position according to the configuration stored in gconf - gtk_window_move(GTK_WINDOW(im_window), position_x, position_y); - gtk_widget_set_visible(im_window, FALSE); -} - -gboolean -im_window_is_active() -{ - if (!im_window) - return FALSE; - else - return gtk_window_is_active(GTK_WINDOW(im_window)); -} - -gboolean -im_window_is_visible() -{ - return gtk_widget_get_visible(im_window_get()); -} - -void -im_window_add(IMWidget *widget) -{ - if (im_window_get()) { - im_window_add_tab(widget); - gtk_widget_show_all(im_window_get()); - } -} - -gint -im_window_get_nb_tabs() -{ - if (im_notebook != NULL) - return gtk_notebook_get_n_pages(GTK_NOTEBOOK(im_notebook)); - else - return 0; -} - -static void -close_tab_cb(GtkButton *button UNUSED, gpointer userdata) -{ - /* We want here to close the current tab */ - im_window_remove_tab(GTK_WIDGET(userdata)); - - /* If no tabs are opened anymore, close the IM window */ - // gtk_widget_destroy (im_window); -} - -static void -im_window_hide_show_tabs() -{ - /* If only one tab is open, do not display the tab, only the content */ - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(im_notebook), - gtk_notebook_get_n_pages(GTK_NOTEBOOK(im_notebook)) != 1); -} - -void -im_window_add_tab(IMWidget *im) -{ - /* Fetch the call */ - callable_obj_t *im_widget_call = calllist_get_call(current_calls_tab, im->call_id); - conference_obj_t *im_widget_conf = conferencelist_get(current_calls_tab, im->call_id); - - /* A container to include the tab label and the close button */ - GtkWidget *tab_container = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3); - GtkWidget *tab_label; - im->tab = tab_container; - - if (im_widget_call) - tab_label = gtk_label_new(*im_widget_call->_display_name ? im_widget_call->_display_name : im_widget_call->_peer_number); - else if (im_widget_conf) - tab_label = gtk_label_new("Conferencing"); - else - tab_label = gtk_label_new(""); - - GtkWidget *tab_close_button = gtk_button_new(); - - /* Pack it all */ - gtk_button_set_relief(GTK_BUTTON(tab_close_button), GTK_RELIEF_NONE); - gtk_box_pack_start(GTK_BOX(tab_container), tab_label, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(tab_container), tab_close_button, FALSE, FALSE, 0); - gtk_container_add(GTK_CONTAINER(tab_close_button), gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU)); - - /* Connect a signal to the close button on each tab, to be able to close the tabs individually */ - g_signal_connect(tab_close_button, "clicked", G_CALLBACK(close_tab_cb), im); - - /* Show it */ - gtk_widget_show_all(im_notebook); - gtk_widget_show_all(tab_container); - - /* Add the page to the notebook */ - guint tab_index = gtk_notebook_append_page(GTK_NOTEBOOK(im_notebook), GTK_WIDGET(im), tab_container); - - /* TODO Switch to the newly opened tab. Still not working */ - DEBUG("InstantMessaging: Switch to tab: %i", tab_index); - gtk_notebook_set_current_page(GTK_NOTEBOOK(im_notebook), -1); - - /* Decide whether or not displaying the tabs of the notebook */ - im_window_hide_show_tabs(); -} - -void -im_window_show_tab(GtkWidget *widget) -{ - int pageIndex = gtk_notebook_page_num(GTK_NOTEBOOK(im_notebook), widget); - - if (pageIndex != -1) - gtk_notebook_set_current_page(GTK_NOTEBOOK(im_notebook), pageIndex); -} - -void -im_window_remove_tab(GtkWidget *widget) -{ - // Remove the widget from the window - - /* We want here to close the current tab */ - guint page_index = gtk_notebook_page_num(GTK_NOTEBOOK(im_notebook), GTK_WIDGET(widget)); - gtk_notebook_remove_page(GTK_NOTEBOOK(im_notebook), page_index); - - /* Need to do some memory clean up, so that we could re-open an Im widget for this call later. */ - IMWidget *im = IM_WIDGET(widget); - callable_obj_t *call = calllist_get_call(current_calls_tab, im->call_id); - conference_obj_t *conf = conferencelist_get(current_calls_tab, im->call_id); - - if (call) - call->_im_widget = NULL; - - if (conf) - conf->_im_widget = NULL; - - /* Decide whether or not displaying the tabs of the notebook */ - im_window_hide_show_tabs(); -} diff --git a/gnome/src/mainwindow.c b/gnome/src/mainwindow.c index 8f5e408ad260fb71bf6900a1e54c908ccff04a63..aec0fce31253a0c74a616b79f6e874cd79344d26 100644 --- a/gnome/src/mainwindow.c +++ b/gnome/src/mainwindow.c @@ -35,6 +35,7 @@ #endif #include "gtk2_wrappers.h" +#include "account_schema.h" #include "actions.h" #include "dbus.h" #include "calltree.h" @@ -53,6 +54,7 @@ #include "config/audioconf.h" #include "str_utils.h" #include "seekslider.h" +#include "messaging/message_tab.h" #include "eel-gconf-extensions.h" @@ -78,6 +80,8 @@ static gchar *status_current_message; static gboolean focus_is_on_searchbar = FALSE; +static gboolean pause_grabber = FALSE; + void focus_on_searchbar_out() { @@ -90,6 +94,17 @@ focus_on_searchbar_in() focus_is_on_searchbar = TRUE; } +/** + * Save the vpaned size + */ +static void +on_messaging_paned_position_change(GtkPaned* paned, GtkScrollType scroll_type UNUSED,gpointer user_data UNUSED) +{ + int height = gtk_paned_get_position(paned); + eel_gconf_set_integer(CONF_MESSAGING_HEIGHT, height); + set_message_tab_height(paned,height); +} + /** * Handle main window resizing */ @@ -98,6 +113,10 @@ static gboolean window_configure_cb(GtkWidget *win UNUSED, GdkEventConfigure *ev eel_gconf_set_integer(CONF_MAIN_WINDOW_WIDTH, event->width); eel_gconf_set_integer(CONF_MAIN_WINDOW_HEIGHT, event->height); + gint height = 0; + gint width = 0; + gtk_widget_get_size_request(get_tab_box(),&width,&height); + int pos_x, pos_y; gtk_window_get_position(GTK_WINDOW(window), &pos_x, &pos_y); eel_gconf_set_integer(CONF_MAIN_WINDOW_POSITION_X, pos_x); @@ -147,35 +166,38 @@ main_window_ask_quit() static gboolean on_key_released(GtkWidget *widget UNUSED, GdkEventKey *event, gpointer user_data UNUSED) { - if (focus_is_on_searchbar) - return TRUE; + if (!pause_grabber) { + if (focus_is_on_searchbar) + return TRUE; + + if (event->keyval == GDK_KEY_Return) { + if (calltab_has_name(active_calltree_tab, CURRENT_CALLS)) { + sflphone_keypad(event->keyval, event->string); + return TRUE; + } else if (calltab_has_name(active_calltree_tab, HISTORY)) + return FALSE; + } + + // If a modifier key is pressed, it's a shortcut, pass along + if (event->state & GDK_CONTROL_MASK || event->state & GDK_MOD1_MASK || + event->keyval == '<' || + event->keyval == '>' || + event->keyval == '\"'|| + event->keyval == GDK_KEY_Tab || + event->keyval == GDK_KEY_Return || + event->keyval == GDK_KEY_Left || + event->keyval == GDK_KEY_Up || + event->keyval == GDK_KEY_Right || + event->keyval == GDK_KEY_Down || + (event->keyval >= GDK_KEY_F1 && event->keyval <= GDK_KEY_F12) || + event->keyval == ' ') + return FALSE; + else + sflphone_keypad(event->keyval, event->string); - if (event->keyval == GDK_KEY_Return) { - if (calltab_has_name(active_calltree_tab, CURRENT_CALLS)) { - sflphone_keypad(event->keyval, event->string); - return TRUE; - } else if (calltab_has_name(active_calltree_tab, HISTORY)) - return FALSE; + return TRUE; } - - // If a modifier key is pressed, it's a shortcut, pass along - if (event->state & GDK_CONTROL_MASK || event->state & GDK_MOD1_MASK || - event->keyval == '<' || - event->keyval == '>' || - event->keyval == '\"'|| - event->keyval == GDK_KEY_Tab || - event->keyval == GDK_KEY_Return || - event->keyval == GDK_KEY_Left || - event->keyval == GDK_KEY_Up || - event->keyval == GDK_KEY_Right || - event->keyval == GDK_KEY_Down || - (event->keyval >= GDK_KEY_F1 && event->keyval <= GDK_KEY_F12) || - event->keyval == ' ') - return FALSE; - else - sflphone_keypad(event->keyval, event->string); - - return TRUE; + return FALSE; } static void pack_main_window_start(GtkBox *box, GtkWidget *widget, gboolean expand, gboolean fill, guint padding) @@ -249,6 +271,10 @@ create_main_window() subvbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); gtk_box_set_homogeneous(GTK_BOX(subvbox), FALSE); + /*Create the messaging tab container*/ + GtkWidget *tab_widget = get_tab_box(); + gtk_widget_show (tab_widget); + /* Populate the main window */ GtkWidget *widget = create_menus(ui_manager); gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); @@ -256,14 +282,37 @@ create_main_window() widget = create_toolbar_actions(ui_manager); pack_main_window_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0); + /* Setup call main widget*/ +#if GTK_MAJOR_VERSION == 2 + GtkWidget *vpaned = gtk_vpaned_new(); +#else + GtkWidget *vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL); +#endif + current_calls_tab->mainwidget = vpaned; + + int messaging_height = eel_gconf_get_integer(CONF_MESSAGING_HEIGHT); + set_message_tab_height(GTK_PANED(vpaned),messaging_height); + + gtk_widget_show (vpaned); + gtk_box_pack_start(GTK_BOX(vbox), vpaned, TRUE, TRUE, 0); + + /* Setup history main widget */ + GtkWidget *history_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + history_tab->mainwidget = history_vbox; + gtk_box_set_homogeneous(GTK_BOX(history_vbox), FALSE); + gtk_box_pack_start(GTK_BOX(history_vbox), history_tab->tree, TRUE, TRUE, 0); + /* Add tree views */ - gtk_box_pack_start(GTK_BOX(vbox), current_calls_tab->tree, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(vbox), history_tab->tree, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), contacts_tab->tree, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), history_vbox, TRUE, TRUE, 0); + gtk_paned_pack1 (GTK_PANED (vpaned), current_calls_tab->tree, TRUE, FALSE); + gtk_paned_pack2 (GTK_PANED (vpaned), tab_widget, FALSE, FALSE); + + g_signal_connect(G_OBJECT(vpaned), "notify::position" , G_CALLBACK(on_messaging_paned_position_change), current_calls_tab); - /* Add playback scale */ + /* Add playback scale and setup history tab */ seekslider = GTK_WIDGET(sfl_seekslider_new()); - pack_main_window_start(GTK_BOX(vbox), seekslider, FALSE, TRUE, 0); + pack_main_window_start(GTK_BOX(history_vbox), seekslider, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), subvbox, FALSE, FALSE, 0); @@ -299,7 +348,7 @@ create_main_window() gtk_widget_show_all(window); /* dont't show the history */ - gtk_widget_hide(history_tab->tree); + gtk_widget_hide(history_vbox); /* dont't show the contact list */ gtk_widget_hide(contacts_tab->tree); @@ -432,7 +481,7 @@ main_window_zrtp_not_supported(callable_obj_t * c) if (account != NULL) { warning_enabled = account_lookup(account, - ACCOUNT_ZRTP_NOT_SUPP_WARNING); + CONFIG_ZRTP_NOT_SUPP_WARNING); DEBUG("Warning Enabled %s", warning_enabled); } else { DEBUG("Account is null callID %s", c->_callID); @@ -440,7 +489,7 @@ main_window_zrtp_not_supported(callable_obj_t * c) if (properties) warning_enabled = g_hash_table_lookup(properties, - ACCOUNT_ZRTP_NOT_SUPP_WARNING); + CONFIG_ZRTP_NOT_SUPP_WARNING); } if (utf8_case_equal(warning_enabled, "true")) { @@ -537,3 +586,10 @@ main_window_reset_playback_scale() { sfl_seekslider_reset((SFLSeekSlider *)seekslider); } + + +void +main_window_pause_keygrabber(gboolean value) +{ + pause_grabber = value; +} diff --git a/gnome/src/mainwindow.h b/gnome/src/mainwindow.h index f71d81cf95692102963a58cd3f3f82ad4e1a8f4b..495318687b0be91dfc02d6d4730485b276e5d0d0 100644 --- a/gnome/src/mainwindow.h +++ b/gnome/src/mainwindow.h @@ -124,6 +124,11 @@ void main_window_show_playback_scale(); */ void main_window_hide_playback_scale(); +/** + * Pause the key grabber while an other widget is focussed + */ +void main_window_pause_keygrabber(gboolean value); + void main_window_reset_playback_scale(); #endif diff --git a/gnome/src/messaging/Makefile.am b/gnome/src/messaging/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..6ed62c990d8e0510ba7f06dae9319a0e4073ef77 --- /dev/null +++ b/gnome/src/messaging/Makefile.am @@ -0,0 +1,11 @@ +include ../../globals.mak + +noinst_LTLIBRARIES = libmessaging.la + +libmessaging_la_SOURCES = message_tab.c message_tab.h + +libmessaging_la_LDFLAGS = + +libmessaging_la_LIBADD = @GTK_LIBS@ @GLIB_LIBS@ + +libmessaging_la_CFLAGS = @GTK_CFLAGS@ @GLIB_CFLAGS@ @DBUSGLIB_CFLAGS@ diff --git a/gnome/src/messaging/message_tab.c b/gnome/src/messaging/message_tab.c new file mode 100644 index 0000000000000000000000000000000000000000..c9e36d8bbf9e50d1cb5739c7d3e08987c89aaef6 --- /dev/null +++ b/gnome/src/messaging/message_tab.c @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2012 Savoir-Faire Linux Inc. + * Author: Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ +#include "message_tab.h" + +#include "../dbus/dbus.h" +#include <glib.h> +#include "../mainwindow.h" +#include <string.h> + +static GtkWidget *tab_box = NULL ; +static GHashTable *tabs = NULL ; +static gboolean visible = FALSE; +static GtkPaned *paned = NULL ; +static int vpanes_s = -1 ; +static int skip_height = -3 ; + + +static GtkTextIter* start_link = NULL; +static GtkTextIter* end_link = NULL; + + +void append_message ( message_tab* self , const gchar* name , const gchar* message); +void new_text_message ( callable_obj_t* call , const gchar* message ); +message_tab * create_messaging_tab_common(const gchar* call_id, const gchar *label); +message_tab * create_messaging_tab(callable_obj_t* call UNUSED); + +/////////////////////HELPERS///////////////////////// + + +/*I really don't know why we need this, but without, it doesn't work*/ +message_tab * +force_lookup(const gchar *id) +{ + GList *list = g_hash_table_get_keys(tabs); + for (guint k=0;k<g_list_length(list);k++) { + if (!strcmp(id,(gchar*)g_list_nth(list,k)->data)) { + return g_hash_table_lookup(tabs,(const gchar*)g_list_nth(list,k)->data); + } + } + return NULL; +} + +void +disable_conference_calls(conference_obj_t *call) +{ + if (tabs) { + guint size = g_slist_length(call->participant_list); + for (guint i = 0; i < size;i++) { + const gchar* id = (gchar*)g_slist_nth(call->participant_list,i)->data; + message_tab *tab = g_hash_table_lookup(tabs,id); + tab = force_lookup(id); + if (tab) { + gtk_widget_hide(tab->entry); + } + } + } +} + + + +/////////////////////GETTERS///////////////////////// + +GtkWidget *get_tab_box() +{ + if (!tab_box) { + tab_box = gtk_notebook_new(); + gtk_notebook_set_scrollable(GTK_NOTEBOOK(tab_box),TRUE); + } + return tab_box; +} + + + +/////////////////////SETTERS///////////////////////// + +void +hide_show_common() +{ + if (visible) { + gtk_widget_show(get_tab_box()); + } + else { + gtk_widget_hide(get_tab_box()); + } +} + +void +toogle_messaging() +{ + visible = !visible; + hide_show_common(); +} + +void +hide_messaging() +{ + visible = TRUE; + hide_show_common(); +} + +void +show_messaging() +{ + visible = FALSE; +// hide_show_common(); + gtk_widget_show(get_tab_box()); + if (vpanes_s > 0) { + gtk_paned_set_position(GTK_PANED(paned),vpanes_s); + } +} + +void +set_message_tab_height(GtkPaned* _paned, int height) +{ + if ( skip_height >=0 || skip_height == -3 ) { + paned = _paned; + vpanes_s = height; + } + skip_height++; +} + + + +//////////////////////SLOTS////////////////////////// + +static void +on_enter(GtkEntry *entry, gpointer user_data) +{ + start_link = NULL; + end_link = NULL; + message_tab *tab = (message_tab*)user_data; + append_message(tab,(gchar*)"Me",gtk_entry_get_text(entry)); + if (tab->call) + dbus_send_text_message(tab->call->_callID,gtk_entry_get_text(entry)); + else if (tab->conf) + dbus_send_text_message(tab->conf->_confID,gtk_entry_get_text(entry)); + gtk_entry_set_text(entry,""); +} + +static void +on_close(GtkWidget *button UNUSED, gpointer data) +{ + message_tab *tab = (message_tab*)data; + gtk_widget_destroy(tab->widget); + if (tab->call) + g_hash_table_remove(tabs,tab->call->_callID); + else if (tab->conf) + g_hash_table_remove(tabs,tab->conf->_confID); +} + +static void +on_focus_in(GtkEntry *entry UNUSED, gpointer user_data UNUSED) +{ + main_window_pause_keygrabber(TRUE); +} + +static void +on_focus_out(GtkEntry *entry UNUSED, gpointer user_data UNUSED) +{ + main_window_pause_keygrabber(FALSE); +} + +static void +on_clicked(GtkTextBuffer *textbuffer UNUSED, GtkTextIter *location UNUSED, GtkTextMark *mark UNUSED, gpointer user_data UNUSED) +{ + if (start_link && end_link && gtk_text_iter_compare(start_link,location) <= 0 && gtk_text_iter_compare(location,end_link) <= 0) { + gchar* text = gtk_text_buffer_get_text(textbuffer,start_link,end_link,FALSE); + start_link = NULL; + end_link = NULL; + if (strlen(text)) { + const gchar* argv[3] = {"x-www-browser",text,(char*)NULL}; + g_spawn_async(NULL,(gchar**)argv,NULL,G_SPAWN_SEARCH_PATH|G_SPAWN_STDOUT_TO_DEV_NULL|G_SPAWN_STDERR_TO_DEV_NULL,NULL,NULL,NULL,NULL); + } + } +} + +static void +on_cursor_motion(GtkTextView *view UNUSED, GdkEvent *event, gpointer data) +{ + /* Convert mouse position into text iterators*/ + gint x,y; + GtkTextIter cursor_pos,end_iter,end_match,start_match,end_match_b,start_match_b,start_real,end_real; + gtk_text_buffer_get_end_iter (((message_tab*) data)->buffer, &end_iter ); + gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW(view),GTK_TEXT_WINDOW_TEXT,((GdkEventMotion*)event)->x,((GdkEventMotion*)event)->y,&x,&y); + gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(view),&cursor_pos,x,y ); + gboolean ret = gtk_text_iter_backward_search(&cursor_pos," ",GTK_TEXT_SEARCH_TEXT_ONLY | GTK_TEXT_SEARCH_VISIBLE_ONLY,&start_match_b,&end_match_b,NULL); + if ( ret ) { + if (gtk_text_iter_forward_search(&cursor_pos," ",GTK_TEXT_SEARCH_TEXT_ONLY | GTK_TEXT_SEARCH_VISIBLE_ONLY,&start_match,&end_match,NULL) + && gtk_text_iter_get_line(&end_match) == gtk_text_iter_get_line(&cursor_pos)) { + start_real = end_match_b; + end_real = start_match; + } + else { + gtk_text_iter_forward_visible_line(&cursor_pos); + start_real = end_match_b; + end_real = cursor_pos ; + } + + /*Get the word under cursor*/ + gchar* text = gtk_text_buffer_get_text(((message_tab*) data)->buffer,&start_real,&end_real,FALSE); + + /*Match the regex*/ + GError *error = NULL; + gchar *pattern_string = "^http\\://[a-zA-Z0-9\\-\\.]+\\.[a-zA-Z]{2,3}(/\\S*)?$"; + GRegex *regex = g_regex_new( pattern_string, 0, 0, &error ); + GMatchInfo *match_info = NULL; + GdkWindow *win = gtk_text_view_get_window(GTK_TEXT_VIEW(view),GTK_TEXT_WINDOW_TEXT); + + g_regex_match( regex, text, 0, &match_info ); + if (g_match_info_matches( match_info )) { + /*Is a link*/ + while( g_match_info_matches( match_info ) ) { + g_match_info_next( match_info, &error ); + if (gtk_text_iter_get_buffer(&start_real) == ((message_tab*) data)->buffer && gtk_text_iter_get_buffer(&end_real) == ((message_tab*) data)->buffer) { + gtk_text_buffer_remove_all_tags(((message_tab*) data)->buffer,&start_real, &end_real); + gtk_text_buffer_apply_tag_by_name(((message_tab*) data)->buffer, "link", &start_real, &end_real); + } + } + GdkCursor *cur = gdk_cursor_new(GDK_HAND2); + start_link = gtk_text_iter_copy(&start_real); + end_link = gtk_text_iter_copy(&end_real); + gdk_window_set_cursor(win,cur); + } + else { + /*Is not a link, cleaning previous link*/ + GdkCursor *cur = gdk_cursor_new(GDK_XTERM); + gdk_window_set_cursor(win,cur); + if (start_link && end_link && gtk_text_iter_get_buffer(start_link) == ((message_tab*) data)->buffer && gtk_text_iter_get_buffer(end_link) == ((message_tab*) data)->buffer) { + gtk_text_buffer_remove_all_tags(((message_tab*) data)->buffer,start_link,end_link ); + /*g_free(start_link); + g_free(end_link);*/ + start_link = NULL; + end_link = NULL; + } + } + } +} + + +/////////////////////MUTATORS//////////////////////// + +void +disable_messaging_tab(const gchar * id) +{ + message_tab *tab = NULL; + if (tabs) + tab = g_hash_table_lookup(tabs, id); + if (tab != NULL) + gtk_widget_hide(tab->entry); +} + +void +append_message(message_tab* self, const gchar* name, const gchar* message) +{ + GtkTextIter current_end,new_end; + gtk_text_buffer_get_end_iter( self->buffer, ¤t_end ); + gtk_text_buffer_insert ( self->buffer, ¤t_end, name, -1 ); + gtk_text_buffer_insert ( self->buffer, ¤t_end, ": ", -1 ); + + gtk_text_buffer_get_end_iter(self->buffer, ¤t_end); + for (unsigned int i=0;i<strlen(name)+2;i++){ + if (!gtk_text_iter_backward_char(¤t_end)) + break; + } + + gtk_text_buffer_get_end_iter(self->buffer, &new_end); + gtk_text_buffer_apply_tag_by_name(self->buffer, "b", ¤t_end, &new_end); + + gtk_text_buffer_insert ( self->buffer, &new_end, message, -1 ); + gtk_text_buffer_insert ( self->buffer, &new_end, "\n" , -1 ); + gtk_text_buffer_get_end_iter( self->buffer, &new_end ); + gtk_text_view_scroll_to_iter( self->view , &new_end,FALSE,0,0,FALSE ); + + start_link = NULL; + end_link = NULL; +} + +void +new_text_message_common(const gchar* call_id, const gchar* message, const gchar *name, gboolean conf) +{ + if (!tabs) return; + message_tab *tab = NULL; + if (tabs) + tab = g_hash_table_lookup(tabs,call_id); + if (!tab && !conf) + tab = create_messaging_tab_common(call_id,name); + else if (!tab && conf) + tab = create_messaging_tab_common(call_id,name); + append_message(tab,name,message); +} + +void +new_text_message(callable_obj_t* call, const gchar* message) +{ + gchar* label_text; + if (strcmp(call->_display_name,"")) + label_text = call->_display_name; + else + label_text = "Peer"; + new_text_message_common(call->_callID,message,label_text,FALSE); +} + +void +new_text_message_conf(conference_obj_t* call, const gchar* message,const gchar* from) +{ + disable_conference_calls(call); + new_text_message_common(call->_confID,message,strlen(from)?from:"Conference",TRUE); +} + + + +//conference_obj_t +message_tab * +create_messaging_tab_common(const gchar* call_id, const gchar *label) +{ + show_messaging(); + /* Do not create a new tab if it already exist */ + message_tab *tab = NULL; + if (tabs) + tab = g_hash_table_lookup(tabs,call_id); + if (tab) { + return tab; + } + + message_tab *self = g_new0(message_tab, 1); + + /* Create the main layout */ + GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + GtkTextBuffer *text_buffer = gtk_text_buffer_new(NULL); + gtk_text_buffer_create_tag(text_buffer, "b", "weight", PANGO_WEIGHT_BOLD,NULL); + gtk_text_buffer_create_tag(text_buffer, "link", "foreground", "#0000FF","underline",PANGO_UNDERLINE_SINGLE); + + /* Create the conversation history widget*/ + GtkWidget *history_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2 ); + GtkWidget *h_left_spacer = gtk_label_new ( "" ); + GtkWidget *h_right_spacer = gtk_label_new ( "" ); + GtkWidget *scoll_area = gtk_scrolled_window_new ( NULL,NULL ); + GtkWidget *text_box_widget = gtk_text_view_new_with_buffer( text_buffer ); + gtk_box_pack_start(GTK_BOX(history_hbox) , h_left_spacer , FALSE , FALSE , 0); + gtk_box_pack_start(GTK_BOX(history_hbox) , scoll_area , TRUE , TRUE , 0); + gtk_box_pack_start(GTK_BOX(history_hbox) , h_right_spacer , FALSE , FALSE , 0); + + gtk_text_view_set_editable ( GTK_TEXT_VIEW(text_box_widget),FALSE ); + gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(text_box_widget),GTK_WRAP_CHAR); + + gtk_container_add(GTK_CONTAINER(scoll_area), text_box_widget); + + g_signal_connect(G_OBJECT(text_box_widget), "motion-notify-event" , G_CALLBACK(on_cursor_motion), self); + g_signal_connect(G_OBJECT(text_buffer ), "mark-set" , G_CALLBACK(on_clicked ), self); + + GtkWidget *line_edit = gtk_entry_new ( ); + GtkWidget *hbox = gtk_box_new ( GTK_ORIENTATION_HORIZONTAL, 1 ); + GtkWidget *left_spacer = gtk_label_new ( "" ); + GtkWidget *right_spacer = gtk_label_new ( "" ); + gtk_box_pack_start(GTK_BOX(hbox) , left_spacer , FALSE , FALSE , 0); + gtk_box_pack_start(GTK_BOX(hbox) , line_edit , TRUE , TRUE , 0); + gtk_box_pack_start(GTK_BOX(hbox) , right_spacer , FALSE , FALSE , 0); + + g_signal_connect(G_OBJECT(line_edit), "activate" , G_CALLBACK(on_enter) , self); + g_signal_connect(G_OBJECT(line_edit), "focus-in-event" , G_CALLBACK(on_focus_in) , self); + g_signal_connect(G_OBJECT(line_edit), "focus-out-event" , G_CALLBACK(on_focus_out), self); + + self->view = GTK_TEXT_VIEW(text_box_widget); + self->widget = vbox ; + self->buffer = text_buffer; + self->entry = line_edit ; + + /* Setup the tab label */ + GtkWidget *tab_label = gtk_label_new ( label ); + GtkWidget *tab_label_vbox = gtk_box_new ( GTK_ORIENTATION_HORIZONTAL, 0 ); + GtkWidget *tab_close_button = gtk_button_new ( ); + GtkWidget *button_image = gtk_image_new_from_stock( GTK_STOCK_CLOSE,GTK_ICON_SIZE_MENU ); + gtk_box_set_spacing (GTK_BOX(tab_label_vbox),0); + + /*TODO make it work*/ + /* GtkRcStyle *style = gtk_rc_style_new(); + style->xthickness = 0; + style->ythickness = 0; + gtk_widget_modify_style(tab_close_button,style);*/ + gtk_button_set_image(GTK_BUTTON(tab_close_button),button_image); + g_signal_connect(G_OBJECT(tab_close_button), "clicked", G_CALLBACK(on_close), self); + + /* Fill the layout ans show everything */ + gtk_box_pack_start(GTK_BOX(vbox) , history_hbox , TRUE , TRUE , 0); + gtk_box_pack_start(GTK_BOX(vbox) , hbox , FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(tab_label_vbox), tab_label , TRUE , TRUE , 0); + gtk_box_pack_start(GTK_BOX(tab_label_vbox), tab_close_button, FALSE, FALSE, 0); + + gtk_widget_show (tab_label ); + gtk_widget_show (tab_close_button); + gtk_widget_show (tab_label_vbox ); + gtk_widget_show (vbox ); + gtk_widget_show (scoll_area ); + gtk_widget_show (text_box_widget ); + gtk_widget_show (history_hbox ); + gtk_widget_show (h_left_spacer ); + gtk_widget_show (h_right_spacer ); + gtk_widget_show (hbox ); + gtk_widget_show (line_edit ); + gtk_widget_show (left_spacer ); + gtk_widget_show (right_spacer ); + + self->index = gtk_notebook_append_page(GTK_NOTEBOOK(get_tab_box()),vbox,tab_label_vbox); + gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(get_tab_box()),vbox,TRUE); + gtk_notebook_set_current_page(GTK_NOTEBOOK(get_tab_box()),self->index); + + /* Keep track of the tab */ + if (!tabs) { + tabs = g_hash_table_new(NULL,g_str_equal); + } + g_hash_table_insert(tabs,(gpointer)call_id,(gpointer)self); + + return self; +} + +message_tab * +create_messaging_tab(callable_obj_t* call) +{ + const gchar *confID = dbus_get_conference_id(call->_callID); + if (strlen(confID) > 0 && ((tabs && force_lookup(confID) == NULL) || !tabs)) { + return create_messaging_tab_common(confID,"Conference"); + } + else if (strlen(confID) > 0 && tabs) { + return force_lookup(confID); + } + gchar* label_text; + if (strcmp(call->_display_name,"")) + label_text = call->_display_name; + else + label_text = call->_peer_number ; + message_tab* self = create_messaging_tab_common(call->_callID,label_text); + + self->call = call; + self->conf = NULL; + return self; +} + +message_tab * +create_messaging_tab_conf(conference_obj_t* call) +{ + message_tab* self = create_messaging_tab_common(call->_confID,"Conference"); + self->conf = call; + self->call = NULL; + + disable_conference_calls(call); + + return self; +} \ No newline at end of file diff --git a/gnome/src/messaging/message_tab.h b/gnome/src/messaging/message_tab.h new file mode 100644 index 0000000000000000000000000000000000000000..1a5bdd562a47819210e08309a2c65208a27a3fad --- /dev/null +++ b/gnome/src/messaging/message_tab.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2012 Savoir-Faire Linux Inc. + * Author: Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +/** + * This file contains functions specific for a tab of the message pane + */ + +#ifndef __MESSAGING_H__ +#define __MESSAGING_H__ + +#include <gtk/gtk.h> +#include <callable_obj.h> +#include <conference_obj.h> +#include <unused.h> + +/** An IM conversation */ +typedef struct { + GtkWidget *widget ; + callable_obj_t *call ; + conference_obj_t *conf ; + gchar *title ; + GtkTextBuffer *buffer ; + GtkTextView *view ; + GtkWidget *entry ; + gint index ; +} message_tab; + +/** + * Create a new message tab or use the existing on if the call exist + * @param call the conversation call + */ +message_tab* create_messaging_tab(callable_obj_t* call); +message_tab* create_messaging_tab_conf(conference_obj_t* call); + +/** Return the main conversation notebook */ +GtkWidget *get_tab_box(); + +/** Add a new text message to an existng conversation or create a new one + * @param call the call + * @param message the new message + */ +void new_text_message(callable_obj_t *call, const gchar *message); +void new_text_message_conf(conference_obj_t *call, const gchar *message,const gchar* from); + + +/** + * Display or hide messaging notebook + */ +void toogle_messaging(); +void hide_messaging(); +void show_messaging(); +void disable_messaging_tab(const gchar *id); + +/** + * Set message tab height + */ +void set_message_tab_height(GtkPaned* _paned, int height); + + +#endif diff --git a/gnome/src/sflnotify.c b/gnome/src/sflnotify.c index 7f8c19a9c1d0644b89ae61153a8840b70a2199f0..ecb705bb12c722968043c18289dd02fd69174f3d 100644 --- a/gnome/src/sflnotify.c +++ b/gnome/src/sflnotify.c @@ -28,9 +28,13 @@ * as that of the covered work. */ +#ifdef HAVE_CONFIG_H #include "config.h" +#endif + #include <glib.h> #include <glib/gi18n.h> +#include "account_schema.h" #include "str_utils.h" #include "eel-gconf-extensions.h" #include "sflnotify.h" @@ -104,8 +108,8 @@ notify_incoming_call(callable_obj_t* c) title = g_markup_printf_escaped("IP-to-IP call"); else { title = g_markup_printf_escaped(_("%s account : %s") , - (gchar*) g_hash_table_lookup(account_list_get_by_id(c->_accountID)->properties , ACCOUNT_TYPE) , - (gchar*) g_hash_table_lookup(account_list_get_by_id(c->_accountID)->properties , ACCOUNT_ALIAS)) ; + (gchar*) g_hash_table_lookup(account_list_get_by_id(c->_accountID)->properties, CONFIG_ACCOUNT_TYPE) , + (gchar*) g_hash_table_lookup(account_list_get_by_id(c->_accountID)->properties, CONFIG_ACCOUNT_ALIAS)) ; } gchar *callerid = g_markup_printf_escaped(_("<i>From</i> %s"), c->_peer_number); @@ -123,8 +127,8 @@ notify_voice_mails(guint count, account_t* acc) #if USE_NOTIFY // the account is different from NULL gchar *title = g_markup_printf_escaped(_("%s account : %s") , - (gchar*) g_hash_table_lookup(acc->properties , ACCOUNT_TYPE) , - (gchar*) g_hash_table_lookup(acc->properties , ACCOUNT_ALIAS)) ; + (gchar*) g_hash_table_lookup(acc->properties, CONFIG_ACCOUNT_TYPE) , + (gchar*) g_hash_table_lookup(acc->properties, CONFIG_ACCOUNT_ALIAS)) ; gchar *body = g_markup_printf_escaped(n_("%d voice mail", "%d voice mails", count), count); create_new_gnome_notification(title, @@ -140,8 +144,8 @@ notify_current_account(account_t* acc) #if USE_NOTIFY // the account is different from NULL gchar *body = g_markup_printf_escaped(_("Calling with %s account <i>%s</i>"), - (gchar*) g_hash_table_lookup(acc->properties, ACCOUNT_TYPE) , - (gchar*) g_hash_table_lookup(acc->properties, ACCOUNT_ALIAS)); + (gchar*) g_hash_table_lookup(acc->properties, CONFIG_ACCOUNT_TYPE) , + (gchar*) g_hash_table_lookup(acc->properties, CONFIG_ACCOUNT_ALIAS)); gchar *title = g_markup_printf_escaped(_("Current account")); diff --git a/gnome/src/sflphone_const.h b/gnome/src/sflphone_const.h index e2b78b5f606faac9c1ebb51bec28830229e769d9..91ccb532de322fc486f051d2f37aef8d0b06696f 100644 --- a/gnome/src/sflphone_const.h +++ b/gnome/src/sflphone_const.h @@ -49,61 +49,9 @@ #define IP2IP_PROFILE "IP2IP" -#define ACCOUNT_ID "Account.id" -#define ACCOUNT_TYPE "Account.type" -#define ACCOUNT_ALIAS "Account.alias" -#define ACCOUNT_ENABLED "Account.enable" -#define ACCOUNT_MAILBOX "Account.mailbox" -#define ACCOUNT_USERAGENT "Account.useragent" -#define ACCOUNT_REGISTRATION_EXPIRE "Account.registrationExpire" -#define ACCOUNT_REGISTRATION_STATUS "Account.registrationStatus" -#define ACCOUNT_REGISTRATION_STATE_CODE "Account.registrationCode" -#define ACCOUNT_REGISTRATION_STATE_DESC "Account.registrationDescription" - -#define ACCOUNT_SIP_STUN_SERVER "STUN.server" -#define ACCOUNT_SIP_STUN_ENABLED "STUN.enable" -#define ACCOUNT_DTMF_TYPE "Account.dtmfType" -#define ACCOUNT_HOSTNAME "Account.hostname" -#define ACCOUNT_USERNAME "Account.username" -#define ACCOUNT_ROUTE "Account.routeset" -#define ACCOUNT_PASSWORD "Account.password" -#define ACCOUNT_REALM "Account.realm" -#define ACCOUNT_KEY_EXCHANGE "SRTP.keyExchange" -#define ACCOUNT_SRTP_ENABLED "SRTP.enable" -#define ACCOUNT_SRTP_RTP_FALLBACK "SRTP.rtpFallback" -#define ACCOUNT_ZRTP_DISPLAY_SAS "ZRTP.displaySAS" -#define ACCOUNT_ZRTP_NOT_SUPP_WARNING "ZRTP.notSuppWarning" -#define ACCOUNT_ZRTP_HELLO_HASH "ZRTP.helloHashEnable" -#define ACCOUNT_DISPLAY_SAS_ONCE "ZRTP.displaySasOnce" -#define KEY_EXCHANGE_NONE "none" #define ZRTP "zrtp" #define SDES "sdes" -#define CONFIG_RINGTONE_PATH "Account.ringtonePath" -#define CONFIG_RINGTONE_ENABLED "Account.ringtoneEnabled" - -#define TLS_LISTENER_PORT "TLS.listenerPort" -#define TLS_ENABLE "TLS.enable" -#define TLS_PORT "TLS.port" -#define TLS_CA_LIST_FILE "TLS.certificateListFile" -#define TLS_CERTIFICATE_FILE "TLS.certificateFile" -#define TLS_PRIVATE_KEY_FILE "TLS.privateKeyFile" -#define TLS_PASSWORD "TLS.password" -#define TLS_METHOD "TLS.method" -#define TLS_CIPHERS "TLS.ciphers" -#define TLS_SERVER_NAME "TLS.serverName" -#define TLS_VERIFY_SERVER "TLS.verifyServer" -#define TLS_VERIFY_CLIENT "TLS.verifyClient" -#define TLS_REQUIRE_CLIENT_CERTIFICATE "TLS.requireClientCertificate" -#define TLS_NEGOTIATION_TIMEOUT_SEC "TLS.negotiationTimeoutSec" -#define TLS_NEGOTIATION_TIMEOUT_MSEC "TLS.negotiationTimemoutMsec" - -#define LOCAL_INTERFACE "Account.localInterface" -#define PUBLISHED_SAMEAS_LOCAL "Account.publishedSameAsLocal" -#define LOCAL_PORT "Account.localPort" -#define PUBLISHED_PORT "Account.publishedPort" -#define PUBLISHED_ADDRESS "Account.publishedAddress" - #define SHORTCUT_PICKUP "pickUp" #define SHORTCUT_HANGUP "hangUp" #define SHORTCUT_POPUP "popupWindow" diff --git a/gnome/src/shortcuts.c b/gnome/src/shortcuts.c index b35333bcec4d894f8b244c17cffe9727a632122c..55d99f182b9952ff750ce9c70f5fad20c5c5614f 100644 --- a/gnome/src/shortcuts.c +++ b/gnome/src/shortcuts.c @@ -106,7 +106,6 @@ toggle_pick_up_hang_up_callback() case CALL_STATE_DIALING: case CALL_STATE_HOLD: case CALL_STATE_CURRENT: - case CALL_STATE_RECORD: case CALL_STATE_RINGING: sflphone_hang_up(); break; @@ -140,7 +139,6 @@ toggle_hold_callback() if (selectedCall) { switch (selectedCall->_state) { case CALL_STATE_CURRENT: - case CALL_STATE_RECORD: sflphone_on_hold(); break; case CALL_STATE_HOLD: diff --git a/gnome/src/uimanager.c b/gnome/src/uimanager.c index a85658a6767cf80b56a191918ccb2aa63fe30cd9..abfed466e530182ca15392c54dbb64ae223ad23d 100644 --- a/gnome/src/uimanager.c +++ b/gnome/src/uimanager.c @@ -46,7 +46,6 @@ #include "uimanager.h" #include "statusicon.h" -#include "widget/imwidget.h" #include "eel-gconf-extensions.h" #include "config/audioconf.h" @@ -61,8 +60,11 @@ #include "eel-gconf-extensions.h" #include "accountlist.h" +#include "account_schema.h" #include "config/accountlistconfigdialog.h" +#include "messaging/message_tab.h" + #include <sys/stat.h> #include <sliders.h> @@ -101,10 +103,6 @@ static GtkWidget * imToolbar_; static GtkWidget * editable_num_; static GtkWidget * edit_dialog_; -enum { - CALLTREE_CALLS, CALLTREE_HISTORY, CALLTREE_CONTACTS -}; - static void remove_from_toolbar(GtkWidget *widget) { @@ -114,7 +112,7 @@ remove_from_toolbar(GtkWidget *widget) gtk_container_remove(GTK_CONTAINER(toolbar_), widget); } -static bool +static gboolean is_non_empty(const char *str) { return str && strlen(str) > 0; @@ -137,7 +135,8 @@ call_mute(void) static void -update_toolbar_for_call(callable_obj_t *selectedCall, gboolean instant_messaging_enabled) { +update_toolbar_for_call(callable_obj_t *selectedCall, gboolean instant_messaging_enabled) +{ int pos = 0; DEBUG("Update actions for call %s", selectedCall->_callID); @@ -215,18 +214,13 @@ update_toolbar_for_call(callable_obj_t *selectedCall, gboolean instant_messaging if (calltab_has_name(active_calltree_tab, CURRENT_CALLS)) { add_to_toolbar(toolbar_, hangUpWidget_, pos++); main_window_hide_playback_scale(); - } - else if (calltab_has_name(active_calltree_tab, HISTORY)) { + } else if (calltab_has_name(active_calltree_tab, HISTORY)) { main_window_show_playback_scale(); - if (is_non_empty(selectedCall->_recordfile)) { + if (is_non_empty(selectedCall->_recordfile)) main_window_set_playback_scale_sensitive(); - } - else { + else main_window_set_playback_scale_unsensitive(); - } - - } - else { + } else { main_window_hide_playback_scale(); } break; @@ -237,39 +231,6 @@ update_toolbar_for_call(callable_obj_t *selectedCall, gboolean instant_messaging g_signal_handler_block(transferToolbar_, transferButtonConnId_); g_signal_handler_block(recordWidget_, recordButtonConnId_); - gtk_action_set_sensitive(hangUpAction_, TRUE); - gtk_action_set_sensitive(recordAction_, TRUE); - gtk_action_set_sensitive(muteAction_, TRUE); - gtk_widget_set_sensitive(holdMenu_, TRUE); - gtk_widget_set_sensitive(holdToolbar_, TRUE); - gtk_widget_set_sensitive(transferToolbar_, TRUE); - gtk_widget_set_sensitive(muteWidget_, TRUE); - if (instant_messaging_enabled) - gtk_action_set_sensitive(imAction_, TRUE); - - pos = 1; - add_to_toolbar(toolbar_, hangUpWidget_, pos++); - add_to_toolbar(toolbar_, holdToolbar_, pos++); - add_to_toolbar(toolbar_, transferToolbar_, pos++); - add_to_toolbar(toolbar_, recordWidget_, pos++); - add_to_toolbar(toolbar_, muteWidget_, pos++); - if (instant_messaging_enabled) { - add_to_toolbar(toolbar_, imToolbar_, pos++); - - gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(transferToolbar_), FALSE); - gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(recordWidget_), FALSE); - - g_signal_handler_unblock(transferToolbar_, transferButtonConnId_); - g_signal_handler_unblock(recordWidget_, recordButtonConnId_); - break; - } - - case CALL_STATE_RECORD: - { - DEBUG("Call State Record"); - g_signal_handler_block(transferToolbar_, transferButtonConnId_); - g_signal_handler_block(recordWidget_, recordButtonConnId_); - gtk_action_set_sensitive(hangUpAction_, TRUE); gtk_action_set_sensitive(recordAction_, TRUE); gtk_action_set_sensitive(muteAction_, TRUE); @@ -290,12 +251,13 @@ update_toolbar_for_call(callable_obj_t *selectedCall, gboolean instant_messaging add_to_toolbar(toolbar_, imToolbar_, pos++); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(transferToolbar_), FALSE); - gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(recordWidget_), TRUE); + gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(recordWidget_), dbus_get_is_recording(selectedCall)); g_signal_handler_unblock(transferToolbar_, transferButtonConnId_); g_signal_handler_unblock(recordWidget_, recordButtonConnId_); break; } + case CALL_STATE_BUSY: case CALL_STATE_FAILURE: { @@ -327,7 +289,6 @@ update_toolbar_for_call(callable_obj_t *selectedCall, gboolean instant_messaging default: ERROR("Unknown state in action update!"); break; - } } } @@ -362,14 +323,11 @@ update_toolbar_for_conference(conference_obj_t * selectedConf, gboolean instant_ main_window_hide_playback_scale(); } else if (calltab_has_name(active_calltree_tab, HISTORY)) { main_window_show_playback_scale(); - if (is_non_empty(selectedConf->_recordfile)) { + if (is_non_empty(selectedConf->_recordfile)) main_window_set_playback_scale_sensitive(); - } - else { + else main_window_set_playback_scale_unsensitive(); - } - } - else { + } else { main_window_hide_playback_scale(); } g_signal_handler_unblock(recordWidget_, recordButtonConnId_); @@ -693,14 +651,12 @@ call_im(void* foo UNUSED) if (calltab_get_selected_type(current_calls_tab) == A_CALL) { if (selectedCall) { - if (!selectedCall->_im_widget) - selectedCall->_im_widget = im_widget_display(selectedCall->_callID); + create_messaging_tab(selectedCall); } else WARN("Sorry. Instant messaging is not allowed outside a call\n"); } else { if (selectedConf) { - if (!selectedConf->_im_widget) - selectedConf->_im_widget = im_widget_display(selectedConf->_confID); + create_messaging_tab_conf(selectedConf); } else WARN("Sorry. Instant messaging is not allowed outside a call\n"); } @@ -917,7 +873,6 @@ edit_paste(void * foo UNUSED) } break; case CALL_STATE_CURRENT: - case CALL_STATE_RECORD: default: { for (unsigned i = 0; i < strlen(no); i++) { gchar * oneNo = g_strndup(&no[i], 1); @@ -976,10 +931,10 @@ call_mailbox_cb(void) { account_t *current = account_list_get_current(); - if (current == NULL) // Should not happens + if (current == NULL) // Should not happen return; - const gchar * const to = g_hash_table_lookup(current->properties, ACCOUNT_MAILBOX); + const gchar * const to = g_hash_table_lookup(current->properties, CONFIG_ACCOUNT_MAILBOX); const gchar * const account_id = g_strdup(current->accountID); callable_obj_t *mailbox_call = create_new_call(CALL, CALL_STATE_DIALING, @@ -993,14 +948,26 @@ call_mailbox_cb(void) calltree_display(current_calls_tab); } +static void +reset_scrollwindow_position(calltab_t *tab) +{ + GList *children = gtk_container_get_children(GTK_CONTAINER(tab->tree)); + /* Calltree scrolled window is first element in list */ + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW(children->data); + g_list_free(children); + GtkAdjustment *adjustment = gtk_scrolled_window_get_vadjustment(scrolled_window); + gtk_adjustment_set_value(adjustment, 0.0); +} + static void toggle_history_cb(GtkToggleAction *action, gpointer user_data UNUSED) { if (gtk_toggle_action_get_active(action)) { + /* Ensure that latest call is visible in history without scrolling */ + reset_scrollwindow_position(history_tab); calltree_display(history_tab); main_window_show_playback_scale(); - } - else { + } else { calltree_display(current_calls_tab); main_window_hide_playback_scale(); } @@ -1011,7 +978,7 @@ toggle_addressbook_cb(GtkToggleAction *action, gpointer user_data UNUSED) { if (gtk_toggle_action_get_active(action)) { calltree_display(contacts_tab); - main_window_show_playback_scale(); + main_window_hide_playback_scale(); } else { calltree_display(current_calls_tab); @@ -1245,9 +1212,9 @@ add_registered_accounts_to_menu(GtkWidget *menu) // Display only the registered accounts if (utf8_case_equal(account_state_name(acc->state), account_state_name(ACCOUNT_STATE_REGISTERED))) { - gchar *alias = g_strconcat(g_hash_table_lookup(acc->properties, ACCOUNT_ALIAS), + gchar *alias = g_strconcat(g_hash_table_lookup(acc->properties, CONFIG_ACCOUNT_ALIAS), " - ", - g_hash_table_lookup(acc->properties, ACCOUNT_TYPE), + g_hash_table_lookup(acc->properties, CONFIG_ACCOUNT_TYPE), NULL); GtkWidget *menu_items = gtk_check_menu_item_new_with_mnemonic(alias); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_items); @@ -1317,7 +1284,6 @@ show_popup_menu(GtkWidget *my_widget, GdkEventButton *event) hangup = TRUE; accounts = TRUE; break; - case CALL_STATE_RECORD: case CALL_STATE_CURRENT: hangup = TRUE; hold = TRUE; diff --git a/gnome/src/video/Makefile.am b/gnome/src/video/Makefile.am index 5a686a5c750789f6ca7269313493997baa59158a..174f433179f0872b961748a6b83114358c5e582a 100644 --- a/gnome/src/video/Makefile.am +++ b/gnome/src/video/Makefile.am @@ -1,8 +1,10 @@ include ../../globals.mak +if SFL_VIDEO + noinst_LTLIBRARIES = libvideo.la -libvideo_la_SOURCES = video_renderer.c +libvideo_la_SOURCES = video_renderer.c video_renderer.h video_callbacks.c video_callbacks.h shm_header.h libvideo_la_LDFLAGS = @CLUTTER_LDFLAGS@ @CLUTTERGTK_LDFLAGS@ @@ -11,3 +13,5 @@ libvideo_la_LIBADD = @CLUTTER_LIBS@ \ libvideo_la_CFLAGS = @CLUTTER_CFLAGS@ \ @GTK_CFLAGS@ @GLIB_CFLAGS@ @CLUTTERGTK_CFLAGS@ @DBUSGLIB_CFLAGS@ + +endif diff --git a/gnome/src/imwindow.h b/gnome/src/video/shm_header.h similarity index 54% rename from gnome/src/imwindow.h rename to gnome/src/video/shm_header.h index d7ef33b7aa9bff0c690cdf37e7100ffc3c6cd8d0..7cb8f6b5d31b94a63c7ed9df1300b0fb6e7ff9bc 100644 --- a/gnome/src/imwindow.h +++ b/gnome/src/video/shm_header.h @@ -1,5 +1,11 @@ /* - * Copyright (C) 2010 Savoir-Faire Linux Inc. + * Copyright (C) 2012 Savoir-Faire Linux Inc. + * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * + * Portions derived from GStreamer: + * Copyright (C) <2009> Collabora Ltd + * @author: Olivier Crete <olivier.crete@collabora.co.uk + * Copyright (C) <2009> Nokia Inc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,52 +33,19 @@ * as that of the covered work. */ -#ifndef __IMWINDOW_H__ -#define __IMWINDOW_H__ - -#include <gtk/gtk.h> - -#include "widget/imwidget.h" - -/** @file imwindow.h - * @brief The IM window of the client. - */ - -/*! @function -@abstract Add IM widget to the IM window - */ -void im_window_add (IMWidget *widget); - -/*! @function - @abstract Remove IM widget from the IM window - */ -void im_window_remove_tab (GtkWidget *widget); - -/** - * Return wether the instant messaging window have been created or not - */ -gboolean im_window_is_active (void); +#ifndef SHM_HEADER_H_ +#define SHM_HEADER_H_ -/** - * Return wether the instant messaging window is visible - */ -gboolean im_window_is_visible (void); +#include <semaphore.h> -/** - * Return the number of tabs already open in instant messaging window - */ -gint im_window_get_nb_tabs (void); +typedef struct { + sem_t notification; + sem_t mutex; -/*! @function -@abstract Add a new tab in the notebook. Each tab is an IM Widget -@param The IM widget -*/ -void im_window_add_tab (IMWidget *widget); + unsigned buffer_gen; + int buffer_size; -/*! @function -@abstract Select the specified tab as current in instant messaging window -@param The tab to be set as current -*/ -void im_window_show_tab (GtkWidget *widget); + char data[0]; +} SHMHeader; #endif diff --git a/gnome/src/video/video_callbacks.c b/gnome/src/video/video_callbacks.c new file mode 100644 index 0000000000000000000000000000000000000000..65cb6c990fe247a79b203066d27f6815e51aad4b --- /dev/null +++ b/gnome/src/video/video_callbacks.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2004-2012 Savoir-Faire Linux Inc. + * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#include "video_callbacks.h" +#include "video_renderer.h" + +#include <clutter/clutter.h> +#include <clutter-gtk/clutter-gtk.h> + +#include <string.h> +#include "logger.h" +#include "config/videoconf.h" +#include "unused.h" + +// FIXME: get rid of these +static GtkWidget *video_window_global = NULL; +static gboolean video_window_fullscreen = FALSE; + +static void +video_window_deleted_cb(GtkWidget *widget UNUSED, gpointer data UNUSED) +{ + if (dbus_has_video_preview_started()) + dbus_stop_video_preview(); +} + +static void +video_window_button_cb(GtkWindow *win, GdkEventButton *event, + gpointer fullscreen) +{ + int *fs = fullscreen; + if (event->type == GDK_2BUTTON_PRESS) { + *fs = !*fs; + if (*fs) + gtk_window_fullscreen(win); + else + gtk_window_unfullscreen(win); + } +} + +static gboolean +try_clutter_init() +{ +#define PRINT_ERR(X) \ + case (X): \ + ERROR("%s", #X); \ + break; + + switch (gtk_clutter_init(NULL, NULL)) { + case CLUTTER_INIT_SUCCESS: + return TRUE; + PRINT_ERR(CLUTTER_INIT_ERROR_UNKNOWN); + PRINT_ERR(CLUTTER_INIT_ERROR_THREADS); + PRINT_ERR(CLUTTER_INIT_ERROR_BACKEND); + PRINT_ERR(CLUTTER_INIT_ERROR_INTERNAL); + } + return FALSE; +#undef PRINT_ERR +} + +static gboolean +video_is_local(const gchar *id) +{ + static const gchar * const LOCAL_VIDEO_ID = "local"; + return g_strcmp0(id, LOCAL_VIDEO_ID) == 0; +} + +void started_decoding_video_cb(DBusGProxy *proxy UNUSED, + gchar *id, gchar *shm_path, gint width, gint height, + GError *error UNUSED, gpointer userdata UNUSED) +{ + if (!video_window_global) { + video_window_global = gtk_window_new(GTK_WINDOW_TOPLEVEL); + video_window_fullscreen = FALSE; + g_signal_connect(video_window_global, "button_press_event", + G_CALLBACK(video_window_button_cb), + &video_window_fullscreen); + g_signal_connect(video_window_global, "delete-event", + G_CALLBACK(video_window_deleted_cb), + NULL); + if (video_is_local(id)) + update_preview_button_label(); + } + + if (!try_clutter_init()) + return; + + GtkWidget *video_area = gtk_clutter_embed_new(); + ClutterActor *stage = gtk_clutter_embed_get_stage(GTK_CLUTTER_EMBED(video_area)); + if (!stage) + gtk_widget_destroy(video_area); + else { + ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; + clutter_stage_set_color(CLUTTER_STAGE(stage), &stage_color); + } + + GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); + gtk_container_add(GTK_CONTAINER(vbox), video_area); + + if (shm_path == 0 || strlen(shm_path) == 0) + return; + + gtk_widget_set_size_request(video_area, width, height); + if (video_window_global) { + gtk_container_add(GTK_CONTAINER(video_window_global), vbox); + gtk_widget_show_all(video_window_global); + } + + DEBUG("Video started for id: %s shm-path:%s width:%d height:%d", + id, shm_path, width, height); + + VideoRenderer *renderer = video_renderer_new(video_area, width, height, shm_path); + if (!video_renderer_run(renderer)) { + g_object_unref(renderer); + ERROR("Could not run video renderer"); + return; + } +} + +void +stopped_decoding_video_cb(DBusGProxy *proxy UNUSED, gchar *id, gchar *shm_path, GError *error UNUSED, gpointer userdata UNUSED) +{ + DEBUG("Video stopped for id %s, shm path %s", id, shm_path); + + if (video_window_global) { + if (GTK_IS_WIDGET(video_window_global)) { + gtk_widget_destroy(video_window_global); + if (video_is_local(id)) + update_preview_button_label(); + } + video_window_global = NULL; + } +} diff --git a/gnome/src/video/video_callbacks.h b/gnome/src/video/video_callbacks.h new file mode 100644 index 0000000000000000000000000000000000000000..bcacdfc5e3628ad8268490baa29e16bad0c4e5e4 --- /dev/null +++ b/gnome/src/video/video_callbacks.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2004-2012 Savoir-Faire Linux Inc. + * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. + * grants you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#ifndef VIDEO_CALLBACKS_H_ +#define VIDEO_CALLBACKS_H_ + +#include "dbus.h" + +void started_decoding_video_cb(DBusGProxy *proxy, gchar *id, gchar *shm_path, + gint width, gint height, GError *error, + gpointer userdata); + +void stopped_decoding_video_cb(DBusGProxy *proxy, gchar *id, gchar *shm_path, + GError *error, gpointer userdata); + +#endif // VIDEO_CALLBACKS_H_ diff --git a/gnome/src/video/video_renderer.c b/gnome/src/video/video_renderer.c index 4cf19ee5a37555bc1659d8923f91bc44448840fa..c53518443248ad048699d505509b4715a4af5b8b 100644 --- a/gnome/src/video/video_renderer.c +++ b/gnome/src/video/video_renderer.c @@ -29,204 +29,92 @@ */ #include "video_renderer.h" - -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/ipc.h> -#include <sys/sem.h> /* semaphore functions and structs. */ -#include <sys/shm.h> - -#include <assert.h> +#include "shm_header.h" +#include <sys/mman.h> +#include <fcntl.h> #include <string.h> +#include <errno.h> #include <clutter/clutter.h> #include <clutter-gtk/clutter-gtk.h> -#include "actions.h" #include "logger.h" #include "unused.h" -static GtkWidget *receivingVideoWindow = NULL; -static gboolean receivingWindowFullscreen = FALSE; -static GtkWidget *receivingVideoArea = NULL; -static VideoRenderer *video_renderer = NULL; - +/* This macro will implement the video_renderer_get_type function + and define a parent class pointer accessible from the whole .c file */ +G_DEFINE_TYPE(VideoRenderer, video_renderer, G_TYPE_OBJECT); #define VIDEO_RENDERER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ VIDEO_RENDERER_TYPE, VideoRendererPrivate)) -/* This macro will implement the video_renderer_get_type function - and define a parent class pointer accessible from the whole .c file */ -G_DEFINE_TYPE (VideoRenderer, video_renderer, G_TYPE_OBJECT); - enum { PROP_0, - PROP_RUNNING, PROP_WIDTH, PROP_HEIGHT, PROP_DRAWAREA, - PROP_SHMKEY, - PROP_SEMKEY, - PROP_VIDEO_BUFFER_SIZE, + PROP_SHM_PATH, PROP_LAST }; -static GParamSpec *properties[PROP_LAST]; +static GParamSpec *properties[PROP_LAST] = { NULL, }; -static void video_renderer_finalize (GObject *gobject); -static void video_renderer_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec); -static void video_renderer_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec); +static void video_renderer_finalize(GObject *gobject); +static void video_renderer_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void video_renderer_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); /* Our private member structure */ struct _VideoRendererPrivate { guint width; guint height; + gchar *shm_path; - gchar *shm_buffer; - gint sem_set_id; - - gint sem_key; - gint shm_key; - gint videobuffersize; ClutterActor *texture; gpointer drawarea; - - gboolean is_running; + gint fd; + SHMHeader *shm_area; + gsize shm_area_len; + guint buffer_gen; }; -/* See /bits/sem.h line 55 for why this is necessary */ -#if _SEM_SEMUN_UNDEFINED -union semun -{ - int val; /* value for SETVAL */ - struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ - unsigned short int *array; /* array for GETALL & SETALL */ - struct seminfo *__buf; /* buffer for IPC_INFO */ -}; -#endif - -/* join and/or create a shared memory segment */ -static int -getShm(unsigned numBytes, int shmKey) -{ - key_t key = shmKey; - /* connect to a segment with 600 permissions - (r--r--r--) */ - int shm_id = shmget(key, numBytes, 0644); - - if (shm_id == -1) - perror("shmget"); - - return shm_id; -} - - -/* attach a shared memory segment */ -static char * -attachShm(int shm_id) -{ - /* attach to the segment and get a pointer to it */ - char *data = shmat(shm_id, (void *)0, 0); - if (data == (char *)(-1)) { - perror("shmat"); - data = NULL; - } - - return data; -} - static void -detachShm(char *data) +video_renderer_class_init(VideoRendererClass *klass) { - /* detach from the segment: */ - if (shmdt(data) == -1) - perror("shmdt"); -} - -static int -get_sem_set(int semKey) -{ - /* this variable will contain the semaphore set. */ - int sem_set_id; - key_t key = semKey; - - /* semaphore value, for semctl(). */ - union semun sem_val; - - /* first we get a semaphore set with a single semaphore, */ - /* whose counter is initialized to '0'. */ - sem_set_id = semget(key, 1, 0600); - if (sem_set_id == -1) { - perror("semget"); - return sem_set_id; - } - sem_val.val = 0; - semctl(sem_set_id, 0, SETVAL, sem_val); - return sem_set_id; -} + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); -static void -video_renderer_class_init (VideoRendererClass *klass) -{ - int i; - GObjectClass *gobject_class; - gobject_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (VideoRendererPrivate)); + g_type_class_add_private(klass, sizeof(VideoRendererPrivate)); gobject_class->finalize = video_renderer_finalize; gobject_class->get_property = video_renderer_get_property; gobject_class->set_property = video_renderer_set_property; - properties[PROP_RUNNING] = g_param_spec_boolean ("running", "Running", - "True if renderer is running", - FALSE, - G_PARAM_READABLE); - - properties[PROP_DRAWAREA] = g_param_spec_pointer ("drawarea", "DrawArea", - "Pointer to the drawing area", - G_PARAM_READABLE|G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY); - - properties[PROP_WIDTH] = g_param_spec_int ("width", "Width", "Width of video", G_MININT, G_MAXINT, -1, - G_PARAM_READABLE|G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY); + properties[PROP_DRAWAREA] = g_param_spec_pointer("drawarea", "DrawArea", + "Pointer to the drawing area", + G_PARAM_READABLE|G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY); + properties[PROP_WIDTH] = g_param_spec_int("width", "Width", "Width of video", G_MININT, G_MAXINT, 0, + G_PARAM_READABLE|G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY); - properties[PROP_HEIGHT] = g_param_spec_int ("height", "Height", "Height of video", G_MININT, G_MAXINT, -1, - G_PARAM_READABLE|G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY); + properties[PROP_HEIGHT] = g_param_spec_int("height", "Height", "Height of video", G_MININT, G_MAXINT, 0, + G_PARAM_READABLE|G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY); - properties[PROP_SHMKEY] = g_param_spec_int ("shmkey", "ShmKey", "Unique key for shared memory identifier", G_MININT, G_MAXINT, -1, - G_PARAM_READABLE|G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY); + properties[PROP_SHM_PATH] = g_param_spec_string("shm-path", "ShmPath", "Unique path for shared memory", "", + G_PARAM_READABLE|G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY); - properties[PROP_SEMKEY] = g_param_spec_int ("semkey", "SemKey", "Unique key for semaphore identifier", G_MININT, G_MAXINT, -1, - G_PARAM_READABLE|G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY); - - properties[PROP_VIDEO_BUFFER_SIZE] = g_param_spec_int ("vbsize", "VideoBufferSize", "Size of shared memory buffer", G_MININT, G_MAXINT, -1, - G_PARAM_READABLE|G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY); - - for (i = PROP_0 + 1; i < PROP_LAST; i++) - g_object_class_install_property (gobject_class, i, properties[i]); + g_object_class_install_properties(gobject_class, + PROP_LAST, + properties); } static void -video_renderer_get_property (GObject *object, guint prop_id, +video_renderer_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - VideoRenderer *renderer; - VideoRendererPrivate *priv; - - renderer = VIDEO_RENDERER(object); - priv = renderer->priv; + VideoRenderer *renderer = VIDEO_RENDERER(object); + VideoRendererPrivate *priv = renderer->priv; - switch (prop_id) - { - case PROP_RUNNING: - g_value_set_boolean (value, priv->is_running); - break; + switch (prop_id) { case PROP_DRAWAREA: g_value_set_pointer(value, priv->drawarea); break; @@ -236,16 +124,9 @@ video_renderer_get_property (GObject *object, guint prop_id, case PROP_HEIGHT: g_value_set_int(value, priv->height); break; - case PROP_SHMKEY: - g_value_set_int(value, priv->shm_key); - break; - case PROP_SEMKEY: - g_value_set_int(value, priv->sem_key); - break; - case PROP_VIDEO_BUFFER_SIZE: - g_value_set_int(value, priv->videobuffersize); + case PROP_SHM_PATH: + g_value_set_string(value, priv->shm_path); break; - default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -259,8 +140,7 @@ video_renderer_set_property (GObject *object, guint prop_id, VideoRenderer *renderer = VIDEO_RENDERER(object); VideoRendererPrivate *priv = renderer->priv; - switch (prop_id) - { + switch (prop_id) { case PROP_DRAWAREA: priv->drawarea = g_value_get_pointer(value); break; @@ -270,94 +150,197 @@ video_renderer_set_property (GObject *object, guint prop_id, case PROP_HEIGHT: priv->height = g_value_get_int(value); break; - case PROP_SHMKEY: - priv->shm_key = g_value_get_int(value); - break; - case PROP_SEMKEY: - priv->sem_key = g_value_get_int(value); - break; - case PROP_VIDEO_BUFFER_SIZE: - priv->videobuffersize = g_value_get_int(value); + case PROP_SHM_PATH: + g_free(priv->shm_path); + priv->shm_path = g_value_dup_string(value); break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void -video_renderer_init (VideoRenderer *self) +video_renderer_init(VideoRenderer *self) { - self->priv = VIDEO_RENDERER_GET_PRIVATE(self); + VideoRendererPrivate *priv; + self->priv = priv = VIDEO_RENDERER_GET_PRIVATE(self); + priv->width = 0; + priv->height = 0; + priv->shm_path = NULL; + priv->texture = NULL; + priv->drawarea = NULL; + priv->fd = -1; + priv->shm_area = MAP_FAILED; + priv->shm_area_len = 0; + priv->buffer_gen = 0; } static void -video_renderer_finalize (GObject *obj) +video_renderer_stop_shm(VideoRenderer *self) { - VideoRenderer *self = VIDEO_RENDERER (obj); - if (self->priv->shm_buffer) - detachShm(self->priv->shm_buffer); + g_return_if_fail(IS_VIDEO_RENDERER(self)); + VideoRendererPrivate *priv = VIDEO_RENDERER_GET_PRIVATE(self); + if (priv->fd >= 0) + close(priv->fd); + priv->fd = -1; + + if (priv->shm_area != MAP_FAILED) + munmap(priv->shm_area, priv->shm_area_len); + priv->shm_area_len = 0; + priv->shm_area = MAP_FAILED; +} + + +static void +video_renderer_finalize(GObject *obj) +{ + VideoRenderer *self = VIDEO_RENDERER(obj); + video_renderer_stop_shm(self); /* Chain up to the parent class */ - G_OBJECT_CLASS (video_renderer_parent_class)->finalize (obj); + G_OBJECT_CLASS(video_renderer_parent_class)->finalize(obj); } -/* - * function: sem_wait. wait for frame from other process - * input: semaphore set ID. - * output: none. - */ -static int -sem_wait(int sem_set_id) +static gboolean +video_renderer_start_shm(VideoRenderer *self) +{ + /* First test that 'self' is of the correct type */ + g_return_val_if_fail(IS_VIDEO_RENDERER(self), FALSE); + VideoRendererPrivate *priv = VIDEO_RENDERER_GET_PRIVATE(self); + if (priv->fd != -1) { + ERROR("fd must be -1"); + return FALSE; + } + + priv->fd = shm_open(priv->shm_path, O_RDWR, 0); + if (priv->fd < 0) { + DEBUG("could not open shm area \"%s\", shm_open failed:%s", priv->shm_path, strerror(errno)); + return FALSE; + } + priv->shm_area_len = sizeof(SHMHeader); + priv->shm_area = mmap(NULL, priv->shm_area_len, PROT_READ | PROT_WRITE, MAP_SHARED, priv->fd, 0); + if (priv->shm_area == MAP_FAILED) { + DEBUG("Could not map shm area, mmap failed"); + return FALSE; + } + return TRUE; +} + +static const gint TIMEOUT_SEC = 1; // 1 second + +static struct timespec +create_timeout() { - /* structure for semaphore operations. */ - struct sembuf sem_op; - - /* wait on the semaphore, unless it's value is non-negative. */ - sem_op.sem_num = 0; - sem_op.sem_op = -1; - sem_op.sem_flg = IPC_NOWAIT; - return semop(sem_set_id, &sem_op, 1); + struct timespec timeout = {0, 0}; + if (clock_gettime(CLOCK_REALTIME, &timeout) == -1) + perror("clock_gettime"); + timeout.tv_sec += TIMEOUT_SEC; + return timeout; } -static void render_clutter(ClutterActor *texture, gpointer data, int width, - int height, int parent_width, int parent_height) + +static gboolean +shm_lock(SHMHeader *shm_area) { - clutter_actor_set_size(texture, parent_width, parent_height); + const struct timespec timeout = create_timeout(); + /* We need an upper limit on how long we'll wait to avoid locking the whole GUI */ + if (sem_timedwait(&shm_area->mutex, &timeout) == ETIMEDOUT) { + ERROR("Timed out before shm lock was acquired"); + return FALSE; + } + return TRUE; +} - const int ROW_STRIDE = 4 * width; - const int BPP = 4; - clutter_texture_set_from_rgb_data(CLUTTER_TEXTURE(texture), data, TRUE, - width, height, ROW_STRIDE, BPP, - CLUTTER_TEXTURE_RGB_FLAG_BGR, NULL); +static void +shm_unlock(SHMHeader *shm_area) +{ + sem_post(&shm_area->mutex); } static gboolean -readFrameFromShm(VideoRendererPrivate *priv) +video_renderer_resize_shm(VideoRendererPrivate *priv) { - int sem_set_id = priv->sem_set_id; + while ((sizeof(SHMHeader) + priv->shm_area->buffer_size) > priv->shm_area_len) { + const size_t new_size = sizeof(SHMHeader) + priv->shm_area->buffer_size; - if (sem_set_id == -1) - return FALSE; + shm_unlock(priv->shm_area); + if (munmap(priv->shm_area, priv->shm_area_len)) { + DEBUG("Could not unmap shared area:%s", strerror(errno)); + return FALSE; + } - if (sem_wait(sem_set_id) == -1) { - if (errno != EAGAIN) { - ERROR("Could not read from shared memory!\n"); - perror("shm: "); + priv->shm_area = mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, priv->fd, 0); + priv->shm_area_len = new_size; + + if (!priv->shm_area) { + priv->shm_area = 0; + DEBUG("Could not remap shared area"); return FALSE; - } else { - return TRUE; /* No new frame, so we'll try later */ } + + priv->shm_area_len = new_size; + if (!shm_lock(priv->shm_area)) + return FALSE; + } + return TRUE; +} + +static void +video_renderer_render_to_texture(VideoRendererPrivate *priv) +{ + if (!shm_lock(priv->shm_area)) + return; + + // wait for a new buffer + while (priv->buffer_gen == priv->shm_area->buffer_gen) { + shm_unlock(priv->shm_area); + const struct timespec timeout = create_timeout(); + // Could not decrement semaphore in time, returning + if (sem_timedwait(&priv->shm_area->notification, &timeout) < 0) + return; + + if (!shm_lock(priv->shm_area)) + return; } + if (!video_renderer_resize_shm(priv)) { + ERROR("Could not resize shared memory"); + return; + } + + const gint BPP = 4; + const gint ROW_STRIDE = BPP * priv->width; + /* update the clutter texture */ + clutter_texture_set_from_rgb_data(CLUTTER_TEXTURE(priv->texture), + (guchar*) priv->shm_area->data, + TRUE, + priv->width, + priv->height, + ROW_STRIDE, + BPP, + CLUTTER_TEXTURE_RGB_FLAG_BGR, + NULL); + priv->buffer_gen = priv->shm_area->buffer_gen; + shm_unlock(priv->shm_area); +} + + +static gboolean +render_frame_from_shm(VideoRendererPrivate *priv) +{ + if (!GTK_IS_WIDGET(priv->drawarea)) + return FALSE; GtkWidget *parent = gtk_widget_get_parent(priv->drawarea); - const gint p_width = gtk_widget_get_allocated_width(parent); - const gint p_height = gtk_widget_get_allocated_height(parent); + if (!parent) + return FALSE; + const gint parent_width = gtk_widget_get_allocated_width(parent); + const gint parent_height = gtk_widget_get_allocated_height(parent); - render_clutter(priv->texture, priv->shm_buffer, priv->width, - priv->height, p_width, p_height); + clutter_actor_set_size(priv->texture, parent_width, parent_height); + video_renderer_render_to_texture(priv); return TRUE; } @@ -365,36 +348,21 @@ readFrameFromShm(VideoRendererPrivate *priv) void video_renderer_stop(VideoRenderer *renderer) { VideoRendererPrivate *priv = VIDEO_RENDERER_GET_PRIVATE(renderer); - g_assert(priv); - gtk_widget_hide(GTK_WIDGET(priv->drawarea)); - - priv->is_running = FALSE; - -#if GLIB_CHECK_VERSION(2,26,0) - g_object_notify_by_pspec(G_OBJECT(renderer), properties[PROP_RUNNING]); -#else - g_object_notify(G_OBJECT(renderer), "running"); -#endif + if (priv && priv->drawarea && GTK_IS_WIDGET(priv->drawarea)) + gtk_widget_hide(GTK_WIDGET(priv->drawarea)); g_object_unref(G_OBJECT(renderer)); } static gboolean -updateTexture(gpointer data) +update_texture(gpointer data) { VideoRenderer *renderer = (VideoRenderer *) data; VideoRendererPrivate *priv = VIDEO_RENDERER_GET_PRIVATE(renderer); - if (!priv->is_running) { - /* renderer was stopped already */ - g_object_unref(G_OBJECT(data)); - return FALSE; - } - - gboolean ret = readFrameFromShm(priv); + const gboolean ret = render_frame_from_shm(priv); if (!ret) { - priv->is_running = FALSE; // no need to notify daemon video_renderer_stop(data); g_object_unref(G_OBJECT(data)); } @@ -408,42 +376,32 @@ updateTexture(gpointer data) * Create a new #VideoRenderer instance. */ VideoRenderer * -video_renderer_new(GtkWidget *drawarea, int width, int height, int shmkey, - int semkey, int vbsize) +video_renderer_new(GtkWidget *drawarea, gint width, gint height, gchar *shm_path) { - return g_object_new(VIDEO_RENDERER_TYPE, "drawarea", (gpointer) drawarea, - "width", width, "height", height, "shmkey", shmkey, - "semkey", semkey, "vbsize", vbsize, NULL); + VideoRenderer *rend = g_object_new(VIDEO_RENDERER_TYPE, "drawarea", (gpointer) drawarea, + "width", width, "height", height, "shm-path", shm_path, NULL); + if (!video_renderer_start_shm(rend)) { + ERROR("Could not start SHM"); + return NULL; + } + return rend; } -int -video_renderer_run(VideoRenderer *renderer) +gboolean +video_renderer_run(VideoRenderer *self) { - VideoRendererPrivate * priv = VIDEO_RENDERER_GET_PRIVATE(renderer); - priv->shm_buffer = NULL; - - int shm_id = getShm(priv->videobuffersize, priv->shm_key); - if (shm_id == -1) - return 1; - - priv->shm_buffer = attachShm(shm_id); - if (!priv->shm_buffer) - return 1; - - priv->sem_set_id = get_sem_set(priv->sem_key); - if (priv->sem_set_id == -1) - return 1; + g_return_val_if_fail(IS_VIDEO_RENDERER(self), FALSE); + VideoRendererPrivate * priv = VIDEO_RENDERER_GET_PRIVATE(self); + g_return_val_if_fail(priv->fd > 0, FALSE); GtkWindow *win = GTK_WINDOW(gtk_widget_get_toplevel(priv->drawarea)); GdkGeometry geom = { - .min_aspect = (double) priv->width / priv->height, - .max_aspect = (double) priv->width / priv->height, + .min_aspect = (gdouble) priv->width / priv->height, + .max_aspect = (gdouble) priv->width / priv->height, }; gtk_window_set_geometry_hints(win, NULL, &geom, GDK_HINT_ASPECT); - if (GTK_CLUTTER_IS_EMBED(priv->drawarea)) - DEBUG("video_renderer: using clutter\n"); - else + if (!GTK_CLUTTER_IS_EMBED(priv->drawarea)) ERROR("Drawing area is not a GtkClutterEmbed widget"); ClutterActor *stage = gtk_clutter_embed_get_stage(GTK_CLUTTER_EMBED(priv->drawarea)); @@ -455,134 +413,11 @@ video_renderer_run(VideoRenderer *renderer) clutter_actor_show_all(stage); /* frames are read and saved here */ - g_object_ref(renderer); - const gdouble FPS = 25.0; - g_timeout_add(1000 / FPS, updateTexture, renderer); - - priv->is_running = TRUE; - /* emit the notify signal for this property */ -#if GLIB_CHECK_VERSION(2, 26, 0) - g_object_notify_by_pspec(G_OBJECT(renderer), properties[PROP_RUNNING]); -#else - g_object_notify(G_OBJECT(data), "running"); -#endif - gtk_widget_show_all(GTK_WIDGET(priv->drawarea)); - - return 0; -} + g_object_ref(self); + const gint FRAME_INTERVAL = 30; // ms + g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, FRAME_INTERVAL, update_texture, self, NULL); -static void -receiving_video_window_deleted_cb(GtkWidget *widget UNUSED, - gpointer data UNUSED) -{ - sflphone_hang_up(); -} - -static void -receiving_video_window_button_cb(GtkWindow *win, GdkEventButton *event, - gpointer fullscreen) -{ - int *fs = fullscreen; - if (event->type == GDK_2BUTTON_PRESS) { - *fs = !*fs; - if (*fs) - gtk_window_fullscreen(win); - else - gtk_window_unfullscreen(win); - } -} - -gboolean -try_clutter_init() -{ - const ClutterInitError clutter_ok = gtk_clutter_init(NULL, NULL); - gboolean result = FALSE; - switch (clutter_ok) { - case CLUTTER_INIT_SUCCESS: - result = TRUE; - break; - case CLUTTER_INIT_ERROR_UNKNOWN: - ERROR("Unknown clutter error"); - break; - case CLUTTER_INIT_ERROR_THREADS: - ERROR("Clutter thread init error"); - break; - case CLUTTER_INIT_ERROR_BACKEND: - ERROR("Clutter backend init error"); - break; - case CLUTTER_INIT_ERROR_INTERNAL: - ERROR("Clutter internal init error"); - break; - } - return result; -} - -void receiving_video_event_cb(DBusGProxy *proxy UNUSED, gint shmKey, - gint semKey, gint videoBufferSize, - gint destWidth, gint destHeight, - GError *error UNUSED, gpointer userdata UNUSED) -{ - if (!receivingVideoWindow) { - receivingVideoWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); - receivingWindowFullscreen = FALSE; - g_signal_connect(receivingVideoWindow, "button_press_event", - G_CALLBACK(receiving_video_window_button_cb), - &receivingWindowFullscreen); - g_signal_connect(receivingVideoWindow, "delete-event", - G_CALLBACK(receiving_video_window_deleted_cb), NULL); - } - - if (!try_clutter_init()) - return; - - if (!receivingVideoArea) { - receivingVideoArea = gtk_clutter_embed_new(); - ClutterActor *stage = gtk_clutter_embed_get_stage(GTK_CLUTTER_EMBED(receivingVideoArea)); - if (!stage) - gtk_widget_destroy(receivingVideoArea); - else { - ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; - clutter_stage_set_color(CLUTTER_STAGE(stage), &stage_color); - } - } - - g_assert(receivingVideoArea); - GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); - gtk_container_add(GTK_CONTAINER(vbox), receivingVideoArea); - - if (shmKey == -1 || semKey == -1 || videoBufferSize == -1) - return; - - gtk_widget_set_size_request(receivingVideoArea, destWidth, destHeight); - gtk_container_add(GTK_CONTAINER(receivingVideoWindow), vbox); - gtk_widget_show_all(receivingVideoWindow); - - DEBUG("Video started for shm:%d sem:%d bufferSz:%d width:%d height:%d", - shmKey, semKey, videoBufferSize, destWidth, destHeight); - - video_renderer = video_renderer_new(receivingVideoArea, destWidth, - destHeight, shmKey, semKey, - videoBufferSize); - g_assert(video_renderer); - if (video_renderer_run(video_renderer)) { - g_object_unref(video_renderer); - video_renderer = NULL; - ERROR("Could not run video renderer"); - } -} - -void stopped_receiving_video_event_cb(DBusGProxy *proxy UNUSED, gint shmKey, - gint semKey, GError *error UNUSED, - gpointer userdata UNUSED) -{ - DEBUG("Video stopped for shm:%d sem:%d", shmKey, semKey); + gtk_widget_show_all(GTK_WIDGET(priv->drawarea)); - if (video_renderer) { - if (receivingVideoWindow) { - if (GTK_IS_WIDGET(receivingVideoWindow)) - gtk_widget_destroy(receivingVideoWindow); - receivingVideoArea = receivingVideoWindow = NULL; - } - video_renderer = NULL; - } + return TRUE; } diff --git a/gnome/src/video/video_renderer.h b/gnome/src/video/video_renderer.h index d10fd381a1bd534394fd2352d1093ec0908a269d..400da4467e382e4e1c0d8409bdcd1bbf64424388 100644 --- a/gnome/src/video/video_renderer.h +++ b/gnome/src/video/video_renderer.h @@ -28,14 +28,12 @@ * as that of the covered work. */ -#ifndef __VIDEO_RENDERER_H__ -#define __VIDEO_RENDERER_H__ +#ifndef VIDEO_RENDERER_H__ +#define VIDEO_RENDERER_H__ #include <glib-object.h> #include <gtk/gtk.h> -#include "dbus.h" - G_BEGIN_DECLS #define VIDEO_RENDERER_TYPE (video_renderer_get_type()) @@ -43,9 +41,6 @@ G_BEGIN_DECLS #define VIDEO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), VIDEO_RENDERER_TYPE, VideoRendererClass)) #define IS_VIDEO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), VIDEO_RENDERER_TYPE)) #define IS_VIDEO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), VIDEO_RENDERER_TYPE)) -#define VIDEO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), VIDEO_RENDERER_TYPE, VideoRendererClass)) -#define VIDEO_RENDERER_CAST(obj) ((VideoRenderer*)(obj)) -#define VIDEO_RENDERER_CLASS_CAST(klass) ((VideoRendererClass*)(klass)) typedef struct _VideoRenderer VideoRenderer; typedef struct _VideoRendererClass VideoRendererClass; @@ -63,18 +58,14 @@ struct _VideoRendererClass { }; /* Public interface */ -VideoRenderer *video_renderer_new(GtkWidget *drawarea, int width, int height, int shmkey, int semkey, int vbsize); -int video_renderer_run(VideoRenderer *preview); -void video_renderer_stop(VideoRenderer *preview); +VideoRenderer * +video_renderer_new(GtkWidget *drawarea, gint width, gint height, gchar *shm_path); -void receiving_video_event_cb(DBusGProxy *proxy, gint shmId, gint semId, - gint videoBufferSize, gint destWidth, - gint destHeight, GError *error, - gpointer userdata); -void stopped_receiving_video_event_cb(DBusGProxy *proxy, gint shmId, gint semId, GError *error, gpointer userdata); +gboolean +video_renderer_run(VideoRenderer *self); -/* Try to init the gtk clutter backend, returns TRUE on success, FALSE otherwise */ -gboolean try_clutter_init(); +void +video_renderer_stop(VideoRenderer *self); G_END_DECLS diff --git a/gnome/src/widget/Makefile.am b/gnome/src/widget/Makefile.am index 3e99001d975b140366330fef7ef652b7c18e961b..3aad2480af3d1cba2f8e22e836b5066f2698ec7b 100644 --- a/gnome/src/widget/Makefile.am +++ b/gnome/src/widget/Makefile.am @@ -2,13 +2,13 @@ include ../../globals.mak noinst_LTLIBRARIES = libwidget.la -libwidget_la_SOURCES = minidialog.h minidialog.c imwidget.c imwidget.h +libwidget_la_SOURCES = minidialog.h minidialog.c libwidget_la_LDFLAGS = $(DBUSGLIB_LDFLAGS) $(LIBNOTIFY_LDFLAGS) \ - $(GTK_LDFLAGS) $(GLIB_LDFLAGS) $(WEBKIT_LDFLAGS) + $(GTK_LDFLAGS) $(GLIB_LDFLAGS) libwidget_la_LIBADD = $(DBUSGLIB_LIBS) $(LIBNOTIFY_LIBS) \ - $(GTK_LIBS) $(GLIB_LIBS) $(WEBKIT_LIBS) + $(GTK_LIBS) $(GLIB_LIBS) libwidget_la_CFLAGS = $(DBUSGLIB_CFLAGS) $(LIBNOTIFY_CFLAGS) \ - $(GTK_CFLAGS) $(GLIB_CFLAGS) $(WEBKIT_CFLAGS) + $(GTK_CFLAGS) $(GLIB_CFLAGS) diff --git a/gnome/src/widget/imwidget.c b/gnome/src/widget/imwidget.c deleted file mode 100644 index bf03d843106ceaa699ff2434c4fa23654bf39711..0000000000000000000000000000000000000000 --- a/gnome/src/widget/imwidget.c +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright (C) 2010 Savoir-Faire Linux Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Additional permission under GNU GPL version 3 section 7: - * - * If you modify this program, or any covered work, by linking or - * combining it with the OpenSSL project's OpenSSL library (or a - * modified version of that library), containing parts covered by the - * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. - * grants you additional permission to convey the resulting work. - * Corresponding Source for a non-source form of such a combination - * shall include the source code for the parts of OpenSSL used as well - * as that of the covered work. - */ - -#include "imwindow.h" -#include "logger.h" -#include "gtk2_wrappers.h" -#include "imwidget.h" -#include "dbus.h" -#include "unused.h" -#include "icons/icon_factory.h" -#include "contacts/calltab.h" -#include "contacts/conferencelist.h" -#include <string.h> -#include <JavaScriptCore/JavaScript.h> -#include <gdk/gdkkeysyms.h> -#include <gtk/gtk.h> - - -#define WEBKIT_DIR "file://" DATA_DIR "/webkit/" - -static void -on_frame_loading_done(GObject *gobject UNUSED, GParamSpec *pspec UNUSED, gpointer user_data) -{ - IMWidget *im = IM_WIDGET(user_data); - - if (!im->first_message || !im->first_message_from) - return; - - switch (webkit_web_frame_get_load_status(WEBKIT_WEB_FRAME(im->web_frame))) { - case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT: - case WEBKIT_LOAD_PROVISIONAL: - case WEBKIT_LOAD_COMMITTED: - break; - case WEBKIT_LOAD_FINISHED: - - if (calllist_get_call(current_calls_tab, im->call_id)) - im_widget_add_message(im, im->first_message_from, im->first_message, 0); - - if (conferencelist_get(current_calls_tab, im->call_id)) - im_widget_add_message(im, im->first_message_from, im->first_message, 0); - - g_free(im->first_message); - g_free(im->first_message_from); - im->first_message = NULL; - im->first_message_from = NULL; - break; - case WEBKIT_LOAD_FAILED: - DEBUG("InstantMessaging: Webkit load failed"); - break; - } -} - -static gchar * -escape_single_quotes(const gchar *message) -{ - gchar **ptr_token = g_strsplit(message, "'", 0); - gchar *string = g_strjoinv("\\'", ptr_token); - - g_strfreev(ptr_token); - return string; -} - -static gchar* -im_widget_add_message_time() -{ - time_t now; - char str[100]; - - /* Compute the current time */ - time(&now); - const struct tm* ptr = localtime(&now); - - /* Get the time of the message. Format: HH:MM::SS */ - strftime(str, sizeof(str), "%R", (const struct tm *) ptr); - return g_strdup(str); -} - -void -im_widget_add_message(IMWidget *im, const gchar *from, const gchar *message, gint level) -{ - g_assert(im); - - /* Compute the date the message was sent */ - gchar *msgtime = im_widget_add_message_time(); - - gchar *css_class = (level == MESSAGE_LEVEL_ERROR) ? "error" : ""; - - gchar *message_escaped = escape_single_quotes(message); - - /* Prepare and execute the Javascript code */ - gchar *script = g_strdup_printf("add_message('%s', '%s', '%s', '%s');", message_escaped, from, css_class, msgtime); - webkit_web_view_execute_script(WEBKIT_WEB_VIEW(im->web_view), script); - - /* Mark it as used */ - im->containText = TRUE; - - /* Cleanup */ - g_free(script); - g_free(message_escaped); - g_free(msgtime); -} - -static gboolean -web_view_nav_requested_cb( - WebKitWebView *web_view UNUSED, - WebKitWebFrame *frame UNUSED, - WebKitNetworkRequest *request, - WebKitWebNavigationAction *navigation_action UNUSED, - WebKitWebPolicyDecision *policy_decision, - gpointer user_data UNUSED) -{ - const gchar *uri = webkit_network_request_get_uri(request); - - /* Always allow files we are serving ourselves. */ - if (!strncmp(uri, WEBKIT_DIR, sizeof(WEBKIT_DIR) - 1)) { - webkit_web_policy_decision_use(policy_decision); - } else { - /* Running a system command to open the URL in the user's default browser */ - gchar *cmd = g_strdup_printf("x-www-browser %s", uri); - - if (system(cmd) == -1) - ERROR("InstantMessaging: Error: executing command %s", cmd); - - webkit_web_policy_decision_ignore(policy_decision); - g_free(cmd); - } - - return TRUE; -} - -static gboolean -on_Textview_changed(GtkWidget *widget UNUSED, GdkEventKey *event, gpointer user_data) -{ - - GtkTextIter start, end; - /* Get all the text in the buffer */ - IMWidget *im = user_data; - - GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(im->textarea)); - - /* Catch the keyboard events */ - if (event->type == GDK_KEY_PRESS) { - - switch (event->keyval) { - case GDK_KEY_Return: - case GDK_KEY_KP_Enter: - - /* We want to send the message on pressing ENTER */ - if (gtk_text_buffer_get_char_count(buffer) != 0) { - /* Fetch the string text */ - gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buffer), &start, &end); - gchar *message = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); - - /* Display our own message in the chat window */ - im_widget_add_message(im, "Me", message, MESSAGE_LEVEL_NORMAL); - - /* Send the message to the peer */ - im_widget_send_message(im->call_id, message); - - /* Empty the buffer */ - gtk_text_buffer_delete(GTK_TEXT_BUFFER(buffer), &start, &end); - - } - - return TRUE; - } - } - - return FALSE; -} - -void -im_widget_send_message(const gchar *id, const gchar *message) -{ - callable_obj_t *im_widget_call = calllist_get_call(current_calls_tab, id); - conference_obj_t *im_widget_conf = conferencelist_get(current_calls_tab, id); - - /* If the call has been hungup, it is not anymore in the current_calls calltab */ - /* So try the history tab */ - if (!im_widget_call) - im_widget_call = calllist_get_call(history_tab, id); - - if (im_widget_conf) - dbus_send_text_message(id, message); - else if (im_widget_call) { - if (im_widget_call->_type == CALL && (im_widget_call->_state == CALL_STATE_CURRENT || - im_widget_call->_state == CALL_STATE_HOLD || - im_widget_call->_state == CALL_STATE_RECORD)) { - /* Ship the message through D-Bus */ - dbus_send_text_message(id, message); - } else { - /* Display an error message */ - im_widget_add_message(IM_WIDGET(im_widget_call->_im_widget), "sflphoned", "Oups, something went wrong! Unable to send text messages outside a call.", MESSAGE_LEVEL_ERROR); - } - } -} - - -static void -im_widget_class_init(IMWidgetClass *klass UNUSED) -{ -} - -static void -im_widget_init(IMWidget *im) -{ - /* A text view to enable users to enter text */ - im->textarea = gtk_text_view_new(); - - /* The webkit widget to display the message */ - im->web_view = webkit_web_view_new(); - GtkWidget *textscrollwin = gtk_scrolled_window_new(NULL, NULL); - GtkWidget *webscrollwin = gtk_scrolled_window_new(NULL, NULL); - im->info_bar = gtk_info_bar_new(); - - /* A bar with the entry text and the button to send the message */ - GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); - gtk_text_view_set_editable(GTK_TEXT_VIEW(im->textarea), TRUE); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(textscrollwin), GTK_POLICY_NEVER, GTK_POLICY_NEVER); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(webscrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_widget_set_size_request(GTK_WIDGET(textscrollwin), -1, 20); - gtk_widget_set_size_request(GTK_WIDGET(im->textarea), -1, 20); - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(im->textarea), GTK_WRAP_CHAR); - // gtk_container_set_resize_mode(GTK_CONTAINER(im->textarea), GTK_RESIZE_PARENT); - - gtk_container_add(GTK_CONTAINER(textscrollwin), im->textarea); - gtk_container_add(GTK_CONTAINER(webscrollwin), im->web_view); - gtk_container_add(GTK_CONTAINER(box), textscrollwin); - gtk_box_pack_start(GTK_BOX(im), im->info_bar, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(im), webscrollwin, TRUE, TRUE, 5); - gtk_box_pack_end(GTK_BOX(im), box, FALSE, FALSE, 2); - g_signal_connect(im->web_view, "navigation-policy-decision-requested", G_CALLBACK(web_view_nav_requested_cb), NULL); - g_signal_connect(im->textarea, "key-press-event", G_CALLBACK(on_Textview_changed), im); - - im->web_frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(im->web_view)); - im->js_context = webkit_web_frame_get_global_context(im->web_frame); - im->js_global = JSContextGetGlobalObject(im->js_context); - webkit_web_view_load_uri(WEBKIT_WEB_VIEW(im->web_view), "file://" DATA_DIR "/webkit/im/im.html"); - - im->containText = FALSE; - - g_signal_connect(G_OBJECT(im->web_frame), "notify", G_CALLBACK(on_frame_loading_done), im); - /* See - * http://developer.gnome.org/gtk3/stable/GtkVBox.html#GtkVBox.description - */ - gtk_orientable_set_orientation (GTK_ORIENTABLE (im), - GTK_ORIENTATION_VERTICAL); - -} - -GType -im_widget_get_type(void) -{ - static GType im_widget_type = 0; - - if (!im_widget_type) { - static const GTypeInfo im_widget_info = { - sizeof(IMWidgetClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) im_widget_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof(IMWidget), - 0, - (GInstanceInitFunc) im_widget_init, - NULL /* value_table */ - }; - - im_widget_type = g_type_register_static( - GTK_TYPE_BOX, - "IMWidget", - &im_widget_info, - 0); - } - - return im_widget_type; -} - - -static GtkWidget* -conf_state_image_widget(conference_state_t state) -{ - switch (state) { - case CONFERENCE_STATE_ACTIVE_ATTACHED: - case CONFERENCE_STATE_ACTIVE_DETACHED: - case CONFERENCE_STATE_ACTIVE_ATTACHED_RECORD: - case CONFERENCE_STATE_ACTIVE_DETACHED_RECORD: - case CONFERENCE_STATE_HOLD: - case CONFERENCE_STATE_HOLD_RECORD: - return gtk_image_new_from_stock(GTK_STOCK_IM, GTK_ICON_SIZE_LARGE_TOOLBAR); - default: - return gtk_image_new_from_stock(GTK_STOCK_FAIL, GTK_ICON_SIZE_LARGE_TOOLBAR); - } -} - -static GtkWidget* -call_state_image_widget(call_state_t state) -{ - switch (state) { - case CALL_STATE_CURRENT: - case CALL_STATE_HOLD: - case CALL_STATE_RECORD: - return gtk_image_new_from_stock(GTK_STOCK_IM, GTK_ICON_SIZE_LARGE_TOOLBAR); - default: - return gtk_image_new_from_stock(GTK_STOCK_IM, GTK_ICON_SIZE_LARGE_TOOLBAR); - } -} - -static void -im_widget_infobar(IMWidget *im) -{ - GtkWidget *infobar = im->info_bar; - GtkWidget *content_area = gtk_info_bar_get_content_area(GTK_INFO_BAR(infobar)); - - /* Fetch call/conference information */ - callable_obj_t *im_widget_call = calllist_get_call(current_calls_tab, im->call_id); - conference_obj_t *im_widget_conf = conferencelist_get(current_calls_tab, im->call_id); - - /* Create the label widgets with the call information saved in the IM Widget struct */ - gchar *msg1; - - if (im_widget_call) - msg1 = g_strdup_printf("Calling %s %s", im_widget_call->_peer_number, im_widget_call->_display_name); - else if (im_widget_conf) - msg1 = g_strdup_printf("Conferencing"); - else - msg1 = g_strdup(""); - - GtkWidget *call_label = gtk_label_new(msg1); - g_free(msg1); - - if (im_widget_call) - im->info_state = call_state_image_widget(im_widget_call->_state); - - if (im_widget_conf) - im->info_state = conf_state_image_widget(im_widget_conf->_state); - - GtkWidget *logoUser = gtk_image_new_from_stock(GTK_STOCK_USER, GTK_ICON_SIZE_LARGE_TOOLBAR); - - /* Pack it all */ - gtk_container_add(GTK_CONTAINER(content_area), logoUser); - gtk_container_add(GTK_CONTAINER(content_area), call_label); - gtk_container_add(GTK_CONTAINER(content_area), im->info_state); - - gtk_info_bar_set_message_type(GTK_INFO_BAR(infobar), GTK_MESSAGE_INFO); - - gtk_widget_show(infobar); -} - -GtkWidget *im_widget_display(const gchar *id) -{ - IMWidget *imwidget = IM_WIDGET(g_object_new(IM_WIDGET_TYPE, NULL)); - imwidget->call_id = id; - im_widget_infobar(imwidget); - im_window_add(imwidget); - - return GTK_WIDGET(imwidget); -} - - -void -im_widget_update_state(IMWidget *im, gboolean active) -{ - if (!im) - return; - - gtk_widget_set_sensitive(im->info_state, active); - - if (active) { /* the call is in current state, so sflphone can send text messages */ - gtk_info_bar_set_message_type(GTK_INFO_BAR(im->info_bar), GTK_MESSAGE_INFO); - } else if (im) { /* the call is over, we can't send text messages anymore */ - gtk_info_bar_set_message_type(GTK_INFO_BAR(im->info_bar), GTK_MESSAGE_WARNING); - gtk_widget_set_tooltip_text(im->info_state, "Call has terminated"); - } -} diff --git a/gnome/src/widget/imwidget.h b/gnome/src/widget/imwidget.h deleted file mode 100644 index 02226cbc7a511cf5761ff54d1279bf0ea4f559c5..0000000000000000000000000000000000000000 --- a/gnome/src/widget/imwidget.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 Savoir-Faire Linux Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Additional permission under GNU GPL version 3 section 7: - * - * If you modify this program, or any covered work, by linking or - * combining it with the OpenSSL project's OpenSSL library (or a - * modified version of that library), containing parts covered by the - * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc. - * grants you additional permission to convey the resulting work. - * Corresponding Source for a non-source form of such a combination - * shall include the source code for the parts of OpenSSL used as well - * as that of the covered work. - */ - - -#ifndef __IM_WIDGET_H__ -#define __IM_WIDGET_H__ - -#include <gtk/gtk.h> -#include <callable_obj.h> -#include <webkit/webkit.h> -#include <conference_obj.h> - -G_BEGIN_DECLS - -#define IM_WIDGET_TYPE (im_widget_get_type()) -#define IM_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), IM_WIDGET_TYPE, IMWidget)) -#define IM_WIDGET_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST((vtable), IM_WIDGET_TYPE, IMWidgetClass)) -#define IS_IM_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), IM_WIDGET_TYPE)) -#define IS_IM_WIDGET_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE((vtable), IM_WIDGET_TYPE)) -#define IM_WIDGET_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS((inst), IM_WIDGET_TYPE, IMWidgetClass)) - -#define MESSAGE_LEVEL_NORMAL 0 -#define MESSAGE_LEVEL_WARNING 1 -#define MESSAGE_LEVEL_ERROR 2 - -typedef struct _IMWidget IMWidget; -typedef struct _IMWidgetClass IMWidgetClass; - -struct _IMWidget { - GtkBox parent_instance; - - /* Private */ - GtkWidget *tab; - GtkWidget *textarea; - GtkWidget *web_view; - GtkWidget *info_bar; - GtkWidget *info_state; - const gchar *call_id; - gchar *first_message; // Message displayed at widget's creation time - gchar *first_message_from; // Sender of the first message (usefull in case of a conference) - WebKitWebFrame *web_frame; // Our web frame - JSGlobalContextRef js_context; // The frame's global JS context - JSObjectRef js_global; // The frame's global context JS object - gboolean containText; -}; - -struct _IMWidgetClass { - GtkBoxClass parent_class; -}; - - -/*! @function -@abstract Display the instant messaging interface for this call. If it has not been created yet, create it and attached it to the imWindow. -@returns A reference on the call attached to the current IM widget -@param The call id to be associated with the IMWidget - */ -GtkWidget *im_widget_display (const gchar*); - -GType im_widget_get_type (void) G_GNUC_CONST; - - -/*! @function -@abstract Add a new message in the webkit view -@param The IMWidget -@param The sender of the message -@param The message to be send -@param The level of the message: NORMAL or ERROR -*/ -void im_widget_add_message (IMWidget *im, const gchar *from, const gchar *message, gint level); - -void im_widget_send_message (const gchar *id, const gchar *message); - -void im_widget_update_state (IMWidget *im, gboolean active); - -G_END_DECLS - -#endif /* __IM_WIDGET_H__ */ diff --git a/gnome/tests/Makefile.am b/gnome/tests/Makefile.am index 9748eb162c3029a28161c284dfa932a6dc8bb515..93384b261576d2ee7609d9acb5bcdaa872be10dd 100644 --- a/gnome/tests/Makefile.am +++ b/gnome/tests/Makefile.am @@ -12,8 +12,8 @@ SFLPHONE_LIBS= $(top_builddir)/src/libsflphone_client.la \ check_global_SOURCES = check_global.c -check_global_CFLAGS = @CHECK_CFLAGS@ @GTK_CFLAGS@ @GLIB_CFLAGS@ @DBUSGLIB_CFLAGS@ @GCONF_CFLAGS@ @WEBKIT_CFLAGS@ -check_global_LDADD = $(SFLPHONE_LIBS) @CHECK_LIBS@ @GLIB_LIBS@ @GTK_LIBS@ @DBUSGLIB_LIBS@ @GCONF_LIBS@ @WEBKIT_LIBS@ -ldl @X11_LIBS@ +check_global_CFLAGS = @CHECK_CFLAGS@ @GTK_CFLAGS@ @GLIB_CFLAGS@ @DBUSGLIB_CFLAGS@ @GCONF_CFLAGS@ +check_global_LDADD = $(SFLPHONE_LIBS) @CHECK_LIBS@ @GLIB_LIBS@ @GTK_LIBS@ @DBUSGLIB_LIBS@ @GCONF_LIBS@ -ldl @X11_LIBS@ check_contacts_SOURCES = check_contacts.c check_contacts_CFLAGS = @CHECK_CFLAGS@ @GTK_CFLAGS@ @@ -25,7 +25,7 @@ check_config_LDADD = $(SFLPHONE_LIBS) @CHECK_LIBS@ check_dbus_SOURCES = check_dbus.c -check_dbus_CFLAGS = @CHECK_CFLAGS@ @GTK_CFLAGS@ @DBUSGLIB_CFLAGS@ @GCONF_CFLAGS@ @WEBKIT_CFLAGS@ +check_dbus_CFLAGS = @CHECK_CFLAGS@ @GTK_CFLAGS@ @DBUSGLIB_CFLAGS@ @GCONF_CFLAGS@ if SFL_VIDEO check_dbus_CFLAGS+=@CLUTTER_CFLAGS@ @CLUTTERGTK_CFLAGS@ endif @@ -34,3 +34,6 @@ check_dbus_LDADD = $(SFLPHONE_LIBS) @CHECK_LIBS@ @GCONF_LIBS@ \ $(top_builddir)/src/widget/libwidget.la \ $(top_builddir)/src/icons/libicons.la @X11_LIBS@ \ @JAVASCRIPT_CORE_GTK_LIBS@ -ldl + +clean-local: + rm -rf *.xml diff --git a/gnome/tests/check_config.c b/gnome/tests/check_config.c index dc330c0e4b9407041aa2586f88383715505e8233..d6461148e6bf79a721193cde4f914b7ec33ea61b 100644 --- a/gnome/tests/check_config.c +++ b/gnome/tests/check_config.c @@ -31,6 +31,8 @@ #include <check.h> #include <stdlib.h> +#define XML_OUTPUT "gnome-check-config.xml" + Suite * config_suite(void) { @@ -45,6 +47,7 @@ main(void) int number_failed; Suite *s = config_suite(); SRunner *sr = srunner_create(s); + srunner_set_xml(sr, XML_OUTPUT); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); diff --git a/gnome/tests/check_contacts.c b/gnome/tests/check_contacts.c index e5c17ea45b231374906a9c5aa9fb9b2673913ed2..497beadb9173dec8870fdf500378f4352489e47c 100644 --- a/gnome/tests/check_contacts.c +++ b/gnome/tests/check_contacts.c @@ -30,6 +30,7 @@ #include <stdlib.h> #include "../src/contacts/addressbook.h" +#define XML_OUTPUT "gnome-check-contacts.xml" START_TEST(test_eds) { @@ -54,6 +55,7 @@ main(void) int number_failed; Suite *s = contacts_suite(); SRunner *sr = srunner_create(s); + srunner_set_xml(sr, XML_OUTPUT); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); diff --git a/gnome/tests/check_dbus.c b/gnome/tests/check_dbus.c index a8d96a0503b8d7df584e97b472d56703d111a368..747d39915d5d48668adb1dea8291645a81549694 100644 --- a/gnome/tests/check_dbus.c +++ b/gnome/tests/check_dbus.c @@ -31,6 +31,8 @@ #include "../src/dbus/dbus.h" +#define XML_OUTPUT "gnome-check-dbus.xml" + START_TEST(test_dbus_connect) { GError *error = NULL; @@ -56,6 +58,7 @@ main(void) int number_failed; Suite *s = dbus_suite(); SRunner *sr = srunner_create(s); + srunner_set_xml(sr, XML_OUTPUT); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); diff --git a/gnome/tests/check_global.c b/gnome/tests/check_global.c index 09ead1053e987e9b6ffc3d24b8d3d4d96ad9e56d..37ba7e441f1b7904d1e04bd202736099abe70b1b 100644 --- a/gnome/tests/check_global.c +++ b/gnome/tests/check_global.c @@ -33,9 +33,11 @@ #include <glib.h> #include "../src/accountlist.h" -#include "../src/sflphone_const.h" +#include "../src/account_schema.h" #include "../src/str_utils.h" +#define XML_OUTPUT "gnome-check-global.xml" + account_t* create_test_account(gchar *alias) { account_t *test; @@ -48,15 +50,15 @@ account_t* create_test_account(gchar *alias) test->properties = g_hash_table_new(NULL, g_str_equal); // Populate the properties - account_replace(test, ACCOUNT_ENABLED, "1"); - account_replace(test, ACCOUNT_ALIAS, alias); - account_replace(test, ACCOUNT_TYPE, "SIP"); - account_replace(test, ACCOUNT_HOSTNAME, "sflphone.org"); - account_replace(test, ACCOUNT_USERNAME, "1260"); - account_replace(test, ACCOUNT_PASSWORD, "NIPAgmLo"); - account_replace(test, ACCOUNT_MAILBOX, ""); - account_replace(test, ACCOUNT_SIP_STUN_SERVER, ""); - account_replace(test, ACCOUNT_SIP_STUN_ENABLED, "0"); + account_replace(test, CONFIG_ACCOUNT_ENABLE, "1"); + account_replace(test, CONFIG_ACCOUNT_ALIAS, alias); + account_replace(test, CONFIG_ACCOUNT_TYPE, "SIP"); + account_replace(test, CONFIG_ACCOUNT_HOSTNAME, "sflphone.org"); + account_replace(test, CONFIG_ACCOUNT_USERNAME, "1260"); + account_replace(test, CONFIG_ACCOUNT_PASSWORD, "NIPAgmLo"); + account_replace(test, CONFIG_ACCOUNT_MAILBOX, ""); + account_replace(test, CONFIG_STUN_SERVER, ""); + account_replace(test, CONFIG_STUN_ENABLE, "0"); return test; } @@ -110,8 +112,8 @@ START_TEST(test_get_current_account) // The current account must be the first we add if (current) { - fail_unless(utf8_case_equal(account_lookup(current, ACCOUNT_ALIAS), - account_lookup(test, ACCOUNT_ALIAS)), + fail_unless(utf8_case_equal(account_lookup(current, CONFIG_ACCOUNT_ALIAS), + account_lookup(test, CONFIG_ACCOUNT_ALIAS)), "ERROR - BAD CURRENT ACCOUNT"); } @@ -122,8 +124,8 @@ START_TEST(test_get_current_account) // The current account must be the first we add if (current) { - fail_unless(utf8_case_equal(account_lookup(current, ACCOUNT_ALIAS), - account_lookup(test2, ACCOUNT_ALIAS)), + fail_unless(utf8_case_equal(account_lookup(current, CONFIG_ACCOUNT_ALIAS), + account_lookup(test2, CONFIG_ACCOUNT_ALIAS)), "ERROR - BAD CURRENT ACCOUNT"); } } @@ -150,6 +152,7 @@ main(void) int number_failed; Suite *s = global_suite(); SRunner *sr = srunner_create(s); + srunner_set_xml(sr, XML_OUTPUT); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); diff --git a/gnome/webkit/Makefile.am b/gnome/webkit/Makefile.am deleted file mode 100644 index 80410916ca0e1e1f0f73c6c98d96d861a5006ddd..0000000000000000000000000000000000000000 --- a/gnome/webkit/Makefile.am +++ /dev/null @@ -1,13 +0,0 @@ -include ../globals.mak - -EXTRA_DIST = $(im_DATA) - -webkitdir = $(datadir)/sflphone/webkit - -imdir = $(webkitdir)/im -im_DATA = im/im.js \ - im/im.html \ - im/im.css \ - im/sflphone.png \ - im/chat_info.png \ - im/error.png diff --git a/gnome/webkit/im/chat_info.png b/gnome/webkit/im/chat_info.png deleted file mode 100644 index 07d3ac7940b2817de5f0497975c883a6c416dd55..0000000000000000000000000000000000000000 Binary files a/gnome/webkit/im/chat_info.png and /dev/null differ diff --git a/gnome/webkit/im/error.png b/gnome/webkit/im/error.png deleted file mode 100644 index f7e60e5238ee0f613c73d556f69f7ff52be7a8bb..0000000000000000000000000000000000000000 Binary files a/gnome/webkit/im/error.png and /dev/null differ diff --git a/gnome/webkit/im/im.css b/gnome/webkit/im/im.css deleted file mode 100644 index 9eb9f88e09c9fc2894cc89c49b959951ed00331e..0000000000000000000000000000000000000000 --- a/gnome/webkit/im/im.css +++ /dev/null @@ -1,66 +0,0 @@ -.message { - -webkit-border-radius: 5px; - background: rgba(242, 242, 242, 0.75); - color: #444; - padding: 1em; - -webkit-box-shadow: 2px 2px 5px #000; - margin: 20px; -overflow: auto; -clear: both; -} - -* { - font-size: 12px; -font-family: "Georgia","Verdana","Arial","Helvetica",sans-serif; -} - -.page { - -} - -body { - background: #fbfdfe; -} - -#logo { - float: right; -} - -.author { - color: #3578B1; - font-weight: bold; -} - -#header { - display: none; - margin: 40px; -} - -#call-info { - float: left; - font-size: 1.1em; -height: 96px; - background: url(chat_info.png) no-repeat center left; - padding: 20px 20px 20px 100px; - border: 2px solid #dedede; - -webkit-border-radius: 25px; -} - -.error .text { - color: #de7575; - font-size: 1.3em; -} - -.error .author { - display: none; -} - -.message-time { - color: #777; - float: right; -} - -#messages { - clear: both; - padding-top: 20px; -} diff --git a/gnome/webkit/im/im.html b/gnome/webkit/im/im.html deleted file mode 100644 index b1b13dc03c9d79afd1cf275954d309ca0e1256b7..0000000000000000000000000000000000000000 --- a/gnome/webkit/im/im.html +++ /dev/null @@ -1,20 +0,0 @@ -<html> - <head> - <script src="im.js"></script> - <link rel="stylesheet" type="text/css" href="im.css" /> - </head> - <body> - <div class='page'> - <div id="header"> - <div id="call-info"> - <h2> Calling <span id='peer-number'></span></h2> - <p>Nom: <span id='peer-name'></span></p> - </div> - <div id="logo"><a href="http://www.sflphone.org" title="sflphone" alt="sflphone"><img src="sflphone.png"/></a></div> - </div> - <div id="messages"> - </div> - </div> - <a id="bottom"></a> - </body> -</html> diff --git a/gnome/webkit/im/im.js b/gnome/webkit/im/im.js deleted file mode 100644 index 1c2ebd1c4e513dc5217c3d0878cfffbb9465ea72..0000000000000000000000000000000000000000 --- a/gnome/webkit/im/im.js +++ /dev/null @@ -1,39 +0,0 @@ -function linkify(text){ - if (text) { - text = text.replace( - /((https?\:\/\/)|(www\.))(\S+)(\w{2,4})(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/gi, - function(url){ - var full_url = url; - if (!full_url.match('^https?:\/\/')) { - full_url = 'http://' + full_url; - } - return '<a href="' + full_url + '">' + url + '</a>'; - } - ); - } - return text; -} - - -function add_message (message, peer_name, class_additionnal, time) -{ - var msgBody = document.getElementById ('messages'); - msgBody.innerHTML = msgBody.innerHTML + '<div class="message ' + class_additionnal + '">' + '<span class="author">[' + peer_name + '] </span><span class="message-time">' + time + '</span><p class="text">' + linkify (message) + '</p></div>' ; - document.getElementById("bottom").scrollIntoView(true); -} - -function add_call_info_header (peer_name, peer_number) -{ - var peerNumber = document.getElementById ('peer-number'); - var peerName = document.getElementById ('peer-name'); - var peerInfo = document.getElementById ('peer-info'); - peerNumber.innerHTML = peer_number; - peerName.innerHTML = peer_name; - -} - -function open_url (uri) { - window.open(''+self.location,'mywin', - 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0'); - -} diff --git a/gnome/webkit/im/sflphone.png b/gnome/webkit/im/sflphone.png deleted file mode 100644 index 03e40a0b35785c02ccc78f982e8fa10d2b5c1fb3..0000000000000000000000000000000000000000 Binary files a/gnome/webkit/im/sflphone.png and /dev/null differ diff --git a/hudson-sflphone-script.sh b/hudson-sflphone-script.sh index aaacf5164ee428829c49137477d6c2ae43bf3f94..665fdfabdebf101bf3a201daf67934f93ec86661 100755 --- a/hudson-sflphone-script.sh +++ b/hudson-sflphone-script.sh @@ -120,11 +120,6 @@ function build_daemon { make clean # Compile src code make -j - # Generate documentation - make doc - if [ $DOXYGEN == 1 ]; then - gen_doxygen - fi # Remove the previous XML test file rm -rf $XML_RESULTS # Compile unit tests @@ -133,6 +128,24 @@ function build_daemon { } function build_gnome { + # Compile the daemon + pushd daemon + killall sflphoned + make distclean + ./autogen.sh + # Compile pjproject first + pushd libs/pjproject + ./autogen.sh + ./configure + make && make dep + popd + ./configure --prefix=/usr + make clean + # Compile src code + make -j + ./src/sflphoned& + popd + # Compile the plugins pushd plugins make distclean diff --git a/kde/CMakeLists.txt b/kde/CMakeLists.txt index eeda16feb5a7d577bc74826543d6c44f180000b5..dfd5635248c982bef2f3e7bf1481a2061d12816f 100755 --- a/kde/CMakeLists.txt +++ b/kde/CMakeLists.txt @@ -12,7 +12,7 @@ INCLUDE( ${LOCAL_CMAKE_MODULE_PATH}/TargetDistclean.cmake REQUIRED) FIND_PACKAGE ( KdepimLibs REQUIRED ) FIND_PACKAGE ( KDE4 REQUIRED ) -FIND_PACKAGE ( Qt4 REQUIRED QtCore QtGui QtXml QtDBus QtTest QtSVG QT_USE_QT* ) +FIND_PACKAGE ( Qt4 REQUIRED QtCore QtGui QtXml QtDBus QtTest QtSVG QtOpenGl QT_USE_QT* ) if (CMAKE_COMPILER_IS_GNUCC) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) @@ -44,7 +44,7 @@ add_subdirectory( man ) add_subdirectory( po ) add_subdirectory( plasma ) -set(PROJECT_VERSION "1.1.0") +set(PROJECT_VERSION "1.1.1") set(ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}) add_custom_target(dist diff --git a/kde/src/AccountListModel.cpp b/kde/src/AccountListModel.cpp deleted file mode 100755 index b2b0e2cf1cc76e5bee22ce48bf62310305bdf72d..0000000000000000000000000000000000000000 --- a/kde/src/AccountListModel.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009-2012 by Savoir-Faire Linux * - * Author : Jérémy Quentin <jeremy.quentin@savoirfairelinux.com> * - * Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com>* - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - **************************************************************************/ - -//Parent -#include "AccountListModel.h" - -//SFLPhone library -#include "lib/sflphone_const.h" - -//SFLPhone -#include "conf/ConfigAccountList.h" - -//Qt -#include <QtGui/QIcon> - -//KDE -#include <KLed> - -///Constructor -AccountListModel::AccountListModel(QObject *parent) - : QAbstractListModel(parent),accounts(NULL) -{ - this->accounts = new ConfigAccountList(); -} - -///Destructor -AccountListModel::~AccountListModel() -{ - if (accounts) delete accounts; -} - - -/***************************************************************************** - * * - * Getters * - * * - ****************************************************************************/ - -///Get data from the model -QVariant AccountListModel::data ( const QModelIndex& index, int role) const -{ - if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) - return QVariant(); - - const Account * account = (*accounts)[index.row()]; - if(index.column() == 0 && role == Qt::DisplayRole) - return QVariant(account->getAlias()); - else if(index.column() == 0 && role == Qt::CheckStateRole) - return QVariant(account->isEnabled() ? Qt::Checked : Qt::Unchecked); - else if(index.column() == 0 && role == Qt::DecorationRole) { - if(! account->isEnabled()) - return QVariant( QIcon( ICON_ACCOUNT_LED_GRAY )); - else if(account->isRegistered()) - return QVariant( QIcon( ICON_ACCOUNT_LED_GREEN )); - else - return QVariant( QIcon( ICON_ACCOUNT_LED_RED )); - } - return QVariant(); -} //data - -///Flags for "index" -Qt::ItemFlags AccountListModel::flags(const QModelIndex & index) const -{ - if (index.column() == 0) - return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable; - return QAbstractItemModel::flags(index); -} - -///Get the account list -QString AccountListModel::getOrderedList() const -{ - return accounts->getOrderedList(); -} - - -/***************************************************************************** - * * - * Setters * - * * - ****************************************************************************/ - -///Set model data -bool AccountListModel::setData(const QModelIndex & index, const QVariant &value, int role) -{ - if (index.isValid() && index.column() == 0 && role == Qt::CheckStateRole) { - (*accounts)[index.row()]->setEnabled(value.toBool()); - emit dataChanged(index, index); - return true; - } - return false; -} - - -/***************************************************************************** - * * - * Mutator * - * * - ****************************************************************************/ - -///Move account up -bool AccountListModel::accountUp( int index ) -{ - if(index > 0 && index <= rowCount()) { - accounts->upAccount(index); - emit dataChanged(this->index(index - 1, 0, QModelIndex()), this->index(index, 0, QModelIndex())); - return true; - } - return false; -} - -///Move account down -bool AccountListModel::accountDown( int index ) -{ - if(index >= 0 && index < rowCount()) { - accounts->downAccount(index); - emit dataChanged(this->index(index, 0, QModelIndex()), this->index(index + 1, 0, QModelIndex())); - return true; - } - return false; -} - -///Remove an account -bool AccountListModel::removeAccount( int index ) -{ - if(index >= 0 && index < rowCount()) { - accounts->removeAccount(accounts->getAccountAt(index)); - emit dataChanged(this->index(index, 0, QModelIndex()), this->index(rowCount(), 0, QModelIndex())); - return true; - } - return false; -} - -///Add an account -bool AccountListModel::addAccount(const QString& alias ) -{ - accounts->addAccount(alias); - emit dataChanged(this->index(0, 0, QModelIndex()), this->index(rowCount(), 0, QModelIndex())); - return true; -} - -///Number of account -int AccountListModel::rowCount(const QModelIndex & /*parent*/) const -{ - return accounts->size(); -} \ No newline at end of file diff --git a/kde/src/AccountListModel.h b/kde/src/AccountListModel.h deleted file mode 100755 index 08e9d4117a6670628da7c9bac81d968e8e85357a..0000000000000000000000000000000000000000 --- a/kde/src/AccountListModel.h +++ /dev/null @@ -1,60 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009-2012 by Savoir-Faire Linux * - * Author : Jérémy Quentin <jeremy.quentin@savoirfairelinux.com> * - * Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com>* - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - **************************************************************************/ -#ifndef ACCOUNTLISTMODEL_H -#define ACCOUNTLISTMODEL_H - -#include <QAbstractListModel> - -//SFLPhone -class ConfigAccountList; - -/// AccountListModel Model for the account list widget -class AccountListModel : public QAbstractListModel -{ -Q_OBJECT -private: - ConfigAccountList* accounts; - -public: - //Constructor - AccountListModel(QObject *parent = 0); - - //Destructor - ~AccountListModel(); - - //Getters - QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const; - int rowCount ( const QModelIndex& parent = QModelIndex() ) const; - Qt::ItemFlags flags ( const QModelIndex& index ) const; - - //Setters - virtual bool setData ( const QModelIndex& index, const QVariant &value, int role) ; - - //Mutators - bool accountUp ( int index ); - bool accountDown ( int index ); - bool removeAccount ( int index ); - bool addAccount ( const QString& alias ); - - QString getOrderedList() const; -}; - -#endif diff --git a/kde/src/AccountView.cpp b/kde/src/AccountView.cpp deleted file mode 100644 index f4cce67744d1d8908db2ab2999ba71fda9c8ddc8..0000000000000000000000000000000000000000 --- a/kde/src/AccountView.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009-2012 by Savoir-Faire Linux * - * Author : Emmanuel Lepage Valle <emmanuel.lepage@savoirfairelinux.com >* - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - **************************************************************************/ - -//Parent -#include "AccountView.h" - - -//Qt -#include <QtGui/QListWidgetItem> - -//KDE -#include <KDebug> - -//SFLPhone library -#include "lib/sflphone_const.h" -#include "lib/configurationmanager_interface_singleton.h" - -///Constructor -AccountView::AccountView() : Account(), m_pItem(0), m_pWidget(0) -{ - -} - -///Destructor -AccountView::~AccountView() -{ - if (m_pWidget) { - delete m_pWidget; - m_pWidget = nullptr; - } - if (m_pItem) delete m_pItem; -} - -///Init -void AccountView::initItem() -{ - if(m_pItem != NULL) - delete m_pItem; - m_pItem = new QListWidgetItem(); - m_pItem->setSizeHint(QSize(140,25)); - m_pItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled|Qt::ItemIsEnabled); - initItemWidget(); -} - -///Init widget -void AccountView::initItemWidget() -{ - if(m_pWidget != NULL) - delete m_pWidget; - - bool enabled = isAccountEnabled(); - m_pWidget = new AccountItemWidget(); - m_pWidget->setEnabled(enabled); - m_pWidget->setAccountText(getAccountAlias()); - - if(isNew() || !enabled) - m_pWidget->setState(AccountItemWidget::Unregistered); - else if(getAccountRegistrationStatus() == ACCOUNT_STATE_REGISTERED || getAccountRegistrationStatus() == ACCOUNT_STATE_READY) - m_pWidget->setState(AccountItemWidget::Registered); - else - m_pWidget->setState(AccountItemWidget::NotWorking); - connect(m_pWidget, SIGNAL(checkStateChanged(bool)), this, SLOT(setEnabled(bool))); -} //initItemWidget - - -/***************************************************************************** - * * - * Getters * - * * - ****************************************************************************/ - -///Get the current item -QListWidgetItem* AccountView::getItem() -{ - return m_pItem; -} - -///Get the current widget -AccountItemWidget* AccountView::getItemWidget() -{ - return m_pWidget; -} - -///Return the state color -QColor AccountView::getStateColor() -{ - if(getAccountRegistrationStatus() == ACCOUNT_STATE_UNREGISTERED) - return Qt::black; - if(getAccountRegistrationStatus() == ACCOUNT_STATE_REGISTERED || getAccountRegistrationStatus() == ACCOUNT_STATE_READY) - return Qt::darkGreen; - return Qt::red; -} - -///Get the color name -const QString& AccountView::getStateColorName() -{ - static const QString black ( "black" ); - static const QString darkGreen( "darkGreen" ); - static const QString red ( "red" ); - if(getAccountRegistrationStatus() == ACCOUNT_STATE_UNREGISTERED) - return black; - if(getAccountRegistrationStatus() == ACCOUNT_STATE_REGISTERED || getAccountRegistrationStatus() == ACCOUNT_STATE_READY) - return darkGreen; - return red; -} - -///Is this item checked? -bool AccountView::isChecked() const -{ - return (m_pWidget)?m_pWidget->getEnabled():false; -} - - -/***************************************************************************** - * * - * Mutator * - * * - ****************************************************************************/ - -///Build an item from an account id -AccountView* AccountView::buildExistingAccountFromId(const QString& _accountId) -{ - //Account* a = Account::buildExistingAccountFromId( _accountId); - ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - AccountView* a = new AccountView(); - a->m_pAccountId = new QString(_accountId); - a->m_pAccountDetails = new MapStringString( configurationManager.getAccountDetails(_accountId).value() ); - a->initItem(); - return a; -} - -///Build an item from an alias -AccountView* AccountView::buildNewAccountFromAlias(const QString& alias) -{ - //Account* a = Account::buildNewAccountFromAlias(alias); - AccountView* a = new AccountView(); - a->m_pAccountDetails = new MapStringString(); - a->setAccountAlias(alias); - a->initItem(); - return a; -} - -///Change LED color -void AccountView::updateState() -{ - if(! isNew()) { - Account::updateState(); - - AccountItemWidget * m_pWidget = getItemWidget(); - if(!isAccountEnabled()) { - kDebug() << "Changing account state to Unregistered"; - if (m_pWidget) m_pWidget->setState(AccountItemWidget::Unregistered); - } - else if(getAccountRegistrationStatus() == ACCOUNT_STATE_REGISTERED || getAccountRegistrationStatus() == ACCOUNT_STATE_READY) { - kDebug() << "Changing account state to Registered"; - if (m_pWidget) m_pWidget->setState(AccountItemWidget::Registered); - } - else { - kDebug() << "Changing account state to NotWorking"; - if (m_pWidget) m_pWidget->setState(AccountItemWidget::NotWorking); - } - } -} //updateState diff --git a/kde/src/AccountView.h b/kde/src/AccountView.h deleted file mode 100644 index 8bafcd2f1d75016804b43a99a91ef75f0699634b..0000000000000000000000000000000000000000 --- a/kde/src/AccountView.h +++ /dev/null @@ -1,64 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009-2012 by Savoir-Faire Linux * - * Author : Emmanuel Lepage Valle <emmanuel.lepage@savoirfairelinux.com >* - * Author : Jérémy Quentin <jeremy.quentin@gmail.com> * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - **************************************************************************/ -#ifndef ACCOUNT_VIEW_H -#define ACCOUNT_VIEW_H - -#include "lib/Account.h" -#include "widgets/AccountItemWidget.h" - -//Qt -class QListWidgetItem; - -//SFLPhone -class AccountItemWidget; - -///AccountView: List widgets displaying accounts -class AccountView : public Account { - public: - //Constructor - AccountView (); - ~AccountView (); - void initItem (); - - - //Getters - QListWidgetItem* getItem () ; - AccountItemWidget* getItemWidget () ; - QColor getStateColor () ; - const QString& getStateColorName () ; - bool isChecked () const; - - ///Return the Qwidget hosted by the QListWidgetItem - - //Mutators - static AccountView* buildExistingAccountFromId (const QString& _accountId ); - static AccountView* buildNewAccountFromAlias (const QString& alias ); - virtual void updateState(); - - private: - //Attributes - QListWidgetItem* m_pItem; - AccountItemWidget* m_pWidget; - - //Private constructor - void initItemWidget(); -}; -#endif diff --git a/kde/src/CMakeLists.txt b/kde/src/CMakeLists.txt index da397da714774101f499e73f46e1f357dee67b79..6193215c6c6c5e75032d903d56cfdeb44b9f967b 100755 --- a/kde/src/CMakeLists.txt +++ b/kde/src/CMakeLists.txt @@ -33,7 +33,6 @@ SET( widgets/SFLPhoneTray.cpp main.cpp AccountWizard.cpp - widgets/AccountItemWidget.cpp widgets/CallTreeItem.cpp widgets/HistoryTreeItem.cpp ActionSetAccountFirst.cpp @@ -46,7 +45,6 @@ SET( conf/dlghooks.cpp conf/dlgaccessibility.cpp conf/dlgvideo.cpp - conf/ConfigAccountList.cpp widgets/Dialpad.cpp widgets/ContactItemWidget.cpp widgets/ContactDock.cpp @@ -55,11 +53,11 @@ SET( widgets/TranslucentButtons.cpp widgets/CategoryDrawer.cpp widgets/CategorizedTreeWidget.cpp + widgets/VideoDock.cpp widgets/VideoWidget.cpp +# widgets/AcceleratedVideoWidget.cpp Codec.cpp - AccountListModel.cpp CallView.cpp - AccountView.cpp ) @@ -96,7 +94,7 @@ QT4_ADD_DBUS_INTERFACES(sflphone_client_kde_SRCS ${KDE4_DBUS_INTERFACES_DIR}/org KDE4_ADD_EXECUTABLE(sflphone-client-kde ${sflphone_client_kde_SRCS} ${QtApp_RCC_SRCS}) -TARGET_LINK_LIBRARIES(sflphone-client-kde ksflphone qtsflphone ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS} ${KDEPIMLIBS_AKONADI_KMIME_LIBS} ${KDEPIMLIBS_AKONADI_LIBS} ${KDEPIMLIBS_AKONADI_CONTACT_LIBS}) +TARGET_LINK_LIBRARIES(sflphone-client-kde ksflphone qtsflphone ${KDE4_KDEUI_LIBS} ${QT_QTOPENGL_LIBRARY} ${KDE4_KIO_LIBS} ${KDEPIMLIBS_AKONADI_KMIME_LIBS} ${KDEPIMLIBS_AKONADI_LIBS} ${KDEPIMLIBS_AKONADI_CONTACT_LIBS}) ########### install files ############### diff --git a/kde/src/CallView.cpp b/kde/src/CallView.cpp index 7ae983f668ba92ac10f2466cff8df15c15079474..415cb98d175f28b3a0bfd500b3686b3e76881310 100644 --- a/kde/src/CallView.cpp +++ b/kde/src/CallView.cpp @@ -39,13 +39,14 @@ #include "lib/Contact.h" #include "lib/sflphone_const.h" #include "lib/callmanager_interface_singleton.h" +#include "klib/AkonadiBackend.h" +#include "klib/ConfigurationSkeleton.h" +#include "klib/HelperFunctions.h" //SFLPhone +#include "SFLPhoneView.h" #include "widgets/CallTreeItem.h" #include "SFLPhone.h" -#include "SFLPhoneView.h" -#include "klib/AkonadiBackend.h" -#include "klib/ConfigurationSkeleton.h" #include "SFLPhoneAccessibility.h" ///CallTreeItemDelegate: Delegates for CallTreeItem @@ -274,28 +275,33 @@ bool CallView::phoneNumberToCall(QTreeWidgetItem *parent, int index, const QMime Q_UNUSED(action) QByteArray encodedPhoneNumber = data->data( MIME_PHONENUMBER ); if (!QString(encodedPhoneNumber).isEmpty()) { - Contact* contact = AkonadiBackend::getInstance()->getContactByPhone(encodedPhoneNumber); + Contact* contact = AkonadiBackend::getInstance()->getContactByPhone(encodedPhoneNumber,true); QString name; name = (contact)?contact->getFormattedName():i18n("Unknown"); Call* call2 = SFLPhone::model()->addDialingCall(name, AccountList::getCurrentAccount()); - call2->appendText(QString(encodedPhoneNumber)); - if (!parent) { - //Dropped on free space - kDebug() << "Adding new dialing call"; - } - else if (parent->childCount() || parent->parent()) { - //Dropped on a conversation - QTreeWidgetItem* call = (parent->parent())?parent->parent():parent; - SFLPhone::model()->addParticipant(SFLPhone::model()->getCall(call),call2); + if (call2) { + call2->appendText(QString(encodedPhoneNumber)); + if (!parent) { + //Dropped on free space + kDebug() << "Adding new dialing call"; + } + else if (parent->childCount() || parent->parent()) { + //Dropped on a conversation + QTreeWidgetItem* call = (parent->parent())?parent->parent():parent; + SFLPhone::model()->addParticipant(SFLPhone::model()->getCall(call),call2); + } + else { + //Dropped on call + call2->actionPerformed(CALL_ACTION_ACCEPT); + int state = SFLPhone::model()->getCall(parent)->getState(); + if(state == CALL_STATE_INCOMING || state == CALL_STATE_DIALING || state == CALL_STATE_TRANSFER || state == CALL_STATE_TRANSF_HOLD) { + SFLPhone::model()->getCall(parent)->actionPerformed(CALL_ACTION_ACCEPT); + } + SFLPhone::model()->createConferenceFromCall(call2,SFLPhone::model()->getCall(parent)); + } } else { - //Dropped on call - call2->actionPerformed(CALL_ACTION_ACCEPT); - int state = SFLPhone::model()->getCall(parent)->getState(); - if(state == CALL_STATE_INCOMING || state == CALL_STATE_DIALING || state == CALL_STATE_TRANSFER || state == CALL_STATE_TRANSF_HOLD) { - SFLPhone::model()->getCall(parent)->actionPerformed(CALL_ACTION_ACCEPT); - } - SFLPhone::model()->createConferenceFromCall(call2,SFLPhone::model()->getCall(parent)); + HelperFunctions::displayNoAccountMessageBox(this); } } return false; @@ -312,7 +318,7 @@ bool CallView::contactToCall(QTreeWidgetItem *parent, int index, const QMimeData Contact* contact = AkonadiBackend::getInstance()->getContactByUid(encodedContact); if (contact) { Call* call2 = NULL; - if (!SFLPhone::app()->view()->selectCallPhoneNumber(call2,contact)) + if (!SFLPhone::app()->view()->selectCallPhoneNumber(&call2,contact)) return false; if (!parent) { //Dropped on free space @@ -325,6 +331,14 @@ bool CallView::contactToCall(QTreeWidgetItem *parent, int index, const QMimeData } else { //Dropped on call +// if (!call2) { +// call2 = SFLPhone::model()->addDialingCall(contact->getFormattedName()); +// } +// QByteArray encodedPhoneNumber = data->data( MIME_PHONENUMBER ); +// if (!encodedPhoneNumber.isEmpty()) { +// call2->setCallNumber(encodedPhoneNumber); +// } + call2->actionPerformed(CALL_ACTION_ACCEPT); int state = SFLPhone::model()->getCall(parent)->getState(); if(state == CALL_STATE_INCOMING || state == CALL_STATE_DIALING || state == CALL_STATE_TRANSFER || state == CALL_STATE_TRANSF_HOLD) { @@ -580,10 +594,13 @@ void CallView::destroyCall(Call* toDestroy) else if (indexOfTopLevelItem(SFLPhone::model()->getIndex(toDestroy)) != -1) takeTopLevelItem(indexOfTopLevelItem(SFLPhone::model()->getIndex(toDestroy))); else if (SFLPhone::model()->getIndex(toDestroy)->parent()) { - QTreeWidgetItem* parent = SFLPhone::model()->getIndex(toDestroy)->parent(); - SFLPhone::model()->getIndex(toDestroy)->parent()->removeChild(SFLPhone::model()->getIndex(toDestroy)); - if (parent->childCount() == 0) /*This should never happen, but it does*/ - takeTopLevelItem(indexOfTopLevelItem(parent)); + QTreeWidgetItem* callIndex = SFLPhone::model()->getIndex(toDestroy); + QTreeWidgetItem* parent = callIndex->parent(); + if (indexOfTopLevelItem(parent) != -1) { + parent->removeChild(callIndex); + if (dynamic_cast<QTreeWidgetItem*>(parent) && parent->childCount() == 0) /*This should never happen, but it does*/ + takeTopLevelItem(indexOfTopLevelItem(parent)); + } } else kDebug() << "Call not found"; diff --git a/kde/src/SFLPhone.cpp b/kde/src/SFLPhone.cpp index 492f95a6a784077ce483adb185ad7a4baf86cf77..03672c1b0f7a5d201e0aa0e66d0684f27969344b 100755 --- a/kde/src/SFLPhone.cpp +++ b/kde/src/SFLPhone.cpp @@ -54,8 +54,10 @@ #include "widgets/ContactDock.h" #include "widgets/HistoryDock.h" #include "widgets/BookmarkDock.h" +#include "widgets/VideoDock.h" #include "klib/ConfigurationSkeleton.h" #include "SFLPhoneAccessibility.h" +#include "lib/VideoModel.h" SFLPhone* SFLPhone::m_sApp = NULL; TreeWidgetCallModel* SFLPhone::m_pModel = NULL; @@ -63,6 +65,9 @@ TreeWidgetCallModel* SFLPhone::m_pModel = NULL; ///Constructor SFLPhone::SFLPhone(QWidget *parent) : KXmlGuiWindow(parent), m_pInitialized(false), m_pView(new SFLPhoneView(this)) +#ifdef ENABLE_VIDEO + ,m_pVideoDW(nullptr) +#endif { setupActions(); m_sApp = this; @@ -71,10 +76,12 @@ SFLPhone::SFLPhone(QWidget *parent) ///Destructor SFLPhone::~SFLPhone() { - ConfigurationSkeleton::setDisplayContactDock ( m_pContactCD->isVisible() ); - ConfigurationSkeleton::setDisplayHistoryDock ( m_pHistoryDW->isVisible() ); - ConfigurationSkeleton::setDisplayBookmarkDock( m_pBookmarkDW->isVisible() ); - + if (!isHidden()) { + ConfigurationSkeleton::setDisplayContactDock ( m_pContactCD->isVisible() ); + ConfigurationSkeleton::setDisplayHistoryDock ( m_pHistoryDW->isVisible() ); + ConfigurationSkeleton::setDisplayBookmarkDock( m_pBookmarkDW->isVisible() ); + } + delete action_accept ; delete action_refuse ; delete action_hold ; @@ -107,6 +114,7 @@ SFLPhone::~SFLPhone() } delete AkonadiBackend::getInstance(); TreeWidgetCallModel::destroy(); + //saveState(); } ///Init everything @@ -119,6 +127,10 @@ bool SFLPhone::initialize() ConfigurationSkeleton::self(); + //Keep these template paramater or the static attribute wont be share between this and the call view, they need to be +// CallModel<CallTreeItem*,QTreeWidgetItem*>* histoModel = new CallModel<CallTreeItem*,QTreeWidgetItem*>(CallModel<CallTreeItem*,QTreeWidgetItem*>::History); +// histoModel->initHistory(); + ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); // accept dnd setAcceptDrops(true); @@ -155,28 +167,40 @@ bool SFLPhone::initialize() m_pHistoryDW = new HistoryDock ( this ); m_pBookmarkDW = new BookmarkDock ( this ); m_pStatusBarWidget = new QLabel ( ); + + //System tray m_pTrayIcon = new SFLPhoneTray ( this->windowIcon(), this ); - + m_pTrayIcon->addAction( action_accept ); + m_pTrayIcon->addAction( action_mailBox ); + m_pTrayIcon->addAction( action_refuse ); + m_pTrayIcon->addAction( action_hold ); + m_pTrayIcon->addAction( action_transfer ); + m_pTrayIcon->addAction( action_record ); + m_pTrayIcon->addSeparator(); + m_pTrayIcon->addAction( action_quit ); + addDockWidget( Qt::TopDockWidgetArea,m_pHistoryDW ); addDockWidget( Qt::TopDockWidgetArea,m_pBookmarkDW ); tabifyDockWidget(m_pBookmarkDW,m_pHistoryDW); + m_pHistoryDW->show(); m_pHistoryDW->setVisible(ConfigurationSkeleton::displayHistoryDock()); m_pBookmarkDW->show(); m_pBookmarkDW->setVisible(ConfigurationSkeleton::displayBookmarkDock()); - //Add bug when the dock is tabbed - /*connect(m_pContactCD, SIGNAL(visibilityChanged(bool)) ,action_showContactDock , SLOT(setChecked(bool))); - connect(m_pBookmarkDW, SIGNAL(visibilityChanged(bool)) ,action_showBookmarkDock, SLOT(setChecked(bool))); - connect(m_pHistoryDW, SIGNAL(visibilityChanged(bool)) ,action_showHistoryDock , SLOT(setChecked(bool)));*/ + setWindowIcon (QIcon(ICON_SFLPHONE) ); + setWindowTitle(i18n("SFLphone") ); + + setupActions(); connect(action_showContactDock, SIGNAL(toggled(bool)),m_pContactCD, SLOT(setVisible(bool))); connect(action_showHistoryDock, SIGNAL(toggled(bool)),m_pHistoryDW, SLOT(setVisible(bool))); connect(action_showBookmarkDock,SIGNAL(toggled(bool)),m_pBookmarkDW,SLOT(setVisible(bool))); - setWindowIcon (QIcon(ICON_SFLPHONE) ); - setWindowTitle(i18n("SFLphone") ); + #ifdef ENABLE_VIDEO + connect(VideoModel::getInstance(),SIGNAL(videoCallInitiated(VideoRenderer*)),this,SLOT(displayVideoDock(VideoRenderer*))); + #endif statusBar()->addWidget(m_pStatusBarWidget); @@ -189,9 +213,9 @@ bool SFLPhone::initialize() m_pView->loadWindow(); move(QCursor::pos().x() - geometry().width()/2, QCursor::pos().y() - geometry().height()/2); - //show(); + show(); - if(configurationManager.getAccountList().value().isEmpty()) { + if (configurationManager.getAccountList().value().size() <= 1) { (new AccountWizard())->show(); } @@ -248,19 +272,20 @@ void SFLPhone::setupActions() action_showContactDock = new KAction(KIcon("edit-find-user") , i18n("Display Contact") , this); action_showContactDock->setCheckable( true ); action_showContactDock->setChecked(ConfigurationSkeleton::displayContactDock()); - + action_showHistoryDock = new KAction(KIcon("view-history") , i18n("Display history") , this); action_showHistoryDock->setCheckable( true ); action_showHistoryDock->setChecked(ConfigurationSkeleton::displayHistoryDock()); - + action_showBookmarkDock = new KAction(KIcon("bookmark-new-list"), i18n("Display bookmark"), this); action_showBookmarkDock->setCheckable( true ); action_showBookmarkDock->setChecked(ConfigurationSkeleton::displayBookmarkDock()); - + action_accountCreationWizard = new KAction(i18n("Account creation wizard"), this); + action_configureShortcut = new KAction(KIcon(KIcon("configure-shortcuts")), i18n("Configure Shortcut"), this); - // SENDER SIGNAL RECEIVER SLOT / + // SENDER SIGNAL RECEIVER SLOT / /**/connect(action_accept, SIGNAL(triggered()), m_pView , SLOT(accept() )); /**/connect(action_refuse, SIGNAL(triggered()), m_pView , SLOT(refuse() )); /**/connect(action_hold, SIGNAL(triggered()), m_pView , SLOT(hold() )); @@ -275,6 +300,7 @@ void SFLPhone::setupActions() /**/connect(action_configureShortcut, SIGNAL(triggered()), this , SLOT(showShortCutEditor() )); /* */ + actionCollection()->addAction("action_accept" , action_accept ); actionCollection()->addAction("action_refuse" , action_refuse ); actionCollection()->addAction("action_hold" , action_hold ); @@ -294,8 +320,9 @@ void SFLPhone::setupActions() actionCollection()->addAction("action_showHistoryDock" , action_showHistoryDock ); actionCollection()->addAction("action_showBookmarkDock" , action_showBookmarkDock ); + QList<KAction*> acList = *SFLPhoneAccessibility::getInstance(); - + foreach(KAction* ac,acList) { actionCollection()->addAction(ac->objectName() , ac); } @@ -330,6 +357,9 @@ TreeWidgetCallModel* SFLPhone::model() m_pModel = new TreeWidgetCallModel(); m_pModel->initCall(); Call::setContactBackend(AkonadiBackend::getInstance()); + #ifdef ENABLE_VIDEO + VideoModel::getInstance(); + #endif } return m_pModel; } @@ -379,12 +409,9 @@ QList<QAction*> SFLPhone::getCallActions() ///Set widgets object name void SFLPhone::setObjectNames() { - m_pView->setObjectName ( "m_pView" ); - statusBar()->setObjectName ( "statusBar" ); - m_pTrayIcon->setObjectName ( "trayIcon" ); - m_pHistoryDW->setObjectName ( "historydock" ); - m_pContactCD->setObjectName ( "contactdock" ); - m_pBookmarkDW->setObjectName( "bookmarkdock" ); + m_pView->setObjectName ( "m_pView" ); + statusBar()->setObjectName ( "statusBar" ); + m_pTrayIcon->setObjectName ( "m_pTrayIcon" ); } @@ -397,6 +424,11 @@ void SFLPhone::setObjectNames() ///[Action]Hide sflphone bool SFLPhone::queryClose() { + if (!isHidden()) { + ConfigurationSkeleton::setDisplayContactDock ( m_pContactCD->isVisible() ); + ConfigurationSkeleton::setDisplayHistoryDock ( m_pHistoryDW->isVisible() ); + ConfigurationSkeleton::setDisplayBookmarkDock( m_pBookmarkDW->isVisible() ); + } hide(); return false; } @@ -411,24 +443,24 @@ void SFLPhone::quitButton() void SFLPhone::changeEvent(QEvent* event) { if (event->type() == QEvent::ActivationChange && m_pIconChanged && isActiveWindow()) { - m_pIconChanged = false; + m_pIconChanged = false; } } ///Change status message -void SFLPhone::on_m_pView_statusMessageChangeAsked(const QString & message) +void SFLPhone::on_m_pView_statusMessageChangeAsked(const QString& message) { m_pStatusBarWidget->setText(message); } ///Change windowtitle -void SFLPhone::on_m_pView_windowTitleChangeAsked(const QString & message) +void SFLPhone::on_m_pView_windowTitleChangeAsked(const QString& message) { setWindowTitle(message); } ///Enable or disable toolbar items -void SFLPhone::on_m_pView_enabledActionsChangeAsked(const bool * enabledActions) +void SFLPhone::on_m_pView_enabledActionsChangeAsked(const bool* enabledActions) { action_accept->setVisible ( enabledActions[SFLPhone::Accept ]); action_refuse->setVisible ( enabledActions[SFLPhone::Refuse ]); @@ -439,7 +471,7 @@ void SFLPhone::on_m_pView_enabledActionsChangeAsked(const bool * enabledActions) } ///Change icons -void SFLPhone::on_m_pView_actionIconsChangeAsked(const QString * actionIcons) +void SFLPhone::on_m_pView_actionIconsChangeAsked(const QString* actionIcons) { action_accept->setIcon ( QIcon(actionIcons[SFLPhone::Accept ])); action_refuse->setIcon ( QIcon(actionIcons[SFLPhone::Refuse ])); @@ -450,7 +482,7 @@ void SFLPhone::on_m_pView_actionIconsChangeAsked(const QString * actionIcons) } ///Change text -void SFLPhone::on_m_pView_actionTextsChangeAsked(const QString * actionTexts) +void SFLPhone::on_m_pView_actionTextsChangeAsked(const QString* actionTexts) { action_accept->setText ( actionTexts[SFLPhone::Accept ]); action_refuse->setText ( actionTexts[SFLPhone::Refuse ]); @@ -475,9 +507,21 @@ void SFLPhone::on_m_pView_recordCheckStateChangeAsked(bool recordCheckState) ///Called when a call is coming void SFLPhone::on_m_pView_incomingCall(const Call* call) { - Contact* contact = ((Call*)call)->getContact(); + Contact* contact = AkonadiBackend::getInstance()->getContactByPhone(call->getPeerPhoneNumber()); if (contact && call) { KNotification::event(KNotification::Notification, i18n("New incomming call"), i18n("New call from: \n") + (call->getPeerName().isEmpty() ? call->getPeerPhoneNumber() : call->getPeerName()),((contact->getPhoto())?*contact->getPhoto():NULL)); } KNotification::event(KNotification::Notification, i18n("New incomming call"), i18n("New call from: \n") + (call->getPeerName().isEmpty() ? call->getPeerPhoneNumber() : call->getPeerName())); } + +#ifdef ENABLE_VIDEO +///Display the video dock +void SFLPhone::displayVideoDock(VideoRenderer* r) +{ + if (!m_pVideoDW) { + m_pVideoDW = new VideoDock(this); + } + m_pVideoDW->setRenderer(r); + m_pVideoDW->show(); +} +#endif \ No newline at end of file diff --git a/kde/src/SFLPhone.h b/kde/src/SFLPhone.h index f791540dc173bd77c3fa393f104860243cbe830f..e43bd07a1684f3d249476a607a85eb6a6a6fd186 100755 --- a/kde/src/SFLPhone.h +++ b/kde/src/SFLPhone.h @@ -38,10 +38,12 @@ class KAction; class Call; class ContactDock; class BookmarkDock; +class VideoDock; class SFLPhoneTray; class SFLPhoneView; class HistoryDock; class CallTreeItem; +class VideoRenderer; typedef CallModel<CallTreeItem*,QTreeWidgetItem*> TreeWidgetCallModel; @@ -106,6 +108,9 @@ private: QDockWidget* m_pCentralDW ; HistoryDock* m_pHistoryDW ; BookmarkDock* m_pBookmarkDW ; + #ifdef ENABLE_VIDEO + VideoDock* m_pVideoDW ; + #endif static SFLPhone* m_sApp; static TreeWidgetCallModel* m_pModel; @@ -144,12 +149,12 @@ private slots: void on_m_pView_actionTextsChangeAsked ( const QString* actionTexts ); void on_m_pView_transferCheckStateChangeAsked ( bool transferCheckState ); void on_m_pView_recordCheckStateChangeAsked ( bool recordCheckState ); - void on_m_pView_incomingCall ( const Call * call ); + void on_m_pView_incomingCall ( const Call* call ); void showShortCutEditor ( ); - - void quitButton(); - + void quitButton ( ); + #ifdef ENABLE_VIDEO + void displayVideoDock ( VideoRenderer* r ); + #endif }; -#endif - +#endif \ No newline at end of file diff --git a/kde/src/SFLPhoneView.cpp b/kde/src/SFLPhoneView.cpp index 238893bba5a951b2d97f1ddc9441df15bf9a85a4..bb1b22059097dbb4158cf9ffa3ab8b79682cca8d 100755 --- a/kde/src/SFLPhoneView.cpp +++ b/kde/src/SFLPhoneView.cpp @@ -51,24 +51,79 @@ #include "lib/instance_interface_singleton.h" #include "lib/sflphone_const.h" #include "lib/Contact.h" +#include "klib/HelperFunctions.h" //ConfigurationDialog* SFLPhoneView::configDialog; +class ColorVisitor : public AccountListColorVisitor { +public: + ColorVisitor(QPalette pal) : m_Pal(pal) { + m_Green = QColor(m_Pal.color(QPalette::Base)); + if (m_Green.green()+20 >= 255) { + m_Green.setRed ( ((int)m_Green.red() -20)); + m_Green.setBlue( ((int)m_Green.blue() -20)); + } + else + m_Green.setGreen(((int)m_Green.green()+20)); + + m_Red = QColor(m_Pal.color(QPalette::Base)); + if (m_Red.red()+20 >= 255) { + m_Red.setGreen( ((int)m_Red.green() -20)); + m_Red.setBlue( ((int)m_Red.blue() -20)); + } + else + m_Red.setRed( ((int)m_Red.red() +20)); + + m_Yellow = QColor(m_Pal.color(QPalette::Base)); + if (m_Yellow.red()+20 >= 255 || m_Green.green()+20 >= 255) { + m_Yellow.setBlue(((int)m_Yellow.blue() -20)); + } + else { + m_Yellow.setGreen(((int)m_Yellow.green()+20)); + m_Yellow.setRed( ((int)m_Yellow.red() +20)); + } + } + + virtual QVariant getColor(const Account* a) { + if(a->getAccountRegistrationStatus() == ACCOUNT_STATE_UNREGISTERED || !a->isEnabled()) + return m_Pal.color(QPalette::Base); + if(a->getAccountRegistrationStatus() == ACCOUNT_STATE_REGISTERED || a->getAccountRegistrationStatus() == ACCOUNT_STATE_READY) { + return m_Green; + } + if(a->getAccountRegistrationStatus() == ACCOUNT_STATE_TRYING) + return m_Yellow; + return m_Red; + } + + virtual QVariant getIcon(const Account* a) { + if (a->currentState() == MODIFIED) + return KIcon("document-save"); + else if (a->currentState() == OUTDATED) { + return KIcon("view-refresh"); + } + return QVariant(); + } +private: + QPalette m_Pal; + QColor m_Green; + QColor m_Yellow; + QColor m_Red; +}; + ///Constructor SFLPhoneView::SFLPhoneView(QWidget *parent) : QWidget(parent), wizard(0), errorWindow(0) { setupUi(this); - ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - - callView->setTitle(i18n("Calls")); QPalette pal = QPalette(palette()); pal.setColor(QPalette::AlternateBase, Qt::lightGray); setPalette(pal); + AccountList::getInstance()->setColorVisitor(new ColorVisitor(pal)); + m_pMessageBoxW->setVisible(false); // SENDER SIGNAL RECEIVER SLOT / @@ -77,10 +132,9 @@ SFLPhoneView::SFLPhoneView(QWidget *parent) /**/connect(callView , SIGNAL(itemChanged(Call*)) , this , SLOT(updateWindowCallState() )); /**///connect(SFLPhone::model() , SIGNAL(volumeChanged(const QString &, double)), this , SLOT(on1_volumeChanged(const QString &, double) )); /**/connect(SFLPhone::model() , SIGNAL(callStateChanged(Call*)) , this , SLOT(updateWindowCallState() )); - /**/connect(SFLPhone::model() , SIGNAL(accountStateChanged(Account*,QString)) , this , SLOT(updateStatusMessage() )); + /**/connect(AccountList::getInstance() , SIGNAL(accountStateChanged(Account*,QString)) , this , SLOT(updateStatusMessage() )); /**/connect(AccountList::getInstance() , SIGNAL(accountListUpdated()) , this , SLOT(updateStatusMessage() )); /**/connect(AccountList::getInstance() , SIGNAL(accountListUpdated()) , this , SLOT(updateWindowCallState() )); - /**/connect(&configurationManager , SIGNAL(accountsChanged()) , AccountList::getInstance() , SLOT(updateAccounts() )); /**/connect(m_pSendMessageLE , SIGNAL(returnPressed()) , this , SLOT(sendMessage() )); /**/connect(m_pSendMessagePB , SIGNAL(clicked()) , this , SLOT(sendMessage() )); /* */ @@ -158,13 +212,11 @@ void SFLPhoneView::typeString(QString str) Call* call = callView->getCurrentItem(); callManager.playDTMF(str); - Call *currentCall = 0; - Call *candidate = 0; + Call* currentCall = nullptr; + Call* candidate = nullptr; - if(call) { - if(call->getState() == CALL_STATE_CURRENT) { - currentCall = call; - } + if(call && call->getState() == CALL_STATE_CURRENT) { + currentCall = call; } foreach (Call* call2, SFLPhone::model()->getCallList()) { @@ -184,6 +236,8 @@ void SFLPhoneView::typeString(QString str) if(!currentCall && candidate) { candidate->appendText(str); } + if (!candidate) + HelperFunctions::displayNoAccountMessageBox(this); } //typeString ///Called when a backspace is detected @@ -196,9 +250,8 @@ void SFLPhoneView::backspace() } else { call->backspaceItemText(); - if(call->getState() == CALL_STATE_OVER) { - if (callView->getCurrentItem()) - callView->removeItem(callView->getCurrentItem()); + if(call->getState() == CALL_STATE_OVER && callView->getCurrentItem()) { + callView->removeItem(callView->getCurrentItem()); } } } @@ -246,8 +299,14 @@ void SFLPhoneView::enter() ///Create a call from the clipboard content void SFLPhoneView::paste() { - QClipboard* cb = QApplication::clipboard();; - typeString(cb->text()); + QClipboard* cb = QApplication::clipboard(); + const QMimeData* data = cb->mimeData(); + if (data->hasFormat(MIME_PHONENUMBER)) { + typeString(data->data(MIME_PHONENUMBER)); + } + else { + typeString(cb->text()); + } } @@ -275,24 +334,26 @@ void SFLPhoneView::action(Call* call, call_action action) } //action ///Select a phone number when calling using a contact -bool SFLPhoneView::selectCallPhoneNumber(Call* call2,Contact* contact) +bool SFLPhoneView::selectCallPhoneNumber(Call** call2,Contact* contact) { if (contact->getPhoneNumbers().count() == 1) { - call2 = SFLPhone::model()->addDialingCall(contact->getFormattedName(),AccountList::getCurrentAccount()); - call2->appendText(contact->getPhoneNumbers()[0]->getNumber()); + *call2 = SFLPhone::model()->addDialingCall(contact->getFormattedName(),AccountList::getCurrentAccount()); + if (*call2) + (*call2)->appendText(contact->getPhoneNumbers()[0]->getNumber()); } else if (contact->getPhoneNumbers().count() > 1) { - bool ok = false; - QHash<QString,QString> map; - QStringList list; + bool ok = false; + QHash<QString,QString> map ; + QStringList list ; foreach (Contact::PhoneNumber* number, contact->getPhoneNumbers()) { map[number->getType()+" ("+number->getNumber()+")"] = number->getNumber(); list << number->getType()+" ("+number->getNumber()+")"; } QString result = QInputDialog::getItem (this, i18n("Select phone number"), i18n("This contact have many phone number, please select the one you wish to call"), list, 0, false, &ok); if (ok) { - call2 = SFLPhone::model()->addDialingCall(contact->getFormattedName(), AccountList::getCurrentAccount()); - call2->appendText(map[result]); + (*call2) = SFLPhone::model()->addDialingCall(contact->getFormattedName(), AccountList::getCurrentAccount()); + if (*call2) + (*call2)->appendText(map[result]); } else { kDebug() << "Operation cancelled"; @@ -306,25 +367,24 @@ bool SFLPhoneView::selectCallPhoneNumber(Call* call2,Contact* contact) return true; } //selectCallPhoneNumber + /***************************************************************************** * * * Update display related code * * * ****************************************************************************/ - ///Change GUI icons void SFLPhoneView::updateWindowCallState() { kDebug() << "Call state changed"; - bool enabledActions[6]= {true,true,true,true,true,true}; - QString buttonIconFiles[6] = {ICON_CALL, ICON_HANGUP, ICON_HOLD, ICON_TRANSFER, ICON_REC_DEL_OFF, ICON_MAILBOX}; - QString actionTexts[6] = {ACTION_LABEL_CALL, ACTION_LABEL_HANG_UP, ACTION_LABEL_HOLD, ACTION_LABEL_TRANSFER, ACTION_LABEL_RECORD, ACTION_LABEL_MAILBOX}; + bool enabledActions [6] = { true ,true , true , true , true , true }; + QString buttonIconFiles[6] = { ICON_CALL , ICON_HANGUP , ICON_HOLD , ICON_TRANSFER , ICON_REC_DEL_OFF , ICON_MAILBOX }; + QString actionTexts [6] = { ACTION_LABEL_CALL, ACTION_LABEL_HANG_UP, ACTION_LABEL_HOLD, ACTION_LABEL_TRANSFER, ACTION_LABEL_RECORD, ACTION_LABEL_MAILBOX }; Call* call = 0; - bool transfer = false; - bool recordActivated = false; //tells whether the call is in recording position + bool transfer(false),recordActivated(false); enabledActions[SFLPhone::Mailbox] = AccountList::getCurrentAccount() && ! AccountList::getCurrentAccount()->getAccountMailbox().isEmpty(); @@ -436,34 +496,13 @@ void SFLPhoneView::updateWindowCallState() kDebug() << "Window updated."; } //updateWindowCallState -///Deprecated? -int SFLPhoneView::phoneNumberTypesDisplayed() -{ - ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - MapStringInt addressBookSettings = configurationManager.getAddressbookSettings().value(); - int typesDisplayed = 0; - if(addressBookSettings[ADDRESSBOOK_DISPLAY_BUSINESS]) { - typesDisplayed = typesDisplayed | KABC::PhoneNumber::Work; - } - - if(addressBookSettings[ADDRESSBOOK_DISPLAY_MOBILE]) { - typesDisplayed = typesDisplayed | KABC::PhoneNumber::Cell; - } - - if(addressBookSettings[ADDRESSBOOK_DISPLAY_HOME]) { - typesDisplayed = typesDisplayed | KABC::PhoneNumber::Home; - } - - return typesDisplayed; -} - ///Change icon of the record button void SFLPhoneView::updateRecordButton() { kDebug() << "updateRecordButton"; CallManagerInterface & callManager = CallManagerInterfaceSingleton::getInstance(); double recVol = callManager.getVolume(RECORD_DEVICE); - if(recVol == 0.00) { + if(recVol == 0.00) { toolButton_recVol->setIcon(QIcon(ICON_REC_VOL_0)); } else if(recVol < 0.33) { @@ -488,7 +527,7 @@ void SFLPhoneView::updateVolumeButton() CallManagerInterface & callManager = CallManagerInterfaceSingleton::getInstance(); double sndVol = callManager.getVolume(SOUND_DEVICE); - if(sndVol == 0.00) { + if(sndVol == 0.00) { toolButton_sndVol->setIcon(QIcon(ICON_SND_VOL_0)); } else if(sndVol < 0.33) { @@ -515,6 +554,8 @@ void SFLPhoneView::updateRecordBar(double _value) int value = (_value > 0)?_value:(int)(recVol * 100); slider_recVol->setValue(value); } + +///Update the volume bar void SFLPhoneView::updateVolumeBar(double _value) { CallManagerInterface & callManager = CallManagerInterfaceSingleton::getInstance(); @@ -527,14 +568,10 @@ void SFLPhoneView::updateVolumeBar(double _value) ///Hide or show the volume control void SFLPhoneView::updateVolumeControls() { - //SFLPhone::app()->action_displayVolumeControls->setChecked(display); - //widget_recVol->setVisible(display); - //widget_sndVol->setVisible(display); toolButton_recVol->setVisible ( SFLPhone::app()->action_displayVolumeControls->isChecked() && ConfigurationSkeleton::displayVolume() ); toolButton_sndVol->setVisible ( SFLPhone::app()->action_displayVolumeControls->isChecked() && ConfigurationSkeleton::displayVolume() ); slider_recVol->setVisible ( SFLPhone::app()->action_displayVolumeControls->isChecked() && ConfigurationSkeleton::displayVolume() ); slider_sndVol->setVisible ( SFLPhone::app()->action_displayVolumeControls->isChecked() && ConfigurationSkeleton::displayVolume() ); - } ///Hide or show the dialpad @@ -553,8 +590,8 @@ void SFLPhoneView::updateStatusMessage() } else { emit statusMessageChangeAsked(i18n("Using account") - + " \'" + account->getAlias() - + "\' (" + account->getAccountRegistrationStatus() + ")"); + + " \'" + account->getAlias() + + "\' (" + account->getAccountRegistrationStatus() + ")"); } } @@ -587,7 +624,7 @@ void SFLPhoneView::displayMessageBox(bool checked) Call* call = callView->getCurrentItem(); m_pMessageBoxW->setVisible(checked && call - && (call->getState() == CALL_STATE_CURRENT + && (call->getState() == CALL_STATE_CURRENT || call->getState() == CALL_STATE_HOLD ) ); @@ -695,7 +732,7 @@ void SFLPhoneView::setAccountFirst(Account * account) else { AccountList::setPriorAccountId(QString()); } - kDebug() << "Current account id" << AccountList::getCurrentAccount()->getAccountId(); + kDebug() << "Current account id" << (AccountList::getCurrentAccount()?AccountList::getCurrentAccount()->getAccountId():"<no account>"); updateStatusMessage(); } @@ -732,7 +769,7 @@ void SFLPhoneView::accept() } else { int state = call->getState(); - if(state == CALL_STATE_RINGING || state == CALL_STATE_CURRENT || state == CALL_STATE_HOLD || state == CALL_STATE_BUSY) + if (state == CALL_STATE_RINGING || state == CALL_STATE_CURRENT || state == CALL_STATE_HOLD || state == CALL_STATE_BUSY) { kDebug() << "Calling when item currently ringing, current, hold or busy. Opening an item."; SFLPhone::model()->addDialingCall(); @@ -797,8 +834,13 @@ void SFLPhoneView::mailBox() Account* account = AccountList::getCurrentAccount(); QString mailBoxNumber = account->getAccountMailbox(); Call* call = SFLPhone::model()->addDialingCall(); - call->appendText(mailBoxNumber); - action(call, CALL_ACTION_ACCEPT); + if (call) { + call->appendText(mailBoxNumber); + action(call, CALL_ACTION_ACCEPT); + } + else { + HelperFunctions::displayNoAccountMessageBox(this); + } } ///Called the there is an error (dbus) @@ -846,4 +888,4 @@ void SFLPhoneView::sendMessage() } } -#include "SFLPhoneView.moc" +#include "SFLPhoneView.moc" \ No newline at end of file diff --git a/kde/src/SFLPhoneView.h b/kde/src/SFLPhoneView.h index e0204f2995a53e7337bd10b401bcf1d5ebd8aefd..8755af06ea51695a7a4a25217fa6aa7ede4849aa 100755 --- a/kde/src/SFLPhoneView.h +++ b/kde/src/SFLPhoneView.h @@ -51,8 +51,8 @@ class SFLPhoneView : public QWidget, public Ui::SFLPhone_view Q_OBJECT private: - AccountWizard * wizard; - QErrorMessage * errorWindow; + AccountWizard* wizard ; + QErrorMessage* errorWindow; protected: @@ -77,21 +77,10 @@ public: virtual ~SFLPhoneView(); //Getters - - QErrorMessage * getErrorWindow(); - //Daemon getters - /** - * Used to sort contacts according to their types with Kabc. - * @return the integer resulting to the flags of the types - * chosen to be displayed in SFLphone configuration. - */ - int phoneNumberTypesDisplayed(); - - - bool selectCallPhoneNumber(Call* call,Contact* contact); + bool selectCallPhoneNumber(Call** call,Contact* contact); private slots: /** @@ -153,7 +142,7 @@ private slots: void updateVolumeBar (double _value = -1); void updateVolumeControls (); void updateDialpad (); - void sendMessage(); + void sendMessage (); @@ -176,11 +165,11 @@ public slots: virtual void keyPressEvent(QKeyEvent *event); - void displayVolumeControls(bool checked = true); - void displayDialpad(bool checked = true); - void displayMessageBox(bool checked = true); - void configureSflPhone(); - void accountCreationWizard(); + void displayVolumeControls ( bool checked = true ); + void displayDialpad ( bool checked = true ); + void displayMessageBox ( bool checked = true ); + void configureSflPhone (); + void accountCreationWizard (); void accept (); void refuse (); void hold (); @@ -196,10 +185,10 @@ public slots: void on_toolButton_recVol_clicked ( bool checked ); void on_toolButton_sndVol_clicked ( bool checked ); - void on1_error(MapStringString details); - void on1_incomingCall(Call* call); - void on1_voiceMailNotify(const QString &accountID, int count); - void on1_volumeChanged(const QString &device, double value); + void on1_error ( MapStringString details ); + void on1_incomingCall ( Call* call ); + void on1_voiceMailNotify( const QString &accountID, int count ); + void on1_volumeChanged ( const QString &device, double value ); signals: ///The status need to be updated diff --git a/kde/src/SFLPhoneapplication.cpp b/kde/src/SFLPhoneapplication.cpp index ba3497f37fe1874528ca67dbd52886838ed0748a..2c16945569b6290861ecedffeffacfac2661708f 100755 --- a/kde/src/SFLPhoneapplication.cpp +++ b/kde/src/SFLPhoneapplication.cpp @@ -73,28 +73,15 @@ void SFLPhoneApplication::initializeMainWindow() // Enable KDE session restore. // int restoredWindow = -1; -// if( kapp->isSessionRestored() ) { -// int n = 0; -// while( KMainWindow::canBeRestored( ++n ) ) { -// if( KMainWindow::classNameOfToplevel( n ) != QLatin1String( "SFLPhone" ) ) { -// continue; -// } -// -// restoredWindow = n; -// break; -// } -// } - - // Create the main window and initialize it -// sflphoneWindow_ = new SFLPhone(); -// if( ! sflphoneWindow_->initialize() ) { -// exit(1); -// return; -// } - - // Initialize KApplication - //setTopWidget( sflphoneWindow_ ); -// sflphoneWindow_->show(); + if( kapp->isSessionRestored() ) { + int n = 0; + while( KMainWindow::canBeRestored( ++n ) ) { + if( KMainWindow::classNameOfToplevel( n ) != QLatin1String( "SFLPhone" ) ) { + continue; + } + break; + } + } } diff --git a/kde/src/SFLPhoneapplication.h b/kde/src/SFLPhoneapplication.h index b7bd24697ca664b87e25549aa4a1602b8ed28b6d..37479e42b17ae8d4e8b9ef3d55cfb4bc7804cd03 100755 --- a/kde/src/SFLPhoneapplication.h +++ b/kde/src/SFLPhoneapplication.h @@ -41,14 +41,9 @@ public: virtual ~SFLPhoneApplication(); private: - //Constructor + //Init void initializeMainWindow(); void initializePaths(); - -private: - // Reference to the sflphone window - //SFLPhone *sflphoneWindow_; - }; #endif // SFLPHONEAPPLICATION_H diff --git a/kde/src/conf/ConfigAccountList.cpp b/kde/src/conf/ConfigAccountList.cpp deleted file mode 100644 index 14c8adc699c9bf11f13051c3b558f63f702cc948..0000000000000000000000000000000000000000 --- a/kde/src/conf/ConfigAccountList.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/************************************************************************************ - * Copyright (C) 2009 by Savoir-Faire Linux * - * Author : Jérémy Quentin <jeremy.quentin@savoirfairelinux.com> * - * Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***********************************************************************************/ - -#include "ConfigAccountList.h" - -//KDE -#include <KDebug> - -#include "lib/sflphone_const.h" -#include "lib/configurationmanager_interface_singleton.h" - -///Constructor -ConfigAccountList::ConfigAccountList(QStringList &_accountIds) : QObject() -{ - accounts = new QVector<AccountView*>(); - for (int i = 0; i < _accountIds.size(); ++i) { - (*accounts) += AccountView::buildExistingAccountFromId(_accountIds[i]); - } -} - -///Constructor -///@param fill Keep the list empty (false), load all account (true) -ConfigAccountList::ConfigAccountList(bool fill) : QObject() -{ - accounts = new QVector<AccountView*>(); - if(fill) - updateAccounts(); -} - -///Destructor -ConfigAccountList::~ConfigAccountList() -{ - foreach(Account* a, *accounts) { - delete a; - } - delete accounts; -} - -///Get an account using a widget -AccountView* ConfigAccountList::getAccountByItem(QListWidgetItem* item) -{ - for (int i = 0; i < accounts->size(); ++i) { - if ((*accounts)[i]->getItem() == item) - return (*accounts)[i]; - } - return NULL; -} - -///Add an account -AccountView* ConfigAccountList::addAccount(const QString& alias) -{ - AccountView* a = AccountView::buildNewAccountFromAlias(alias); - (*accounts) += a; - return a; -} - -///Remove an account -void ConfigAccountList::removeAccount(QListWidgetItem* item) -{ - if(!item) { - kDebug() << "Attempting to remove an account from a NULL item."; - return; - } - - AccountView* a = (AccountView*) getAccountByItem(item); - if(!a) { - kDebug() << "Attempting to remove an unexisting account."; - return; - } - - accounts->remove(accounts->indexOf(a)); -} - -///Operator overload to access an account using its position in the list -AccountView* ConfigAccountList::operator[] (int i) -{ - if (i < accounts->size()) - return (*accounts)[i]; - else - return 0; -} - -///Remove an account -void ConfigAccountList::removeAccount(AccountView* account) -{ - accounts->remove(accounts->indexOf(account)); -} - -///Get an account by id -AccountView* ConfigAccountList::getAccountById(const QString & id) const -{ - if(id.isEmpty()) - return NULL; - for (int i = 0; i < accounts->size(); ++i) { - if (!(*accounts)[i]->isNew() && (*accounts)[i]->getAccountId() == id) - return (*accounts)[i]; - } - return NULL; -} - -///Get an account according to its state -QVector<AccountView*> ConfigAccountList::getAccountByState(QString & state) -{ - QVector<AccountView*> v; - for (int i = 0; i < accounts->size(); ++i) { - if ((*accounts)[i]->getAccountRegistrationStatus() == state) - v += (*accounts)[i]; - } - return v; -} - -///Return the list of all loaded accounts -QVector<AccountView*>& ConfigAccountList::getAccounts() -{ - return *accounts; -} - -///Get account at index 'i' -const AccountView* ConfigAccountList::getAccountAt(int i) const -{ - return (*accounts)[i]; -} - -///Get account at index 'i' -AccountView* ConfigAccountList::getAccountAt (int i) -{ - return (*accounts)[i]; -} - -///Update the list -void ConfigAccountList::update() -{ - ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - AccountView* current; - for (int i = 0; i < accounts->size(); i++) { - current = (*accounts)[i]; - if (!(*accounts)[i]->isNew()) - removeAccount(current); - } - //ask for the list of accounts ids to the configurationManager - QStringList accountIds = configurationManager.getAccountList().value(); - for (int i = 0; i < accountIds.size(); ++i) { - accounts->insert(i, AccountView::buildExistingAccountFromId(accountIds[i])); - } -} - -///Reload accounts -void ConfigAccountList::updateAccounts() -{ - kDebug() << "updateAccounts"; - ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - QStringList accountIds = configurationManager.getAccountList().value(); - accounts->clear(); - for (int i = 0; i < accountIds.size(); ++i) { - (*accounts) += AccountView::buildExistingAccountFromId(accountIds[i]); - } - emit accountListUpdated(); -} - -///Move account up -void ConfigAccountList::upAccount(int index) -{ - if(index <= 0 || index >= size()) { - kDebug() << "Error : index or future index out of range in upAccount."; - return; - } - AccountView* account = getAccountAt(index); - accounts->remove(index); - accounts->insert(index - 1, account); -} - -///Move account down -void ConfigAccountList::downAccount(int index) -{ - if(index < 0 || index >= size() - 1) { - kDebug() << "Error : index or future index out of range in upAccount."; - return; - } - AccountView* account = getAccountAt(index); - accounts->remove(index); - accounts->insert(index + 1, account); -} - -///Get an account list separated by '/' -QString ConfigAccountList::getOrderedList() const -{ - QString order; - for( int i = 0 ; i < size() ; i++) { - order += getAccountAt(i)->getAccountId() + "/"; - } - return order; -} - -///Return a list of all registered accounts -QVector<AccountView*> ConfigAccountList::registeredAccounts() const -{ - QVector<AccountView*> registeredAccounts; - AccountView* current; - for (int i = 0; i < accounts->count(); ++i) { - current = (*accounts)[i]; - if(current->getAccountRegistrationStatus() == ACCOUNT_STATE_REGISTERED) { - kDebug() << current->getAlias() << " : " << current; - registeredAccounts.append(current); - } - } - return registeredAccounts; -} - -///Return the first registered account -AccountView* ConfigAccountList::firstRegisteredAccount() const -{ - AccountView* current; - for (int i = 0; i < accounts->count(); ++i) { - current = (*accounts)[i]; - if(current->getAccountRegistrationStatus() == ACCOUNT_STATE_REGISTERED) - { - return current; - } - } - return NULL; -} - -///Return the number (count) of accounts -int ConfigAccountList::size() const -{ - return accounts->size(); -} - diff --git a/kde/src/conf/ConfigAccountList.h b/kde/src/conf/ConfigAccountList.h deleted file mode 100644 index efd320ed4edad25ff31cc399cc517f250983d6a6..0000000000000000000000000000000000000000 --- a/kde/src/conf/ConfigAccountList.h +++ /dev/null @@ -1,69 +0,0 @@ -/************************************************************************************ - * Copyright (C) 2009 by Savoir-Faire Linux * - * Author : Jérémy Quentin <jeremy.quentin@savoirfairelinux.com> * - * Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***********************************************************************************/ - -#ifndef CONFIG_ACCOUNT_LIST_H -#define CONFIG_ACCOUNT_LIST_H - -#include "../lib/AccountList.h" -#include "../AccountView.h" - -///ConfigAccountList: Account list model -class ConfigAccountList : public QObject { - Q_OBJECT - public: - - ///Constructor - ConfigAccountList(bool fill = true); - ConfigAccountList(QStringList &_accountIds); - ~ConfigAccountList(); - - ///Getters - const AccountView* getAccountAt ( int i ) const; - AccountView* getAccountAt ( int i ) ; - QVector<AccountView*>& getAccounts ( ) ; - AccountView* firstRegisteredAccount ( ) const; - QVector<AccountView*> registeredAccounts ( ) const; - QString getOrderedList ( ) const; - int size ( ) const; - AccountView* getAccountByItem ( QListWidgetItem* item ) ; - QVector<AccountView*> getAccountByState ( QString & state ) ; - AccountView* getAccountById ( const QString & id ) const; - - ///Mutators - virtual AccountView* addAccount ( const QString & alias ); - void removeAccount ( QListWidgetItem* item ); - void removeAccount ( AccountView* account ); - void update ( ); - void updateAccounts ( ); - void upAccount ( int index ); - void downAccount ( int index ); - - ///Operators - AccountView* operator[] (int i); - - private: - QVector<AccountView*>* accounts; - - signals: - ///Emitted when the list change - void accountListUpdated(); -}; - -#endif diff --git a/kde/src/conf/ConfigurationDialog.cpp b/kde/src/conf/ConfigurationDialog.cpp index 960a10c207b9763d1d5aedd0e31a36ffc7b6e127..ac8f470f196ac4cad1e08e065fb3532255f24588 100755 --- a/kde/src/conf/ConfigurationDialog.cpp +++ b/kde/src/conf/ConfigurationDialog.cpp @@ -43,11 +43,11 @@ ConfigurationDialog::ConfigurationDialog(SFLPhoneView *parent) dlgGeneral = new DlgGeneral (this); dlgDisplay = new DlgDisplay (this); - dlgAccounts = new DlgAccounts (this); dlgAudio = new DlgAudio (this); dlgAddressBook = new DlgAddressBook (this); dlgHooks = new DlgHooks (this); dlgAccessibility = new DlgAccessibility (this); + dlgAccounts = new DlgAccounts (this); #ifdef ENABLE_VIDEO dlgVideo = new DlgVideo (this); @@ -64,8 +64,9 @@ ConfigurationDialog::ConfigurationDialog(SFLPhoneView *parent) addPage( dlgVideo , i18n("Video") , "camera-web" ); #endif - connect(this, SIGNAL(applyClicked()), this, SLOT(applyCustomSettings())); - connect(this, SIGNAL(okClicked()), this, SLOT(applyCustomSettings())); + connect(this, SIGNAL(applyClicked()), this, SLOT(applyCustomSettings())); + connect(this, SIGNAL(okClicked()), this, SLOT(applyCustomSettings())); + connect(this, SIGNAL(cancelClicked()), this, SLOT(cancelSettings() )); connect(dlgGeneral, SIGNAL(clearCallHistoryAsked()), this, SIGNAL(clearCallHistoryAsked())); } //ConfigurationDialog @@ -103,6 +104,11 @@ void ConfigurationDialog::updateSettings() dlgAccessibility->updateSettings(); } +void ConfigurationDialog::cancelSettings() +{ + dlgAccounts->cancel(); +} + bool ConfigurationDialog::hasChanged() { bool res = dlgAudio->hasChanged() || dlgAccounts->hasChanged() || dlgGeneral->hasChanged(); diff --git a/kde/src/conf/ConfigurationDialog.h b/kde/src/conf/ConfigurationDialog.h index ecf1cdb7bc989a152ceb88f3813d995a6bb5b74f..964e5e19606ebbe9dc6aef22324b0ca2b684c6ad 100755 --- a/kde/src/conf/ConfigurationDialog.h +++ b/kde/src/conf/ConfigurationDialog.h @@ -90,6 +90,12 @@ public slots: * Disable/Enable Apply Button according to hasChanged() result */ void updateButtons(); + + /** + * Cancel modifications (apply only to model changes) + */ + void cancelSettings(); + /** * @return whether any custom widget has changed in the dialog. */ diff --git a/kde/src/conf/dlgaccounts.cpp b/kde/src/conf/dlgaccounts.cpp index 32514896901db5e3d3fa2499a551dd792f7fe46f..9a9cfe5c4c8501d00c6e3e39e947eabaffc2ad49 100755 --- a/kde/src/conf/dlgaccounts.cpp +++ b/kde/src/conf/dlgaccounts.cpp @@ -37,198 +37,137 @@ #include "conf/ConfigurationDialog.h" #include "lib/configurationmanager_interface_singleton.h" #include "SFLPhoneView.h" -#include "../AccountView.h" #include "lib/sflphone_const.h" - -Private_AddCodecDialog::Private_AddCodecDialog(QList< StringHash > itemList, QStringList currentItems ,QWidget* parent) : KDialog(parent) -{ - codecTable = new QTableWidget(this); - codecTable->verticalHeader()->setVisible(false); - codecTable->setColumnCount(4); - for (int i=0;i<4;i++) { - codecTable->setHorizontalHeaderItem( i, new QTableWidgetItem(0)); - codecTable->horizontalHeader()->setResizeMode(i,QHeaderView::ResizeToContents); - } - - codecTable->setSelectionBehavior(QAbstractItemView::SelectRows); - codecTable->horizontalHeader()->setResizeMode(0,QHeaderView::Stretch); - codecTable->horizontalHeaderItem(0)->setText( "Name" ); - codecTable->horizontalHeaderItem(1)->setText( "Bitrate" ); - codecTable->horizontalHeaderItem(2)->setText( "Frequency" ); - codecTable->horizontalHeaderItem(3)->setText( "Alias" ); - int i =0; - foreach (StringHash aCodec, itemList) { - if ( currentItems.indexOf(aCodec["alias"]) == -1) { - codecTable->setRowCount(i+1); - QTableWidgetItem* cName = new QTableWidgetItem( aCodec["name"] ); - codecTable->setItem( i,0,cName ); - QTableWidgetItem* cBitrate = new QTableWidgetItem( aCodec["bitrate"] ); - codecTable->setItem( i,1,cBitrate ); - QTableWidgetItem* cFrequency = new QTableWidgetItem( aCodec["frequency"] ); - codecTable->setItem( i,2,cFrequency ); - QTableWidgetItem* cAlias = new QTableWidgetItem( aCodec["alias"] ); - codecTable->setItem( i,3,cAlias ); - i++; - } - } - setMainWidget(codecTable); - resize(550,300); - connect(this, SIGNAL(okClicked()), this, SLOT(emitNewCodec())); -} //Private_AddCodecDialog - -///When a new codec is added (ok pressed) -void Private_AddCodecDialog::emitNewCodec() { - if (codecTable->currentRow() >= 0) - emit addCodec(codecTable->item(codecTable->currentRow(),3)->text()); -} +#include "lib/CredentialModel.h" +#include "lib/AudioCodecModel.h" ///Constructor DlgAccounts::DlgAccounts(KConfigDialog* parent) - : QWidget(parent),accountList(NULL) + : QWidget(parent),m_IsLoading(false) { + m_IsLoading = true; setupUi(this); - disconnect(keditlistbox_codec->addButton(),SIGNAL(clicked())); - ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); button_accountUp->setIcon ( KIcon( "go-up" ) ); button_accountDown->setIcon ( KIcon( "go-down" ) ); + m_pVCodecUpPB->setIcon ( KIcon( "go-up" ) ); + m_pVCodecDownPB->setIcon ( KIcon( "go-down" ) ); button_accountAdd->setIcon ( KIcon( "list-add" ) ); button_accountRemove->setIcon ( KIcon( "list-remove" ) ); button_add_credential->setIcon ( KIcon( "list-add" ) ); button_remove_credential->setIcon ( KIcon( "list-remove" ) ); + button_audiocodecUp->setIcon ( KIcon( "go-up" ) ); + button_audiocodecDown->setIcon ( KIcon( "go-down" ) ); + listView_accountList->setModel(AccountList::getInstance()); m_pRingTonePath->setMode(KFile::File | KFile::ExistingOnly); m_pRingTonePath->lineEdit()->setObjectName("m_pRingTonePath"); m_pRingTonePath->lineEdit()->setReadOnly(true); - accountList = new ConfigAccountList(false); loadAccountList(); - loadCodecList(); accountListHasChanged = false; - //toolButton_accountsApply->setEnabled(false); //SLOTS - // SENDER SIGNAL RECEIVER SLOT / - /**/connect(edit1_alias, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); - /**/connect(edit2_protocol, SIGNAL(activated(int)) , this , SLOT(changedAccountList() )); - /**/connect(edit3_server, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); - /**/connect(edit4_user, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); - /**/connect(edit5_password, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); - /**/connect(edit6_mailbox, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); - /**/connect(spinbox_regExpire, SIGNAL(editingFinished()) , this , SLOT(changedAccountList() )); - /**/connect(comboBox_ni_local_address, SIGNAL(currentIndexChanged (int)) , this , SLOT(changedAccountList() )); - /**/connect(button_accountUp, SIGNAL(clicked()) , this , SLOT(changedAccountList() )); - /**/connect(button_accountDown, SIGNAL(clicked()) , this , SLOT(changedAccountList() )); - /**/connect(button_accountAdd, SIGNAL(clicked()) , this , SLOT(changedAccountList() )); - /**/connect(button_accountRemove, SIGNAL(clicked()) , this , SLOT(changedAccountList() )); - /**/connect(edit_tls_private_key_password, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); - /**/connect(spinbox_tls_listener, SIGNAL(editingFinished()) , this , SLOT(changedAccountList() )); - /**/connect(file_tls_authority, SIGNAL(textChanged(const QString &)) , this , SLOT(changedAccountList() )); - /**/connect(file_tls_endpoint, SIGNAL(textChanged(const QString &)) , this , SLOT(changedAccountList() )); - /**/connect(file_tls_private_key, SIGNAL(textChanged(const QString &)) , this , SLOT(changedAccountList() )); - /**/connect(combo_tls_method, SIGNAL(currentIndexChanged(int)) , this , SLOT(changedAccountList() )); - /**/connect(edit_tls_cipher, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); - /**/connect(edit_tls_outgoing, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); - /**/connect(spinbox_tls_timeout_sec, SIGNAL(editingFinished()) , this , SLOT(changedAccountList() )); - /**/connect(spinbox_tls_timeout_msec, SIGNAL(editingFinished()) , this , SLOT(changedAccountList() )); - /**/connect(check_tls_incoming, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); - /**/connect(check_tls_answer, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); - /**/connect(check_tls_requier_cert, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); - /**/connect(group_security_tls, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); - /**/connect(radioButton_pa_same_as_local, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); - /**/connect(radioButton_pa_custom, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); - /**/connect(m_pRingtoneListLW, SIGNAL(currentRowChanged(int)) , this , SLOT(changedAccountList() )); - /**/connect(m_pUseCustomFileCK, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); - /**/connect(m_pCodecsLW, SIGNAL(itemChanged(QListWidgetItem*)), this , SLOT(changedAccountList() )); - /**/connect(m_pCodecsLW, SIGNAL(currentTextChanged(QString)) , this , SLOT(loadVidCodecDetails(QString))); - /**/connect(&configurationManager, SIGNAL(accountsChanged()) , this , SLOT(updateAccountStates() )); - /**/connect(edit_tls_private_key_password, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); - /**/connect(this, SIGNAL(updateButtons()) , parent , SLOT(updateButtons() )); - /**/connect(keditlistbox_codec->listView(), SIGNAL(clicked(QModelIndex)) , this , SLOT(codecClicked(QModelIndex) )); - /**/connect(keditlistbox_codec->addButton(),SIGNAL(clicked()) , this , SLOT(addCodec() )); - /**/connect(keditlistbox_codec, SIGNAL(changed()) , this , SLOT(codecChanged() )); - /**/connect(combo_security_STRP, SIGNAL(currentIndexChanged(int)) , this , SLOT(updateCombo(int) )); - /**/connect(button_add_credential, SIGNAL(clicked()) , this , SLOT(addCredential() )); - /**/connect(button_remove_credential, SIGNAL(clicked()) , this , SLOT(removeCredential() )); - /* */ - - - connect(listWidget_accountList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(accountListChanged(QListWidgetItem*,QListWidgetItem*))); - connect(list_credential, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(selectCredential (QListWidgetItem*,QListWidgetItem*))); + // SENDER SIGNAL RECEIVER SLOT / + /**/connect(edit1_alias, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(edit2_protocol, SIGNAL(activated(int)) , this , SLOT(changedAccountList() )); + /**/connect(edit3_server, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(edit4_user, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(edit5_password, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(edit6_mailbox, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(spinbox_regExpire, SIGNAL(editingFinished()) , this , SLOT(changedAccountList() )); + /**/connect(comboBox_ni_local_address, SIGNAL(currentIndexChanged (int)) , this , SLOT(changedAccountList() )); + /**/connect(button_accountUp, SIGNAL(clicked()) , this , SLOT(changedAccountList() )); + /**/connect(button_accountDown, SIGNAL(clicked()) , this , SLOT(changedAccountList() )); + /**/connect(button_accountAdd, SIGNAL(clicked()) , this , SLOT(otherAccountChanged() )); + /**/connect(button_accountRemove, SIGNAL(clicked()) , this , SLOT(otherAccountChanged() )); + /**/connect(button_audiocodecDown, SIGNAL(clicked()) , this , SLOT(changedAccountList() )); + /**/connect(button_audiocodecUp, SIGNAL(clicked()) , this , SLOT(changedAccountList() )); + /**/connect(edit_tls_private_key_password, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(spinbox_tls_listener, SIGNAL(editingFinished()) , this , SLOT(changedAccountList() )); + /**/connect(m_pBitrateSB, SIGNAL(editingFinished()) , this , SLOT(changedAccountList() )); + /**/connect(file_tls_authority, SIGNAL(textChanged(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(file_tls_endpoint, SIGNAL(textChanged(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(file_tls_private_key, SIGNAL(textChanged(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(combo_tls_method, SIGNAL(currentIndexChanged(int)) , this , SLOT(changedAccountList() )); + /**/connect(edit_tls_cipher, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(edit_tls_outgoing, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(spinbox_tls_timeout_sec, SIGNAL(editingFinished()) , this , SLOT(changedAccountList() )); + /**/connect(spinbox_tls_timeout_msec, SIGNAL(editingFinished()) , this , SLOT(changedAccountList() )); + /**/connect(check_tls_incoming, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); + /**/connect(check_tls_answer, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); + /**/connect(check_tls_requier_cert, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); + /**/connect(group_security_tls, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); + /**/connect(radioButton_pa_same_as_local, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); + /**/connect(radioButton_pa_custom, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); + /**/connect(m_pRingtoneListLW, SIGNAL(currentRowChanged(int)) , this , SLOT(changedAccountList() )); + /**/connect(m_pUseCustomFileCK, SIGNAL(clicked(bool)) , this , SLOT(changedAccountList() )); + /**/connect(m_pCodecsLW, SIGNAL(itemChanged(QListWidgetItem*)) , this , SLOT(changedAccountList() )); + /**/connect(edit_credential_realm, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(edit_credential_auth, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(edit_credential_password, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(m_pCodecsLW, SIGNAL(currentTextChanged(QString)) , this , SLOT(loadVidCodecDetails(QString) )); + ///**/connect(&configurationManager, SIGNAL(accountsChanged()) , this , SLOT(updateAccountStates() )); + /**/connect(edit_tls_private_key_password, SIGNAL(textEdited(const QString &)) , this , SLOT(changedAccountList() )); + /**/connect(this, SIGNAL(updateButtons()) , parent , SLOT(updateButtons() )); + /**/connect(combo_security_STRP, SIGNAL(currentIndexChanged(int)) , this , SLOT(updateCombo(int) )); + /**/connect(button_add_credential, SIGNAL(clicked()) , this , SLOT(addCredential() )); + /**/connect(button_remove_credential, SIGNAL(clicked()) , this , SLOT(removeCredential() )); + /**/connect(edit5_password, SIGNAL(textEdited(const QString &)) , this , SLOT(main_password_field_changed() )); + /**/connect(edit_credential_password, SIGNAL(textEdited(const QString &)) , this , SLOT(main_credential_password_changed() )); + /**/connect(button_audiocodecUp, SIGNAL(clicked()) , this , SLOT(moveAudioCodecUp() )); + /**/connect(button_audiocodecDown, SIGNAL(clicked()) , this , SLOT(moveAudioCodecDown() )); + /**/connect(m_pVCodecUpPB, SIGNAL(clicked()) , this , SLOT(moveVideoCodecUp() )); + /**/connect(m_pVCodecDownPB, SIGNAL(clicked()) , this , SLOT(moveVideoCodecDown() )); + /**/connect(AccountList::getInstance(), SIGNAL(accountEnabledChanged(Account*)), this , SLOT(otherAccountChanged() )); + /* */ + + connect(listView_accountList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(accountListChanged(QModelIndex,QModelIndex))); + connect(listView_accountList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(updateAccountListCommands())); + //Disable control connect(radioButton_pa_same_as_local, SIGNAL(clicked(bool)) , this , SLOT(enablePublished())); connect(radioButton_pa_custom, SIGNAL(clicked(bool)) , this , SLOT(enablePublished())); -} //DlgAccounts - -///Destructor -DlgAccounts::~DlgAccounts() -{ - accountList->disconnect(); - if (accountList) delete accountList; -} -///Save the account list, necessary for new and removed accounts -void DlgAccounts::saveAccountList() -{ - ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - disconnectAccountsChangedSignal(); - //save the account being edited - if(listWidget_accountList->currentItem()) { - saveAccount(listWidget_accountList->currentItem()); + if (AccountList::getInstance()->index(0,0).isValid()) { + listView_accountList->setCurrentIndex(AccountList::getInstance()->index(0,0)); + loadAccount(listView_accountList->currentIndex()); } - QStringList accountIds= QStringList(configurationManager.getAccountList().value()); - - //create or update each account from accountList - for (int i = 0; i < accountList->size(); i++) { - AccountView* current = (*accountList)[i]; - QString currentId; - current->save(); - currentId = QString(current->getAccountId()); - } - - //remove accounts that are in the configurationManager but not in the client - for (int i = 0; i < accountIds.size(); i++) { - if(!accountList->getAccountById(accountIds[i])) { - configurationManager.removeAccount(accountIds[i]); - } - } - - configurationManager.setAccountsOrder(accountList->getOrderedList()); - connectAccountsChangedSignal(); -} //saveAccountList - -void DlgAccounts::connectAccountsChangedSignal() -{ - kDebug() << "connectAccountsChangedSignal"; - ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - connect(&configurationManager, SIGNAL(accountsChanged()), - this, SLOT(updateAccountStates())); -} + m_IsLoading = false; +} //DlgAccounts -void DlgAccounts::disconnectAccountsChangedSignal() +///Destructor +DlgAccounts::~DlgAccounts() { - kDebug() << "disconnectAccountsChangedSignal"; - ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - disconnect(&configurationManager, SIGNAL(accountsChanged()), - this, SLOT(updateAccountStates())); + //accountList->disconnect(); + //if (accountList) delete accountList; } ///Save an account using the values from the widgets -void DlgAccounts::saveAccount(QListWidgetItem * item) +void DlgAccounts::saveAccount(QModelIndex item) { - QString protocolsTab[] = ACCOUNT_TYPES_TAB; + Account* account = AccountList::getInstance()->getAccountByModelIndex(item); - if(! item) { + if(!item.isValid()) { kDebug() << "Attempting to save details of an account from a NULL item"; return; } - AccountView* account = accountList->getAccountByItem(item); if(!account) { - kDebug() << "Attempting to save details of an unexisting account : " << item->text(); + kDebug() << "Attempting to save details of an unexisting account : " << (item.data(Qt::DisplayRole).toString()); + return; + } + + //There is no point to save something that is unaltered, all it will cause is daemon corruption + if (account->currentState() != NEW and account->currentState() != MODIFIED) { + kDebug() << "Nothing to be saved"; return; } + + m_IsLoading = true; + QString protocolsTab[] = ACCOUNT_TYPES_TAB; + //ACCOUNT DETAILS // WIDGET VALUE / /**/account->setAccountAlias ( edit1_alias->text() ); @@ -237,7 +176,7 @@ void DlgAccounts::saveAccount(QListWidgetItem * item) /**/account->setAccountUsername ( edit4_user->text() ); /**/account->setAccountPassword ( edit5_password->text() ); /**/account->setAccountMailbox ( edit6_mailbox->text() ); - /**/account->setAccountEnabled ( account->isChecked() ); + /**/account->setAccountEnabled ( item.data(Qt::CheckStateRole).toBool() ); /**/account->setAccountRegistrationExpire ( spinbox_regExpire->value() ); /**/ /**/ /* Security */ @@ -251,7 +190,7 @@ void DlgAccounts::saveAccount(QListWidgetItem * item) /**/account->setTlsServerName ( edit_tls_outgoing->text() ); /**/account->setTlsNegotiationTimeoutSec ( spinbox_tls_timeout_sec->value() ); /**/account->setTlsNegotiationTimeoutMsec ( spinbox_tls_timeout_msec->value() ); - ///**/account->setTlsMethod ( QString::number(combo_security_STRP->currentIndex()) ); + ///**/account->setTlsMethod ( QString::number(combo_security_STRP->currentIndex()) ); /**/account->setTlsVerifyServer ( check_tls_incoming->isChecked() ); /**/account->setTlsVerifyClient ( check_tls_answer->isChecked() ); /**/account->setTlsRequireClientCertificate ( check_tls_requier_cert->isChecked() ); @@ -272,20 +211,7 @@ void DlgAccounts::saveAccount(QListWidgetItem * item) /**/account->setRingtonePath ( m_pRingTonePath->url().path() ); // / - QStringList _codecList; - foreach (QString aCodec, keditlistbox_codec->items()) { - foreach (StringHash _aCodec, codecList) { - if (_aCodec["alias"] == aCodec) { - _codecList << _aCodec["id"]; - } - } - } - - ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - configurationManager.setActiveAudioCodecList(_codecList, account->getAccountId()); - kDebug() << "Account codec have been saved" << _codecList << account->getAccountId(); - - if (m_pRingtoneListLW->selectedItems().size() == 1 && m_pRingtoneListLW->currentItem() ) { + if (m_pRingtoneListLW->selectedItems().size() == 1 && m_pRingtoneListLW->currentIndex().isValid() ) { QListWidgetItem* selectedRingtone = m_pRingtoneListLW->currentItem(); RingToneListItem* ringtoneWidget = qobject_cast<RingToneListItem*>(m_pRingtoneListLW->itemWidget(selectedRingtone)); if (ringtoneWidget) { @@ -293,26 +219,29 @@ void DlgAccounts::saveAccount(QListWidgetItem * item) } } - QStringList activeCodecs; - for (int i=0;i < m_pCodecsLW->count();i++) { - QListWidgetItem* item = m_pCodecsLW->item(i); - if (item->checkState() == Qt::Checked) { - activeCodecs << item->text(); - } - } - VideoCodec::setActiveCodecList(account,activeCodecs); - - saveCredential(account->getAccountId()); + if (m_pCodecsLW->currentIndex().isValid()) + m_pCodecsLW->model()->setData(m_pCodecsLW->currentIndex(),m_pBitrateSB->value(),VideoCodecModel::BITRATE_ROLE); + saveCredential(); + m_IsLoading = false; } //saveAccount -void DlgAccounts::loadAccount(QListWidgetItem * item) +void DlgAccounts::cancel() +{ + Account* account = AccountList::getInstance()->getAccountByModelIndex(listView_accountList->currentIndex()); + if (account) { + account->performAction(CANCEL); + } +} + +void DlgAccounts::loadAccount(QModelIndex item) { - if(! item ) { - kDebug() << "Attempting to load details of an account from a NULL item"; + m_IsLoading = true; + if(! item.isValid() ) { + kDebug() << "Attempting to load details of an account from a NULL item (" << item.row() << ")"; return; } - AccountView* account = accountList->getAccountByItem(item); + Account* account = AccountList::getInstance()->getAccountByModelIndex(item); if(! account ) { kDebug() << "Attempting to load details of an unexisting account"; return; @@ -331,25 +260,11 @@ void DlgAccounts::loadAccount(QListWidgetItem * item) delete protocolsList; - - loadCredentails(account->getAccountId()); - - if (credentialList.size() > 0) { - bool found = false; - foreach(CredentialData data,credentialList) { - if (data.name == account->getAccountUsername()) { - edit5_password->setText( data.password ); - found = true; - } - } - if (!found) { - //Better than nothing, can happen if username change - edit5_password->setText( credentialList[0].password ); - } - } - else { + QModelIndex idx = account->getCredentialsModel()->index(0,0); + if (idx.isValid()) + edit5_password->setText(account->getCredentialsModel()->data(idx,CredentialModel::PASSWORD_ROLE).toString()); + else edit5_password->setText(""); - } switch (account->getTlsMethod()) { @@ -410,11 +325,34 @@ void DlgAccounts::loadAccount(QListWidgetItem * item) /**/combo_security_STRP->setCurrentIndex ( account->getTlsMethod ()); /* */ + account->getVideoCodecModel()->reload(); + + disconnect(list_credential->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(selectCredential (QModelIndex,QModelIndex))); + list_credential->setModel(account->getCredentialsModel()); + connect(list_credential->selectionModel() ,SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(selectCredential (QModelIndex,QModelIndex))); + + disconnect(list_audiocodec->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(selectedCodecChanged(const QModelIndex&,const QModelIndex&))); + disconnect(list_audiocodec->model(),SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(changedAccountList())); + list_audiocodec->setModel(account->getAudioCodecModel()); + connect(list_audiocodec->model(),SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(changedAccountList())); + connect(list_audiocodec->selectionModel() ,SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(selectedCodecChanged(const QModelIndex&,const QModelIndex&))); + + #ifdef ENABLE_VIDEO + disconnect(m_pCodecsLW->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(loadVidCodecDetails(const QModelIndex&,const QModelIndex&))); + disconnect(m_pCodecsLW->model(),SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(changedAccountList())); + m_pCodecsLW->setModel(account->getVideoCodecModel()); + connect(m_pCodecsLW->model(),SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(changedAccountList())); + connect(m_pCodecsLW->selectionModel() ,SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(loadVidCodecDetails(const QModelIndex&,const QModelIndex&))); + #endif + + if (account->getAccountAlias() == "IP2IP") { frame2_editAccounts->setTabEnabled(0,false); frame2_editAccounts->setTabEnabled(1,false); + frame2_editAccounts->setTabEnabled(2,true ); frame2_editAccounts->setTabEnabled(3,false); frame2_editAccounts->setTabEnabled(4,false); + frame2_editAccounts->setTabEnabled(5,true ); } else { frame2_editAccounts->setTabEnabled(0,true); @@ -451,19 +389,8 @@ void DlgAccounts::loadAccount(QListWidgetItem * item) } if (!found) m_pRingtoneListLW->setDisabled(true); - #ifdef ENABLE_VIDEO - m_pCodecsLW->clear(); - QList<VideoCodec*> codecs = VideoCodec::getCodecList(); - QList<VideoCodec*> activeCodecs = VideoCodec::getActiveCodecList(account); - foreach(VideoCodec* codec,codecs) { - if (codec) { - QListWidgetItem* i = new QListWidgetItem(codec->getName()); - i->setCheckState((activeCodecs.indexOf(codec) != -1)?Qt::Checked:Qt::Unchecked); - m_pCodecsLW->addItem(i); - } - } - #else - m_pVideoCodecGB->setVisible(false); + #ifndef ENABLE_VIDEO + m_pVideoCodecGB->setVisible(false); #endif comboBox_ni_local_address->clear(); @@ -473,17 +400,6 @@ void DlgAccounts::loadAccount(QListWidgetItem * item) spinBox_ni_local_port->setValue(account->getLocalPort()); comboBox_ni_local_address->setCurrentIndex(comboBox_ni_local_address->findText(account->getLocalInterface())); //TODO need to load the list first - QVector<int> activeCodecList = configurationManager.getActiveAudioCodecList(account->getAccountId()); - keditlistbox_codec->clear(); - foreach (int aCodec, activeCodecList) { - foreach (StringHash _aCodec, codecList) { - if (_aCodec["id"] == QString::number(aCodec)) - keditlistbox_codec->insertItem(_aCodec["alias"]); - } - } - - - if(protocolIndex == 0) { // if sip selected checkbox_stun->setChecked(account->isAccountSipStunEnabled()); line_stun->setText( account->getAccountSipStunServer() ); @@ -505,79 +421,70 @@ void DlgAccounts::loadAccount(QListWidgetItem * item) updateStatusLabel(account); enablePublished(); frame2_editAccounts->setEnabled(true); + m_IsLoading = false; + account->performAction(EDIT); } //loadAccount ///Load an account void DlgAccounts::loadAccountList() { - accountList->updateAccounts(); - listWidget_accountList->clear(); - for (int i = 0; i < accountList->size(); ++i) { - addAccountToAccountList((*accountList)[i]); - } - if (listWidget_accountList->count() > 0 && listWidget_accountList->currentItem() == NULL) - listWidget_accountList->setCurrentRow(0); + AccountList::getInstance()->updateAccounts(); + if (listView_accountList->model()->rowCount() > 0 && !listView_accountList->currentIndex().isValid()) + listView_accountList->setCurrentIndex(listView_accountList->model()->index(0,0)); + else if (listView_accountList->currentIndex().isValid()) + frame2_editAccounts->setEnabled(true); else frame2_editAccounts->setEnabled(false); } -///Add an account to the list -void DlgAccounts::addAccountToAccountList(AccountView* account) +///Called when one of the child widget is modified +void DlgAccounts::changedAccountList() { - QListWidgetItem * item = account->getItem(); - QWidget * widget = account->getItemWidget(); - connect(widget, SIGNAL(checkStateChanged(bool)), this, SLOT(changedAccountList())); - listWidget_accountList->addItem(item); - listWidget_accountList->setItemWidget(item, widget); + if (!m_IsLoading) { + Account* acc = AccountList::getInstance()->getAccountByModelIndex(listView_accountList->currentIndex()); + if (acc) { + acc->performAction(MODIFY); + } + accountListHasChanged = true; + emit updateButtons(); + } } -///Called when one of the child widget is modified -void DlgAccounts::changedAccountList() +void DlgAccounts::otherAccountChanged() { accountListHasChanged = true; emit updateButtons(); } ///Callback when the account change -void DlgAccounts::accountListChanged( QListWidgetItem * current, QListWidgetItem * previous ) +void DlgAccounts::accountListChanged(QModelIndex current, QModelIndex previous) { - kDebug() << "on_listWidget_accountList_currentItemChanged"; saveAccount(previous); + Account* acc = AccountList::getInstance()->getAccountByModelIndex(previous); + if (acc->currentState() == EDITING || acc->currentState() == OUTDATED) + acc->performAction(CANCEL); + loadAccount(current); - updateAccountListCommands(); + //updateAccountListCommands(); } void DlgAccounts::on_button_accountUp_clicked() { kDebug() << "on_button_accountUp_clicked"; - int currentRow = listWidget_accountList->currentRow(); - QListWidgetItem * prevItem = listWidget_accountList->takeItem(currentRow); - AccountView* account = accountList->getAccountByItem(prevItem); - //we need to build a new item to set the itemWidget back - account->initItem(); - QListWidgetItem * item = account->getItem(); - AccountItemWidget * widget = account->getItemWidget(); - accountList->upAccount(currentRow); - listWidget_accountList->insertItem ( currentRow - 1 , item ); - listWidget_accountList->setItemWidget ( item, widget ); - listWidget_accountList->setCurrentItem ( item ); -} //on_button_accountUp_clicked + QModelIndex index = listView_accountList->currentIndex(); + Account* acc = AccountList::getInstance()->getAccountByModelIndex(index); + AccountList::getInstance()->accountUp(index.row()); + listView_accountList->setCurrentIndex(acc->getIndex()); +} void DlgAccounts::on_button_accountDown_clicked() { kDebug() << "on_button_accountDown_clicked"; - int currentRow = listWidget_accountList->currentRow(); - QListWidgetItem * prevItem = listWidget_accountList->takeItem(currentRow); - AccountView* account = accountList->getAccountByItem(prevItem); - //we need to build a new item to set the itemWidget back - account->initItem(); - QListWidgetItem * item = account->getItem(); - AccountItemWidget * widget = account->getItemWidget(); - accountList->downAccount(currentRow); - listWidget_accountList->insertItem ( currentRow + 1 , item ); - listWidget_accountList->setItemWidget ( item, widget ); - listWidget_accountList->setCurrentItem ( item ); -} //on_button_accountDown_clicked + QModelIndex index = listView_accountList->currentIndex(); + Account* acc = AccountList::getInstance()->getAccountByModelIndex(index); + AccountList::getInstance()->accountDown(index.row()); + listView_accountList->setCurrentIndex(acc->getIndex()); +} void DlgAccounts::on_button_accountAdd_clicked() { @@ -585,10 +492,9 @@ void DlgAccounts::on_button_accountAdd_clicked() QString itemName = QInputDialog::getText(this, "New account", "Enter new account's alias"); itemName = itemName.simplified(); if (!itemName.isEmpty()) { - AccountView* account = accountList->addAccount(itemName); - addAccountToAccountList(account); - int r = listWidget_accountList->count() - 1; - listWidget_accountList->setCurrentRow(r); + AccountList::getInstance()->addAccount(itemName); + int r = listView_accountList->model()->rowCount() - 1; + listView_accountList->setCurrentIndex(listView_accountList->model()->index(r,0)); frame2_editAccounts->setEnabled(true); } } //on_button_accountAdd_clicked @@ -596,32 +502,23 @@ void DlgAccounts::on_button_accountAdd_clicked() void DlgAccounts::on_button_accountRemove_clicked() { kDebug() << "on_button_accountRemove_clicked"; - int r = listWidget_accountList->currentRow(); - QListWidgetItem * item = listWidget_accountList->takeItem(r); - accountList->removeAccount(item); - listWidget_accountList->setCurrentRow( (r >= listWidget_accountList->count()) ? r-1 : r ); -} - -void DlgAccounts::on_edit1_alias_textChanged(const QString & text) -{ - kDebug() << "on_edit1_alias_textChanged"; - AccountItemWidget * widget = (AccountItemWidget *) listWidget_accountList->itemWidget(listWidget_accountList->currentItem()); - widget->setAccountText(text); + AccountList::getInstance()->removeAccount(listView_accountList->currentIndex()); + listView_accountList->setCurrentIndex(listView_accountList->model()->index(0,0)); } void DlgAccounts::updateAccountListCommands() { kDebug() << "updateAccountListCommands"; bool buttonsEnabled[4] = {true,true,true,true}; - if(! listWidget_accountList->currentItem()) { + if(! listView_accountList->currentIndex().isValid()) { buttonsEnabled[0] = false; buttonsEnabled[1] = false; buttonsEnabled[3] = false; } - else if(listWidget_accountList->currentRow() == 0) { + else if(listView_accountList->currentIndex().row() == 0) { buttonsEnabled[0] = false; } - if(listWidget_accountList->currentRow() == listWidget_accountList->count() - 1) { + if(listView_accountList->currentIndex().row() == listView_accountList->model()->rowCount() - 1) { buttonsEnabled[1] = false; } @@ -631,33 +528,75 @@ void DlgAccounts::updateAccountListCommands() button_accountRemove->setEnabled ( buttonsEnabled[3] ); } -void DlgAccounts::loadVidCodecDetails(const QString& text) + +void DlgAccounts::main_password_field_changed() +{ + list_credential->model()->setData(list_credential->model()->index(0,0),edit5_password->text(),CredentialModel::PASSWORD_ROLE); +} + +void DlgAccounts::main_credential_password_changed() +{ + if (list_credential->currentIndex().row() == 0) { + edit5_password->setText(edit_credential_password->text()); + } +} + +void DlgAccounts::moveAudioCodecUp() +{ + if (((AudioCodecModel*) list_audiocodec->model())->moveUp(list_audiocodec->currentIndex())) + list_audiocodec->setCurrentIndex(list_audiocodec->model()->index(list_audiocodec->currentIndex().row()-1,0)); +} + +void DlgAccounts::moveAudioCodecDown() +{ + if (((AudioCodecModel*) list_audiocodec->model())->moveDown(list_audiocodec->currentIndex())) + list_audiocodec->setCurrentIndex(list_audiocodec->model()->index(list_audiocodec->currentIndex().row()+1,0)); +} + +void DlgAccounts::moveVideoCodecUp() { - VideoCodec* codec = VideoCodec::getCodec(text); - if (codec) - m_pBitrateL->setText(codec->getBitrate()); + if (((VideoCodecModel*) m_pCodecsLW->model())->moveUp(m_pCodecsLW->currentIndex())) + m_pCodecsLW->setCurrentIndex(m_pCodecsLW->model()->index(m_pCodecsLW->currentIndex().row()-1,0)); +} + +void DlgAccounts::moveVideoCodecDown() +{ + if (((VideoCodecModel*) m_pCodecsLW->model())->moveDown(m_pCodecsLW->currentIndex())) + m_pCodecsLW->setCurrentIndex(m_pCodecsLW->model()->index(m_pCodecsLW->currentIndex().row()+1,0)); +} + +void DlgAccounts::loadVidCodecDetails(const QModelIndex& current,const QModelIndex& previous) +{ + if (previous != current && previous.isValid()) { + m_pCodecsLW->model()->setData(previous,m_pBitrateSB->value(),VideoCodecModel::BITRATE_ROLE); + } + + int bitrate = m_pCodecsLW->model()->data(current,VideoCodecModel::BITRATE_ROLE).toInt(); + m_pBitrateSB->setValue(bitrate); } void DlgAccounts::updateAccountStates() { kDebug() << "updateAccountStates"; - for (int i = 0; i < accountList->size(); i++) { - AccountView* current = accountList->getAccountAt(i); + for (int i = 0; i < AccountList::getInstance()->size(); i++) { + Account* current = AccountList::getInstance()->getAccountAt(i); current->updateState(); } - updateStatusLabel(listWidget_accountList->currentItem()); + updateStatusLabel(listView_accountList->currentIndex()); } -void DlgAccounts::updateStatusLabel(QListWidgetItem * item) +void DlgAccounts::updateStatusLabel(QModelIndex item) { - if(! item ) { - return; - } - AccountView* account = accountList->getAccountByItem(item); - updateStatusLabel(account); + kDebug() << "MODEL index is" << item.row(); + if(!item.isValid()) { + return; + } + Account* account = AccountList::getInstance()->getAccountByModelIndex(item); + if (account) + updateStatusLabel(account); } -void DlgAccounts::updateStatusLabel(AccountView* account) +void DlgAccounts::updateStatusLabel(Account* account) { if(! account ) { return; @@ -676,8 +615,14 @@ bool DlgAccounts::hasChanged() void DlgAccounts::updateSettings() { if(accountListHasChanged) { - saveAccountList(); - //toolButton_accountsApply->setEnabled(false); + if(listView_accountList->currentIndex().isValid()) { + saveAccount(listView_accountList->currentIndex()); + Account* acc = AccountList::getInstance()->getAccountByModelIndex(listView_accountList->currentIndex()); + if (acc->currentState() == EDITING || acc->currentState() == OUTDATED) + acc->performAction(CANCEL); + } + + AccountList::getInstance()->save(); accountListHasChanged = false; } } @@ -690,78 +635,14 @@ void DlgAccounts::updateWidgets() accountListHasChanged = false; } -///Get the codecs -void DlgAccounts::loadCodecList() -{ - ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - QVector<int> codecIdList = configurationManager.getAudioCodecList(); - QStringList tmpNameList; - - foreach (int aCodec, codecIdList) { - QStringList codec = configurationManager.getAudioCodecDetails(aCodec); - QHash<QString, QString> _codec; - _codec[ "name" ] = codec[0]; - _codec[ "frequency" ] = codec[1]; - _codec[ "bitrate" ] = codec[2]; - _codec[ "id" ] = QString::number(aCodec); - - tmpNameList << _codec["name"]; - - codecList.push_back(_codec); - } - - //Generate a relative alias for each codec - for (int i =0; i < codecList.size();i++) { - if (tmpNameList.indexOf(codecList[i]["name"]) == tmpNameList.lastIndexOf(codecList[i]["name"])) { - codecList[i]["alias"] = codecList[i]["name"]; - } - else { - codecList[i]["alias"] = codecList[i]["name"] + " (" + codecList[i]["frequency"] + ")"; - } - } -} //loadCodecList - - -void DlgAccounts::codecClicked(const QModelIndex& model) -{ - Q_UNUSED(model) - foreach (StringHash aCodec, codecList) { - if (aCodec["alias"] == keditlistbox_codec->currentText()) { - label_bitrate_value->setText ( aCodec["bitrate"] ); - label_frequency_value->setText ( aCodec["frequency"] ); - } - } - if (keditlistbox_codec->items().size() == codecList.size()) - keditlistbox_codec->addButton()->setEnabled(false); - else - keditlistbox_codec->addButton()->setEnabled(true); -} //codecClicked - -void DlgAccounts::addCodec(QString name) -{ - if (name.isEmpty()) { - Private_AddCodecDialog* aDialog = new Private_AddCodecDialog(codecList, keditlistbox_codec->items(), this); - aDialog->show(); - connect(aDialog, SIGNAL(addCodec(QString)), this, SLOT(addCodec(QString))); - } - else { - keditlistbox_codec->insertItem(name); - accountListHasChanged = true; - emit updateButtons(); - } -} //addCodec - -void DlgAccounts::codecChanged() -{ - if (keditlistbox_codec->items().size() == codecList.size()) - keditlistbox_codec->addButton()->setEnabled(false); - else - keditlistbox_codec->addButton()->setEnabled(true); - - accountListHasChanged = true; - emit updateButtons(); +void DlgAccounts::selectedCodecChanged(const QModelIndex& current,const QModelIndex& previous) +{ + Q_UNUSED(previous) + label_bitrate_value->setText ( list_audiocodec->model()->data(current,AudioCodecModel::BITRATE_ROLE) .toString()); + label_frequency_value->setText ( list_audiocodec->model()->data(current,AudioCodecModel::SAMPLERATE_ROLE).toString()); } + void DlgAccounts::updateCombo(int value) { Q_UNUSED(value) @@ -790,71 +671,50 @@ void DlgAccounts::updateCombo(int value) } } //updateCombo - -void DlgAccounts::loadCredentails(QString accountId) { - credentialInfo.clear(); - credentialList.clear(); - list_credential->clear(); - ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - VectorMapStringString credentials = configurationManager.getCredentials(accountId); - for (int i=0; i < credentials.size(); i++) { - QListWidgetItem* newItem = new QListWidgetItem(); - newItem->setText(credentials[i][ CONFIG_ACCOUNT_USERNAME ]); - CredentialData data; - data.pointer = newItem ; - data.name = credentials[i][ CONFIG_ACCOUNT_USERNAME ] ; - data.password = credentials[i][ CONFIG_ACCOUNT_PASSWORD ] ; - data.realm = credentials[i][ CONFIG_ACCOUNT_REALM ] ; - credentialInfo[newItem] = data; - credentialList << data; - list_credential->addItem(newItem); +void DlgAccounts::saveCredential() +{ + QModelIndex index = listView_accountList->currentIndex(); + Account* acc = AccountList::getInstance()->getAccountByModelIndex(index); + QModelIndex currentCredential = list_credential->currentIndex(); + if (currentCredential.isValid()) { + acc->getCredentialsModel()->setData(currentCredential,edit_credential_auth->text() , CredentialModel::NAME_ROLE ); + acc->getCredentialsModel()->setData(currentCredential,edit_credential_password->text(), CredentialModel::PASSWORD_ROLE ); + acc->getCredentialsModel()->setData(currentCredential,edit_credential_realm->text() , CredentialModel::REALM_ROLE ); } -} //loadCredentails -void DlgAccounts::saveCredential(QString accountId) { - ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - //configurationManager.setNumberOfCredential(accountId, list_credential->count()); //TODO - VectorMapStringString toReturn; - - for (int i=0; i < list_credential->count();i++) { - QListWidgetItem* currentItem = list_credential->item(i); - MapStringString credentialData; - credentialData[CONFIG_ACCOUNT_USERNAME] = credentialInfo[currentItem].name ; - credentialData[CONFIG_ACCOUNT_PASSWORD] = credentialInfo[currentItem].password ; - credentialData[CONFIG_ACCOUNT_REALM] = credentialInfo[currentItem].realm ; - toReturn << credentialData; - } - configurationManager.setCredentials(accountId,toReturn); + if (acc) + acc->saveCredentials(); } //saveCredential -void DlgAccounts::addCredential() { - QListWidgetItem* newItem = new QListWidgetItem(); - newItem->setText(i18n("New credential")); - credentialInfo[newItem] = {newItem, i18n("New credential"), "",""}; +void DlgAccounts::addCredential() +{ + QModelIndex index = listView_accountList->currentIndex(); + Account* acc = AccountList::getInstance()->getAccountByModelIndex(index); - selectCredential(newItem,list_credential->currentItem()); - list_credential->addItem(newItem); - list_credential->setCurrentItem(newItem); + QModelIndex idx = acc->getCredentialsModel()->addCredentials(); + list_credential->setCurrentIndex(idx); } //addCredential -void DlgAccounts::selectCredential(QListWidgetItem* item, QListWidgetItem* previous) { - if (previous) { - credentialInfo[previous].realm = edit_credential_realm->text(); - credentialInfo[previous].name = edit_credential_auth->text(); - credentialInfo[previous].password = edit_credential_password->text(); - previous->setText(edit_credential_auth->text()); - } - list_credential->setCurrentItem ( item ); - edit_credential_realm->setText ( credentialInfo[item].realm ); - edit_credential_auth->setText ( credentialInfo[item].name ); - edit_credential_password->setText ( credentialInfo[item].password ); +void DlgAccounts::selectCredential(QModelIndex item, QModelIndex previous) +{ + list_credential->model()->setData(previous,edit_credential_auth->text() , CredentialModel::NAME_ROLE ); + list_credential->model()->setData(previous,edit_credential_password->text(), CredentialModel::PASSWORD_ROLE ); + list_credential->model()->setData(previous,edit_credential_realm->text() , CredentialModel::REALM_ROLE ); + + edit_credential_realm->setText ( list_credential->model()->data(item,CredentialModel::REALM_ROLE) .toString()); + edit_credential_auth->setText ( list_credential->model()->data(item,CredentialModel::NAME_ROLE) .toString()); + edit_credential_password->setText ( list_credential->model()->data(item,CredentialModel::PASSWORD_ROLE) .toString()); + edit_credential_realm->setEnabled ( true ); edit_credential_auth->setEnabled ( true ); edit_credential_password->setEnabled ( true ); +//TODO } //selectCredential void DlgAccounts::removeCredential() { - list_credential->takeItem(list_credential->currentRow()); + Account* acc = AccountList::getInstance()->getAccountByModelIndex(listView_accountList->currentIndex()); + acc->getCredentialsModel()->removeCredentials(list_credential->currentIndex()); + list_credential->setCurrentIndex(acc->getCredentialsModel()->index(0,0)); } void DlgAccounts::enablePublished() diff --git a/kde/src/conf/dlgaccounts.h b/kde/src/conf/dlgaccounts.h index c8782145fc4f3179ca16df8ead98eecfc0546bdf..f226b328d75f5f0c327fb0fd8b81aca57b5544c0 100755 --- a/kde/src/conf/dlgaccounts.h +++ b/kde/src/conf/dlgaccounts.h @@ -22,8 +22,7 @@ #define DLGACCOUNTS_H #include "ui_dlgaccountsbase.h" -#include "AccountView.h" -#include "ConfigAccountList.h" +#include "../lib/Account.h" #include "../lib/callmanager_interface_singleton.h" //Qt @@ -34,34 +33,8 @@ class QWidget; //KDE class KConfigDialog; -///@struct CredentialData store credential informations -struct CredentialData { - QListWidgetItem* pointer ; - QString name ; - QString password; - QString realm ; -}; - //Typedef typedef QHash<QString, QString> StringHash; //Needed to fix a Qt foreach macro argument parsing bug -typedef QHash<QListWidgetItem*, CredentialData> QListWidgetItemHash; //Needed to fix a Qt foreach macro argument parsing bug -typedef QList<CredentialData> CredentialList; - -///Private_AddCodecDialog: Little dialog to add codec to the list -class Private_AddCodecDialog : public KDialog { - Q_OBJECT - public: - Private_AddCodecDialog(QList< StringHash > itemList, QStringList currentItems ,QWidget* parent = 0); - - private: - QTableWidget* codecTable; - - private slots: - void emitNewCodec(); - - signals: - void addCodec(QString alias); -}; ///RingToneListItem: Ringtone list widget class RingToneListItem : public QWidget @@ -116,7 +89,9 @@ public: DlgAccounts(KConfigDialog *parent = 0); ~DlgAccounts(); - void saveAccount(QListWidgetItem * item); + void saveAccount(QModelIndex item); + + void cancel(); /** * Fills the settings form in the right side with the @@ -132,54 +107,49 @@ public: * @param item the item with which to fill the settings form * */ - void loadAccount(QListWidgetItem * item); + void loadAccount(QModelIndex item); private: ///Attributes - ConfigAccountList* accountList ; QList<StringHash> codecList ; - QListWidgetItemHash credentialInfo ; - CredentialList credentialList ; bool accountListHasChanged ; - QMap<QString,QString> m_hRingtonePath; - - ///Mutators - void loadCodecList(); + QMap<QString,QString> m_hRingtonePath ; + bool m_IsLoading ; public slots: - void saveAccountList (); void loadAccountList (); bool hasChanged (); void updateSettings (); void updateWidgets (); private slots: - void changedAccountList (); - void connectAccountsChangedSignal (); - void disconnectAccountsChangedSignal (); - void on_button_accountUp_clicked (); - void on_button_accountDown_clicked (); - void on_button_accountAdd_clicked (); - void on_button_accountRemove_clicked (); - void codecChanged (); - void addCredential (); - void removeCredential (); - void enablePublished (); - void updateAccountStates (); - void updateAccountListCommands (); - - void loadVidCodecDetails ( const QString& name ); - void codecClicked ( const QModelIndex& model ); - void updateStatusLabel ( QListWidgetItem* item ); - void accountListChanged ( QListWidgetItem* current , QListWidgetItem * previous ); - void selectCredential ( QListWidgetItem* item , QListWidgetItem* previous ); - void addAccountToAccountList ( AccountView* account ); - void updateStatusLabel ( AccountView* account ); - void addCodec ( QString name = "" ); - void updateCombo ( int value ); - void loadCredentails ( QString accountId ); - void saveCredential ( QString accountId ); - void on_edit1_alias_textChanged ( const QString& text ); + void changedAccountList (); + void otherAccountChanged (); + void on_button_accountUp_clicked (); + void on_button_accountDown_clicked (); + void on_button_accountAdd_clicked (); + void on_button_accountRemove_clicked (); + void addCredential (); + void removeCredential (); + void enablePublished (); + void updateAccountStates (); + void updateAccountListCommands (); + void main_password_field_changed (); + void main_credential_password_changed (); + + void moveAudioCodecUp (); + void moveAudioCodecDown (); + void moveVideoCodecUp (); + void moveVideoCodecDown (); + + void loadVidCodecDetails ( const QModelIndex& current, const QModelIndex& previous ); + void selectedCodecChanged ( const QModelIndex& current, const QModelIndex& previous ); + void updateStatusLabel ( QModelIndex item ); + void accountListChanged ( QModelIndex current, QModelIndex previous ); + void selectCredential ( QModelIndex item, QModelIndex previous ); + void updateStatusLabel ( Account* account ); + void updateCombo ( int value ); + void saveCredential ( ); signals: diff --git a/kde/src/conf/dlgaccountsbase.ui b/kde/src/conf/dlgaccountsbase.ui index e5732f0b940ea9c4268e62876027105bda0c311d..ae6f32e14efc23336ce339afc094fa3254b17b66 100755 --- a/kde/src/conf/dlgaccountsbase.ui +++ b/kde/src/conf/dlgaccountsbase.ui @@ -59,34 +59,6 @@ <enum>QFrame::Raised</enum> </property> <layout class="QGridLayout" name="gridLayout_13"> - <item row="0" column="0" colspan="5"> - <widget class="QListWidget" name="listWidget_accountList"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>150</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>16777215</height> - </size> - </property> - <property name="whatsThis"> - <string>By default, when you place a call, sflphone will use the first account in this list which is "registered". Change the order of the accounts using the "Up" and "Down" arrows. Enable/disable them by checking/unchecking them on the left of the item. Add or remove some with "Plus" and "Sub" buttons. Edit the selected account with the form on the right.</string> - </property> - <property name="dragEnabled"> - <bool>true</bool> - </property> - </widget> - </item> <item row="1" column="0"> <widget class="QToolButton" name="button_accountUp"> <property name="toolTip"> @@ -185,6 +157,34 @@ </property> </spacer> </item> + <item row="0" column="0" colspan="5"> + <widget class="QListView" name="listView_accountList"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>150</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>16777215</height> + </size> + </property> + <property name="whatsThis"> + <string>By default, when you place a call, sflphone will use the first account in this list which is "registered". Change the order of the accounts using the "Up" and "Down" arrows. Enable/disable them by checking/unchecking them on the left of the item. Add or remove some with "Plus" and "Sub" buttons. Edit the selected account with the form on the right.</string> + </property> + <property name="dragEnabled"> + <bool>true</bool> + </property> + </widget> + </item> </layout> </widget> </item> @@ -196,6 +196,12 @@ <verstretch>0</verstretch> </sizepolicy> </property> + <property name="minimumSize"> + <size> + <width>500</width> + <height>0</height> + </size> + </property> <property name="currentIndex"> <number>0</number> </property> @@ -560,6 +566,36 @@ <string>Audio codecs</string> </property> <layout class="QGridLayout" name="gridLayout_7"> + <item row="1" column="0"> + <widget class="QLabel" name="label_frequency"> + <property name="text"> + <string>Sample rate:</string> + </property> + </widget> + </item> + <item row="1" column="4"> + <widget class="QToolButton" name="button_audiocodecUp"> + <property name="toolTip"> + <string>Get this account up</string> + </property> + <property name="whatsThis"> + <string>By default, when you place a call, sflphone will use the first account in this list which is "registered". Change the order of the accounts using the "Up" and "Down" arrows.</string> + </property> + <property name="text"> + <string>Up</string> + </property> + <property name="shortcut"> + <string notr="true">Up, PgUp</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLabel" name="label_bitrate_value"> + <property name="text"> + <string>-</string> + </property> + </widget> + </item> <item row="1" column="2"> <spacer name="horizontalSpacer_6"> <property name="orientation"> @@ -567,7 +603,7 @@ </property> <property name="sizeHint" stdset="0"> <size> - <width>461</width> + <width>448</width> <height>20</height> </size> </property> @@ -580,31 +616,29 @@ </property> </widget> </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_frequency"> - <property name="text"> - <string>Frequency: </string> - </property> - </widget> + <item row="0" column="0" colspan="6"> + <widget class="QListView" name="list_audiocodec"/> </item> - <item row="2" column="1" colspan="2"> - <widget class="QLabel" name="label_bitrate_value"> + <item row="3" column="0"> + <widget class="QLabel" name="label_bitrate"> <property name="text"> - <string>-</string> + <string>Bitrate:</string> </property> </widget> </item> - <item row="0" column="0" colspan="3"> - <widget class="KEditListBox" name="keditlistbox_codec"> - <property name="buttons"> - <set>KEditListBox::All</set> + <item row="1" column="5"> + <widget class="QToolButton" name="button_audiocodecDown"> + <property name="toolTip"> + <string>Get this account down</string> + </property> + <property name="whatsThis"> + <string>By default, when you place a call, sflphone will use the first account in this list which is "registered". Change the order of the accounts using the "Up" and "Down" arrows.</string> </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_bitrate"> <property name="text"> - <string>Bitrate:</string> + <string>Down</string> + </property> + <property name="shortcut"> + <string notr="true">Down, PgDown</string> </property> </widget> </item> @@ -617,8 +651,25 @@ <string>Video codecs</string> </property> <layout class="QGridLayout" name="gridLayout_12"> - <item row="0" column="0" colspan="2"> - <widget class="QListWidget" name="m_pCodecsLW"/> + <item row="1" column="1"> + <widget class="QSpinBox" name="m_pBitrateSB"> + <property name="maximum"> + <number>100000</number> + </property> + </widget> + </item> + <item row="1" column="2"> + <spacer name="horizontalSpacer_11"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> </item> <item row="1" column="0"> <widget class="QLabel" name="label"> @@ -633,19 +684,23 @@ </property> </widget> </item> - <item row="1" column="1"> - <widget class="QLabel" name="m_pBitrateL"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <item row="1" column="4"> + <widget class="QToolButton" name="m_pVCodecDownPB"> + <property name="text"> + <string>Down</string> </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QToolButton" name="m_pVCodecUpPB"> <property name="text"> - <string>-</string> + <string>Up</string> </property> </widget> </item> + <item row="0" column="0" colspan="5"> + <widget class="QListView" name="m_pCodecsLW"/> + </item> </layout> </widget> </item> @@ -669,16 +724,6 @@ <string>Credential</string> </attribute> <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0" rowspan="2" colspan="3"> - <widget class="QListWidget" name="list_credential"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> - </item> <item row="2" column="0"> <spacer name="horizontalSpacer_2"> <property name="orientation"> @@ -782,6 +827,16 @@ </property> </widget> </item> + <item row="0" column="0" rowspan="2" colspan="3"> + <widget class="QListView" name="list_credential"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> </layout> </widget> <widget class="QWidget" name="tab_2"> @@ -1214,11 +1269,6 @@ </layout> </widget> <customwidgets> - <customwidget> - <class>KEditListBox</class> - <extends>QGroupBox</extends> - <header>keditlistbox.h</header> - </customwidget> <customwidget> <class>KLineEdit</class> <extends>QLineEdit</extends> diff --git a/kde/src/conf/dlggeneral.cpp b/kde/src/conf/dlggeneral.cpp index bdad9077e70fec611d16b6eca7289e7fe7efa107..aa248000865902c4ff597837e5206101d4c50048 100755 --- a/kde/src/conf/dlggeneral.cpp +++ b/kde/src/conf/dlggeneral.cpp @@ -24,6 +24,7 @@ #include "klib/ConfigurationSkeleton.h" #include "conf/ConfigurationDialog.h" +#include "lib/configurationmanager_interface_singleton.h" DlgGeneral::DlgGeneral(KConfigDialog *parent) : QWidget(parent),m_HasChanged(false) @@ -34,6 +35,9 @@ DlgGeneral::DlgGeneral(KConfigDialog *parent) kcfg_historyMax->setValue(ConfigurationSkeleton::historyMax()); kcfg_minimumRowHeight->setEnabled(ConfigurationSkeleton::limitMinimumRowHeight()); + ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); + m_pAlwaysRecordCK->setChecked(configurationManager.getIsAlwaysRecording()); + //Need to be ordered m_lCallDetails[ "Display Icon" ] = "displayCallIcon" ; m_lCallDetails[ "Display Security" ] = "displayCallSecure" ; @@ -53,8 +57,9 @@ DlgGeneral::DlgGeneral(KConfigDialog *parent) i->setCheckState((checked)?Qt::Checked:Qt::Unchecked); m_pDetailsList->addItem(m_lItemList[iter.value()] = i); } - connect(m_pDetailsList, SIGNAL(itemChanged(QListWidgetItem*)) , this , SLOT( changed() )); - connect(this , SIGNAL(updateButtons()) , parent, SLOT( updateButtons())); + connect(m_pDetailsList , SIGNAL(itemChanged(QListWidgetItem*)) , this , SLOT( changed() )); + connect(m_pAlwaysRecordCK, SIGNAL(clicked(bool) ) , this , SLOT( changed() )); + connect(this , SIGNAL(updateButtons() ) , parent, SLOT( updateButtons())); } DlgGeneral::~DlgGeneral() @@ -84,5 +89,10 @@ void DlgGeneral::updateSettings() ConfigurationSkeleton::self()->findItem(iter.value())->setProperty(m_lItemList[iter.value()]->checkState() == Qt::Checked); } ConfigurationSkeleton::setHistoryMax(kcfg_historyMax->value()); + + ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); + configurationManager.setIsAlwaysRecording(m_pAlwaysRecordCK->isChecked()); + m_HasChanged = false; + } diff --git a/kde/src/conf/dlggeneralbase.ui b/kde/src/conf/dlggeneralbase.ui index 09bee1db3d281c6b9da5a7252a365650d2cf265c..6f41bb3bfeb8d7d007fef10d352bc86ab9732562 100755 --- a/kde/src/conf/dlggeneralbase.ui +++ b/kde/src/conf/dlggeneralbase.ui @@ -25,10 +25,13 @@ <property name="title"> <string>Call history</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_21"> - <item> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> <widget class="QWidget" name="widget_historyCapacity_2" native="true"> <layout class="QHBoxLayout" name="horizontalLayout_11"> + <property name="margin"> + <number>0</number> + </property> <item> <widget class="QCheckBox" name="kcfg_enableHistory"> <property name="text"> @@ -37,28 +40,28 @@ </widget> </item> <item> - <widget class="KIntSpinBox" name="kcfg_historyMax"> + <widget class="QLabel" name="label"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="minimum"> - <number>-8</number> + <property name="text"> + <string>days</string> </property> </widget> </item> <item> - <widget class="QLabel" name="label"> + <widget class="KIntSpinBox" name="kcfg_historyMax"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="text"> - <string>days</string> + <property name="minimum"> + <number>-8</number> </property> </widget> </item> @@ -78,7 +81,14 @@ </layout> </widget> </item> - <item> + <item row="1" column="0"> + <widget class="QCheckBox" name="kcfg_displayHistoryStatus"> + <property name="text"> + <string>Display incomming, outgoing and missed for history items</string> + </property> + </widget> + </item> + <item row="2" column="0"> <widget class="QToolButton" name="toolButton_historyClear"> <property name="text"> <string>Clear history</string> @@ -97,22 +107,12 @@ </sizepolicy> </property> <property name="title"> - <string>Connection</string> + <string>Recording</string> </property> <layout class="QFormLayout" name="formLayout_13"> <property name="fieldGrowthPolicy"> <enum>QFormLayout::ExpandingFieldsGrow</enum> </property> - <item row="0" column="0"> - <widget class="QLabel" name="label_SIPPort_2"> - <property name="text"> - <string>SIP Port</string> - </property> - <property name="buddy"> - <cstring>kcfg_SIPPort</cstring> - </property> - </widget> - </item> <item row="0" column="1"> <widget class="QWidget" name="widget_SIPPort_2" native="true"> <property name="minimumSize"> @@ -126,7 +126,11 @@ <number>0</number> </property> <item> - <widget class="KIntSpinBox" name="kcfg_SIPPort"/> + <widget class="QCheckBox" name="m_pAlwaysRecordCK"> + <property name="text"> + <string>Always record calls</string> + </property> + </widget> </item> <item> <widget class="QLabel" name="label_WarningSIPPort"> @@ -150,6 +154,9 @@ <string>Visible call details</string> </property> <layout class="QGridLayout" name="gridLayout"> + <item row="1" column="0" colspan="2"> + <widget class="QListWidget" name="m_pDetailsList"/> + </item> <item row="0" column="0" colspan="2"> <layout class="QHBoxLayout" name="horizontalLayout"> <item> @@ -181,9 +188,6 @@ </item> </layout> </item> - <item row="1" column="0" colspan="2"> - <widget class="QListWidget" name="m_pDetailsList"/> - </item> </layout> </widget> </item> diff --git a/kde/src/conf/dlgvideo.cpp b/kde/src/conf/dlgvideo.cpp index 089df5186068e5ad486b1b5a54d7267b2f3d265c..e6755558d7baf0617232f37264d06c6d3bc8fd10 100644 --- a/kde/src/conf/dlgvideo.cpp +++ b/kde/src/conf/dlgvideo.cpp @@ -19,7 +19,7 @@ #include "dlgvideo.h" #include "../lib/VideoDevice.h" -#include "../lib/VideoCodec.h" +#include "../lib/VideoCodecModel.h" #include "../lib/VideoModel.h" #include <KDebug> @@ -44,6 +44,10 @@ DlgVideo::DlgVideo(QWidget *parent) if (devices.size()) loadDevice(devices[0]->getDeviceId()); + + if (VideoModel::getInstance()->isPreviewing()) { + m_pPreviewPB->setText(i18n("Stop preview")); + } } diff --git a/kde/src/conf/dlgvideobase.ui b/kde/src/conf/dlgvideobase.ui index 53d61b5eef0466daa297016d3eb2c75fdaafa8bd..db2af5a1c0ae2abb9dbed086f1fe15eff61d7dc4 100644 --- a/kde/src/conf/dlgvideobase.ui +++ b/kde/src/conf/dlgvideobase.ui @@ -106,7 +106,14 @@ </layout> </item> <item row="0" column="1"> - <widget class="VideoWidget" name="m_pPreviewGV"/> + <widget class="VideoWidget" name="m_pPreviewGV" native="true"> + <property name="minimumSize"> + <size> + <width>40</width> + <height>40</height> + </size> + </property> + </widget> </item> <item row="1" column="1"> <widget class="QPushButton" name="m_pPreviewPB"> @@ -134,13 +141,13 @@ </layout> </widget> <customwidgets> - <customwidget> + <customwidget> <class>VideoWidget</class> <extends>QWidget</extends> - <header location="global">widgets/VideoWidget.h</header> + <header>widgets/VideoWidget.h</header> <container>1</container> </customwidget> - </customwidgets> + </customwidgets> <resources/> <connections/> </ui> diff --git a/kde/src/klib/AkonadiBackend.cpp b/kde/src/klib/AkonadiBackend.cpp index f0504f2b8ba73f6da13b8005fb4283423319b434..2e330882a68f14fd7f524411da6a2043aa856f96 100644 --- a/kde/src/klib/AkonadiBackend.cpp +++ b/kde/src/klib/AkonadiBackend.cpp @@ -65,6 +65,7 @@ AkonadiBackend::AkonadiBackend(QObject* parent) : ContactBackend(parent) AkonadiBackend::~AkonadiBackend() { CallModel<>::destroy(); + delete m_pSession; } @@ -105,6 +106,32 @@ Contact* AkonadiBackend::getContactByUid(const QString& uid) } +/***************************************************************************** + * * + * Helper * + * * + ****************************************************************************/ + +KABC::PhoneNumber::Type nameToType(QString name) +{ + if (name == "Home" ) return KABC::PhoneNumber::Home ; + else if (name == "Work" ) return KABC::PhoneNumber::Work ; + else if (name == "Msg" ) return KABC::PhoneNumber::Msg ; + else if (name == "Pref" ) return KABC::PhoneNumber::Pref ; + else if (name == "Voice" ) return KABC::PhoneNumber::Voice; + else if (name == "Fax" ) return KABC::PhoneNumber::Fax ; + else if (name == "Cell" ) return KABC::PhoneNumber::Cell ; + else if (name == "Video" ) return KABC::PhoneNumber::Video; + else if (name == "Bbs" ) return KABC::PhoneNumber::Bbs ; + else if (name == "Modem" ) return KABC::PhoneNumber::Modem; + else if (name == "Car" ) return KABC::PhoneNumber::Car ; + else if (name == "Isdn" ) return KABC::PhoneNumber::Isdn ; + else if (name == "Pcs" ) return KABC::PhoneNumber::Pcs ; + else if (name == "Pager" ) return KABC::PhoneNumber::Pager; + return KABC::PhoneNumber::Home; +} + + /***************************************************************************** * * * Mutator * @@ -159,6 +186,7 @@ ContactList AkonadiBackend::update(Akonadi::Collection collection) aContact->setPhoto(0); m_AddrHash[tmp.uid()] = tmp; + m_ItemHash[tmp.uid()] = item; } } m_pContacts = m_ContactByUid.values(); @@ -169,18 +197,24 @@ ContactList AkonadiBackend::update(Akonadi::Collection collection) ///Edit backend value using an updated frontend contact void AkonadiBackend::editContact(Contact* contact,QWidget* parent) { - KABC::Addressee ct = m_AddrHash[contact->getUid()]; - if (ct.uid() != contact->getUid()) { + Akonadi::Item item = m_ItemHash[contact->getUid()]; + if (!(item.hasPayload<KABC::Addressee>() && item.payload<KABC::Addressee>().uid() == contact->getUid())) { kDebug() << "Contact not found"; return; } - Akonadi::ContactEditorDialog *editor = new Akonadi::ContactEditorDialog( Akonadi::ContactEditorDialog::EditMode, parent ); - Akonadi::Item item(rand()); - item.setPayload<KABC::Addressee>(ct); if ( item.isValid() ) { - editor->setContact(item); - editor->exec(); + Akonadi::ContactEditor *editor = new Akonadi::ContactEditor( Akonadi::ContactEditor::EditMode, parent ); + editor->loadContact(item); + KDialog* dlg = new KDialog(parent); + dlg->setMainWidget(editor); + dlg->exec(); + if ( !editor->saveContact() ) { + kDebug() << "Unable to save new contact to storage"; + return; + } + delete editor; + delete dlg; } } //editContact @@ -198,20 +232,7 @@ void AkonadiBackend::addNewContact(Contact* contact,QWidget* parent) foreach (Contact::PhoneNumber* nb, contact->getPhoneNumbers()) { KABC::PhoneNumber pn; - if (nb->getType() == "Home" ) pn.setType(KABC::PhoneNumber::Home ); - else if (nb->getType() == "Work" ) pn.setType(KABC::PhoneNumber::Work ); - else if (nb->getType() == "Msg" ) pn.setType(KABC::PhoneNumber::Msg ); - else if (nb->getType() == "Pref" ) pn.setType(KABC::PhoneNumber::Pref ); - else if (nb->getType() == "Voice" ) pn.setType(KABC::PhoneNumber::Voice ); - else if (nb->getType() == "Fax" ) pn.setType(KABC::PhoneNumber::Fax ); - else if (nb->getType() == "Cell" ) pn.setType(KABC::PhoneNumber::Cell ); - else if (nb->getType() == "Video" ) pn.setType(KABC::PhoneNumber::Video ); - else if (nb->getType() == "Bbs" ) pn.setType(KABC::PhoneNumber::Bbs ); - else if (nb->getType() == "Modem" ) pn.setType(KABC::PhoneNumber::Modem ); - else if (nb->getType() == "Car" ) pn.setType(KABC::PhoneNumber::Car ); - else if (nb->getType() == "Isdn" ) pn.setType(KABC::PhoneNumber::Isdn ); - else if (nb->getType() == "Pcs" ) pn.setType(KABC::PhoneNumber::Pcs ); - else if (nb->getType() == "Pager" ) pn.setType(KABC::PhoneNumber::Pager ); + pn.setType(nameToType(nb->getType())); pn.setNumber(nb->getNumber()); newContact.insertPhoneNumber(pn); @@ -245,6 +266,35 @@ void AkonadiBackend::addNewContact(Contact* contact) addNewContact(contact,0); } +///Add a new phone number to an existing contact +void AkonadiBackend::addPhoneNumber(Contact* contact, QString number, QString type) +{ + Akonadi::Item item = m_ItemHash[contact->getUid()]; + if (!(item.hasPayload<KABC::Addressee>() && item.payload<KABC::Addressee>().uid() == contact->getUid())) { + kDebug() << "Contact not found"; + return; + } + if ( item.isValid() ) { + KABC::Addressee payload = item.payload<KABC::Addressee>(); + payload.insertPhoneNumber(KABC::PhoneNumber(number,nameToType(type))); + item.setPayload<KABC::Addressee>(payload); + Akonadi::ContactEditor *editor = new Akonadi::ContactEditor( Akonadi::ContactEditor::EditMode, (QWidget*)nullptr ); + editor->loadContact(item); + + KDialog* dlg = new KDialog(0); + dlg->setMainWidget(editor); + dlg->exec(); + if ( !editor->saveContact() ) { + kDebug() << "Unable to save new contact to storage"; + return; + } + delete editor; + } + else { + kDebug() << "Invalid item"; + } +} + /***************************************************************************** * * diff --git a/kde/src/klib/AkonadiBackend.h b/kde/src/klib/AkonadiBackend.h index 1643dfbffedc4cc8d11b7fda4b48071e17fd71f1..26f8be449ed6fba70403b53af3926fd5c7381aaa 100644 --- a/kde/src/klib/AkonadiBackend.h +++ b/kde/src/klib/AkonadiBackend.h @@ -39,6 +39,7 @@ namespace Akonadi { class Session; class CollectionModel; class Collection; + class Item; } //SFLPhone @@ -49,14 +50,15 @@ class LIB_EXPORT AkonadiBackend : public ContactBackend { Q_OBJECT public: static ContactBackend* getInstance(); - Contact* getContactByPhone ( const QString& phoneNumber ,bool resolveDNS = false ); - Contact* getContactByUid ( const QString& uid ); - void editContact ( Contact* contact , QWidget* parent = 0 ); - void addNewContact ( Contact* contact , QWidget* parent = 0 ); + Contact* getContactByPhone ( const QString& phoneNumber ,bool resolveDNS = false ); + Contact* getContactByUid ( const QString& uid ); + void editContact ( Contact* contact , QWidget* parent = 0 ); + void addNewContact ( Contact* contact , QWidget* parent = 0 ); + virtual void addPhoneNumber( Contact* contact , QString number, QString type ); - virtual void editContact ( Contact* contact ); - virtual void addNewContact ( Contact* contact ); - virtual ~AkonadiBackend ( ); + virtual void editContact ( Contact* contact ); + virtual void addNewContact ( Contact* contact ); + virtual ~AkonadiBackend ( ); private: AkonadiBackend(QObject* parent); @@ -66,6 +68,7 @@ private: Akonadi::Session* m_pSession ; Akonadi::Collection m_Collection ; QHash<QString,KABC::Addressee> m_AddrHash ; + QHash<QString,Akonadi::Item> m_ItemHash ; ContactList m_pContacts ; protected: diff --git a/kde/src/klib/HelperFunctions.cpp b/kde/src/klib/HelperFunctions.cpp index e242eb25d757f7d78765ce29418d7f7623ef2faf..61c21f6c9641c5e6e469c12dfc60371713915422 100644 --- a/kde/src/klib/HelperFunctions.cpp +++ b/kde/src/klib/HelperFunctions.cpp @@ -26,6 +26,7 @@ //KDE #include <KLocale> +#include <KMessageBox> //SFLPhone #include "../lib/Contact.h" @@ -73,4 +74,10 @@ QString HelperFunctions::escapeHtmlEntities(QString str) str = str.replace(">",">"); } return str; +} + +///Display a message box +void HelperFunctions::displayNoAccountMessageBox(QWidget* parent) +{ + KMessageBox::error(parent,i18n("No registered accounts")); } \ No newline at end of file diff --git a/kde/src/klib/HelperFunctions.h b/kde/src/klib/HelperFunctions.h index 8f6f99f081ab6bff4fca67fd4bfa9c93663e6308..326550b26827e73f53ad38d3194a68dc56cde5f4 100644 --- a/kde/src/klib/HelperFunctions.h +++ b/kde/src/klib/HelperFunctions.h @@ -40,5 +40,6 @@ public: static ContactHash toHash(QList<Contact*> contacts); static QString normStrippped(QString str); static QString escapeHtmlEntities(QString str); + static void displayNoAccountMessageBox(QWidget* parent = nullptr); }; #endif \ No newline at end of file diff --git a/kde/src/klib/SortableDockCommon.cpp b/kde/src/klib/SortableDockCommon.cpp index 9c7ec5e9ec0f251e3627e104e3fb384237f9c919..1295d0733188a9aef416669e4e2ce8557e9a8cb2 100644 --- a/kde/src/klib/SortableDockCommon.cpp +++ b/kde/src/klib/SortableDockCommon.cpp @@ -47,31 +47,30 @@ StaticEventHandler::StaticEventHandler(QObject* parent, QStringList* list) : QOb ///Update the days constant, necessary to cycle after midnight void StaticEventHandler::update() { - (*m_pList)= { - "Today" ,//0 - "Yesterday" ,//1 - QDate::currentDate().addDays(-2).toString("dddd").toAscii(),//2 - QDate::currentDate().addDays(-3).toString("dddd").toAscii(),//3 - QDate::currentDate().addDays(-4).toString("dddd").toAscii(),//4 - QDate::currentDate().addDays(-5).toString("dddd").toAscii(),//5 - QDate::currentDate().addDays(-6).toString("dddd").toAscii(),//6 - "Last week" ,//7 - "Two weeks ago" ,//8 - "Three weeks ago" ,//9 - "Last month" ,//10 - "Two months ago" ,//11 - "Three months ago" ,//12 - "Four months ago" ,//13 - "Five months ago" ,//14 - "Six months ago" ,//15 - "Seven months ago" ,//16 - "Eight months ago" ,//17 - "Nine months ago" ,//18 - "Ten months ago" ,//19 - "Eleven months ago" ,//20 - "Twelve months ago" ,//21 - "Last year" ,//22 - "Very long time ago" ,//23 - "Never" //24 - }; + (*m_pList) << + "Today" <<//0 + "Yesterday" <<//1 + QDate::currentDate().addDays(-2).toString("dddd").toAscii()<<//2 + QDate::currentDate().addDays(-3).toString("dddd").toAscii()<<//3 + QDate::currentDate().addDays(-4).toString("dddd").toAscii()<<//4 + QDate::currentDate().addDays(-5).toString("dddd").toAscii()<<//5 + QDate::currentDate().addDays(-6).toString("dddd").toAscii()<<//6 + "Last week" <<//7 + "Two weeks ago" <<//8 + "Three weeks ago" <<//9 + "Last month" <<//10 + "Two months ago" <<//11 + "Three months ago" <<//12 + "Four months ago" <<//13 + "Five months ago" <<//14 + "Six months ago" <<//15 + "Seven months ago" <<//16 + "Eight months ago" <<//17 + "Nine months ago" <<//18 + "Ten months ago" <<//19 + "Eleven months ago" <<//20 + "Twelve months ago" <<//21 + "Last year" <<//22 + "Very long time ago" <<//23 + "Never" ;//24 } diff --git a/kde/src/klib/dataengine/sflphonEngine.cpp b/kde/src/klib/dataengine/sflphonEngine.cpp index 90f22a4def13b0925e01e03b8b0b438a901c06ca..fe069e771be20299cafcee1a8eab396ef948f016 100644 --- a/kde/src/klib/dataengine/sflphonEngine.cpp +++ b/kde/src/klib/dataengine/sflphonEngine.cpp @@ -165,8 +165,9 @@ void SFLPhoneEngine::updateHistory() ///Load/Update calllist void SFLPhoneEngine::updateCallList() { - //As of KDE 4.8, an empty source is ignored, adding an invisible entry - QStringList keys = {"peerName","peerNumber","stateName","state","id"}; + //As of KDE 4.8, an empty source are ignored, adding an invisible entry + QStringList keys; + keys << "peerName" << "peerNumber" << "stateName" << "state" << "id"; QHash<QString,QVariant> fake; foreach (QString key, keys) { fake[key] = ""; @@ -268,8 +269,10 @@ void SFLPhoneEngine::updateCollection() void SFLPhoneEngine::updateContacts() { //As of KDE 4.8, an empty source is ignored, adding an invisible entry - QStringList keys = {"nickName","firstName" ,"secondName","formattedName","organization", - "Uid" ,"preferredEmail","type" ,"group" ,"department" }; + QStringList keys; + keys << "nickName" << "firstName" << "secondName" << "formattedName" << "organization" << + "Uid" << "preferredEmail" << "type" << "group" << + "department"; QHash<QString,QVariant> fake; foreach(QString key,keys) { diff --git a/kde/src/klib/dataengine/sflphoneService.cpp b/kde/src/klib/dataengine/sflphoneService.cpp index 0cd5d591b0b5bd3037e7e41bfbd2d547534a8d1c..ad508512b9fbd219852aa908e22b2b160bdb5409 100644 --- a/kde/src/klib/dataengine/sflphoneService.cpp +++ b/kde/src/klib/dataengine/sflphoneService.cpp @@ -42,8 +42,10 @@ CallJob::CallJob(QObject* parent, const QString& operation, const QVariantMap& p void CallJob::start() { Call* call = SFLPhoneEngine::getModel()->addDialingCall(m_Number,m_pAccount); - call->setCallNumber(m_Number); - call->actionPerformed(CALL_ACTION_ACCEPT); + if (call) { + call->setCallNumber(m_Number); + call->actionPerformed(CALL_ACTION_ACCEPT); + } } diff --git a/kde/src/klib/sflphone-client-kde.kcfg b/kde/src/klib/sflphone-client-kde.kcfg index 5ea2f50fa025442b7f7b413ded90f1572a358135..577042870ef48e826c228c3aebabf0bf32eb6cca 100755 --- a/kde/src/klib/sflphone-client-kde.kcfg +++ b/kde/src/klib/sflphone-client-kde.kcfg @@ -5,13 +5,6 @@ <group name="main"> <!-- General Settings --> - - <entry name="SIPPort" type="Int"> - <label>Defines the port that will be used for SIP communication.</label> - <min> 1025 </min> - <max> 65536 </max> - <default> 1025 </default> - </entry> <entry name="enableHistory" type="Bool"> <label>Defines whether sflphone should keep a history of calls.</label> </entry> @@ -61,6 +54,10 @@ <label>The minimum number of pixel for a call item</label> <default> 48 </default> </entry> + <entry name="displayHistoryStatus" type="Bool"> + <label>Display incomming, outgoing or missed</label> + <default> true </default> + </entry> <!-- Display Settings --> @@ -97,6 +94,7 @@ </entry> <entry name="displayDataRange" type="Bool"> <label>Defines whether call history is restricted to a specific date range</label> + <default>false</default> </entry> <entry name="displayPopularAsBookmark" type="Bool"> <label>Defines whether or not to display the 10 most popular phone number as bookmark automagically</label> diff --git a/kde/src/lib/Account.cpp b/kde/src/lib/Account.cpp index 187672ca5ef8376ac59d0faf58e3b4d662da841a..2351cb7c295e70932956c22f39831b3747187bc5 100644 --- a/kde/src/lib/Account.cpp +++ b/kde/src/lib/Account.cpp @@ -27,13 +27,26 @@ //SFLPhone #include "sflphone_const.h" -#include "VideoCodec.h" //SFLPhone lib #include "configurationmanager_interface_singleton.h" #include "callmanager_interface_singleton.h" #include "video_interface_singleton.h" #include "AccountList.h" +#include "CredentialModel.h" +#include "AudioCodecModel.h" +#include "VideoCodecModel.h" + +const account_function Account::stateMachineActionsOnState[6][7] = { +/* NOTHING EDIT RELOAD SAVE REMOVE MODIFY CANCEL */ +/*READY */{ &Account::nothing, &Account::edit , &Account::reload , &Account::nothing, &Account::remove , &Account::modify , &Account::nothing },/**/ +/*EDITING */{ &Account::nothing, &Account::nothing, &Account::outdate, &Account::nothing, &Account::remove , &Account::modify , &Account::cancel },/**/ +/*OUTDATED */{ &Account::nothing, &Account::nothing, &Account::nothing, &Account::nothing, &Account::remove , &Account::reloadMod, &Account::reload },/**/ +/*NEW */{ &Account::nothing, &Account::nothing, &Account::nothing, &Account::save , &Account::remove , &Account::nothing , &Account::nothing },/**/ +/*MODIFIED */{ &Account::nothing, &Account::nothing, &Account::nothing, &Account::save , &Account::remove , &Account::nothing , &Account::reload },/**/ +/*REMOVED */{ &Account::nothing, &Account::nothing, &Account::nothing, &Account::nothing, &Account::nothing, &Account::nothing , &Account::cancel } /**/ +/* */ +}; ///Match state name to user readable string const QString& account_state_name(const QString& s) @@ -71,7 +84,8 @@ const QString& account_state_name(const QString& s) } //account_state_name ///Constructors -Account::Account():m_pAccountId(NULL),m_pAccountDetails(NULL) +Account::Account():m_pAccountId(NULL),m_pAccountDetails(NULL),m_pCredentials(nullptr),m_pAudioCodecs(nullptr),m_CurrentState(READY), +m_pVideoCodecs(nullptr) { CallManagerInterface& callManager = CallManagerInterfaceSingleton::getInstance(); connect(&callManager,SIGNAL(registrationStateChanged(QString,QString,int)),this,SLOT(accountChanged(QString,QString,int))); @@ -81,21 +95,10 @@ Account::Account():m_pAccountId(NULL),m_pAccountDetails(NULL) Account* Account::buildExistingAccountFromId(const QString& _accountId) { qDebug() << "Building an account from id: " << _accountId; - ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); Account* a = new Account(); a->m_pAccountId = new QString(_accountId); - QMap<QString,QString> aDetails = configurationManager.getAccountDetails(_accountId); - - if (!aDetails.count()) { - qDebug() << "Account not found"; - return NULL; - } - a->m_pAccountDetails = new MapStringString(aDetails); - //Enable for debug - // foreach (QString str, *aDetails) { - // qDebug() << aDetails->key(str) << str; - // } + a->performAction(AccountEditAction::RELOAD); return a; } //buildExistingAccountFromId @@ -115,7 +118,8 @@ Account::~Account() { disconnect(); delete m_pAccountId; - delete m_pAccountDetails; + if (m_pCredentials) delete m_pCredentials; + if (m_pAccountDetails) delete m_pAccountDetails; } @@ -129,8 +133,8 @@ Account::~Account() void Account::accountChanged(QString accountId,QString state,int) { if (m_pAccountId && accountId == *m_pAccountId) { - Account::updateState(); - stateChanged(getStateName(state)); + if (Account::updateState()) + emit stateChanged(getStateName(state)); } } @@ -157,7 +161,7 @@ const QString& Account::getAccountId() const return EMPTY_STRING; //WARNING May explode } - return *m_pAccountId; + return *m_pAccountId; } ///Get this account details @@ -210,6 +214,65 @@ bool Account::isRegistered() const return (getAccountDetail(ACCOUNT_REGISTRATION_STATUS) == ACCOUNT_STATE_REGISTERED); } +///Return the model index of this item +QModelIndex Account::getIndex() +{ + for (int i=0;i < AccountList::getInstance()->m_pAccounts->size();i++) { + if (this == (*AccountList::getInstance()->m_pAccounts)[i]) { + return AccountList::getInstance()->index(i,0); + } + } + return QModelIndex(); +} + +///Return status color name +QString Account::getStateColorName() const +{ + if(getAccountRegistrationStatus() == ACCOUNT_STATE_UNREGISTERED) + return "black"; + if(getAccountRegistrationStatus() == ACCOUNT_STATE_REGISTERED || getAccountRegistrationStatus() == ACCOUNT_STATE_READY) + return "darkGreen"; + return "red"; +} + +///Return status Qt color, QColor is not part of QtCore, use using the global variant +Qt::GlobalColor Account::getStateColor() const +{ + if(getAccountRegistrationStatus() == ACCOUNT_STATE_UNREGISTERED) + return Qt::darkGray; + if(getAccountRegistrationStatus() == ACCOUNT_STATE_REGISTERED || getAccountRegistrationStatus() == ACCOUNT_STATE_READY) + return Qt::darkGreen; + if(getAccountRegistrationStatus() == ACCOUNT_STATE_TRYING) + return Qt::darkYellow; + return Qt::darkRed; +} + +///Create and return the credential model +CredentialModel* Account::getCredentialsModel() +{ + if (!m_pCredentials) { + reloadCredentials(); + } + return m_pCredentials; +} + +///Create and return the audio codec model +AudioCodecModel* Account::getAudioCodecModel() +{ + if (!m_pAudioCodecs) { + reloadAudioCodecs(); + } + return m_pAudioCodecs; +} + +///Create and return the video codec model +VideoCodecModel* Account::getVideoCodecModel() +{ + if (!m_pVideoCodecs) { + m_pVideoCodecs = new VideoCodecModel(this); + } + return m_pVideoCodecs; +} /***************************************************************************** * * @@ -220,13 +283,27 @@ bool Account::isRegistered() const ///Set account details void Account::setAccountDetails(const MapStringString& m) { + if (m_pAccountDetails) + delete m_pAccountDetails; *m_pAccountDetails = m; } ///Set a specific detail -void Account::setAccountDetail(const QString& param, const QString& val) +bool Account::setAccountDetail(const QString& param, const QString& val) { - (*m_pAccountDetails)[param] = val; + QString buf = (*m_pAccountDetails)[param]; + if (param == ACCOUNT_REGISTRATION_STATUS) { + (*m_pAccountDetails)[param] = val; + emit detailChanged(this,param,val,buf); + } + else { + performAction(AccountEditAction::MODIFY); + if (m_CurrentState == MODIFIED || m_CurrentState == NEW) { + (*m_pAccountDetails)[param] = val; + emit detailChanged(this,param,val,buf); + } + } + return m_CurrentState == MODIFIED || m_CurrentState == NEW; } ///Set the account id @@ -241,7 +318,7 @@ void Account::setAccountId(const QString& id) ///Set account enabled void Account::setEnabled(bool checked) { - setAccountDetail(ACCOUNT_ENABLED, checked ? REGISTRATION_ENABLED_TRUE : REGISTRATION_ENABLED_FALSE); + setAccountEnabled(checked); } /***************************************************************************** @@ -250,15 +327,20 @@ void Account::setEnabled(bool checked) * * ****************************************************************************/ -///Update the account -void Account::updateState() +/**Update the account + * @return if the state changed + */ +bool Account::updateState() { if(! isNew()) { ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); MapStringString details = configurationManager.getAccountDetails(getAccountId()).value(); QString status = details[ACCOUNT_REGISTRATION_STATUS]; + QString currentStatus = getAccountRegistrationStatus(); setAccountDetail(ACCOUNT_REGISTRATION_STATUS, status); //Update -internal- object state + return status == currentStatus; } + return true; } ///Save the current account to the daemon @@ -283,8 +365,115 @@ void Account::save() (*AccountList::getInstance()->m_pAccounts) << this; } + performAction(AccountEditAction::RELOAD); updateState(); + m_CurrentState = READY; + } + m_pVideoCodecs->save(); + saveAudioCodecs(); +} + +///Synchronise with the daemon, this need to be done manually to prevent reloading the account while it is being edited +void Account::reload() +{ + qDebug() << "Reloading" << getAccountId(); + ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); + QMap<QString,QString> aDetails = configurationManager.getAccountDetails(getAccountId()); + + if (!aDetails.count()) { + qDebug() << "Account not found"; + } + else { + if (m_pAccountDetails) { + delete m_pAccountDetails; + m_pAccountDetails = nullptr; + } + m_pAccountDetails = new MapStringString(aDetails); } + m_CurrentState = READY; +} + +void Account::reloadCredentials() +{ + if (!m_pCredentials) { + m_pCredentials = new CredentialModel(this); + } + m_pCredentials->clear(); + ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); + VectorMapStringString credentials = configurationManager.getCredentials(getAccountId()); + for (int i=0; i < credentials.size(); i++) { + QModelIndex idx = m_pCredentials->addCredentials(); + m_pCredentials->setData(idx,credentials[i][ CONFIG_ACCOUNT_USERNAME ],CredentialModel::NAME_ROLE ); + m_pCredentials->setData(idx,credentials[i][ CONFIG_ACCOUNT_PASSWORD ],CredentialModel::PASSWORD_ROLE); + m_pCredentials->setData(idx,credentials[i][ CONFIG_ACCOUNT_REALM ],CredentialModel::REALM_ROLE ); + } +} + +void Account::saveCredentials() { + if (m_pCredentials) { + ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); + VectorMapStringString toReturn; + for (int i=0; i < m_pCredentials->rowCount();i++) { + QModelIndex idx = m_pCredentials->index(i,0); + MapStringString credentialData; + QString username = m_pCredentials->data(idx,CredentialModel::NAME_ROLE ).toString(); + username = (username.isEmpty())?getAccountUsername():username; + credentialData[ CONFIG_ACCOUNT_USERNAME] = m_pCredentials->data(idx,CredentialModel::NAME_ROLE ).toString(); + credentialData[ CONFIG_ACCOUNT_PASSWORD] = m_pCredentials->data(idx,CredentialModel::PASSWORD_ROLE ).toString(); + credentialData[ CONFIG_ACCOUNT_REALM ] = m_pCredentials->data(idx,CredentialModel::REALM_ROLE ).toString(); + toReturn << credentialData; + } + configurationManager.setCredentials(getAccountId(),toReturn); + } +} + +void Account::reloadAudioCodecs() +{ + if (!m_pAudioCodecs) { + m_pAudioCodecs = new AudioCodecModel(this); + } + m_pAudioCodecs->clear(); + ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); + QVector<int> codecIdList = configurationManager.getAudioCodecList(); + QVector<int> activeCodecList = configurationManager.getActiveAudioCodecList(getAccountId()); + QStringList tmpNameList; + + foreach (int aCodec, activeCodecList) { + QStringList codec = configurationManager.getAudioCodecDetails(aCodec); + QModelIndex idx = m_pAudioCodecs->addAudioCodec(); + m_pAudioCodecs->setData(idx,codec[0] ,AudioCodecModel::NAME_ROLE ); + m_pAudioCodecs->setData(idx,codec[1] ,AudioCodecModel::SAMPLERATE_ROLE ); + m_pAudioCodecs->setData(idx,codec[2] ,AudioCodecModel::BITRATE_ROLE ); + m_pAudioCodecs->setData(idx,aCodec ,AudioCodecModel::ID_ROLE ); + m_pAudioCodecs->setData(idx, Qt::Checked ,Qt::CheckStateRole ); + if (codecIdList.indexOf(aCodec)!=-1) + codecIdList.remove(codecIdList.indexOf(aCodec)); + } + + foreach (int aCodec, codecIdList) { + QStringList codec = configurationManager.getAudioCodecDetails(aCodec); + QModelIndex idx = m_pAudioCodecs->addAudioCodec(); + m_pAudioCodecs->setData(idx,codec[0],AudioCodecModel::NAME_ROLE ); + m_pAudioCodecs->setData(idx,codec[1],AudioCodecModel::SAMPLERATE_ROLE ); + m_pAudioCodecs->setData(idx,codec[2],AudioCodecModel::BITRATE_ROLE ); + m_pAudioCodecs->setData(idx,aCodec ,AudioCodecModel::ID_ROLE ); + + m_pAudioCodecs->setData(idx, Qt::Unchecked ,Qt::CheckStateRole); + } +} + +void Account::saveAudioCodecs() { + QStringList _codecList; + for (int i=0; i < m_pAudioCodecs->rowCount();i++) { + QModelIndex idx = m_pAudioCodecs->index(i,0); + if (m_pAudioCodecs->data(idx,Qt::CheckStateRole) == Qt::Checked) { + _codecList << m_pAudioCodecs->data(idx,AudioCodecModel::ID_ROLE).toString(); + } + } + + ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); + configurationManager.setActiveAudioCodecList(_codecList, getAccountId()); + qDebug() << "Account codec have been saved" << _codecList << getAccountId(); } /***************************************************************************** diff --git a/kde/src/lib/Account.h b/kde/src/lib/Account.h index 63361d57338e68d37cbcbb2d1abb895bcdfb3949..07d672395ea36cf31952b12508eb6261837dac97 100644 --- a/kde/src/lib/Account.h +++ b/kde/src/lib/Account.h @@ -27,13 +27,39 @@ class QString; //SFLPhone -#include "VideoCodec.h" +#include "VideoCodecModel.h" #include "sflphone_const.h" #include "typedefs.h" #include "dbus/metatypes.h" +class CredentialModel; +class AudioCodecModel; +class VideoCodecModel; const QString& account_state_name(const QString& s); +typedef void (Account::*account_function)(); + +///@enum AccountEditState: Manage how and when an account can be reloaded or change state +enum AccountEditState { + READY =0, + EDITING =1, + OUTDATED =2, + NEW =3, + MODIFIED =4, + REMOVED =5 +}; + +///@enum AccountEditAction +enum AccountEditAction { + NOTHING =0, + EDIT =1, + RELOAD =2, + SAVE =3, + REMOVE =4, + MODIFY =5, + CANCEL =6 +}; + ///Account: a daemon account (SIP or AIX) class LIB_EXPORT Account : public QObject { Q_OBJECT @@ -43,7 +69,18 @@ class LIB_EXPORT Account : public QObject { //Constructors static Account* buildExistingAccountFromId(const QString& _accountId); static Account* buildNewAccountFromAlias(const QString& alias); - + + /** + *Perform an action + * @return If the state changed + */ + bool performAction(AccountEditAction action) { + AccountEditState curState = m_CurrentState; + (this->*(stateMachineActionsOnState[m_CurrentState][action]))(); + return curState != m_CurrentState; + } + AccountEditState currentState() const {return m_CurrentState;}; + //Getters bool isNew() const; const QString& getAccountId() const; @@ -53,84 +90,91 @@ class LIB_EXPORT Account : public QObject { const QString& getAlias() const; bool isEnabled() const; bool isRegistered() const; + QModelIndex getIndex() ; + QString getStateColorName() const; + Qt::GlobalColor getStateColor() const; + + CredentialModel* getCredentialsModel(); + AudioCodecModel* getAudioCodecModel(); + VideoCodecModel* getVideoCodecModel(); ///Return the account hostname - QString getAccountHostname () { return getAccountDetail(ACCOUNT_HOSTNAME ) ;} + QString getAccountHostname () const { return getAccountDetail(ACCOUNT_HOSTNAME ) ;} ///Return if the account is enabled - bool isAccountEnabled () { return (getAccountDetail(ACCOUNT_ENABLED ) == "true")?1:0 ;} + bool isAccountEnabled () const { return (getAccountDetail(ACCOUNT_ENABLED ) == "true")?1:0 ;} ///Return the account user name - QString getAccountUsername () { return getAccountDetail(ACCOUNT_USERNAME ) ;} + QString getAccountUsername () const { return getAccountDetail(ACCOUNT_USERNAME ) ;} ///Return the account mailbox address - QString getAccountMailbox () { return getAccountDetail(ACCOUNT_MAILBOX ) ;} + QString getAccountMailbox () const { return getAccountDetail(ACCOUNT_MAILBOX ) ;} /// - bool isAccountDisplaySasOnce () { return (getAccountDetail(ACCOUNT_DISPLAY_SAS_ONCE ) == "true")?1:0 ;} + bool isAccountDisplaySasOnce () const { return (getAccountDetail(ACCOUNT_DISPLAY_SAS_ONCE ) == "true")?1:0 ;} ///Return the account security fallback - bool isAccountSrtpRtpFallback () { return (getAccountDetail(ACCOUNT_SRTP_RTP_FALLBACK ) == "true")?1:0 ;} + bool isAccountSrtpRtpFallback () const { return (getAccountDetail(ACCOUNT_SRTP_RTP_FALLBACK ) == "true")?1:0 ;} /// - bool isAccountZrtpDisplaySas () { return (getAccountDetail(ACCOUNT_ZRTP_DISPLAY_SAS ) == "true")?1:0 ;} + bool isAccountZrtpDisplaySas () const { return (getAccountDetail(ACCOUNT_ZRTP_DISPLAY_SAS ) == "true")?1:0 ;} ///Return if the other side support warning - bool isAccountZrtpNotSuppWarning () { return (getAccountDetail(ACCOUNT_ZRTP_NOT_SUPP_WARNING ) == "true")?1:0 ;} + bool isAccountZrtpNotSuppWarning () const { return (getAccountDetail(ACCOUNT_ZRTP_NOT_SUPP_WARNING ) == "true")?1:0 ;} /// - bool isAccountZrtpHelloHash () { return (getAccountDetail(ACCOUNT_ZRTP_HELLO_HASH ) == "true")?1:0 ;} + bool isAccountZrtpHelloHash () const { return (getAccountDetail(ACCOUNT_ZRTP_HELLO_HASH ) == "true")?1:0 ;} ///Return if the account is using a STUN server - bool isAccountSipStunEnabled () { return (getAccountDetail(ACCOUNT_SIP_STUN_ENABLED ) == "true")?1:0 ;} + bool isAccountSipStunEnabled () const { return (getAccountDetail(ACCOUNT_SIP_STUN_ENABLED ) == "true")?1:0 ;} ///Return the account STUN server - QString getAccountSipStunServer () { return getAccountDetail(ACCOUNT_SIP_STUN_SERVER ) ;} + QString getAccountSipStunServer () const { return getAccountDetail(ACCOUNT_SIP_STUN_SERVER ) ;} ///Return when the account expire (require renewal) - int getAccountRegistrationExpire () { return getAccountDetail(ACCOUNT_REGISTRATION_EXPIRE ).toInt() ;} + int getAccountRegistrationExpire () const { return getAccountDetail(ACCOUNT_REGISTRATION_EXPIRE ).toInt() ;} ///Return if the published address is the same as the local one - bool isPublishedSameAsLocal () { return (getAccountDetail(PUBLISHED_SAMEAS_LOCAL ) == "true")?1:0 ;} + bool isPublishedSameAsLocal () const { return (getAccountDetail(PUBLISHED_SAMEAS_LOCAL ) == "true")?1:0 ;} ///Return the account published address - QString getPublishedAddress () { return getAccountDetail(PUBLISHED_ADDRESS ) ;} + QString getPublishedAddress () const { return getAccountDetail(PUBLISHED_ADDRESS ) ;} ///Return the account published port - int getPublishedPort () { return getAccountDetail(PUBLISHED_PORT ).toUInt() ;} + int getPublishedPort () const { return getAccountDetail(PUBLISHED_PORT ).toUInt() ;} ///Return the account tls password - QString getTlsPassword () { return getAccountDetail(TLS_PASSWORD ) ;} + QString getTlsPassword () const { return getAccountDetail(TLS_PASSWORD ) ;} ///Return the account TLS port - int getTlsListenerPort () { return getAccountDetail(TLS_LISTENER_PORT ).toInt() ;} + int getTlsListenerPort () const { return getAccountDetail(TLS_LISTENER_PORT ).toInt() ;} ///Return the account TLS certificate authority list file - QString getTlsCaListFile () { return getAccountDetail(TLS_CA_LIST_FILE ) ;} + QString getTlsCaListFile () const { return getAccountDetail(TLS_CA_LIST_FILE ) ;} ///Return the account TLS certificate - QString getTlsCertificateFile () { return getAccountDetail(TLS_CERTIFICATE_FILE ) ;} + QString getTlsCertificateFile () const { return getAccountDetail(TLS_CERTIFICATE_FILE ) ;} ///Return the account private key - QString getTlsPrivateKeyFile () { return getAccountDetail(TLS_PRIVATE_KEY_FILE ) ;} + QString getTlsPrivateKeyFile () const { return getAccountDetail(TLS_PRIVATE_KEY_FILE ) ;} ///Return the account cipher - QString getTlsCiphers () { return getAccountDetail(TLS_CIPHERS ) ;} + QString getTlsCiphers () const { return getAccountDetail(TLS_CIPHERS ) ;} ///Return the account TLS server name - QString getTlsServerName () { return getAccountDetail(TLS_SERVER_NAME ) ;} + QString getTlsServerName () const { return getAccountDetail(TLS_SERVER_NAME ) ;} ///Return the account negotiation timeout in seconds - int getTlsNegotiationTimeoutSec () { return getAccountDetail(TLS_NEGOTIATION_TIMEOUT_SEC ).toInt() ;} + int getTlsNegotiationTimeoutSec () const { return getAccountDetail(TLS_NEGOTIATION_TIMEOUT_SEC ).toInt() ;} ///Return the account negotiation timeout in milliseconds - int getTlsNegotiationTimeoutMsec () { return getAccountDetail(TLS_NEGOTIATION_TIMEOUT_MSEC ).toInt() ;} + int getTlsNegotiationTimeoutMsec () const { return getAccountDetail(TLS_NEGOTIATION_TIMEOUT_MSEC ).toInt() ;} ///Return the account TLS verify server - bool isTlsVerifyServer () { return (getAccountDetail(TLS_VERIFY_SERVER ) == "true")?1:0 ;} + bool isTlsVerifyServer () const { return (getAccountDetail(TLS_VERIFY_SERVER ) == "true")?1:0 ;} ///Return the account TLS verify client - bool isTlsVerifyClient () { return (getAccountDetail(TLS_VERIFY_CLIENT ) == "true")?1:0 ;} + bool isTlsVerifyClient () const { return (getAccountDetail(TLS_VERIFY_CLIENT ) == "true")?1:0 ;} ///Return if it is required for the peer to have a certificate - bool isTlsRequireClientCertificate () { return (getAccountDetail(TLS_REQUIRE_CLIENT_CERTIFICATE) == "true")?1:0 ;} + bool isTlsRequireClientCertificate () const { return (getAccountDetail(TLS_REQUIRE_CLIENT_CERTIFICATE) == "true")?1:0 ;} ///Return the account TLS security is enabled - bool isTlsEnable () { return (getAccountDetail(TLS_ENABLE ) == "true")?1:0 ;} + bool isTlsEnable () const { return (getAccountDetail(TLS_ENABLE ) == "true")?1:0 ;} ///Return the account the TLS encryption method - int getTlsMethod () { return getAccountDetail(TLS_METHOD ).toInt() ;} + int getTlsMethod () const { return getAccountDetail(TLS_METHOD ).toInt() ;} ///Return the account alias - QString getAccountAlias () { return getAccountDetail(ACCOUNT_ALIAS ) ;} + QString getAccountAlias () const { return getAccountDetail(ACCOUNT_ALIAS ) ;} ///Return if the ringtone are enabled - bool isRingtoneEnabled () { return (getAccountDetail(CONFIG_RINGTONE_ENABLED ) == "true")?1:0 ;} + bool isRingtoneEnabled () const { return (getAccountDetail(CONFIG_RINGTONE_ENABLED ) == "true")?1:0 ;} ///Return the account ringtone path - QString getRingtonePath () { return getAccountDetail(CONFIG_RINGTONE_PATH ) ;} + QString getRingtonePath () const { return getAccountDetail(CONFIG_RINGTONE_PATH ) ;} ///Return the account local port - int getLocalPort () { return getAccountDetail(LOCAL_PORT).toInt() ;} + int getLocalPort () const { return getAccountDetail(LOCAL_PORT).toInt() ;} ///Return the account local interface - QString getLocalInterface () { return getAccountDetail(LOCAL_INTERFACE) ;} + QString getLocalInterface () const { return getAccountDetail(LOCAL_INTERFACE) ;} ///Return the account registration status - QString getAccountRegistrationStatus () { return getAccountDetail(ACCOUNT_REGISTRATION_STATUS) ;} + QString getAccountRegistrationStatus () const { return getAccountDetail(ACCOUNT_REGISTRATION_STATUS) ;} ///Return the account type - QString getAccountType () { return getAccountDetail(ACCOUNT_TYPE) ;} + QString getAccountType () const { return getAccountDetail(ACCOUNT_TYPE) ;} //Setters void setAccountId (const QString& id ); void setAccountDetails (const MapStringString& m ); - void setAccountDetail (const QString& param, const QString& val ); + bool setAccountDetail (const QString& param, const QString& val ); #ifdef ENABLE_VIDEO void setActiveVideoCodecList(QList<VideoCodec*> codecs); QList<VideoCodec*> getActiveVideoCodecList(); @@ -202,14 +246,17 @@ class LIB_EXPORT Account : public QObject { void setRingtoneEnabled (bool detail){setAccountDetail(CONFIG_RINGTONE_ENABLED ,detail?"true":"false");} //Updates - virtual void updateState(); + virtual bool updateState(); //Operators bool operator==(const Account&)const; //Mutator - void save(); - + void saveCredentials(); + void saveAudioCodecs(); + void reloadCredentials(); + void reloadAudioCodecs(); + protected: //Constructors Account(); @@ -224,8 +271,29 @@ class LIB_EXPORT Account : public QObject { private slots: void accountChanged(QString accountId,QString stateName, int state); + private: + //State actions + void nothing() {}; + void edit() {m_CurrentState = EDITING ;emit changed(this);}; + void modify() {m_CurrentState = MODIFIED;emit changed(this);}; + void remove() {m_CurrentState = REMOVED ;emit changed(this);}; + void cancel() {m_CurrentState = READY ;emit changed(this);}; + void outdate(){m_CurrentState = OUTDATED;emit changed(this);}; + void reload(); + void save(); + void reloadMod() {reload();modify();}; + + CredentialModel* m_pCredentials; + AudioCodecModel* m_pAudioCodecs; + VideoCodecModel* m_pVideoCodecs; + AccountEditState m_CurrentState; + static const account_function stateMachineActionsOnState[6][7]; + + signals: ///The account state (Invalif,Trying,Registered) changed void stateChanged(QString state); + void detailChanged(Account* a,QString name,QString newVal, QString oldVal); + void changed(Account* a); }; #endif diff --git a/kde/src/lib/AccountList.cpp b/kde/src/lib/AccountList.cpp index e3ec241aee38e1b321c3a14b27a9d4503c510ca0..53fe91823ad4d1f9b80c3d42427ff54db3446db6 100644 --- a/kde/src/lib/AccountList.cpp +++ b/kde/src/lib/AccountList.cpp @@ -26,26 +26,40 @@ //SFLPhone library #include "configurationmanager_interface_singleton.h" +#include "callmanager_interface_singleton.h" AccountList* AccountList::m_spAccountList = nullptr; QString AccountList::m_sPriorAccountId = "" ; ///Constructors -AccountList::AccountList(QStringList & _accountIds) +AccountList::AccountList(QStringList & _accountIds) : m_pColorVisitor(nullptr) { m_pAccounts = new QVector<Account*>(); for (int i = 0; i < _accountIds.size(); ++i) { - (*m_pAccounts) += Account::buildExistingAccountFromId(_accountIds[i]); + Account* a = Account::buildExistingAccountFromId(_accountIds[i]); + (*m_pAccounts) += a; + emit dataChanged(index(size()-1,0),index(size()-1,0)); + connect(a,SIGNAL(changed(Account*)),this,SLOT(accountChanged(Account*))); } + CallManagerInterface& callManager = CallManagerInterfaceSingleton::getInstance(); + connect(&callManager, SIGNAL(registrationStateChanged(QString,QString,int)),this,SLOT(accountChanged(QString,QString,int))); + + ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); + connect(&configurationManager,SIGNAL(accountsChanged()),this,SLOT(updateAccounts())); } ///Constructors ///@param fill Whether to fill the list with accounts from configurationManager or not. -AccountList::AccountList(bool fill) +AccountList::AccountList(bool fill) : m_pColorVisitor(nullptr) { m_pAccounts = new QVector<Account *>(); if(fill) updateAccounts(); + CallManagerInterface& callManager = CallManagerInterfaceSingleton::getInstance(); + connect(&callManager, SIGNAL(registrationStateChanged(QString,QString,int)),this,SLOT(accountChanged(QString,QString,int))); + + ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); + connect(&configurationManager,SIGNAL(accountsChanged()),this,SLOT(updateAccounts())); } ///Destructor @@ -74,6 +88,46 @@ void AccountList::destroy() m_spAccountList = nullptr; } +///Account status changed +void AccountList::accountChanged(const QString& account,const QString& state, int code) +{ + Q_UNUSED(code) + Account* a = getAccountById(account); + if (!a) { + ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); + QStringList accountIds = configurationManager.getAccountList().value(); + for (int i = 0; i < accountIds.size(); ++i) { + if (!getAccountById(accountIds[i])) { + Account* a = Account::buildExistingAccountFromId(accountIds[i]); + m_pAccounts->insert(i, a); + connect(a,SIGNAL(changed(Account*)),this,SLOT(accountChanged(Account*))); + emit dataChanged(index(i,0),index(size()-1)); + } + } + foreach (Account* a, *m_pAccounts) { + int idx =accountIds.indexOf(a->getAccountId()); + if (idx == -1 && (a->currentState() == READY || a->currentState() == REMOVED)) { + m_pAccounts->remove(idx); + emit dataChanged(index(idx - 1, 0), index(m_pAccounts->size()-1, 0)); + } + } + a = getAccountById(account); + } + if (a) + emit accountStateChanged(a,a->getStateName(state)); + else + qDebug() << "Account not found"; +} + +///Tell the model something changed +void AccountList::accountChanged(Account* a) +{ + int idx = (*m_pAccounts).indexOf(a); + if (idx != -1) { + emit dataChanged(index(idx, 0), index(idx, 0)); + } +} + /***************************************************************************** * * @@ -85,16 +139,19 @@ void AccountList::destroy() void AccountList::update() { ConfigurationManagerInterface & configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); - Account * current; + Account* current; for (int i = 0; i < m_pAccounts->size(); i++) { current = (*m_pAccounts)[i]; - if (!(*m_pAccounts)[i]->isNew()) + if (!(*m_pAccounts)[i]->isNew() && (current->currentState() != NEW || current->currentState() != MODIFIED || current->currentState() != OUTDATED)) removeAccount(current); } //ask for the list of accounts ids to the configurationManager QStringList accountIds = configurationManager.getAccountList().value(); for (int i = 0; i < accountIds.size(); ++i) { - m_pAccounts->insert(i, Account::buildExistingAccountFromId(accountIds[i])); + Account* a = Account::buildExistingAccountFromId(accountIds[i]); + m_pAccounts->insert(i, a); + emit dataChanged(index(i,0),index(size()-1,0)); + connect(a,SIGNAL(changed(Account*)),this,SLOT(accountChanged(Account*))); } } //update @@ -104,14 +161,74 @@ void AccountList::updateAccounts() qDebug() << "updateAccounts"; ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); QStringList accountIds = configurationManager.getAccountList().value(); - m_pAccounts->clear(); + //m_pAccounts->clear(); for (int i = 0; i < accountIds.size(); ++i) { - qDebug() << "updateAccounts " << accountIds[i]; - (*m_pAccounts) += Account::buildExistingAccountFromId(accountIds[i]); + Account* acc = getAccountById(accountIds[i]); + if (!acc) { + qDebug() << "updateAccounts " << accountIds[i]; + Account* a = Account::buildExistingAccountFromId(accountIds[i]); + (*m_pAccounts) += a; + connect(a,SIGNAL(changed(Account*)),this,SLOT(accountChanged(Account*))); + emit dataChanged(index(size()-1,0),index(size()-1,0)); + } + else { + acc->performAction(RELOAD); + } } emit accountListUpdated(); } //updateAccounts +///Save accounts details and reload it +void AccountList::save() +{ + ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); + QStringList accountIds= QStringList(configurationManager.getAccountList().value()); + + //create or update each account from accountList + for (int i = 0; i < size(); i++) { + Account* current = (*this)[i]; + QString currentId; + //current->save(); + current->performAction(AccountEditAction::SAVE); + currentId = QString(current->getAccountId()); + } + + //remove accounts that are in the configurationManager but not in the client + for (int i = 0; i < accountIds.size(); i++) { + if(!getAccountById(accountIds[i])) { + configurationManager.removeAccount(accountIds[i]); + } + } + + configurationManager.setAccountsOrder(getOrderedList()); +} + +///Move account up +bool AccountList::accountUp( int index ) +{ + if(index > 0 && index <= rowCount()) { + Account* account = getAccountAt(index); + m_pAccounts->remove(index); + m_pAccounts->insert(index - 1, account); + emit dataChanged(this->index(index - 1, 0, QModelIndex()), this->index(index, 0, QModelIndex())); + return true; + } + return false; +} + +///Move account down +bool AccountList::accountDown( int index ) +{ + if(index >= 0 && index < rowCount()) { + Account* account = getAccountAt(index); + m_pAccounts->remove(index); + m_pAccounts->insert(index + 1, account); + emit dataChanged(this->index(index, 0, QModelIndex()), this->index(index + 1, 0, QModelIndex())); + return true; + } + return false; +} + /***************************************************************************** * * @@ -192,7 +309,7 @@ Account* AccountList::firstRegisteredAccount() const Account* current; for (int i = 0; i < m_pAccounts->count(); ++i) { current = (*m_pAccounts)[i]; - if(current && current->getAccountRegistrationStatus() == ACCOUNT_STATE_REGISTERED) + if(current && current->getAccountRegistrationStatus() == ACCOUNT_STATE_REGISTERED && current->isAccountEnabled()) return current; else if (current && (current->getAccountRegistrationStatus() == ACCOUNT_STATE_READY) && m_pAccounts->count() == 1) return current; @@ -214,12 +331,16 @@ int AccountList::size() const ///Return the current account Account* AccountList::getCurrentAccount() { - Account* priorAccount = AccountList::getInstance()->getAccountById(m_sPriorAccountId); - if(priorAccount && priorAccount->getAccountDetail(ACCOUNT_REGISTRATION_STATUS) == ACCOUNT_STATE_REGISTERED ) { + Account* priorAccount = getInstance()->getAccountById(m_sPriorAccountId); + if(priorAccount && priorAccount->getAccountDetail(ACCOUNT_REGISTRATION_STATUS) == ACCOUNT_STATE_REGISTERED && priorAccount->isAccountEnabled() ) { return priorAccount; } else { - return AccountList::getInstance()->firstRegisteredAccount(); + Account* a = getInstance()->firstRegisteredAccount(); + if (a) + return getInstance()->firstRegisteredAccount(); + else + return getInstance()->getAccountById("IP2IP"); } } //getCurrentAccount @@ -229,6 +350,47 @@ QString AccountList::getPriorAccoundId() return m_sPriorAccountId; } +///Get data from the model +QVariant AccountList::data ( const QModelIndex& index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) + return QVariant(); + + const Account * account = (*m_pAccounts)[index.row()]; + if(index.column() == 0 && role == Qt::DisplayRole) + return QVariant(account->getAlias()); + else if(index.column() == 0 && role == Qt::CheckStateRole) + return QVariant(account->isEnabled() ? Qt::Checked : Qt::Unchecked); + else if (role == Qt::BackgroundRole) { + if (m_pColorVisitor) + return m_pColorVisitor->getColor(account); + else + return QVariant(account->getStateColor()); + } + else if(index.column() == 0 && role == Qt::DecorationRole && m_pColorVisitor) { + return m_pColorVisitor->getIcon(account); + } + return QVariant(); +} //data + +///Flags for "index" +Qt::ItemFlags AccountList::flags(const QModelIndex & index) const +{ + if (index.column() == 0) + return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; + return QAbstractItemModel::flags(index); +} + +///Number of account +int AccountList::rowCount(const QModelIndex & /*parent*/) const +{ + return m_pAccounts->size(); +} + +Account* AccountList::getAccountByModelIndex(QModelIndex item) const { + return (*m_pAccounts)[item.row()]; +} + /***************************************************************************** * * @@ -240,15 +402,26 @@ QString AccountList::getPriorAccoundId() Account* AccountList::addAccount(QString & alias) { Account* a = Account::buildNewAccountFromAlias(alias); + connect(a,SIGNAL(changed(Account*)),this,SLOT(accountChanged(Account*))); (*m_pAccounts) += a; + + emit dataChanged(index(m_pAccounts->size()-1,0), index(m_pAccounts->size()-1,0)); return a; } ///Remove an account void AccountList::removeAccount(Account* account) { + if (not account) return; qDebug() << "Removing" << m_pAccounts; - m_pAccounts->remove(m_pAccounts->indexOf(account)); + int aindex = m_pAccounts->indexOf(account); + m_pAccounts->remove(aindex); + emit dataChanged(index(aindex,0), index(m_pAccounts->size()-1,0)); +} + +void AccountList::removeAccount( QModelIndex index ) +{ + removeAccount(getAccountByModelIndex(index)); } ///Set the previous account used @@ -256,6 +429,27 @@ void AccountList::setPriorAccountId(const QString& value) { m_sPriorAccountId = value; } +///Set model data +bool AccountList::setData(const QModelIndex & index, const QVariant &value, int role) +{ + if (index.isValid() && index.column() == 0 && role == Qt::CheckStateRole) { + bool prevEnabled = (*m_pAccounts)[index.row()]->isEnabled(); + (*m_pAccounts)[index.row()]->setEnabled(value.toBool()); + emit dataChanged(index, index); + if (prevEnabled != value.toBool()) + emit accountEnabledChanged((*m_pAccounts)[index.row()]); + return true; + } + emit dataChanged(index, index); + return false; +} + +///Set QAbstractItemModel BackgroundRole visitor +void AccountList::setColorVisitor(AccountListColorVisitor* visitor) +{ + m_pColorVisitor = visitor; +} + /***************************************************************************** * * diff --git a/kde/src/lib/AccountList.h b/kde/src/lib/AccountList.h index 96f793cce060bb7f89027971a9ff053a8ef8a93b..a985f7e41621b1e8e615f28fcd05ff753dea603c 100644 --- a/kde/src/lib/AccountList.h +++ b/kde/src/lib/AccountList.h @@ -23,13 +23,16 @@ #include <QtCore/QVector> +#include <QtCore/QAbstractListModel> #include "Account.h" #include "typedefs.h" #include "dbus/metatypes.h" +class AccountListColorVisitor; + ///AccountList: List of all daemon accounts -class LIB_EXPORT AccountList : public QObject{ +class LIB_EXPORT AccountList : public QAbstractListModel { Q_OBJECT public: @@ -50,18 +53,30 @@ public: static Account* getCurrentAccount ( ); static QString getPriorAccoundId ( ); + //Getters + QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const; + int rowCount ( const QModelIndex& parent = QModelIndex() ) const; + Qt::ItemFlags flags ( const QModelIndex& index ) const; + Account* getAccountByModelIndex(QModelIndex item) const; + //Setters - static void setPriorAccountId(const QString& value ); + static void setPriorAccountId( const QString& value ); + virtual bool setData ( const QModelIndex& index, const QVariant &value, int role); + void setColorVisitor ( AccountListColorVisitor* visitor ); //Mutators - virtual Account* addAccount ( QString & alias ) ; - void removeAccount ( Account* account ) ; - QVector<Account*> registeredAccounts( ) const; + virtual Account* addAccount ( QString & alias ) ; + void removeAccount ( Account* account ) ; + void removeAccount ( QModelIndex index ) ; + QVector<Account*> registeredAccounts( ) const; + void save ( ) ; + bool accountUp ( int index ) ; + bool accountDown ( int index ) ; //Operators Account* operator[] (int i) ; const Account* operator[] (int i) const; - + private: //Constructors & Destructors AccountList(QStringList & _accountIds); @@ -72,14 +87,30 @@ private: QVector<Account*>* m_pAccounts; static AccountList* m_spAccountList; static QString m_sPriorAccountId; + AccountListColorVisitor* m_pColorVisitor; public slots: void update(); void updateAccounts(); + +private slots: + void accountChanged(const QString& account,const QString& state, int code); + void accountChanged(Account* a); signals: ///The account list changed void accountListUpdated(); + ///Emitted when an account state change + void accountStateChanged( Account* account, QString state); + void accountEnabledChanged(Account* source); +}; + +///SFLPhonelib Qt does not link to QtGui, and does not need to, this allow to add runtime Gui support +class LIB_EXPORT AccountListColorVisitor { +public: + virtual QVariant getColor(const Account* a) = 0; + virtual QVariant getIcon(const Account* a) = 0; + virtual ~AccountListColorVisitor() {} }; #endif diff --git a/kde/src/lib/AudioCodecModel.cpp b/kde/src/lib/AudioCodecModel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f5b11ce73c9f8bcf9ef7bcdc6cd2d0f71bba25f8 --- /dev/null +++ b/kde/src/lib/AudioCodecModel.cpp @@ -0,0 +1,146 @@ +/************************************************************************************ + * Copyright (C) 2012 by Savoir-Faire Linux * + * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***********************************************************************************/ +#include "AudioCodecModel.h" + +#include <QtCore/QDebug> + +///Constructor +AudioCodecModel::AudioCodecModel(QObject* parent) : QAbstractListModel(parent) { + +} + +///Model data +QVariant AudioCodecModel::data(const QModelIndex& index, int role) const { + if(index.column() == 0 && role == Qt::DisplayRole) + return QVariant(m_lAudioCodecs[index.row()]->name); + else if(index.column() == 0 && role == Qt::CheckStateRole) + return QVariant(m_lEnabledCodecs[m_lAudioCodecs[index.row()]->id] ? Qt::Checked : Qt::Unchecked); + else if (index.column() == 0 && role == AudioCodecModel::NAME_ROLE) { + return m_lAudioCodecs[index.row()]->name; + } + else if (index.column() == 0 && role == AudioCodecModel::BITRATE_ROLE) { + return m_lAudioCodecs[index.row()]->bitrate; + } + else if (index.column() == 0 && role == AudioCodecModel::SAMPLERATE_ROLE) { + return m_lAudioCodecs[index.row()]->samplerate; + } + else if (index.column() == 0 && role == AudioCodecModel::ID_ROLE) { + return m_lAudioCodecs[index.row()]->id; + } + return QVariant(); +} + +///Number of audio codecs +int AudioCodecModel::rowCount(const QModelIndex& parent) const { + Q_UNUSED(parent) + return m_lAudioCodecs.size(); +} + +///Model flags +Qt::ItemFlags AudioCodecModel::flags(const QModelIndex& index) const { + if (index.column() == 0) + return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; + return QAbstractItemModel::flags(index); +} + +///Set audio codec data +bool AudioCodecModel::setData( const QModelIndex& index, const QVariant &value, int role) { + if (index.column() == 0 && role == AudioCodecModel::NAME_ROLE) { + m_lAudioCodecs[index.row()]->name = value.toString(); + emit dataChanged(index, index); + return true; + } + else if (index.column() == 0 && role == AudioCodecModel::BITRATE_ROLE) { + m_lAudioCodecs[index.row()]->bitrate = value.toString(); + emit dataChanged(index, index); + return true; + } + else if(index.column() == 0 && role == Qt::CheckStateRole) { + m_lEnabledCodecs[m_lAudioCodecs[index.row()]->id] = value.toBool(); + emit dataChanged(index, index); + return true; + } + else if (index.column() == 0 && role == AudioCodecModel::SAMPLERATE_ROLE) { + m_lAudioCodecs[index.row()]->samplerate = value.toString(); + emit dataChanged(index, index); + return true; + } + else if (index.column() == 0 && role == AudioCodecModel::ID_ROLE) { + m_lAudioCodecs[index.row()]->id = value.toInt(); + emit dataChanged(index, index); + return true; + } + return false; +} + +///Add a new audio codec +QModelIndex AudioCodecModel::addAudioCodec() { + m_lAudioCodecs << new AudioCodecData; + emit dataChanged(index(m_lAudioCodecs.size()-1,0), index(m_lAudioCodecs.size()-1,0)); + return index(m_lAudioCodecs.size()-1,0); +} + +///Remove audio codec at 'idx' +void AudioCodecModel::removeAudioCodec(QModelIndex idx) { + qDebug() << "REMOVING" << idx.row() << m_lAudioCodecs.size(); + if (idx.isValid()) { + m_lAudioCodecs.removeAt(idx.row()); + emit dataChanged(idx, index(m_lAudioCodecs.size()-1,0)); + qDebug() << "DONE" << m_lAudioCodecs.size(); + } + else { + qDebug() << "Failed to remove an invalid audio codec"; + } +} + +///Remove everything +void AudioCodecModel::clear() +{ + foreach(AudioCodecData* data, m_lAudioCodecs) { + delete data; + } + m_lAudioCodecs.clear(); + m_lEnabledCodecs.clear(); +} + +///Increase codec priority +bool AudioCodecModel::moveUp(QModelIndex idx) +{ + if(idx.row() > 0 && idx.row() <= rowCount()) { + AudioCodecData* data = m_lAudioCodecs[idx.row()]; + m_lAudioCodecs.removeAt(idx.row()); + m_lAudioCodecs.insert(idx.row() - 1, data); + emit dataChanged(index(idx.row() - 1, 0, QModelIndex()), index(idx.row(), 0, QModelIndex())); + return true; + } + return false; +} + +///Decrease codec priority +bool AudioCodecModel::moveDown(QModelIndex idx) +{ + if(idx.row() >= 0 && idx.row() < rowCount()) { + AudioCodecData* data = m_lAudioCodecs[idx.row()]; + m_lAudioCodecs.removeAt(idx.row()); + m_lAudioCodecs.insert(idx.row() + 1, data); + emit dataChanged(index(idx.row(), 0, QModelIndex()), index(idx.row() + 1, 0, QModelIndex())); + return true; + } + return false; +} \ No newline at end of file diff --git a/kde/src/lib/AudioCodecModel.h b/kde/src/lib/AudioCodecModel.h new file mode 100644 index 0000000000000000000000000000000000000000..ec50ca268aa8de69e71eda26f7dfd2572a5c78f4 --- /dev/null +++ b/kde/src/lib/AudioCodecModel.h @@ -0,0 +1,68 @@ +/************************************************************************************ + * Copyright (C) 2012 by Savoir-Faire Linux * + * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***********************************************************************************/ +#ifndef AUDIO_CODEC_MODEL_H +#define AUDIO_CODEC_MODEL_H + +#include <QtCore/QString> +#include <QtCore/QAbstractListModel> +#include "typedefs.h" + +///AudioCodecModel: A model for account audio codec +class LIB_EXPORT AudioCodecModel : public QAbstractListModel { + Q_OBJECT +public: + //friend class Account; + //Roles + static const int ID_ROLE = 103; + static const int NAME_ROLE = 100; + static const int BITRATE_ROLE = 101; + static const int SAMPLERATE_ROLE = 102; + + //Constructor + AudioCodecModel(QObject* parent =nullptr); + + //Abstract model member + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole ) const; + int rowCount(const QModelIndex& parent = QModelIndex() ) const; + Qt::ItemFlags flags(const QModelIndex& index ) const; + virtual bool setData(const QModelIndex& index, const QVariant &value, int role); + + //Mutator + QModelIndex addAudioCodec(); + void removeAudioCodec(QModelIndex idx); + void clear(); + bool moveUp(QModelIndex idx); + bool moveDown(QModelIndex idx); + +private: + ///@struct AudioCodecData store audio codec informations + struct AudioCodecData { + int id ; + QString name ; + QString bitrate ; + QString samplerate; + }; + + //Attributes + QList<AudioCodecData*> m_lAudioCodecs; + QMap<int,bool> m_lEnabledCodecs; + +}; + +#endif \ No newline at end of file diff --git a/kde/src/lib/CMakeLists.txt b/kde/src/lib/CMakeLists.txt index d2075b874aa73d5aafeb374751f887904d2d3409..2a0945de0b27eb505b6c9c84a501282f9a7ba6fa 100644 --- a/kde/src/lib/CMakeLists.txt +++ b/kde/src/lib/CMakeLists.txt @@ -33,9 +33,12 @@ set( qtsflphone_LIB_SRCS HistoryModel.cpp Contact.cpp ContactBackend.cpp - VideoCodec.cpp + VideoCodecModel.cpp VideoModel.cpp + VideoRenderer.cpp VideoDevice.cpp + CredentialModel.cpp + AudioCodecModel.cpp configurationmanager_interface_singleton.cpp callmanager_interface_singleton.cpp instance_interface_singleton.cpp @@ -105,6 +108,8 @@ QT4_ADD_DBUS_INTERFACE( kde4_add_library( qtsflphone SHARED ${qtsflphone_LIB_SRCS} ) target_link_libraries( qtsflphone + -lrt + -lpthread ${QT_QTDBUS_LIBRARY} ${QT_QTCORE_LIBRARY} ) @@ -121,9 +126,12 @@ set( qtsflphone_LIB_HDRS HistoryModel.h Contact.h ContactBackend.h - VideoCodec.h + VideoCodecModel.h VideoModel.h VideoDevice.h + VideoRenderer.h + CredentialModel.h + AudioCodecModel.h configurationmanager_interface_singleton.h callmanager_interface_singleton.h instance_interface_singleton.h diff --git a/kde/src/lib/Call.cpp b/kde/src/lib/Call.cpp index 8d1ffc6a128e1ee22794859f96d1c152322581f0..69ca8b979bad48d08b9d21b7514e66387601396c 100644 --- a/kde/src/lib/Call.cpp +++ b/kde/src/lib/Call.cpp @@ -22,11 +22,13 @@ #include "Call.h" //SFLPhone library -#include "CallModel.h" #include "callmanager_interface_singleton.h" #include "configurationmanager_interface_singleton.h" #include "ContactBackend.h" #include "Contact.h" +#include "Account.h" +#include "AccountList.h" +#include "VideoModel.h" const call_state Call::actionPerformedStateMap [13][5] = @@ -44,7 +46,7 @@ const call_state Call::actionPerformedStateMap [13][5] = /*OVER */ {CALL_STATE_ERROR , CALL_STATE_ERROR , CALL_STATE_ERROR , CALL_STATE_ERROR , CALL_STATE_ERROR },/**/ /*ERROR */ {CALL_STATE_ERROR , CALL_STATE_ERROR , CALL_STATE_ERROR , CALL_STATE_ERROR , CALL_STATE_ERROR },/**/ /*CONF */ {CALL_STATE_ERROR , CALL_STATE_CURRENT , CALL_STATE_TRANSFER , CALL_STATE_CURRENT , CALL_STATE_CURRENT },/**/ -/*CONF_HOLD */ {CALL_STATE_ERROR , CALL_STATE_HOLD , CALL_STATE_TRANSF_HOLD , CALL_STATE_HOLD , CALL_STATE_HOLD } /**/ +/*CONF_HOLD */ {CALL_STATE_ERROR , CALL_STATE_HOLD , CALL_STATE_TRANSF_HOLD , CALL_STATE_HOLD , CALL_STATE_HOLD },/**/ };// @@ -63,7 +65,7 @@ const function Call::actionPerformedFunctionMap[13][5] = /*OVER */ {&Call::nothing , &Call::nothing , &Call::nothing , &Call::nothing , &Call::nothing },/**/ /*ERROR */ {&Call::nothing , &Call::nothing , &Call::nothing , &Call::nothing , &Call::nothing },/**/ /*CONF */ {&Call::nothing , &Call::hangUp , &Call::nothing , &Call::hold , &Call::setRecord },/**/ -/*CONF_HOLD */ {&Call::nothing , &Call::hangUp , &Call::nothing , &Call::unhold , &Call::setRecord } /**/ +/*CONF_HOLD */ {&Call::nothing , &Call::hangUp , &Call::nothing , &Call::unhold , &Call::setRecord },/**/ };// @@ -82,7 +84,7 @@ const call_state Call::stateChangedStateMap [13][6] = /*OVER */ {CALL_STATE_OVER , CALL_STATE_OVER , CALL_STATE_OVER , CALL_STATE_OVER , CALL_STATE_OVER , CALL_STATE_OVER },/**/ /*ERROR */ {CALL_STATE_ERROR , CALL_STATE_ERROR , CALL_STATE_ERROR , CALL_STATE_ERROR , CALL_STATE_ERROR , CALL_STATE_ERROR },/**/ /*CONF */ {CALL_STATE_CURRENT , CALL_STATE_CURRENT , CALL_STATE_BUSY , CALL_STATE_HOLD , CALL_STATE_OVER , CALL_STATE_FAILURE },/**/ -/*CONF_HOLD */ {CALL_STATE_HOLD , CALL_STATE_CURRENT , CALL_STATE_BUSY , CALL_STATE_HOLD , CALL_STATE_OVER , CALL_STATE_FAILURE } /**/ +/*CONF_HOLD */ {CALL_STATE_HOLD , CALL_STATE_CURRENT , CALL_STATE_BUSY , CALL_STATE_HOLD , CALL_STATE_OVER , CALL_STATE_FAILURE },/**/ };// const function Call::stateChangedFunctionMap[13][6] = @@ -100,7 +102,7 @@ const function Call::stateChangedFunctionMap[13][6] = /*OVER */ {&Call::nothing , &Call::warning , &Call::warning , &Call::warning , &Call::stop , &Call::warning },/**/ /*ERROR */ {&Call::nothing , &Call::nothing , &Call::nothing , &Call::nothing , &Call::stop , &Call::nothing },/**/ /*CONF */ {&Call::nothing , &Call::nothing , &Call::warning , &Call::nothing , &Call::stop , &Call::nothing },/**/ -/*CONF_HOLD */ {&Call::nothing , &Call::nothing , &Call::warning , &Call::nothing , &Call::stop , &Call::nothing } /**/ +/*CONF_HOLD */ {&Call::nothing , &Call::nothing , &Call::warning , &Call::nothing , &Call::stop , &Call::nothing },/**/ };// const char * Call::historyIcons[3] = {ICON_HISTORY_INCOMING, ICON_HISTORY_OUTGOING, ICON_HISTORY_MISSED}; @@ -186,7 +188,7 @@ Call* Call::buildExistingCall(QString callId) call->m_pStartTime = new QDateTime(QDateTime::currentDateTime()) ; call->m_Recording = callManager.getIsRecording(callId) ; - call->m_HistoryState = getHistoryStateFromDaemonCallState(details[CALL_STATE], details[CALL_TYPE]) ; + call->m_HistoryState = getHistoryStateFromType(details[STATE_KEY]); return call; } //buildExistingCall @@ -250,50 +252,22 @@ Call* Call::buildHistoryCall(const QString & callId, uint startTimeStamp, uint s } call->m_HistoryState = getHistoryStateFromType(type); + return call; } ///Get the history state from the type (see Call.cpp header) history_state Call::getHistoryStateFromType(QString type) { - if(type == DAEMON_HISTORY_TYPE_MISSED ) + if(type == MISSED_STRING ) return MISSED ; - else if(type == DAEMON_HISTORY_TYPE_OUTGOING ) + else if(type == OUTGOING_STRING ) return OUTGOING ; - else if(type == DAEMON_HISTORY_TYPE_INCOMING ) + else if(type == INCOMING_STRING ) return INCOMING ; return NONE ; } -///Get the type from an history state (see Call.cpp header) -QString Call::getTypeFromHistoryState(history_state historyState) -{ - if(historyState == MISSED ) - return DAEMON_HISTORY_TYPE_MISSED ; - else if(historyState == OUTGOING ) - return DAEMON_HISTORY_TYPE_OUTGOING ; - else if(historyState == INCOMING ) - return DAEMON_HISTORY_TYPE_INCOMING ; - return QString() ; -} - -///Get history state from daemon -history_state Call::getHistoryStateFromDaemonCallState(QString daemonCallState, QString daemonCallType) -{ - if((daemonCallState == DAEMON_CALL_STATE_INIT_CURRENT || daemonCallState == DAEMON_CALL_STATE_INIT_HOLD) && daemonCallType == DAEMON_CALL_TYPE_INCOMING ) - return INCOMING ; - else if((daemonCallState == DAEMON_CALL_STATE_INIT_CURRENT || daemonCallState == DAEMON_CALL_STATE_INIT_HOLD) && daemonCallType == DAEMON_CALL_TYPE_OUTGOING ) - return OUTGOING ; - else if(daemonCallState == DAEMON_CALL_STATE_INIT_BUSY ) - return OUTGOING ; - else if(daemonCallState == DAEMON_CALL_STATE_INIT_INACTIVE && daemonCallType == DAEMON_CALL_TYPE_INCOMING ) - return INCOMING ; - else if(daemonCallState == DAEMON_CALL_STATE_INIT_INACTIVE && daemonCallType == DAEMON_CALL_TYPE_OUTGOING ) - return MISSED ; - else - return NONE ; -} //getHistoryStateFromDaemonCallState - ///Get the start sate from the daemon state call_state Call::getStartStateFromDaemonCallState(QString daemonCallState, QString daemonCallType) { @@ -332,8 +306,6 @@ daemon_call_state Call::toDaemonCallState(const QString& stateName) return DAEMON_CALL_STATE_CURRENT ; if(stateName == QString(CALL_STATE_CHANGE_UNHOLD_CURRENT) ) return DAEMON_CALL_STATE_CURRENT ; - if(stateName == QString(CALL_STATE_CHANGE_UNHOLD_RECORD) ) - return DAEMON_CALL_STATE_CURRENT ; if(stateName == QString(CALL_STATE_CHANGE_HOLD) ) return DAEMON_CALL_STATE_HOLD ; if(stateName == QString(CALL_STATE_CHANGE_BUSY) ) @@ -457,13 +429,15 @@ call_state Call::getCurrentState() const ///Get the call recording bool Call::getRecording() const { + CallManagerInterface & callManager = CallManagerInterfaceSingleton::getInstance(); + ((Call*) this)->m_Recording = callManager.getIsRecording(m_CallId); return m_Recording; } ///Get the call account id -const QString& Call::getAccountId() const +Account* Call::getAccount() const { - return m_Account; + return AccountList::getInstance()->getAccountById(m_Account); } ///Is this call a conference @@ -479,13 +453,13 @@ const QString& Call::getConfId() const } ///Get the recording path -const QString& Call::getRecordingPath() const +const QString& Call::getRecordingPath() const { return m_RecordingPath; } ///Get the current codec -QString Call::getCurrentCodecName() const +QString Call::getCurrentCodecName() const { CallManagerInterface & callManager = CallManagerInterfaceSingleton::getInstance(); return callManager.getCurrentAudioCodecName(m_CallId); @@ -539,6 +513,12 @@ Contact* Call::getContact() return m_pContact; } +///Return the renderer associated with this call or nullptr +VideoRenderer* Call::getVideoRenderer() +{ + return VideoModel::getInstance()->getRenderer(this); +} + /***************************************************************************** * * diff --git a/kde/src/lib/Call.h b/kde/src/lib/Call.h index 9b2d90f5f8e65b9fc3f7201c4070ab388aedabf5..c8b1afcaf4f869f380bcd46dd59ab881af29dcf9 100644 --- a/kde/src/lib/Call.h +++ b/kde/src/lib/Call.h @@ -34,6 +34,8 @@ class QWidget; class ContactBackend; class Contact; +class Account; +class VideoRenderer; /** @enum daemon_call_state_t @@ -52,7 +54,9 @@ typedef enum /** Call is over */ DAEMON_CALL_STATE_HUNG_UP, /** Call has failed */ - DAEMON_CALL_STATE_FAILURE + DAEMON_CALL_STATE_FAILURE, + /** Call is recording+current */ + DAEMON_CALL_STATE_RECORD, } daemon_call_state; /** @enum call_action @@ -130,9 +134,7 @@ public: //Static getters static history_state getHistoryStateFromType ( QString type ); - static QString getTypeFromHistoryState ( history_state historyState ); static call_state getStartStateFromDaemonCallState ( QString daemonCallState, QString daemonCallType ); - static history_state getHistoryStateFromDaemonCallState ( QString daemonCallState, QString daemonCallType ); //Getters call_state getState () const; @@ -142,7 +144,7 @@ public: call_state getCurrentState () const; history_state getHistoryState () const; bool getRecording () const; - const QString& getAccountId () const; + Account* getAccount () const; bool isHistory () const; QString getStopTimeStamp () const; QString getStartTimeStamp () const; @@ -155,7 +157,8 @@ public: const QString& getCallNumber () const; const QString& getRecordingPath () const; const QString toHumanStateName () const; - Contact* getContact() ; + Contact* getContact () ; + VideoRenderer* getVideoRenderer () ; //Automated function call_state stateChanged(const QString & newState); diff --git a/kde/src/lib/CallModel.cpp b/kde/src/lib/CallModel.cpp index c3a1da74083b7cc91d8524e9d4917df36f636b04..bc5dd6eb2c061ade0fb192aab7a524704ea3492c 100644 --- a/kde/src/lib/CallModel.cpp +++ b/kde/src/lib/CallModel.cpp @@ -20,7 +20,8 @@ //Parent #include <CallModel.h> -#include <HistoryModel.h> +#include "video_interface_singleton.h" +#include "HistoryModel.h" bool CallModelBase::dbusInit = false; CallMap CallModelBase::m_sActiveCalls; @@ -30,9 +31,10 @@ CallModelBase::CallModelBase(QObject* parent) : QObject(parent) { if (!dbusInit) { CallManagerInterface& callManager = CallManagerInterfaceSingleton::getInstance(); + VideoInterface& interface = VideoInterfaceSingleton::getInstance(); //SLOTS - // SENDER SIGNAL RECEIVER SLOT / + /* SENDER SIGNAL RECEIVER SLOT */ /**/connect(&callManager, SIGNAL( callStateChanged (const QString &, const QString & ) ), this , SLOT( callStateChanged ( const QString &, const QString & ) ) ); /**/connect(&callManager, SIGNAL( incomingCall (const QString &, const QString &, const QString & ) ), this , SLOT( incomingCall ( const QString &, const QString & ) ) ); /**/connect(&callManager, SIGNAL( conferenceCreated (const QString & ) ), this , SLOT( incomingConference ( const QString & ) ) ); @@ -40,11 +42,12 @@ CallModelBase::CallModelBase(QObject* parent) : QObject(parent) /**/connect(&callManager, SIGNAL( conferenceRemoved (const QString & ) ), this , SLOT( conferenceRemovedSlot ( const QString & ) ) ); /**/connect(&callManager, SIGNAL( voiceMailNotify (const QString &, int ) ), this , SLOT( voiceMailNotifySlot ( const QString &, int ) ) ); /**/connect(&callManager, SIGNAL( volumeChanged (const QString &, double ) ), this , SLOT( volumeChangedSlot ( const QString &, double ) ) ); + /**/connect(&interface , SIGNAL( startedDecoding (QString,QString,int,int ) ), this, SLOT( startedDecoding ( const QString &, const QString & ) ) ); + /**/connect(&interface , SIGNAL( stoppedDecoding (QString,QString ) ), this, SLOT( stoppedDecoding ( const QString &, const QString & ) ) ); /* */ connect(HistoryModel::self(),SIGNAL(newHistoryCall(Call*)),this,SLOT(addPrivateCall(Call*))); - connect(&callManager, SIGNAL(registrationStateChanged(QString,QString,int)),this,SLOT(accountChanged(QString,QString,int))); dbusInit = true; foreach(Call* call,HistoryModel::getHistory()){ @@ -167,16 +170,6 @@ Call* CallModelBase::addConferenceS(Call* conf) return conf; } -///Account status changed -void CallModelBase::accountChanged(const QString& account,const QString& state, int code) -{ - Q_UNUSED(code) - Account* a = AccountList::getInstance()->getAccountById(account); - if (a) { - emit accountStateChanged(a,a->getStateName(state)); - } -} - ///Remove it from active calls void CallModelBase::removeActiveCall(Call* call) { @@ -185,6 +178,27 @@ void CallModelBase::removeActiveCall(Call* call) //m_sActiveCalls[call->getCallId()] = nullptr; } +///Updating call state when video is added +void CallModelBase::startedDecoding(const QString& callId, const QString& shmKey ) +{ + Q_UNUSED(callId) + Q_UNUSED(shmKey) +// Call* call = getCall(callId); +// if (call) { +// +// } +} + +///Updating call state when video is removed +void CallModelBase::stoppedDecoding(const QString& callId, const QString& shmKey) +{ + Q_UNUSED(callId) + Q_UNUSED(shmKey) +// Call* call = getCall(callId); +// if (call) { +// +// } +} /***************************************************************************** * * diff --git a/kde/src/lib/CallModel.h b/kde/src/lib/CallModel.h index 0aa6110d6985eaa1baf8cae1b2e94daa22177746..a4711570ed57ad57692271f35c10c673d451e33e 100644 --- a/kde/src/lib/CallModel.h +++ b/kde/src/lib/CallModel.h @@ -65,6 +65,10 @@ private slots: void conferenceRemovedSlot ( const QString& confId ); void voiceMailNotifySlot ( const QString& accountID , int count ); void volumeChangedSlot ( const QString& device , double value ); + void removeActiveCall ( Call* call ); + void addPrivateCall ( Call* call ); + void startedDecoding ( const QString& callId , const QString& shmKey ); + void stoppedDecoding ( const QString& callId , const QString& shmKey ); protected: virtual Call* findCallByCallId ( const QString& callId ) = 0; @@ -77,10 +81,6 @@ protected: //Attributes static CallMap m_sActiveCalls; -private slots: - void removeActiveCall(Call*); - void accountChanged(const QString& account,const QString& state, int code); - void addPrivateCall(Call* call); private: static bool dbusInit; @@ -104,7 +104,7 @@ signals: ///Emitted when a call is added void callAdded ( Call* call , Call* parent ); ///Emitted when an account state change - void accountStateChanged ( Account* account, QString state ); + //void accountStateChanged ( Account* account, QString state ); }; /** diff --git a/kde/src/lib/CallModel.hpp b/kde/src/lib/CallModel.hpp index b94ff209073458adaa7aab5ac89cf9a32f721174..5aa455656034f7bd47c8876252781dd12bdb8cfe 100644 --- a/kde/src/lib/CallModel.hpp +++ b/kde/src/lib/CallModel.hpp @@ -108,7 +108,7 @@ CALLMODEL_TEMPLATE bool CALLMODEL_T::initCall() QStringList confList = callManager.getConferenceList(); foreach (QString confId, confList) { - CallModelBase::addConferenceS(addConference(confId)); + CallModelBase::addConferenceS(addConference(confId)); } } m_sCallInit = true; @@ -199,9 +199,13 @@ CALLMODEL_TEMPLATE Call* CALLMODEL_T::addCallCommon(Call* call) CALLMODEL_TEMPLATE Call* CALLMODEL_T::addDialingCall(const QString& peerName, Account* account) { Account* acc = (account)?account:AccountList::getCurrentAccount(); - - Call* call = Call::buildDialingCall(generateCallId(), peerName, acc->getAccountId()); - return addCallCommon(call); + if (acc) { + Call* call = Call::buildDialingCall(generateCallId(), peerName, acc->getAccountId()); + return addCallCommon(call); + } + else { + return nullptr; + } } //addDialingCall ///Create a new incomming call when the daemon is being called @@ -296,7 +300,10 @@ CALLMODEL_TEMPLATE Call* CALLMODEL_T::addConference(const QString & confID) qDebug() << "Invalid call"; return 0; } - Call* newConf = new Call(confID, m_sPrivateCallList_callId[callList[0]]->call_real->getAccountId()); + + Call* newConf; + if (m_sPrivateCallList_callId[callList[0]]->call_real->getAccount()) + newConf = new Call(confID, m_sPrivateCallList_callId[callList[0]]->call_real->getAccount()->getAccountId()); InternalStruct* aNewStruct = new InternalStruct; aNewStruct->call_real = newConf; diff --git a/kde/src/lib/ContactBackend.h b/kde/src/lib/ContactBackend.h index c55f846bf58d9a64069716fc303c234f09bec3c6..fbeca06c437ccc153491a85e6fb8fbef4b9b85d1 100644 --- a/kde/src/lib/ContactBackend.h +++ b/kde/src/lib/ContactBackend.h @@ -52,6 +52,9 @@ public: virtual void editContact ( Contact* contact ) = 0; ///Add a new contact to the backend virtual void addNewContact ( Contact* contact ) = 0; + + ///Add a new phone number to an existing contact + virtual void addPhoneNumber( Contact* contact , QString number, QString type )=0; protected: virtual ContactList update_slot ( ) = 0; diff --git a/kde/src/lib/CredentialModel.cpp b/kde/src/lib/CredentialModel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dd7329c4f49c957ac0663d5c840553ea7cd04ff2 --- /dev/null +++ b/kde/src/lib/CredentialModel.cpp @@ -0,0 +1,106 @@ +/************************************************************************************ + * Copyright (C) 2012 by Savoir-Faire Linux * + * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***********************************************************************************/ +#include "CredentialModel.h" + +#include <QtCore/QDebug> + +///Constructor +CredentialModel::CredentialModel(QObject* parent) : QAbstractListModel(parent) { + +} + +///Model data +QVariant CredentialModel::data(const QModelIndex& index, int role) const { + if(index.column() == 0 && role == Qt::DisplayRole) + return QVariant(m_lCredentials[index.row()]->name); +// else if(index.column() == 0 && role == Qt::CheckStateRole) +// return QVariant(account->isEnabled() ? Qt::Checked : Qt::Unchecked); + else if (index.column() == 0 && role == CredentialModel::NAME_ROLE) { + return m_lCredentials[index.row()]->name; + } + else if (index.column() == 0 && role == CredentialModel::PASSWORD_ROLE) { + return m_lCredentials[index.row()]->password; + } + else if (index.column() == 0 && role == CredentialModel::REALM_ROLE) { + return m_lCredentials[index.row()]->realm; + } + return QVariant(); +} + +///Number of credentials +int CredentialModel::rowCount(const QModelIndex& parent) const { + Q_UNUSED(parent) + return m_lCredentials.size(); +} + +///Model flags +Qt::ItemFlags CredentialModel::flags(const QModelIndex& index) const { + if (index.column() == 0) + return QAbstractItemModel::flags(index) /*| Qt::ItemIsUserCheckable*/ | Qt::ItemIsEnabled | Qt::ItemIsSelectable; + return QAbstractItemModel::flags(index); +} + +///Set credential data +bool CredentialModel::setData( const QModelIndex& index, const QVariant &value, int role) { + if (index.column() == 0 && role == CredentialModel::NAME_ROLE) { + m_lCredentials[index.row()]->name = value.toString(); + emit dataChanged(index, index); + return true; + } + else if (index.column() == 0 && role == CredentialModel::PASSWORD_ROLE) { + m_lCredentials[index.row()]->password = value.toString(); + emit dataChanged(index, index); + return true; + } + else if (index.column() == 0 && role == CredentialModel::REALM_ROLE) { + m_lCredentials[index.row()]->realm = value.toString(); + emit dataChanged(index, index); + return true; + } + return false; +} + +///Add a new credential +QModelIndex CredentialModel::addCredentials() { + m_lCredentials << new CredentialData2; + emit dataChanged(index(m_lCredentials.size()-1,0), index(m_lCredentials.size()-1,0)); + return index(m_lCredentials.size()-1,0); +} + +///Remove credential at 'idx' +void CredentialModel::removeCredentials(QModelIndex idx) { + qDebug() << "REMOVING" << idx.row() << m_lCredentials.size(); + if (idx.isValid()) { + m_lCredentials.removeAt(idx.row()); + emit dataChanged(idx, index(m_lCredentials.size()-1,0)); + qDebug() << "DONE" << m_lCredentials.size(); + } + else { + qDebug() << "Failed to remove an invalid credential"; + } +} + +///Remove everything +void CredentialModel::clear() +{ + foreach(CredentialData2* data, m_lCredentials) { + delete data; + } + m_lCredentials.clear(); +} \ No newline at end of file diff --git a/kde/src/lib/CredentialModel.h b/kde/src/lib/CredentialModel.h new file mode 100644 index 0000000000000000000000000000000000000000..0d6013a09c810ebd3886c0bceb7e469c971579fc --- /dev/null +++ b/kde/src/lib/CredentialModel.h @@ -0,0 +1,64 @@ +/************************************************************************************ + * Copyright (C) 2012 by Savoir-Faire Linux * + * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***********************************************************************************/ +#ifndef CREDENTIAL_MODEL_H +#define CREDENTIAL_MODEL_H + +#include <QtCore/QString> +#include <QtCore/QAbstractListModel> +#include "typedefs.h" + + +///CredentialModel: A model for account credentials +class LIB_EXPORT CredentialModel : public QAbstractListModel { + Q_OBJECT +public: + //friend class Account; + //Roles + static const int NAME_ROLE = 100; + static const int PASSWORD_ROLE = 101; + static const int REALM_ROLE = 102; + + //Constructor + CredentialModel(QObject* parent =nullptr); + + //Abstract model member + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole ) const; + int rowCount(const QModelIndex& parent = QModelIndex() ) const; + Qt::ItemFlags flags(const QModelIndex& index ) const; + virtual bool setData(const QModelIndex& index, const QVariant &value, int role); + + //Mutator + QModelIndex addCredentials(); + void removeCredentials(QModelIndex idx); + void clear(); + +private: + ///@struct CredentialData store credential informations + struct CredentialData2 { + QString name ; + QString password; + QString realm ; + }; + + //Attributes + QList<CredentialData2*> m_lCredentials; + +}; + +#endif \ No newline at end of file diff --git a/kde/src/lib/VideoCodec.cpp b/kde/src/lib/VideoCodec.cpp deleted file mode 100644 index f34a1cc59b705b70e92bd31fc5a87772366b2abf..0000000000000000000000000000000000000000 --- a/kde/src/lib/VideoCodec.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/************************************************************************************ - * Copyright (C) 2012 by Savoir-Faire Linux * - * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***********************************************************************************/ -#include "VideoCodec.h" -#include "Call.h" -#include "Account.h" -#include "video_interface_singleton.h" - -QHash<QString,VideoCodec*> VideoCodec::m_slCodecs; -bool VideoCodec::m_sInit = false; - -///Private constructor -VideoCodec::VideoCodec(QString codecName) -{ - VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - QMap<QString,QString> details = interface.getCodecDetails(codecName); - m_Name = details[ "name" ];//TODO do not use stringlist - m_Bitrate = details[ "bitrate" ];//TODO do not use stringlist -} - -///Init the device list -void VideoCodec::init() -{ - VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - QStringList codecs = interface.getCodecList(); - foreach(QString codec,codecs) { - m_slCodecs[codec] = new VideoCodec(codec); - } - - m_sInit = true; -} - -///Get a codec from a name -VideoCodec* VideoCodec::getCodec(QString name) -{ - return m_slCodecs[name]; -} - -///Get the current call codec -//TODO move to call.h? -VideoCodec* VideoCodec::getCurrentCodec(Call* call) -{ - if (!m_sInit) init(); - VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - return getCodec(interface.getCurrentCodecName(call->getCallId())); -} - -///Get the complete video codec list -QList<VideoCodec*> VideoCodec::getCodecList() -{ - if (!m_sInit) init(); - return m_slCodecs.values(); -} - -///Get the list of active codecs -QList<VideoCodec*> VideoCodec::getActiveCodecList(Account* account) -{ - VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - QStringList codecs = interface.getActiveCodecList(account->getAccountId()); - QList<VideoCodec*> toReturn; - foreach(QString codec,codecs) { - toReturn << getCodec(codec); - } - return toReturn; -} - -///Set active codecs -void VideoCodec::setActiveCodecList(Account* account, QStringList codecs) -{ - VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - interface.setActiveCodecList(codecs,account->getAccountId()); -} - -///Get the current codec name -QString VideoCodec::getName() -{ - return m_Name; -} - -///Get the current codec id -QString VideoCodec::getBitrate() -{ - return m_Bitrate; -} \ No newline at end of file diff --git a/kde/src/lib/VideoCodecModel.cpp b/kde/src/lib/VideoCodecModel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b8f32f9f6d49a4876136532a700c79ffa31b4047 --- /dev/null +++ b/kde/src/lib/VideoCodecModel.cpp @@ -0,0 +1,167 @@ +/************************************************************************************ + * Copyright (C) 2012 by Savoir-Faire Linux * + * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***********************************************************************************/ +#include "VideoCodecModel.h" +#include "Call.h" +#include "Account.h" +#include "video_interface_singleton.h" + +///Get data from the model +QVariant VideoCodecModel::data( const QModelIndex& index, int role) const +{ + if(index.column() == 0 && role == Qt::DisplayRole) + return QVariant(m_lCodecs[index.row()]->getName()); + else if(index.column() == 0 && role == Qt::CheckStateRole) { + return QVariant(m_lCodecs[index.row()]->getEnabled()?Qt::Checked:Qt::Unchecked); + } + else if (index.column() == 0 && role == VideoCodecModel::BITRATE_ROLE) + return QVariant(m_lCodecs[index.row()]->getBitrate()); + return QVariant(); +} + +///The number of codec +int VideoCodecModel::rowCount( const QModelIndex& parent ) const +{ + Q_UNUSED(parent) + return m_lCodecs.size(); +} + +///Items flag +Qt::ItemFlags VideoCodecModel::flags( const QModelIndex& index ) const +{ + if (index.column() == 0) + return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; + return QAbstractItemModel::flags(index); +} + +///Set the codec data (codecs can't be added or removed that way) +bool VideoCodecModel::setData(const QModelIndex& index, const QVariant &value, int role) +{ + + if (index.column() == 0 && role == Qt::CheckStateRole) { + m_lCodecs[index.row()]->setEnabled(value == Qt::Checked); + emit dataChanged(index, index); + return true; + } + else if (index.column() == 0 && role == VideoCodecModel::BITRATE_ROLE) { + m_lCodecs[index.row()]->setBitrate(value.toInt()); + emit dataChanged(index, index); + } + return false; +} + +///Constructor +VideoCodecModel::VideoCodecModel(Account* account) : QAbstractListModel(),m_pAccount(account) +{ + reload(); +} + +///Force a model reload from dbus +void VideoCodecModel::reload() +{ + m_lCodecs.clear(); + VideoInterface& interface = VideoInterfaceSingleton::getInstance(); + VectorMapStringString codecs = interface.getCodecs(m_pAccount->getAccountId()); + foreach(MapStringString h,codecs) { + VideoCodec* c = new VideoCodec(h["name"],h["bitrate"].toInt(),h["enabled"]=="true"); + m_lCodecs << c; + } + emit dataChanged(index(0,0), index(m_lCodecs.size()-1,0)); +} + +///Save the current model over dbus +void VideoCodecModel::save() +{ + VideoInterface& interface = VideoInterfaceSingleton::getInstance(); + VectorMapStringString toSave; + foreach(VideoCodec* vc,m_lCodecs) { + MapStringString details; + details[ "name" ] = vc->getName (); + details[ "bitrate" ] = QString::number(vc->getBitrate()); + details[ "enabled" ] = vc->getEnabled()?"true":"false"; + toSave << details; + } + interface.setCodecs(m_pAccount->getAccountId(),toSave); +} + +///Increase codec priority +bool VideoCodecModel::moveUp(QModelIndex idx) +{ + if(idx.row() > 0 && idx.row() <= rowCount()) { + VideoCodec* data = m_lCodecs[idx.row()]; + m_lCodecs.removeAt(idx.row()); + m_lCodecs.insert(idx.row() - 1, data); + emit dataChanged(index(idx.row() - 1, 0, QModelIndex()), index(idx.row(), 0, QModelIndex())); + return true; + } + return false; +} + +///Decrease codec priority +bool VideoCodecModel::moveDown(QModelIndex idx) +{ + if(idx.row() >= 0 && idx.row() < rowCount()) { + VideoCodec* data = m_lCodecs[idx.row()]; + m_lCodecs.removeAt(idx.row()); + m_lCodecs.insert(idx.row() + 1, data); + emit dataChanged(index(idx.row(), 0, QModelIndex()), index(idx.row() + 1, 0, QModelIndex())); + return true; + } + return false; +} + + +QHash<QString,VideoCodec*> VideoCodec::m_slCodecs; +bool VideoCodec::m_sInit = false; + +///Private constructor +VideoCodec::VideoCodec(QString codecName, uint bitRate, bool enabled) : +m_Name(codecName),m_Bitrate(bitRate),m_Enabled(enabled) +{ + +} + +///Get the current codec name +QString VideoCodec::getName() const +{ + return m_Name; +} + +///Get the current codec id +uint VideoCodec::getBitrate() const +{ + return m_Bitrate; +} + +///Get the current codec id +bool VideoCodec::getEnabled() const +{ + return m_Enabled; +} + +///Set the codec bitrate +void VideoCodec::setBitrate(const uint bitrate) +{ + m_Bitrate = bitrate; +} + +///Set if the codec is enabled +void VideoCodec::setEnabled(const bool enabled) +{ + m_Enabled = enabled; +} \ No newline at end of file diff --git a/kde/src/lib/VideoCodec.h b/kde/src/lib/VideoCodecModel.h similarity index 63% rename from kde/src/lib/VideoCodec.h rename to kde/src/lib/VideoCodecModel.h index c693006af4601a76e46ee6e922a7f32e33dd6f7e..6a5c59f09ef8851fadd5e1aeaa87c981f84c167e 100644 --- a/kde/src/lib/VideoCodec.h +++ b/kde/src/lib/VideoCodecModel.h @@ -20,6 +20,7 @@ #define VIDEO_CODEC_H #include "typedefs.h" +#include <QtCore/QAbstractListModel> //Qt class QStringList; @@ -32,33 +33,59 @@ class Account; class VideoCodec; typedef QHash<QString,VideoCodec*> CodecHash; +class LIB_EXPORT VideoCodecModel : public QAbstractListModel { + Q_OBJECT + friend class Account; +public: + //Roles + static const int BITRATE_ROLE = 101; + + //Model functions + QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const; + int rowCount ( const QModelIndex& parent = QModelIndex() ) const; + Qt::ItemFlags flags ( const QModelIndex& index ) const; + virtual bool setData ( const QModelIndex& index, const QVariant &value, int role) ; + + void reload(); + void save(); + bool moveUp(QModelIndex idx); + bool moveDown(QModelIndex idx); + +private: + //Private constructor, can only be called by 'Account' + VideoCodecModel(Account* account); + + //Attrbutes + QList<VideoCodec*> m_lCodecs; + Account* m_pAccount; +}; + ///VideoCodec: Codecs used for video calls class LIB_EXPORT VideoCodec { + friend class VideoCodecModel; public: - - //Static getter - static VideoCodec* getCodec(QString name); - static VideoCodec* getCurrentCodec(Call* call); - static QList<VideoCodec*> getCodecList(); - static QList<VideoCodec*> getActiveCodecList(Account* account); - //Static setters static void setActiveCodecList(Account* account, QStringList codecs); //Getters - QString getName(); - QString getBitrate(); + QString getName () const; + uint getBitrate() const; + bool getEnabled() const; + + //Setters + void setBitrate(const uint bitrate); + void setEnabled(const bool enabled); private: //Constructor - VideoCodec(QString codecName); + VideoCodec(QString codecName, uint bitRate, bool enabled); ~VideoCodec(){}; - static void init(); //Attributes static CodecHash m_slCodecs; QString m_Name; - QString m_Bitrate; + uint m_Bitrate; + bool m_Enabled; static bool m_sInit; }; #endif \ No newline at end of file diff --git a/kde/src/lib/VideoDevice.cpp b/kde/src/lib/VideoDevice.cpp index a81a60fb2678f661ecb67ebe8a3f883f71d0199d..68dd69244832829c3530e27b237a222bb8419f50 100644 --- a/kde/src/lib/VideoDevice.cpp +++ b/kde/src/lib/VideoDevice.cpp @@ -32,7 +32,7 @@ QList<VideoDevice*> VideoDevice::getDeviceList() { QHash<QString,VideoDevice*> devices; VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - QStringList deviceList = interface.getInputDeviceList(); + QStringList deviceList = interface.getDeviceList(); foreach(QString device,deviceList) { if (!m_slDevices[device]) devices[device] = new VideoDevice(device); @@ -58,56 +58,56 @@ VideoDevice* VideoDevice::getDevice(QString name) QStringList VideoDevice::getRateList(VideoChannel channel, Resolution resolution) { VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - return interface.getInputDeviceRateList(m_DeviceId,channel,resolution.toString()); + return interface.getDeviceRateList(m_DeviceId,channel,resolution.toString()); } ///Get the valid channel list QList<VideoChannel> VideoDevice::getChannelList() { VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - return interface.getInputDeviceChannelList(m_DeviceId); + return interface.getDeviceChannelList(m_DeviceId); } ///Set the current device rate void VideoDevice::setRate(VideoRate rate) { VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - interface.setInputDeviceRate(rate); + interface.setActiveDeviceRate(rate); } ///Set the current resolution void VideoDevice::setResolution(Resolution resolution) //??? No device { VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - interface.setInputDeviceSize(resolution.toString()); + interface.setActiveDeviceSize(resolution.toString()); } ///Set the current device channel void VideoDevice::setChannel(VideoChannel channel) //??? No device { VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - interface.setInputDeviceChannel(channel); + interface.setActiveDeviceChannel(channel); } ///Get the current resolution Resolution VideoDevice::getResolution() { VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - return Resolution(interface.getInputDeviceSize()); + return Resolution(interface.getActiveDeviceSize()); } ///Get the current channel VideoChannel VideoDevice::getChannel() //??? No device { VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - return interface.getInputDeviceChannel(); + return interface.getActiveDeviceChannel(); } ///Get the current rate VideoRate VideoDevice::getRate() { VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - return interface.getInputDeviceRate(); + return interface.getActiveDeviceRate(); } ///Get a list of valid resolution @@ -115,7 +115,7 @@ QList<Resolution> VideoDevice::getResolutionList(VideoChannel channel) { QList<Resolution> toReturn; VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - QStringList list = interface.getInputDeviceSizeList(m_DeviceId,channel); + QStringList list = interface.getDeviceSizeList(m_DeviceId,channel); foreach(QString res,list) { toReturn << Resolution(res); } diff --git a/kde/src/lib/VideoDevice.h b/kde/src/lib/VideoDevice.h index 18322baf556ed1c9fb5c1f762de3304071dab699..e34573137151060aeccb281e77f1dd8359959139 100644 --- a/kde/src/lib/VideoDevice.h +++ b/kde/src/lib/VideoDevice.h @@ -21,6 +21,7 @@ #include "typedefs.h" #include <QStringList> +#include <QtCore/QSize> ///@typedef VideoChannel A channel available in a Device typedef QString VideoChannel; @@ -38,6 +39,8 @@ struct LIB_EXPORT Resolution { height=size.split("x")[1].toInt(); } } + Resolution(const Resolution& res):width(res.width),height(res.height){} + Resolution(const QSize& size):width(size.width()),height(size.height()){} //Getter QString toString() { return QString::number(width)+"x"+QString::number(height);} diff --git a/kde/src/lib/VideoModel.cpp b/kde/src/lib/VideoModel.cpp index cdc8418ea0108679adc90ccb93277744dc2120d4..b792881a8370baf2a08b9125dad7cd233c62a17c 100644 --- a/kde/src/lib/VideoModel.cpp +++ b/kde/src/lib/VideoModel.cpp @@ -19,106 +19,31 @@ #include "VideoModel.h" //Posix -#include <sys/ipc.h> -#include <sys/sem.h> -#include <sys/shm.h> -#include <unistd.h> - -//Qt -#include <QSharedMemory> -#include <QDBusPendingReply> +// #include <sys/ipc.h> +// #include <sys/sem.h> +// #include <sys/shm.h> +// #include <fcntl.h> +// #include <unistd.h> +// #include <sys/mman.h> +// #include <semaphore.h> //SFLPhone #include "video_interface_singleton.h" #include "VideoDevice.h" +#include "Call.h" +#include "CallModel.h" +#include "VideoRenderer.h" //Static member VideoModel* VideoModel::m_spInstance = NULL; -///@namespace ShmManager Low level function to access shared memory -namespace ShmManager { - ///Get the shared memory key - static int getShm(unsigned numBytes, int shmKey) - { - key_t key = shmKey; - int shm_id = shmget(key, numBytes, 0644); - - if (shm_id == -1) - qDebug() << ("shmget"); - - return shm_id; - } - - ///Get the shared buffer - static void * attachShm(int shm_id) - { - void *data = shmat(shm_id, (void *)0, 0); - if (data == (char *)(-1)) { - qDebug() << ("shmat"); - data = NULL; - } - - return data; - } - - ///Detach shared ownership of the buffer - static void detachShm(char *data) - { - /* detach from the segment: */ - if (shmdt(data) == -1) - qDebug() << ("shmdt"); - } - - #if _SEM_SEMUN_UNDEFINED - union semun - { - int val ; /* value for SETVAL */ - struct semid_ds* buf ; /* buffer for IPC_STAT & IPC_SET */ - unsigned short int* array; /* array for GETALL & SETALL */ - struct seminfo* __buf; /* buffer for IPC_INFO */ - }; - #endif - - ///Get the sempahor key - static int get_sem_set(int semKey) - { - int sem_set_id; - key_t key = semKey; - - union semun sem_val; - - sem_set_id = semget(key, 1, 0600); - if (sem_set_id == -1) { - qDebug() << ("semget"); - return sem_set_id; - } - sem_val.val = 0; - semctl(sem_set_id, 0, SETVAL, sem_val); - return sem_set_id; - } - - ///Is a new frame ready to be fetched - static int sem_wait(int sem_set_id) - { - /* structure for semaphore operations. */ - struct sembuf sem_op; - - /* wait on the semaphore, unless it's value is non-negative. */ - sem_op.sem_num = 0; - sem_op.sem_op = -1; - sem_op.sem_flg = IPC_NOWAIT; - return semop(sem_set_id, &sem_op, 1); - } -}; - ///Constructor -VideoModel::VideoModel():m_BufferSize(0),m_ShmKey(0),m_SemKey(0),m_Res(0,0),m_pTimer(0),m_PreviewState(false), -m_Attached(false) +VideoModel::VideoModel():m_BufferSize(0),m_ShmKey(0),m_SemKey(0),m_PreviewState(false) { VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - connect( &interface , SIGNAL(receivingEvent(int,int,int,int,int) ), this, SLOT( receivingEvent(int,int,int,int,int) )); connect( &interface , SIGNAL(deviceEvent() ), this, SLOT( deviceEvent() )); - connect( &interface , SIGNAL(stoppedReceivingEvent(int,int) ), this, SLOT( stoppedReceivingEvent(int,int) )); + connect( &interface , SIGNAL(startedDecoding(QString,QString,int,int)), this, SLOT( startedDecoding(QString,QString,int,int) )); + connect( &interface , SIGNAL(stoppedDecoding(QString,QString) ), this, SLOT( stoppedDecoding(QString,QString ) )); } ///Singleton @@ -130,18 +55,29 @@ VideoModel* VideoModel::getInstance() return m_spInstance; } +///Return the call renderer or nullptr +VideoRenderer* VideoModel::getRenderer(Call* call) +{ + if (!call) return nullptr; + return m_lRenderers[call->getCallId()]; +} + +///Get the video preview renderer +VideoRenderer* VideoModel::getPreviewRenderer() +{ + if (!m_lRenderers["local"]) { + VideoInterface& interface = VideoInterfaceSingleton::getInstance(); + m_lRenderers["local"] = new VideoRenderer("", Resolution(interface.getActiveDeviceSize())); + } + return m_lRenderers["local"]; +} + ///Stop video preview void VideoModel::stopPreview() { VideoInterface& interface = VideoInterfaceSingleton::getInstance(); interface.stopPreview(); m_PreviewState = false; - if (m_pTimer) - m_pTimer->stop(); - if (m_Attached) { - ShmManager::detachShm((char*)m_pBuffer); - m_Attached = false; - } } ///Start video preview @@ -149,22 +85,8 @@ void VideoModel::startPreview() { if (m_PreviewState) return; VideoInterface& interface = VideoInterfaceSingleton::getInstance(); - QDBusPendingReply<int,int,int,int,int> reply = interface.startPreview(); - reply.waitForFinished(); - if (!reply.isError()) { - m_Res.width = reply.argumentAt(0).toInt(); - m_Res.height = reply.argumentAt(1).toInt(); - m_ShmKey = reply.argumentAt(2).toInt(); - m_SemKey = reply.argumentAt(3).toInt(); - m_BufferSize = reply.argumentAt(4).toInt(); - if (!m_pTimer) { - m_pTimer = new QTimer(this); - connect(m_pTimer,SIGNAL(timeout()),this,SLOT(timedEvents())); - } - m_pTimer->setInterval(42); - m_pTimer->start(); - m_PreviewState = true; - } + interface.startPreview(); + m_PreviewState = true; } ///Is the video model fetching preview from a camera @@ -179,61 +101,67 @@ void VideoModel::setBufferSize(uint size) m_BufferSize = size; } -///Event callback -void VideoModel::receivingEvent(int shmKey, int semKey, int videoBufferSize, int destWidth, int destHeight) -{ - m_ShmKey = (uint)shmKey ; - m_ShmKey = (uint)semKey ; - m_BufferSize = videoBufferSize; - m_Res.width = destWidth ; - m_Res.height = destHeight ; - - -} - -///Callback when video is stopped -void VideoModel::stoppedReceivingEvent(int shmKey, int semKey) -{ - m_ShmKey = (uint)shmKey; - m_ShmKey = (uint)semKey; -} - ///Event callback void VideoModel::deviceEvent() { } -///Update the buffer -void VideoModel::timedEvents() -{ - if ( !m_Attached ) { - int shm_id = ShmManager::getShm(m_BufferSize, m_ShmKey); - m_pBuffer = ShmManager::attachShm(shm_id); - m_Attached = true; - m_SetSetId = ShmManager::get_sem_set(m_SemKey); - } +///Return the current resolution +// Resolution VideoModel::getActiveResolution() +// { +// return m_Res; +// } - int ret = ShmManager::sem_wait(m_SetSetId); - if (ret != -1) { - QByteArray array((char*)m_pBuffer,m_BufferSize); - m_Frame.resize(0); - m_Frame = array; - emit frameUpdated(); +///A video is not being rendered +void VideoModel::startedDecoding(QString id, QString shmPath, int width, int height) +{ + Q_UNUSED(id) + qDebug() << "PREVIEW ID" << id; +// m_pRenderer->m_ShmPath = shmPath; +// m_Res.width = width ; +// m_Res.height = height ; +// m_pRenderer->m_Width = width ; +// m_pRenderer->m_Height = height ; +// m_pRenderer->m_isRendering = true; +// m_pRenderer->startShm(); + +// if (!m_pTimer) { +// m_pTimer = new QTimer(this); +// connect(m_pTimer,SIGNAL(timeout()),this,SLOT(timedEvents())); +// m_pTimer->setInterval(42); +// } +// m_pTimer->start(); + + if (m_lRenderers[id] == nullptr ) { + m_lRenderers[id] = new VideoRenderer(shmPath,Resolution(width,height)); } else { - usleep(rand()%100000); //Be sure it can come back in sync + VideoRenderer* renderer = m_lRenderers[id]; + renderer->setShmPath(shmPath); + renderer->setResolution(QSize(width,height)); } -} -///Return the current framerate -QByteArray VideoModel::getCurrentFrame() -{ - return m_Frame; +// if (!m_pRenderer) +// m_pRenderer = m_lRenderers[id]; + + m_lRenderers[id]->startRendering(); + if (id != "local") { + qDebug() << "Starting video for call" << id; + emit videoCallInitiated(m_lRenderers[id]); + } } -///Return the current resolution -Resolution VideoModel::getActiveResolution() +///A video stopped being rendered +void VideoModel::stoppedDecoding(QString id, QString shmPath) { - return m_Res; + Q_UNUSED(shmPath) + if ( m_lRenderers[id] ) + m_lRenderers[id]->stopRendering(); +// m_pRenderer->m_isRendering = false; +// m_pRenderer->stopShm(); + qDebug() << "Video stopped for call" << id; + emit videoStopped(); +// if (m_pTimer) +// m_pTimer->stop(); } \ No newline at end of file diff --git a/kde/src/lib/VideoModel.h b/kde/src/lib/VideoModel.h index ae8f4b9eef8af4535ecdb9cae94120ece7741592..511efaf43a07435c7a30e00777a0ba4c26ca671f 100644 --- a/kde/src/lib/VideoModel.h +++ b/kde/src/lib/VideoModel.h @@ -23,11 +23,13 @@ #include <QtCore/QObject> //Qt -class QSharedMemory; -class QTimer; +#include <QtCore/QHash> //SFLPhone #include "VideoDevice.h" +class VideoRenderer; +class Call; +struct SHMHeader; ///VideoModel: Video event dispatcher class LIB_EXPORT VideoModel : public QObject { @@ -38,8 +40,8 @@ public: //Getters bool isPreviewing (); - QByteArray getCurrentFrame (); - Resolution getActiveResolution(); + VideoRenderer* getRenderer(Call* call); + VideoRenderer* getPreviewRenderer(); //Setters void setBufferSize(uint size); @@ -52,30 +54,31 @@ private: static VideoModel* m_spInstance; //Attributes - bool m_Attached ; - bool m_PreviewState; - uint m_BufferSize ; - uint m_ShmKey ; - uint m_SemKey ; - int m_SetSetId ; - Resolution m_Res ; - QByteArray m_Frame ; - QTimer* m_pTimer ; - void* m_pBuffer ; + bool m_Attached ; + bool m_PreviewState; + uint m_BufferSize ; + uint m_ShmKey ; + uint m_SemKey ; + int m_SetSetId ; + void* m_pBuffer ; + QHash<QString,VideoRenderer*> m_lRenderers; public slots: void stopPreview (); void startPreview(); private slots: - void receivingEvent(int shmKey, int semKey, int videoBufferSize, int destWidth, int destHeight); - void stoppedReceivingEvent(int shmKey, int semKey); + void startedDecoding(QString id, QString shmPath, int width, int height); + void stoppedDecoding(QString id, QString shmPath); void deviceEvent(); - void timedEvents(); signals: ///Emitted when a new frame is ready void frameUpdated(); + ///Emmitted when the video is stopped, before the framebuffer become invalid + void videoStopped(); + ///Emmitted when a call make video available + void videoCallInitiated(VideoRenderer*); }; #endif \ No newline at end of file diff --git a/kde/src/lib/VideoRenderer.cpp b/kde/src/lib/VideoRenderer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9071ae4d0ae527b99d78e99a9b619f0ad102c4f3 --- /dev/null +++ b/kde/src/lib/VideoRenderer.cpp @@ -0,0 +1,311 @@ +/************************************************************************************ + * Copyright (C) 2012 by Savoir-Faire Linux * + * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***********************************************************************************/ +#include "VideoRenderer.h" + +#include <QtCore/QDebug> + +#include <sys/ipc.h> +#include <sys/sem.h> +#include <sys/shm.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <semaphore.h> + +#include <QtCore/QTimer> + +///Shared memory object +struct SHMHeader { + sem_t notification; + sem_t mutex; + + unsigned m_BufferGen; + int m_BufferSize; + + char m_Data[0]; +}; + +///Constructor +VideoRenderer::VideoRenderer(QString shmPath, Resolution res): QObject(0), + m_Width(0), m_Height(0), m_ShmPath(QString()), fd(-1), + m_pShmArea((SHMHeader*)MAP_FAILED), m_ShmAreaLen(0), m_BufferGen(0), + m_isRendering(false),m_pTimer(nullptr),m_Res(res) +{ + m_ShmPath = shmPath ; + m_Width = res.width ; + m_Height = res.height ; +} + +///Destructor +VideoRenderer::~VideoRenderer() +{ + stopShm(); + //delete m_pShmArea; +} + +///Get the data from shared memory and transform it into a QByteArray +QByteArray VideoRenderer::renderToBitmap(QByteArray& data,bool& ok) +{ + if (!m_isRendering) { + return QByteArray(); + } + + if (!shmLock()) { + ok = false; + return QByteArray(); + } + + // wait for a new buffer + while (m_BufferGen == m_pShmArea->m_BufferGen) { + shmUnlock(); + const struct timespec timeout = createTimeout(); + int err = sem_timedwait(&m_pShmArea->notification, &timeout); + // Useful for debugging +// switch (errno ) { +// case EINTR: +// qDebug() << "Unlock failed: Interrupted function call (POSIX.1); see signal(7)"; +// ok = false; +// return QByteArray(); +// break; +// case EINVAL: +// qDebug() << "Unlock failed: Invalid argument (POSIX.1)"; +// ok = false; +// return QByteArray(); +// break; +// case EAGAIN: +// qDebug() << "Unlock failed: Resource temporarily unavailable (may be the same value as EWOULDBLOCK) (POSIX.1)"; +// ok = false; +// return QByteArray(); +// break; +// case ETIMEDOUT: +// qDebug() << "Unlock failed: Connection timed out (POSIX.1)"; +// ok = false; +// return QByteArray(); +// break; +// } + if (err < 0) { + ok = false; + return QByteArray(); + } + + if (!shmLock()) { + ok = false; + return QByteArray(); + } + } + + if (!resizeShm()) { + qDebug() << "Could not resize shared memory"; + ok = false; + return QByteArray(); + } + + if (data.size() != m_pShmArea->m_BufferSize) + data.resize(m_pShmArea->m_BufferSize); + memcpy(data.data(),m_pShmArea->m_Data,m_pShmArea->m_BufferSize); + m_BufferGen = m_pShmArea->m_BufferGen; + shmUnlock(); + return data; +} + +///Connect to the shared memory +bool VideoRenderer::startShm() +{ + if (fd != -1) { + qDebug() << "fd must be -1"; + return false; + } + + fd = shm_open(m_ShmPath.toAscii(), O_RDWR, 0); + if (fd < 0) { + qDebug() << "could not open shm area \"%s\", shm_open failed:%s" << m_ShmPath << strerror(errno); + return false; + } + m_ShmAreaLen = sizeof(SHMHeader); + m_pShmArea = (SHMHeader*) mmap(NULL, m_ShmAreaLen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (m_pShmArea == MAP_FAILED) { + qDebug() << "Could not map shm area, mmap failed"; + return false; + } + return true; +} + +///Disconnect from the shared memory +void VideoRenderer::stopShm() +{ + if (fd >= 0) + close(fd); + fd = -1; + + if (m_pShmArea != MAP_FAILED) + munmap(m_pShmArea, m_ShmAreaLen); + m_ShmAreaLen = 0; + m_pShmArea = (SHMHeader*) MAP_FAILED; +} + +///Resize the shared memory +bool VideoRenderer::resizeShm() +{ + while ((sizeof(SHMHeader) + m_pShmArea->m_BufferSize) > m_ShmAreaLen) { + const size_t new_size = sizeof(SHMHeader) + m_pShmArea->m_BufferSize; + + shmUnlock(); + if (munmap(m_pShmArea, m_ShmAreaLen)) { + qDebug() << "Could not unmap shared area:%s" << strerror(errno); + return false; + } + + m_pShmArea = (SHMHeader*) mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + m_ShmAreaLen = new_size; + + if (!m_pShmArea) { + m_pShmArea = 0; + qDebug() << "Could not remap shared area"; + return false; + } + + m_ShmAreaLen = new_size; + if (!shmLock()) + return false; + } + return true; +} + +///Lock the memory while the copy is being made +bool VideoRenderer::shmLock() +{ + const timespec timeout = createTimeout(); + /* We need an upper limit on how long we'll wait to avoid locking the whole GUI */ + if (sem_timedwait(&m_pShmArea->mutex, &timeout) == ETIMEDOUT) { + qDebug() << "Timed out before shm lock was acquired"; + return false; + } + return true; +} + +///Remove the lock, allow a new frame to be drawn +void VideoRenderer::shmUnlock() +{ + sem_post(&m_pShmArea->mutex); +} + +///Create a SHM timeout +timespec VideoRenderer::createTimeout() +{ + timespec timeout = {0, 0}; + if (clock_gettime(CLOCK_REALTIME, &timeout) == -1) + qDebug() << "clock_gettime"; + timeout.tv_sec += TIMEOUT_SEC; + return timeout; +} + + +/***************************************************************************** + * * + * Slots * + * * + ****************************************************************************/ + +///Update the buffer +void VideoRenderer::timedEvents() +{ + bool ok = true; + QByteArray ba; + renderToBitmap(m_Frame,ok); + if (ok == true) { + emit frameUpdated(); + } + else { + qDebug() << "Frame dropped"; + usleep(rand()%1000); //Be sure it can come back in sync + } +} + +///Start the rendering loop +void VideoRenderer::startRendering() +{ + startShm(); + if (!m_pTimer) { + m_pTimer = new QTimer(this); + connect(m_pTimer,SIGNAL(timeout()),this,SLOT(timedEvents())); + m_pTimer->setInterval(42); + } + m_pTimer->start(); + m_isRendering = true; +} + +///Stop the rendering loop +void VideoRenderer::stopRendering() +{ + m_isRendering = false; + stopShm(); + //qDebug() << "Video stopped for call" << id; + //emit videoStopped(); + if (m_pTimer) + m_pTimer->stop(); +} + + +/***************************************************************************** + * * + * Getters * + * * + ****************************************************************************/ + +///Get the raw bytes directly from the SHM, not recommanded, but optimal +const char* VideoRenderer::rawData() +{ + return m_pShmArea->m_Data; +} + +///Is this redenrer active +bool VideoRenderer::isRendering() +{ + return m_isRendering; +} + +///Return the current framerate +QByteArray VideoRenderer::getCurrentFrame() +{ + return m_Frame; +} + +///Return the current resolution +Resolution VideoRenderer::getActiveResolution() +{ + return m_Res; +} + +/***************************************************************************** + * * + * Setters * + * * + ****************************************************************************/ + +void VideoRenderer::setResolution(QSize size) +{ + m_Res = size; + m_Width = size.width(); + m_Height = size.height(); +} + +void VideoRenderer::setShmPath(QString path) +{ + m_ShmPath = path; +} \ No newline at end of file diff --git a/kde/src/lib/VideoRenderer.h b/kde/src/lib/VideoRenderer.h new file mode 100644 index 0000000000000000000000000000000000000000..bfbfa45ef10762d82ebe80d06645638c39fc756a --- /dev/null +++ b/kde/src/lib/VideoRenderer.h @@ -0,0 +1,93 @@ +/************************************************************************************ + * Copyright (C) 2012 by Savoir-Faire Linux * + * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***********************************************************************************/ +#ifndef VIDEO_RENDERER_H +#define VIDEO_RENDERER_H + +//Base +#include <QtCore/QObject> +#include "typedefs.h" + +//Qt +class QTimer; + +//SFLPhone +#include "VideoDevice.h" +struct SHMHeader; + +///Manage shared memory and convert it to QByteArray +class LIB_EXPORT VideoRenderer : public QObject { + Q_OBJECT + + public: + //Constructor + VideoRenderer (QString shmPath,Resolution res); + ~VideoRenderer(); + + //Mutators + bool resizeShm(); + void stopShm (); + bool startShm (); + + //Getters + QByteArray renderToBitmap(QByteArray& data, bool& ok); + const char* rawData (); + bool isRendering (); + QByteArray getCurrentFrame (); + Resolution getActiveResolution(); + + //Setters + void setResolution(QSize size); + void setShmPath (QString path); + + private: + //Attributes + uint m_Width ; + uint m_Height ; + QString m_ShmPath ; + int fd ; + SHMHeader* m_pShmArea ; + signed int m_ShmAreaLen ; + uint m_BufferGen ; + bool m_isRendering; + QTimer* m_pTimer ; + QByteArray m_Frame ; + Resolution m_Res ; + + //Constants + static const int TIMEOUT_SEC = 1; // 1 second + + //Helpers + timespec createTimeout(); + bool shmLock (); + void shmUnlock (); + + private slots: + void timedEvents(); + + public slots: + void startRendering(); + void stopRendering (); + + signals: + ///Emitted when a new frame is ready + void frameUpdated(); + +}; + +#endif \ No newline at end of file diff --git a/kde/src/lib/dbus/video_controls-introspec.xml b/kde/src/lib/dbus/video_controls-introspec.xml index 6ff150582983ee04b309e840221b231a7c69b051..73f6d7e8d893eb5a7dc0f2c84748810dfcc21c06 100755 --- a/kde/src/lib/dbus/video_controls-introspec.xml +++ b/kde/src/lib/dbus/video_controls-introspec.xml @@ -1,33 +1,37 @@ <?xml version="1.0" ?> <node name="/video_controls-introspec" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> <interface name="org.sflphone.SFLphone.VideoControls"> - <!-- Video device methods --> + <!-- Video device methods --> - <method name="getInputDeviceList" tp:name-for-bindings="getInputDeviceList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="getDeviceList" tp:name-for-bindings="getDeviceList"> + <tp:docstring>Returns a list of the detected v4l2 devices</tp:docstring> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> + <arg type="as" name="list" direction="out"> + </arg> + </method> - <method name="getInputDeviceChannelList" tp:name-for-bindings="getInputDeviceChannelList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="s" name="device" direction="in"> - </arg> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="getDeviceChannelList" tp:name-for-bindings="getDeviceChannelList"> + <tp:docstring>Returns a list of the channels available for a given v4l2 device</tp:docstring> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> + <arg type="s" name="device" direction="in"> + </arg> + <arg type="as" name="list" direction="out"> + </arg> + </method> - <method name="getInputDeviceSizeList" tp:name-for-bindings="getInputDeviceSizeList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="s" name="device" direction="in"> - </arg> - <arg type="s" name="channel" direction="in"> - </arg> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="getDeviceSizeList" tp:name-for-bindings="getDeviceSizeList"> + <tp:docstring>Returns a list of the resolutions available for a given channel of a given v4l2 device</tp:docstring> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> + <arg type="s" name="device" direction="in"> + </arg> + <arg type="s" name="channel" direction="in"> + </arg> + <arg type="as" name="list" direction="out"> + </arg> + </method> - <method name="getInputDeviceRateList" tp:name-for-bindings="getInputDeviceRateList"> + <method name="getDeviceRateList" tp:name-for-bindings="getDeviceRateList"> + <tp:docstring>Returns a list of the framerates available for a given resolution of a given channel of a given v4l2 device</tp:docstring> <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> <arg type="s" name="device" direction="in"> </arg> @@ -37,122 +41,115 @@ </arg> <arg type="as" name="list" direction="out"> </arg> - </method> - - <method name="getInputDevice" tp:name-for-bindings="getInputDevice"> - <arg type="s" name="device" direction="out"> - </arg> - </method> - - <method name="getInputDeviceChannel" tp:name-for-bindings="getInputDeviceChannel"> - <arg type="s" name="channel" direction="out"> - </arg> - </method> + </method> - <method name="getInputDeviceSize" tp:name-for-bindings="getInputDeviceSize"> - <arg type="s" name="size" direction="out"> - </arg> - </method> + <method name="getActiveDevice" tp:name-for-bindings="getActiveDevice"> + <arg type="s" name="device" direction="out"> + </arg> + </method> - <method name="getInputDeviceRate" tp:name-for-bindings="getInputDeviceRate"> - <arg type="s" name="rate" direction="out"> - </arg> - </method> + <method name="getActiveDeviceChannel" tp:name-for-bindings="getActiveDeviceChannel"> + <arg type="s" name="channel" direction="out"> + </arg> + </method> - <method name="setInputDevice" tp:name-for-bindings="setInputDevice"> - <arg type="s" name="device" direction="in"> - </arg> - </method> + <method name="getActiveDeviceSize" tp:name-for-bindings="getActiveDeviceSize"> + <arg type="s" name="size" direction="out"> + </arg> + </method> - <method name="setInputDeviceChannel" tp:name-for-bindings="setInputDeviceChannel"> - <arg type="s" name="channel" direction="in"> - </arg> - </method> + <method name="getActiveDeviceRate" tp:name-for-bindings="getActiveDeviceRate"> + <arg type="s" name="rate" direction="out"> + </arg> + </method> - <method name="setInputDeviceSize" tp:name-for-bindings="setInputDeviceSize"> - <arg type="s" name="size" direction="in"> - </arg> - </method> + <method name="setActiveDevice" tp:name-for-bindings="setActiveDevice"> + <arg type="s" name="device" direction="in"> + </arg> + </method> - <method name="setInputDeviceRate" tp:name-for-bindings="setInputDeviceRate"> - <arg type="s" name="rate" direction="in"> - </arg> - </method> + <method name="setActiveDeviceChannel" tp:name-for-bindings="setActiveDeviceChannel"> + <arg type="s" name="channel" direction="in"> + </arg> + </method> - <!-- Video Codec related methods --> + <method name="setActiveDeviceSize" tp:name-for-bindings="setActiveDeviceSize"> + <arg type="s" name="size" direction="in"> + </arg> + </method> - <method name="getCodecList" tp:name-for-bindings="getCodecList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="setActiveDeviceRate" tp:name-for-bindings="setActiveDeviceRate"> + <arg type="s" name="rate" direction="in"> + </arg> + </method> - <method name="getCodecDetails" tp:name-for-bindings="getCodecDetails"> - <arg type="s" name="codec" direction="in"> - </arg> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="MapStringString"/> - <arg type="a{ss}" name="details" direction="out" tp:type="String_String_Map"> - </arg> - </method> + <!-- Video Codec related methods --> - <method name="getActiveCodecList" tp:name-for-bindings="getActiveCodecList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorString"/> - <arg type="s" name="accountID" direction="in"> - </arg> - <arg type="as" name="list" direction="out"> - </arg> - </method> + <method name="getCodecs" tp:name-for-bindings="getCodecs"> + <tp:docstring>Gets the hashtable describing all the codecs and their parameters for a given account</tp:docstring> + <arg type="s" name="accountID" direction="in"> + </arg> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="VectorMapStringString"/> + <arg type="aa{ss}" name="details" direction="out"> + </arg> + </method> - <method name="setActiveCodecList" tp:name-for-bindings="setActiveCodecList"> - <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="VectorString"/> - <arg type="as" name="list" direction="in"> - </arg> + <method name="setCodecs" tp:name-for-bindings="setCodecs"> + <tp:docstring>Sets a vector of hashtables describing codecs and their parameters for a given account, one hashtable per codec</tp:docstring> <arg type="s" name="accountID" direction="in"> </arg> - </method> - - <method name="startPreview" tp:name-for-bindings="startPreview"> - <arg type="i" name="width" direction="out"> - </arg> - <arg type="i" name="height" direction="out"> - </arg> - <arg type="i" name="shmKey" direction="out"> - </arg> - <arg type="i" name="semKey" direction="out"> + <annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="VectorMapStringString"/> + <arg type="aa{ss}" name="details" direction="in"> </arg> - <arg type="i" name="videoBufferSize" direction="out"> - </arg> - </method> + </method> - <method name="stopPreview" tp:name-for-bindings="stopPreview"> - </method> + <method name="startPreview" tp:name-for-bindings="startPreview"> + <tp:docstring> Starts the video preview, which renders the active v4l2 device's video to shared memory. Useful for testing/debugging camera settings</tp:docstring> + </method> - <signal name="deviceEvent" tp:name-for-bindings="deviceEvent"> - </signal> + <method name="stopPreview" tp:name-for-bindings="stopPreview"> + </method> - <signal name="receivingEvent" tp:name-for-bindings="receivingEvent"> - <arg type="i" name="shmKey"> - </arg> - <arg type="i" name="semKey"> - </arg> - <arg type="i" name="videoBufferSize"> - </arg> - <arg type="i" name="destWidth"> - </arg> - <arg type="i" name="destHeight"> - </arg> - </signal> + <method name="hasPreviewStarted" tp:name-for-bindings="hasPreviewStarted"> + <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="Bool"/> + <arg type="b" name="started" direction="out"> + <tp:docstring>Returns true if the preview has already started, false otherwise</tp:docstring> + </arg> + </method> - <signal name="stoppedReceivingEvent" tp:name-for-bindings="stoppedReceivingEvent"> - <arg type="i" name="shmKey"> - </arg> - <arg type="i" name="semKey"> - </arg> - </signal> + <signal name="deviceEvent" tp:name-for-bindings="deviceEvent"> + <tp:docstring>Signal triggered by changes in the detected v4l2 devices, e.g. a camera being unplugged.</tp:docstring> + </signal> + + <signal name="startedDecoding" tp:name-for-bindings="startedDecoding"> + <tp:docstring>Signal triggered when video is available in a shared memory buffer.</tp:docstring> + <arg type="s" name="id"> + <tp:docstring>The ID of the call associated with the video, or "local" in the case of local video</tp:docstring> + </arg> + <arg type="s" name="shmPath"> + <tp:docstring>The path of the newly created shared memory</tp:docstring> + </arg> + <arg type="i" name="width"> + <tp:docstring>The width of the video in the shared memory</tp:docstring> + </arg> + <arg type="i" name="height"> + <tp:docstring>The height of the video in the shared memory</tp:docstring> + </arg> + </signal> + + <signal name="stoppedDecoding" tp:name-for-bindings="stoppedDecoding"> + <tp:docstring>Signal triggered when video is no longer available in a shared memory buffer.</tp:docstring> + <arg type="s" name="id"> + <tp:docstring>The ID of the call associated with the video, or "local" in the case of local video</tp:docstring> + </arg> + <arg type="s" name="shmPath"> + <tp:docstring>The path of the newly created shared memory</tp:docstring> + </arg> + </signal> <method name="getCurrentCodecName" tp:name-for-bindings="getCurrentCodecName"> <arg type="s" name="callID" direction="in"/> <arg type="s" name="codecName" direction="out"/> </method> - </interface> + </interface> </node> diff --git a/kde/src/lib/sflphone_const.h b/kde/src/lib/sflphone_const.h index c1b79b9098e177c8d743a90f723be2c0029e68ff..06a8d7009879b0c59ac7495821d5f6ffe40b5de2 100644 --- a/kde/src/lib/sflphone_const.h +++ b/kde/src/lib/sflphone_const.h @@ -222,7 +222,6 @@ #define CALL_STATE_CHANGE_BUSY "BUSY" #define CALL_STATE_CHANGE_FAILURE "FAILURE" #define CALL_STATE_CHANGE_UNHOLD_CURRENT "UNHOLD_CURRENT" -#define CALL_STATE_CHANGE_UNHOLD_RECORD "UNHOLD_RECORD" #define CALL_STATE_CHANGE_UNKNOWN "UNKNOWN" #define CONF_STATE_CHANGE_HOLD "HOLD" diff --git a/kde/src/lib/video_interface_singleton.h b/kde/src/lib/video_interface_singleton.h index e594b967642a72aafff4e6548152ab74cbd1f1af..ea6192e0c15d4ea97b2ecd392be3000c971c3334 100644 --- a/kde/src/lib/video_interface_singleton.h +++ b/kde/src/lib/video_interface_singleton.h @@ -17,14 +17,14 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***********************************************************************************/ -#ifndef INSTANCE_INTERFACE_SINGLETON_H -#define INSTANCE_INTERFACE_SINGLETON_H +#ifndef VIDEO_INTERFACE_SINGLETON_H +#define VIDEO_INTERFACE_SINGLETON_H #include "src/lib/video_dbus_interface.h" #include "typedefs.h" /** - * @author Jérémy Quentin <jeremy.quentin@savoirfairelinux.com> + * @author Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> */ class LIB_EXPORT VideoInterfaceSingleton { diff --git a/kde/src/main.cpp b/kde/src/main.cpp index 523a03b2c29f4a5c10c1ea250822720bad5fec3c..b3438f5abe6882fb482e56129e01849fcc347bff 100755 --- a/kde/src/main.cpp +++ b/kde/src/main.cpp @@ -40,7 +40,6 @@ #include "klib/ConfigurationSkeleton.h" #include "CallView.h" #include "SFLPhone.h" -#include "AccountListModel.h" #include "lib/instance_interface_singleton.h" //SFLPhone library @@ -49,7 +48,7 @@ static const char description[] = "A KDE 4 Client for SFLphone"; -static const char version[] = "1.1.0"; +static const char version[] = "1.1.1"; SFLPhoneApplication* app; void quitOnSignal(int signal) diff --git a/kde/src/test/CMakeLists.txt b/kde/src/test/CMakeLists.txt index 47d2f51f86f58dd38660c2905c4198e5d99a9bd6..93cb016032d14d84672a1c5734ebeb532c83377f 100644 --- a/kde/src/test/CMakeLists.txt +++ b/kde/src/test/CMakeLists.txt @@ -38,4 +38,22 @@ target_link_libraries( account ${KDE4_KDEUI_LIBS} ksflphone qtsflphone +) + +#CALL tests +set( call_SRCS call_test.cpp ) + +kde4_add_unit_test( call + TESTNAME "Basic_calls" + ${call_SRCS} +) + + + +target_link_libraries( call + ${KDE4_KDECORE_LIBS} + ${QT_QTTEST_LIBRARY} + ${KDE4_KDEUI_LIBS} + ksflphone + qtsflphone ) \ No newline at end of file diff --git a/kde/src/test/account_test.cpp b/kde/src/test/account_test.cpp index e96961a8daafd89cac7ed6cf5cbe8133c67e0c75..b85c87ae6f549b6a90d9688e8a149c4a2ec02e6a 100644 --- a/kde/src/test/account_test.cpp +++ b/kde/src/test/account_test.cpp @@ -75,6 +75,7 @@ private slots: void testAccountSipStunEnabled (); void testPublishedSameAsLocal (); void testConfigRingtoneEnabled (); + void testDisableAllAccounts (); private: QString id; @@ -144,6 +145,7 @@ void AccountTests::testAccountAlias ()/*QString detail*/ QFETCH(QString, alias); acc->setAccountAlias(alias); + acc->save(); QCOMPARE( acc->getAccountAlias(), alias ); } @@ -152,8 +154,10 @@ void AccountTests::testAccountType ()/*QString detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setAccountType("IAX"); + acc->save(); QCOMPARE( acc->getAccountType(), QString("IAX") ); acc->setAccountType("SIP"); + acc->save(); QCOMPARE( acc->getAccountType(), QString("SIP") ); //Test invalid @@ -176,6 +180,7 @@ void AccountTests::testAccountHostname () Account* acc = AccountList::getInstance()->getAccountById(id); QFETCH(QString, hostname); acc->setAccountHostname(hostname); + acc->save(); QCOMPARE( acc->getAccountHostname(), hostname ); } @@ -197,6 +202,7 @@ void AccountTests::testAccountHostnameInvalid () QFETCH(QString, hostname); Account* acc = AccountList::getInstance()->getAccountById(id); acc->setAccountHostname(hostname); + acc->save(); QVERIFY(acc->getAccountHostname() != hostname); } @@ -204,6 +210,7 @@ void AccountTests::testAccountUsername ()/*QString detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setAccountHostname("1234567879"); + acc->save(); QString username = acc->getAccountHostname(); QCOMPARE(username,QString("1234567879")); @@ -230,6 +237,7 @@ void AccountTests::testAccountMailbox ()/*QString detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setAccountMailbox("1234567879"); + acc->save(); QString mailbox = acc->getAccountMailbox(); QCOMPARE(mailbox,QString("1234567879")); @@ -239,6 +247,7 @@ void AccountTests::testTlsPassword ()/*QString detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setTlsPassword("1234567879"); + acc->save(); QString tlspass = acc->getTlsPassword(); QCOMPARE(tlspass,QString("1234567879")); } @@ -271,6 +280,7 @@ void AccountTests::testTlsServerName ()/*QString detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setTlsServerName("qwerty"); + acc->save(); QString tlsserver = acc->getTlsServerName(); QCOMPARE(tlsserver,QString("qwerty")); } @@ -279,6 +289,7 @@ void AccountTests::testAccountSipStunServer ()/*QString detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setAccountSipStunServer("qwerty"); + acc->save(); QString tlsserver = acc->getAccountSipStunServer(); QCOMPARE(tlsserver,QString("qwerty")); } @@ -331,6 +342,7 @@ void AccountTests::testRingtonePath ()/*QString detail*/ Account* acc = AccountList::getInstance()->getAccountById(id); QFETCH(QString, path); acc->setRingtonePath(path); + acc->save(); if (path.indexOf("invalid") != -1) QCOMPARE(acc->getRingtonePath() == path ,false); else @@ -347,10 +359,12 @@ void AccountTests::testAccountRegistrationExpire ()/*int detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setAccountRegistrationExpire(10000); + acc->save(); QCOMPARE(acc->getAccountRegistrationExpire(),10000); //Time machines are not on the market yet acc->setAccountRegistrationExpire(-10000); + acc->save(); QCOMPARE(acc->getAccountRegistrationExpire() == -10000,false); } @@ -358,11 +372,13 @@ void AccountTests::testTlsNegotiationTimeoutSec ()/*int detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setTlsNegotiationTimeoutSec(10000); + acc->save(); QCOMPARE(acc->getTlsNegotiationTimeoutSec(),10000); //Time machines are not on the market yet acc->setTlsNegotiationTimeoutSec(-10000); + acc->save(); QCOMPARE(acc->getTlsNegotiationTimeoutSec() == -10000,false); } @@ -371,10 +387,12 @@ void AccountTests::testTlsNegotiationTimeoutMsec ()/*int detail*/ Account* acc = AccountList::getInstance()->getAccountById(id); acc->setTlsNegotiationTimeoutMsec(10000); + acc->save(); QCOMPARE(acc->getTlsNegotiationTimeoutMsec(),10000); //Time machines are not on the market yet acc->setTlsNegotiationTimeoutMsec(-10000); + acc->save(); QCOMPARE(acc->getTlsNegotiationTimeoutMsec() == -10000,false); } @@ -395,6 +413,7 @@ void AccountTests::testLocalPort ()/*short detail*/ Account* acc = AccountList::getInstance()->getAccountById(id); QFETCH(int, port); acc->setLocalPort(port); + acc->save(); if (port < 0 || port > 65533) QCOMPARE(acc->getLocalPort() == port,false); else @@ -418,6 +437,7 @@ void AccountTests::testTlsListenerPort ()/*short detail*/ Account* acc = AccountList::getInstance()->getAccountById(id); QFETCH(int, port); acc->setTlsListenerPort(port); + acc->save(); if (port < 0 || port > 65533) QCOMPARE(acc->getTlsListenerPort() == port,false); else @@ -441,6 +461,7 @@ void AccountTests::testPublishedPort ()/*short detail*/ Account* acc = AccountList::getInstance()->getAccountById(id); QFETCH(int, port); acc->setPublishedPort(port); + acc->save(); if (port < 0 || port > 65533) QCOMPARE(acc->getPublishedPort() == port,false); else @@ -472,8 +493,10 @@ void AccountTests::testTlsRequireClientCertificate ()/*bool detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setTlsRequireClientCertificate(true); + acc->save(); QCOMPARE(acc->isTlsRequireClientCertificate(),true); acc->setTlsRequireClientCertificate(false); + acc->save(); QCOMPARE(acc->isTlsRequireClientCertificate(),false); } @@ -481,8 +504,10 @@ void AccountTests::testTlsEnable ()/*bool detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setTlsEnable(true); + acc->save(); QCOMPARE(acc->isTlsEnable(),true); acc->setTlsEnable(false); + acc->save(); QCOMPARE(acc->isTlsEnable(),false); } @@ -490,8 +515,10 @@ void AccountTests::testAccountDisplaySasOnce ()/*bool detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setAccountDisplaySasOnce(true); + acc->save(); QCOMPARE(acc->isAccountDisplaySasOnce(),true); acc->setAccountDisplaySasOnce(false); + acc->save(); QCOMPARE(acc->isAccountDisplaySasOnce(),false); } @@ -499,8 +526,10 @@ void AccountTests::testAccountSrtpRtpFallback ()/*bool detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setAccountSrtpRtpFallback(true); + acc->save(); QCOMPARE(acc->isAccountSrtpRtpFallback(),true); acc->setAccountSrtpRtpFallback(false); + acc->save(); QCOMPARE(acc->isAccountSrtpRtpFallback(),false); } @@ -508,8 +537,10 @@ void AccountTests::testAccountZrtpDisplaySas ()/*bool detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setAccountSrtpRtpFallback(true); + acc->save(); QCOMPARE(acc->isAccountSrtpRtpFallback(),true); acc->setAccountSrtpRtpFallback(false); + acc->save(); QCOMPARE(acc->isAccountSrtpRtpFallback(),false); } @@ -517,8 +548,10 @@ void AccountTests::testAccountZrtpNotSuppWarning ()/*bool detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setAccountZrtpNotSuppWarning(true); + acc->save(); QCOMPARE(acc->isAccountZrtpNotSuppWarning(),true); acc->setAccountZrtpNotSuppWarning(false); + acc->save(); QCOMPARE(acc->isAccountZrtpNotSuppWarning(),false); } @@ -526,8 +559,10 @@ void AccountTests::testAccountZrtpHelloHash ()/*bool detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setAccountZrtpHelloHash(true); + acc->save(); QCOMPARE(acc->isAccountZrtpHelloHash(),true); acc->setAccountZrtpHelloHash(false); + acc->save(); QCOMPARE(acc->isAccountZrtpHelloHash(),false); } @@ -535,8 +570,10 @@ void AccountTests::testAccountSipStunEnabled ()/*bool detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setAccountSipStunEnabled(true); + acc->save(); QCOMPARE(acc->isAccountSipStunEnabled(),true); acc->setAccountSipStunEnabled(false); + acc->save(); QCOMPARE(acc->isAccountSipStunEnabled(),false); } @@ -544,8 +581,10 @@ void AccountTests::testPublishedSameAsLocal ()/*bool detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setPublishedSameAsLocal(true); + acc->save(); QCOMPARE(acc->isPublishedSameAsLocal(),true); acc->setPublishedSameAsLocal(false); + acc->save(); QCOMPARE(acc->isPublishedSameAsLocal(),false); } @@ -553,17 +592,42 @@ void AccountTests::testConfigRingtoneEnabled ()/*bool detail*/ { Account* acc = AccountList::getInstance()->getAccountById(id); acc->setRingtoneEnabled(true); + acc->save(); QCOMPARE(acc->isRingtoneEnabled(),true); acc->setRingtoneEnabled(false); + acc->save(); QCOMPARE(acc->isRingtoneEnabled(),false); } //END Testing every account attributes +//BEGIN testing account list + +void AccountTests::testDisableAllAccounts() +{ + QList<bool> saveState; + //Disable all accounts + for (int i=0;i<AccountList::getInstance()->size();i++) { + saveState << (*AccountList::getInstance())[i]->isAccountEnabled(); + (*AccountList::getInstance())[i]->setAccountEnabled(false); + (*AccountList::getInstance())[i]->save(); + } + + QCOMPARE(AccountList::getCurrentAccount(),(Account*)NULL); + + //Restore state + for (int i=0;i<AccountList::getInstance()->size();i++) { + (*AccountList::getInstance())[i]->setAccountEnabled(saveState[i]); + (*AccountList::getInstance())[i]->save(); + } +} + +//END testing account list + //BEGIN cleanup void AccountTests::cleanupTestCase() { - //AccountList::getInstance()->removeAccount(AccountList::getInstance()->getAccountById(id)); - //QCOMPARE( AccountList::getInstance()->getAccountById(id) == nullptr, true); + AccountList::getInstance()->removeAccount(AccountList::getInstance()->getAccountById(id)); + QCOMPARE( AccountList::getInstance()->getAccountById(id) == nullptr, true); } //END cleanup diff --git a/kde/src/test/call_test.cpp b/kde/src/test/call_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..164eca30233222551794c919951467300d7d39b9 --- /dev/null +++ b/kde/src/test/call_test.cpp @@ -0,0 +1,45 @@ +#include <QString> +#include <QtTest> +#include <QtCore> + +#include "../src/lib/configurationmanager_interface_singleton.h" +#include "../src/lib/callmanager_interface_singleton.h" +#include "../src/lib/instance_interface_singleton.h" +#include "../src/lib/AccountList.h" +#include "../src/lib/CallModel.h" + +CallModel<>* m_pModel = new CallModel<>(); + +class CallTests: public QObject +{ + Q_OBJECT +private slots: + void testCallWithoutAccounts(); +private: + +}; + +///When there is no accounts, no call should be created +void CallTests::testCallWithoutAccounts() +{ + QMap<Account*,bool> saveState; + //Disable all accounts + for (int i=0;i<AccountList::getInstance()->size();i++) { + saveState[(*AccountList::getInstance())[i]] = (*AccountList::getInstance())[i]->isAccountEnabled(); + qDebug() << "Disabling" << (*AccountList::getInstance())[i]->getAccountId(); + (*AccountList::getInstance())[i]->setAccountEnabled(false); + (*AccountList::getInstance())[i]->save(); + } + + Call* call = m_pModel->addDialingCall("test call", AccountList::getCurrentAccount()); + QCOMPARE( call, (Call*)NULL ); + + //Restore state + for (int i=0;i<AccountList::getInstance()->size();i++) { + (*AccountList::getInstance())[i]->setAccountEnabled(saveState[(*AccountList::getInstance())[i]]); + (*AccountList::getInstance())[i]->save(); + } +} + +QTEST_MAIN(CallTests) +#include "call_test.moc" diff --git a/kde/src/test/dbus_test.cpp b/kde/src/test/dbus_test.cpp index 11102e2f009995d043732f0d77155e4322dd8d0d..c4d081452d8b1223756b6a0a93e97c1c168cf0cd 100644 --- a/kde/src/test/dbus_test.cpp +++ b/kde/src/test/dbus_test.cpp @@ -10,48 +10,11 @@ class DBusTests: public QObject { Q_OBJECT private slots: - /*void testValidity(); - void testMonth_data(); - void testMonth();*/ void testConfigurationManagerConnection(); void testCallManagerConnection(); void testInstanceManagerConnection(); }; -/*void DBusTests::testValidity() -{ - QDate date( 1967, 3, 12 ); - QVERIFY( date.isValid() ); -} - -void DBusTests::testMonth_data() -{ - QTest::addColumn<int>("year"); // the year we are testing - QTest::addColumn<int>("month"); // the month we are testing - QTest::addColumn<int>("day"); // the day we are testing - QTest::addColumn<QString>("monthName"); // the name of the month - - QTest::newRow("1967/3/11") << 1967 << 3 << 11 << QString("March"); - QTest::newRow("1966/1/10") << 1966 << 1 << 10 << QString("January"); - QTest::newRow("1999/9/19") << 1999 << 9 << 19 << QString("September"); - // more rows of dates can go in here... -} - -void DBusTests::testMonth() -{ - QFETCH(int, year); - QFETCH(int, month); - QFETCH(int, day); - QFETCH(QString, monthName); - - - - QDate date; - date.setYMD( year, month, day); - QCOMPARE( date.month(), month ); - QCOMPARE( QDate::longMonthName(date.month()), monthName ); -}*/ - void DBusTests::testConfigurationManagerConnection() { ConfigurationManagerInterface& configurationManager = ConfigurationManagerInterfaceSingleton::getInstance(); diff --git a/kde/src/widgets/AcceleratedVideoWidget.cpp b/kde/src/widgets/AcceleratedVideoWidget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3c3dd2dc883bbd53290d8770dba250b69fb690af --- /dev/null +++ b/kde/src/widgets/AcceleratedVideoWidget.cpp @@ -0,0 +1,529 @@ +/*************************************************************************** + * Copyright (C) 2009-2012 by Savoir-Faire Linux * + * Author : Emmanuel Lepage Valle <emmanuel.lepage@savoirfairelinux.com >* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + **************************************************************************/ +#include "AcceleratedVideoWidget.h" +#include <QPixmap> +#include <KDebug> +#include "../lib/VideoModel.h" +#include "../lib/VideoRenderer.h" + + + +#include <QtGui/QImage> + #include <QtCore/QPropertyAnimation> + +static const qreal FACE_SIZE = 0.4; + +static const qreal speeds[] = { 1.8f, 2.4f, 3.6f }; +static const qreal amplitudes[] = { 2.0f, 2.5f, 3.0f }; + +static inline void qSetColor(float colorVec[], QColor c) +{ + colorVec[0] = c.redF(); + colorVec[1] = c.greenF(); + colorVec[2] = c.blueF(); + colorVec[3] = c.alphaF(); +} + +int Geometry::append(const QVector3D &a, const QVector3D &n, const QVector2D &t) +{ + int v = vertices.count(); + vertices.append(a); + normals.append(n); + texCoords.append(t); + faces.append(v); + colors.append(QVector4D(0.6f, 0.6f, 0.6f, 1.0f)); + return v; +} + +void Geometry::addQuad(const QVector3D &a, const QVector3D &b, + const QVector3D &c, const QVector3D &d, + const QVector<QVector2D> &tex) +{ + QVector3D norm = QVector3D::normal(a, b, c); + // append first triangle + int aref = append(a, norm, tex[0]); + append(b, norm, tex[1]); + int cref = append(c, norm, tex[2]); + // append second triangle + faces.append(aref); + faces.append(cref); + append(d, norm, tex[3]); +} + +void Geometry::loadArrays() const +{ + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, vertices.constData()); + glNormalPointer(GL_FLOAT, 0, normals.constData()); + glTexCoordPointer(2, GL_FLOAT, 0, texCoords.constData()); + glColorPointer(4, GL_FLOAT, 0, colors.constData()); +} + +void Geometry::setColors(int start, GLfloat colorArray[4][4]) +{ + int off = faces[start]; + for (int i = 0; i < 4; ++i) + colors[i + off] = QVector4D(colorArray[i][0], + colorArray[i][1], + colorArray[i][2], + colorArray[i][3]); +} + +Tile::Tile(const QVector3D &loc) + : location(loc) + , start(0) + , count(0) + , useFlatColor(false) + , geom(0) +{ + qSetColor(faceColor, QColor(Qt::darkGray)); +} + +void Tile::setColors(GLfloat colorArray[4][4]) +{ + useFlatColor = true; + geom->setColors(start, colorArray); +} + +static inline void qMultMatrix(const QMatrix4x4 &mat) +{ + if (sizeof(qreal) == sizeof(GLfloat)) + glMultMatrixf((GLfloat*)mat.constData()); +#ifndef QT_OPENGL_ES + else if (sizeof(qreal) == sizeof(GLdouble)) + glMultMatrixd((GLdouble*)mat.constData()); +#endif + else + { + GLfloat fmat[16]; + qreal const *r = mat.constData(); + for (int i = 0; i < 16; ++i) + fmat[i] = r[i]; + glMultMatrixf(fmat); + } +} + +void Tile::draw() const +{ + QMatrix4x4 mat; + mat.translate(location); + mat.rotate(orientation); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + qMultMatrix(mat); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, faceColor); + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, geom->indices() + start); + glPopMatrix(); +} + +TileBuilder::TileBuilder(Geometry *g, qreal depth, qreal size) + : verts(4) + , tex(4) + , start(g->count()) + , count(0) + , geom(g) +{ + // front face - make a square with bottom-left at origin + verts[br].setX(size); + verts[tr].setX(size); + verts[tr].setY(size); + verts[tl].setY(size); + + // these vert numbers are good for the tex-coords + for (int i = 0; i < 4; ++i) + tex[i] = verts[i].toVector2D(); + + // now move verts half cube width across so cube is centered on origin + for (int i = 0; i < 4; ++i) + verts[i] -= QVector3D(size / 2.0f, size / 2.0f, -depth); + + // add the front face + g->addQuad(verts[bl], verts[br], verts[tr], verts[tl], tex); + + count = g->count() - start; +} + +void TileBuilder::initialize(Tile *tile) const +{ + tile->start = start; + tile->count = count; + tile->geom = geom; + qSetColor(tile->faceColor, color); +} + +Tile *TileBuilder::newTile(const QVector3D &loc) const +{ + Tile *tile = new Tile(loc); + initialize(tile); + return tile; +} + +Cube::Cube(const QVector3D &loc) + : Tile(loc) + , rot(0.0f) + , r(0), a(0) +{ +} + +Cube::~Cube() +{ +} + +void Cube::setAltitude(qreal a) +{ + if (location.y() != a) + { + location.setY(a); + emit changed(); + } +} + +void Cube::setRange(qreal r) +{ + if (location.x() != r) + { + location.setX(r); + emit changed(); + } +} + +void Cube::setRotation(qreal r) +{ + if (r != rot) + { + orientation = QQuaternion::fromAxisAndAngle(QVector3D(1.0f, 1.0f, 1.0f), r); + emit changed(); + } +} + +void Cube::removeBounce() +{ + delete a; + a = 0; + delete r; + r = 0; +} + +void Cube::startAnimation() +{ + if (r) + { + r->start(); + r->setCurrentTime(startx); + } + if (a) + a->start(); + if (rtn) + rtn->start(); +} + +void Cube::setAnimationPaused(bool paused) +{ + if (paused) + { + if (r) + r->pause(); + if (a) + a->pause(); + if (rtn) + rtn->pause(); + } + else + { + if (r) + r->resume(); + if (a) + a->resume(); + if (rtn) + rtn->resume(); + } +} + +CubeBuilder::CubeBuilder(Geometry *g, qreal depth, qreal size) + : TileBuilder(g, depth) + , ix(0) +{ + for (int i = 0; i < 4; ++i) + verts[i].setZ(size / 2.0f); + // back face - "extrude" verts down + QVector<QVector3D> back(verts); + for (int i = 0; i < 4; ++i) + back[i].setZ(-size / 2.0f); + + // add the back face + g->addQuad(back[br], back[bl], back[tl], back[tr], tex); + + // add the sides + g->addQuad(back[bl], back[br], verts[br], verts[bl], tex); + g->addQuad(back[br], back[tr], verts[tr], verts[br], tex); + g->addQuad(back[tr], back[tl], verts[tl], verts[tr], tex); + g->addQuad(back[tl], back[bl], verts[bl], verts[tl], tex); + + count = g->count() - start; +} + +Cube *CubeBuilder::newCube(const QVector3D &loc) const +{ + Cube *c = new Cube(loc); + initialize(c); + qreal d = 4000.0f; + qreal d3 = d / 3.0f; + // Animate movement from left to right + c->r = new QPropertyAnimation(c, "range"); + c->r->setStartValue(-1.3f); + c->r->setEndValue(1.3f); + c->startx = ix * d3 * 3.0f; + c->r->setDuration(d * 4.0f); + c->r->setLoopCount(-1); + c->r->setEasingCurve(QEasingCurve(QEasingCurve::CosineCurve)); + // Animate movement from bottom to top + c->a = new QPropertyAnimation(c, "altitude"); + c->a->setEndValue(loc.y()); + c->a->setStartValue(loc.y() + amplitudes[ix]); + c->a->setDuration(d / speeds[ix]); + c->a->setLoopCount(-1); + c->a->setEasingCurve(QEasingCurve(QEasingCurve::CosineCurve)); + // Animate rotation + c->rtn = new QPropertyAnimation(c, "rotation"); + c->rtn->setStartValue(c->rot); + c->rtn->setEndValue(359.0f); + c->rtn->setDuration(d * 2.0f); + c->rtn->setLoopCount(-1); + c->rtn->setDuration(d / 2); + ix = (ix + 1) % 3; + return c; +} + + +void AcceleratedVideoWidget::newFrameEvent() +{ + qDebug() << "New frame event"; + QSize size(VideoModel::getInstance()->getRenderer()->getActiveResolution().width, VideoModel::getInstance()->getRenderer()->getActiveResolution().height); + m_Image = QImage((uchar*)VideoModel::getInstance()->getRenderer()->rawData() , size.width(), size.height(), QImage::Format_ARGB32 ); + paintGL(); +} + +static GLfloat colorArray[][4] = { + {0.243f , 0.423f , 0.125f , 1.0f}, + {0.176f , 0.31f , 0.09f , 1.0f}, + {0.4f , 0.69f , 0.212f , 1.0f}, + {0.317f , 0.553f , 0.161f , 1.0f} + }; + + AcceleratedVideoWidget::AcceleratedVideoWidget(QWidget *parent) + : QGLWidget(QGLFormat(QGL::SampleBuffers), parent) + , geom(0) + , cube(0) + { + // create the pbuffer + QGLFormat pbufferFormat = format(); + pbufferFormat.setSampleBuffers(false); + pbuffer = new QGLPixelBuffer(QSize(512, 512), pbufferFormat, this); + setWindowTitle(tr("OpenGL pbuffers")); + initializeGeometry(); + connect(VideoModel::getInstance(),SIGNAL(frameUpdated()),this,SLOT(newFrameEvent())); + } + + AcceleratedVideoWidget::~AcceleratedVideoWidget() + { + pbuffer->releaseFromDynamicTexture(); + glDeleteTextures(1, &dynamicTexture); + delete pbuffer; + + qDeleteAll(cubes); + qDeleteAll(tiles); + delete cube; + } + + void AcceleratedVideoWidget::initializeGL() + { + initCommon(); + glShadeModel(GL_SMOOTH); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + static GLfloat lightPosition[4] = { 0.5, 5.0, 7.0, 1.0 }; + glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); + initPbuffer(); + cube->startAnimation(); + connect(cube, SIGNAL(changed()), this, SLOT(update())); + for (int i = 0; i < 3; ++i) + { + cubes[i]->startAnimation(); + connect(cubes[i], SIGNAL(changed()), this, SLOT(update())); + } + } + + void AcceleratedVideoWidget::paintGL() + { + QSize size(VideoModel::getInstance()->getRenderer()->getActiveResolution().width, VideoModel::getInstance()->getRenderer()->getActiveResolution().height); + if (size != minimumSize()) + setMinimumSize(size); + + pbuffer->makeCurrent(); + drawPbuffer(); + // On direct render platforms, drawing onto the pbuffer context above + // automatically updates the dynamic texture. For cases where rendering + // directly to a texture is not supported, explicitly copy. + if (!hasDynamicTextureUpdate) + pbuffer->updateDynamicTexture(dynamicTexture); + makeCurrent(); + + // Use the pbuffer as a texture to render the scene + glBindTexture(GL_TEXTURE_2D, dynamicTexture); + + // set up to render the scene + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + glTranslatef(0.0f, 0.0f, -10.0f); + + // draw the background + glPushMatrix(); + glScalef(aspect, 1.0f, 1.0f); + for (int i = 0; i < tiles.count(); ++i) + tiles[i]->draw(); + glPopMatrix(); + + // draw the bouncing cubes + for (int i = 0; i < cubes.count(); ++i) + cubes[i]->draw(); + } + + void AcceleratedVideoWidget::initializeGeometry() + { + geom = new Geometry(); + CubeBuilder cBuilder(geom, 0.5); + cBuilder.setColor(QColor(255, 255, 255, 212)); + // build the 3 bouncing, spinning cubes + for (int i = 0; i < 3; ++i) + cubes.append(cBuilder.newCube(QVector3D((float)(i-1), -1.5f, 5 - i))); + + // build the spinning cube which goes in the dynamic texture + cube = cBuilder.newCube(); + cube->removeBounce(); + + // build the background tiles + TileBuilder tBuilder(geom); + tBuilder.setColor(QColor(Qt::white)); + for (int c = -2; c <= +2; ++c) + for (int r = -2; r <= +2; ++r) + tiles.append(tBuilder.newTile(QVector3D(c, r, 0))); + + // graded backdrop for the pbuffer scene + TileBuilder bBuilder(geom, 0.0f, 2.0f); + bBuilder.setColor(QColor(102, 176, 54, 210)); + backdrop = bBuilder.newTile(QVector3D(0.0f, 0.0f, -1.5f)); + backdrop->setColors(colorArray); + } + + void AcceleratedVideoWidget::initCommon() + { + qglClearColor(QColor(Qt::darkGray)); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glEnable(GL_MULTISAMPLE); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + glEnable(GL_TEXTURE_2D); + + geom->loadArrays(); + } + + void AcceleratedVideoWidget::perspectiveProjection() + { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + #ifdef QT_OPENGL_ES + glFrustumf(-aspect, +aspect, -1.0, +1.0, 4.0, 15.0); + #else + glFrustum(-aspect, +aspect, -1.0, +1.0, 4.0, 15.0); + #endif + glMatrixMode(GL_MODELVIEW); + } + + void AcceleratedVideoWidget::orthographicProjection() + { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + #ifdef QT_OPENGL_ES + glOrthof(-1.0, +1.0, -1.0, +1.0, -90.0, +90.0); + #else + glOrtho(-1.0, +1.0, -1.0, +1.0, -90.0, +90.0); + #endif + glMatrixMode(GL_MODELVIEW); + } + + void AcceleratedVideoWidget::resizeGL(int width, int height) + { + glViewport(0, 0, width, height); + aspect = (qreal)width / (qreal)(height ? height : 1); + perspectiveProjection(); + } + +void AcceleratedVideoWidget::drawPbuffer() +{ + cubeTexture = bindTexture(m_Image); + //initPbuffer(); + + orthographicProjection(); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glDisable(GL_TEXTURE_2D); + backdrop->draw(); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, cubeTexture); + glDisable(GL_CULL_FACE); + cube->draw(); + glEnable(GL_CULL_FACE); + + glFlush(); +} + + void AcceleratedVideoWidget::initPbuffer() + { + pbuffer->makeCurrent(); + +// cubeTexture = bindTexture(QImage("/home/lepagee/ccu_12.png")); + cubeTexture = bindTexture(m_Image); + + initCommon(); + + // generate a texture that has the same size/format as the pbuffer + dynamicTexture = pbuffer->generateDynamicTexture(); + + // bind the dynamic texture to the pbuffer - this is a no-op under X11 + hasDynamicTextureUpdate = pbuffer->bindToDynamicTexture(dynamicTexture); + makeCurrent(); + } + + void AcceleratedVideoWidget::setAnimationPaused(bool enable) + { + cube->setAnimationPaused(enable); + for (int i = 0; i < 3; ++i) + cubes[i]->setAnimationPaused(enable); + } \ No newline at end of file diff --git a/kde/src/widgets/AcceleratedVideoWidget.h b/kde/src/widgets/AcceleratedVideoWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..1d80f1030c7a7f340e4452a0cb1d6d4caad8f73f --- /dev/null +++ b/kde/src/widgets/AcceleratedVideoWidget.h @@ -0,0 +1,181 @@ +/*************************************************************************** + * Copyright (C) 2009-2012 by Savoir-Faire Linux * + * Author : Emmanuel Lepage Valle <emmanuel.lepage@savoirfairelinux.com >* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + **************************************************************************/ +#ifndef ACCELERATEDVIDEOWIDGET_H +#define ACCELERATEDVIDEOWIDGET_H +#include <QGLPixelBuffer> +#include <QGLWidget> +#include <QPixmap> + + #include <QtOpenGL/qgl.h> + #include <QtCore/qvector.h> + #include <QtGui/qmatrix4x4.h> + #include <QtGui/qvector3d.h> + #include <QtGui/qvector2d.h> + + +class Geometry; +//class Cube; +class Tile; + + + +class QPropertyAnimation; + +class Geometry +{ +public: + void loadArrays() const; + void addQuad(const QVector3D &a, const QVector3D &b, + const QVector3D &c, const QVector3D &d, + const QVector<QVector2D> &tex); + void setColors(int start, GLfloat colors[4][4]); + const GLushort *indices() const { return faces.constData(); } + int count() const { return faces.count(); } +private: + QVector<GLushort> faces; + QVector<QVector3D> vertices; + QVector<QVector3D> normals; + QVector<QVector2D> texCoords; + QVector<QVector4D> colors; + int append(const QVector3D &a, const QVector3D &n, const QVector2D &t); + void addTri(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &n); + friend class Tile; +}; + +class Tile +{ +public: + void draw() const; + void setColors(GLfloat[4][4]); +protected: + Tile(const QVector3D &loc = QVector3D()); + QVector3D location; + QQuaternion orientation; +private: + int start; + int count; + bool useFlatColor; + GLfloat faceColor[4]; + Geometry *geom; + friend class TileBuilder; +}; + +class TileBuilder +{ +public: + enum { bl, br, tr, tl }; + TileBuilder(Geometry *, qreal depth = 0.0f, qreal size = 1.0f); + Tile *newTile(const QVector3D &loc = QVector3D()) const; + void setColor(QColor c) { color = c; } +protected: + void initialize(Tile *) const; + QVector<QVector3D> verts; + QVector<QVector2D> tex; + int start; + int count; + Geometry *geom; + QColor color; +}; + +class Cube : public QObject, public Tile +{ + Q_OBJECT + Q_PROPERTY(qreal range READ range WRITE setRange) + Q_PROPERTY(qreal altitude READ altitude WRITE setAltitude) + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation) +public: + Cube(const QVector3D &loc = QVector3D()); + ~Cube(); + qreal range() { return location.x(); } + void setRange(qreal r); + qreal altitude() { return location.y(); } + void setAltitude(qreal a); + qreal rotation() { return rot; } + void setRotation(qreal r); + void removeBounce(); + void startAnimation(); + void setAnimationPaused(bool paused); +signals: + void changed(); +private: + qreal rot; + QPropertyAnimation *r; + QPropertyAnimation *a; + QPropertyAnimation *rtn; + qreal startx; + friend class CubeBuilder; +}; + +class CubeBuilder : public TileBuilder +{ +public: + CubeBuilder(Geometry *, qreal depth = 0.0f, qreal size = 1.0f); + Cube *newCube(const QVector3D &loc = QVector3D()) const; +private: + mutable int ix; +}; + + + + + + +///Hardware accelerated version of VideoWidget +class AcceleratedVideoWidget : public QGLWidget +{ + Q_OBJECT +public: + AcceleratedVideoWidget(QWidget* parent); + ~AcceleratedVideoWidget(); + +private: + QGLPixelBuffer* m_pPixBuf; + QImage m_Image; + + qreal aspect; + GLuint dynamicTexture; + GLuint cubeTexture; + bool hasDynamicTextureUpdate; + QGLPixelBuffer *pbuffer; + Geometry *geom; + Cube *cube; + Tile *backdrop; + QList<Cube *> cubes; + QList<Tile *> tiles; + + void initializeGeometry(); + void initPbuffer(); + void initCommon(); + void perspectiveProjection(); + void orthographicProjection(); + void drawPbuffer(); + void setAnimationPaused(bool enable); + +protected: + void initializeGL(); + void resizeGL(int w, int h); + void paintGL(); + void mousePressEvent(QMouseEvent *) { setAnimationPaused(true); } + void mouseReleaseEvent(QMouseEvent *) { setAnimationPaused(false); } + +private slots: + void newFrameEvent(); +}; +#endif \ No newline at end of file diff --git a/kde/src/widgets/AccountItemWidget.cpp b/kde/src/widgets/AccountItemWidget.cpp deleted file mode 100755 index d5c4fc73a7afd52cbf9b30bff5c76d8e17aef0eb..0000000000000000000000000000000000000000 --- a/kde/src/widgets/AccountItemWidget.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009-2012 by Savoir-Faire Linux * - * Author : Jérémy Quentin <jeremy.quentin@savoirfairelinux.com> * - * Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com>* - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - **************************************************************************/ - -//Parent -#include "AccountItemWidget.h" - -//Qt -#include <QtGui/QHBoxLayout> -#include <QtGui/QCheckBox> -#include <QtGui/QLabel> -#include <QtGui/QIcon> - -//KDE -#include <KDebug> -#include <KLed> - -//SFLPhone library -#include "lib/sflphone_const.h" - -///Constructor -AccountItemWidget::AccountItemWidget(QWidget *parent) - : QWidget(parent),m_pLed(nullptr),m_pCheckBox(nullptr),m_pTextLabel(nullptr) -{ - m_pCheckBox = new QCheckBox(this); - m_pCheckBox->setObjectName("m_pCheckBox"); - - m_pLed = new QLabel(); - m_pLed->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); - m_pTextLabel = new QLabel(); - - QSpacerItem* horizontalSpacer = new QSpacerItem(16777215, 20, QSizePolicy::Preferred, QSizePolicy::Minimum); - QHBoxLayout* hlayout = new QHBoxLayout(); - hlayout->setContentsMargins( 0,0,0,0 ); - hlayout->addWidget ( m_pCheckBox ); - hlayout->addWidget ( m_pTextLabel ); - hlayout->addItem ( horizontalSpacer ); - hlayout->addWidget ( m_pLed ); - - this->setLayout(hlayout); - m_State = Unregistered; - m_Enabled = false; - updateDisplay(); - - QMetaObject::connectSlotsByName(this); -} //AccountItemWidget - -///Destructor -AccountItemWidget::~AccountItemWidget() -{ - disconnect(); - if (m_pLed) delete m_pLed ; - if (m_pCheckBox) delete m_pCheckBox ; - if (m_pTextLabel) delete m_pTextLabel; -} - - -/***************************************************************************** - * * - * Mutator * - * * - ****************************************************************************/ - -///Update the LED widget color -void AccountItemWidget::updateStateDisplay() -{ - switch(m_State) { - case Registered: - if (m_pLed) m_pLed->setPixmap(QPixmap(ICON_ACCOUNT_LED_GREEN)); - break; - case Unregistered: - if (m_pLed) m_pLed->setPixmap(QPixmap(ICON_ACCOUNT_LED_GRAY)); - break; - case NotWorking: - if (m_pLed) m_pLed->setPixmap(QPixmap(ICON_ACCOUNT_LED_RED)); - break; - default: - kDebug() << "Calling AccountItemWidget::setState with value " << m_State << ", not part of enum AccountItemWidget::State."; - } -} //updateStateDisplay - -///If this item is enable or not -void AccountItemWidget::updateEnabledDisplay() -{ - m_pCheckBox->setCheckState(m_Enabled ? Qt::Checked : Qt::Unchecked); -} - -///Update the widget -void AccountItemWidget::updateDisplay() -{ - updateStateDisplay(); - updateEnabledDisplay(); -} - - -/***************************************************************************** - * * - * Setters * - * * - ****************************************************************************/ - -///Set the model state of the widget -void AccountItemWidget::setState(int state) -{ - m_State = state; - updateStateDisplay(); -} - -///If this widget is enabled or not -void AccountItemWidget::setEnabled(bool enabled) -{ - m_Enabled = enabled; - updateEnabledDisplay(); -} - -///Set the widget text -void AccountItemWidget::setAccountText(const QString& text) -{ - this->m_pTextLabel->setText(text); -} - -///Is this widget enabled -bool AccountItemWidget::getEnabled() -{ - return m_pCheckBox->checkState(); -} - - -/***************************************************************************** - * * - * SLOTS * - * * - ****************************************************************************/ - -///Model state changed -void AccountItemWidget::on_m_pCheckBox_stateChanged(int state) -{ - emit checkStateChanged(state == Qt::Checked); -} diff --git a/kde/src/widgets/AccountItemWidget.h b/kde/src/widgets/AccountItemWidget.h deleted file mode 100755 index 0958a86ac4da22eaaa51392d0f8e5c049ae492d3..0000000000000000000000000000000000000000 --- a/kde/src/widgets/AccountItemWidget.h +++ /dev/null @@ -1,77 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009-2012 by Savoir-Faire Linux * - * Author : Jérémy Quentin <jeremy.quentin@savoirfairelinux.com> * - * Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com>* - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef ACCOUNTITEMWIDGET_H -#define ACCOUNTITEMWIDGET_H - -//Base -#include <QWidget> - -//Qt -class QCheckBox; -class QLabel; - - -///AccountItemWidget: Widget for the config dialog account list -class AccountItemWidget : public QWidget -{ -Q_OBJECT - -public: - //Enum - enum State {Registered, Unregistered, NotWorking}; - - //Constructors & Destructors - AccountItemWidget(QWidget *parent = 0); - ~AccountItemWidget(); - - //Getters - int getState (); - bool getEnabled(); - - //Setters - void setState (int state ); - void setEnabled (bool enabled ); - void setAccountText (const QString& text ); - - //Updates - void updateStateDisplay (); - void updateEnabledDisplay (); - void updateDisplay (); - -private: - //Attributes - int m_State ; - bool m_Enabled ; - QLabel* m_pLed ; - QCheckBox* m_pCheckBox ; - QLabel* m_pTextLabel; - -private slots: - void on_m_pCheckBox_stateChanged(int state); - -signals: - ///Emitted when the account is checked - void checkStateChanged(bool checked); - -}; - -#endif diff --git a/kde/src/widgets/BookmarkDock.cpp b/kde/src/widgets/BookmarkDock.cpp index fd22816c4cece10b7a329abf6a6414b760db9da4..5db807b6424c0e54c61b8e876e5dbac8533cfe40 100644 --- a/kde/src/widgets/BookmarkDock.cpp +++ b/kde/src/widgets/BookmarkDock.cpp @@ -48,7 +48,7 @@ class QNumericTreeWidgetItem : public QTreeWidgetItem { public: QNumericTreeWidgetItem(QTreeWidget* parent):QTreeWidgetItem(parent),widget(0),weight(-1){} - QNumericTreeWidgetItem(QTreeWidgetItem* parent):QTreeWidgetItem(parent),widget(0),weight(-1){} + QNumericTreeWidgetItem(QTreeWidgetItem* parent=0):QTreeWidgetItem(parent),widget(0),weight(-1){} HistoryTreeItem* widget; int weight; private: @@ -126,7 +126,7 @@ BookmarkDock::~BookmarkDock() ///Add a new bookmark void BookmarkDock::addBookmark_internal(const QString& phone) { - HistoryTreeItem* widget = new HistoryTreeItem(m_pItemView,phone); + HistoryTreeItem* widget = new HistoryTreeItem(m_pItemView,phone,true); QTreeWidgetItem* item = NULL; if (widget->getName() == i18n("Unknown") || widget->getName().isEmpty()) { @@ -149,6 +149,25 @@ void BookmarkDock::addBookmark(const QString& phone) ConfigurationSkeleton::setBookmarkList(ConfigurationSkeleton::bookmarkList() << phone); } +///Remove a bookmark +void BookmarkDock::removeBookmark(const QString& phone) +{ + foreach (HistoryTreeItem* w,m_pBookmark) { + if (w->getPhoneNumber() == phone) { + QTreeWidgetItem* item = w->getItem(); + m_pItemView->removeItem(item); + QStringList bookmarks = ConfigurationSkeleton::bookmarkList(); + if (bookmarks.indexOf(phone)!= -1) { + bookmarks.removeAt(bookmarks.indexOf(phone)); + ConfigurationSkeleton::setBookmarkList(bookmarks); + } + if (m_pBookmark.indexOf(w)!=-1) { + m_pBookmark.removeAt(m_pBookmark.indexOf(w)); + } + } + } +} + ///Filter the list void BookmarkDock::filter(QString text) { @@ -173,7 +192,7 @@ void BookmarkDock::reload() QStringList cl = HistoryModel::getNumbersByPopularity(); for (int i=0;i < ((cl.size() < 10)?cl.size():10);i++) { QNumericTreeWidgetItem* item = m_pItemView->addItem<QNumericTreeWidgetItem>(i18n("Popular")); - HistoryTreeItem* widget = new HistoryTreeItem(m_pItemView,cl[i]); + HistoryTreeItem* widget = new HistoryTreeItem(m_pItemView,cl[i],true); widget->setItem(item); m_pItemView->setItemWidget(item,0,widget); m_pBookmark << widget; diff --git a/kde/src/widgets/BookmarkDock.h b/kde/src/widgets/BookmarkDock.h index 8dfbad2eed102a9d5f6b7a4bcac8080b74bc16d6..efe3686fcda37951a3542532c83f803775f1b072 100644 --- a/kde/src/widgets/BookmarkDock.h +++ b/kde/src/widgets/BookmarkDock.h @@ -49,6 +49,7 @@ public: //Mutators void addBookmark(const QString& phone); + void removeBookmark(const QString& phone); private: //Attributes CategorizedTreeWidget* m_pItemView ; diff --git a/kde/src/widgets/CallTreeItem.cpp b/kde/src/widgets/CallTreeItem.cpp index fecfa75d784b7853989b24d0d06f5582a65c827f..545fea76bc118db3dc2e01429c9a61dcaa0717a6 100644 --- a/kde/src/widgets/CallTreeItem.cpp +++ b/kde/src/widgets/CallTreeItem.cpp @@ -328,7 +328,7 @@ void CallTreeItem::updated() } else if (m_pIconL) { QString str = QString(callStateIcons[state]); - if (m_pContact && !m_pItemCall->isConference()) { + if (m_pContact && !m_pItemCall->isConference() && m_pContact->getPhoto()) { QPixmap pxm = (*m_pContact->getPhoto()).scaled(QSize(m_Height,m_Height)); QPainter painter(&pxm); QPixmap status(str); diff --git a/kde/src/widgets/CategorizedTreeWidget.cpp b/kde/src/widgets/CategorizedTreeWidget.cpp index 75f6498bb96140443bb6594c53b49fb6e9346807..e32c6c9a3ed35d6315abf0ca8600264418f10d18 100644 --- a/kde/src/widgets/CategorizedTreeWidget.cpp +++ b/kde/src/widgets/CategorizedTreeWidget.cpp @@ -180,4 +180,22 @@ void CategorizedTreeWidget::drawBranches(QPainter* painter, const QRect& rect, c QVector<QTreeWidgetItem*> CategorizedTreeWidget::realItems() const { return m_lItems; +} + +void CategorizedTreeWidget::removeItem(QTreeWidgetItem* item) +{ + for (int i=0;i<topLevelItemCount();i++) { + QTreeWidgetItem* topL = topLevelItem(i); + if (topL == item) { + takeTopLevelItem(i); + } + else { + for (int k=0;k<topL->childCount();k++) { + QTreeWidgetItem* childL = topL->child(k); + if (childL == item) { + topL->removeChild(childL); + } + } + } + } } \ No newline at end of file diff --git a/kde/src/widgets/CategorizedTreeWidget.h b/kde/src/widgets/CategorizedTreeWidget.h index c8cf482a3fbd52a18b9f804bb48fe59ece33f351..d273353ff757490204351728a1b7cb6ebee12a3a 100644 --- a/kde/src/widgets/CategorizedTreeWidget.h +++ b/kde/src/widgets/CategorizedTreeWidget.h @@ -39,9 +39,10 @@ class CategorizedTreeWidget : public QTreeWidget explicit CategorizedTreeWidget(QWidget *parent = 0); public: - template <class T = QTreeWidgetItem> T* addItem(QString category); + template <class T = QTreeWidgetItem> T* addItem(QString category,bool top = false); template <class T = QTreeWidgetItem> T* addCategory(QString name); - + void removeItem(QTreeWidgetItem* item); + QVector<QTreeWidgetItem*> realItems() const; Q_SIGNALS: @@ -53,7 +54,7 @@ class CategorizedTreeWidget : public QTreeWidget QVector<QTreeWidgetItem*> m_lItems; }; -template <class T> T* CategorizedTreeWidget::addItem(QString category) +template <class T> T* CategorizedTreeWidget::addItem(QString category,bool top) { QTreeWidgetItem* categoryItem = 0; for (int i = 0; i < topLevelItemCount(); ++i) { @@ -68,8 +69,10 @@ template <class T> T* CategorizedTreeWidget::addItem(QString category) } setItemHidden(categoryItem,false); - T* iwdg = new T(categoryItem); + T* iwdg = new T((top)?0:categoryItem); resizeColumnToContents(0); + if (top) + categoryItem->insertChild(0,iwdg); m_lItems << iwdg; return iwdg; } diff --git a/kde/src/widgets/ContactDock.cpp b/kde/src/widgets/ContactDock.cpp index e51f1aa9bd4b66a72138153d57922671f123b19d..a451bc37565ce46f782afecd4f88243a42ad65ae 100644 --- a/kde/src/widgets/ContactDock.cpp +++ b/kde/src/widgets/ContactDock.cpp @@ -59,7 +59,7 @@ class QNumericTreeWidgetItem_hist : public QTreeWidgetItem { public: QNumericTreeWidgetItem_hist(QTreeWidget* parent):QTreeWidgetItem(parent),widget(0),weight(-1){} - QNumericTreeWidgetItem_hist(QTreeWidgetItem* parent):QTreeWidgetItem(parent),widget(0),weight(-1){} + QNumericTreeWidgetItem_hist(QTreeWidgetItem* parent=0):QTreeWidgetItem(parent),widget(0),weight(-1){} ContactItemWidget* widget; QString number; int weight; @@ -86,9 +86,14 @@ protected: { e->accept(); Call* call = SFLPhone::model()->addDialingCall(m_pName, AccountList::getCurrentAccount()); - call->setCallNumber(m_pNumber); - call->setPeerName(m_pName); - call->actionPerformed(CALL_ACTION_ACCEPT); + if (call) { + call->setCallNumber(m_pNumber); + call->setPeerName(m_pName); + call->actionPerformed(CALL_ACTION_ACCEPT); + } + else { + HelperFunctions::displayNoAccountMessageBox(this); + } } private: QString m_pNumber; @@ -409,7 +414,7 @@ void ContactDock::keyPressEvent(QKeyEvent* event) { QNumericTreeWidgetItem_hist* item = dynamic_cast<QNumericTreeWidgetItem_hist*>(m_pContactView->selectedItems()[0]); if (item) { Call* call = NULL; - SFLPhone::app()->view()->selectCallPhoneNumber(call,item->widget->getContact()); + SFLPhone::app()->view()->selectCallPhoneNumber(&call,item->widget->getContact()); } } } diff --git a/kde/src/widgets/ContactItemWidget.cpp b/kde/src/widgets/ContactItemWidget.cpp index 38c4ef282c87c07b0fd65d6cfcca9e45771bfa87..caf11735c968f96561993aa3994468bb3bd3077f 100644 --- a/kde/src/widgets/ContactItemWidget.cpp +++ b/kde/src/widgets/ContactItemWidget.cpp @@ -23,6 +23,7 @@ //Qt #include <QtCore/QMimeData> +#include <QtCore/QProcess> #include <QtGui/QApplication> #include <QtGui/QClipboard> #include <QtGui/QGridLayout> @@ -45,6 +46,7 @@ #include "klib/AkonadiBackend.h" #include "widgets/BookmarkDock.h" #include "klib/ConfigurationSkeleton.h" +#include "klib/HelperFunctions.h" #include "widgets/TranslucentButtons.h" #include "SFLPhone.h" @@ -60,7 +62,7 @@ ContactItemWidget::ContactItemWidget(QWidget *parent) setContextMenuPolicy(Qt::CustomContextMenu); setAcceptDrops(true); - m_pCallAgain = new KAction(this); + m_pCallAgain = new KAction(this); m_pCallAgain->setShortcut ( Qt::CTRL + Qt::Key_Enter ); m_pCallAgain->setText ( i18n("Call Again") ); m_pCallAgain->setIcon ( KIcon("call-start") ); @@ -70,22 +72,24 @@ ContactItemWidget::ContactItemWidget(QWidget *parent) m_pEditContact->setText ( i18n("Edit contact") ); m_pEditContact->setIcon ( KIcon("contact-new") ); - m_pCopy = new KAction(this); + m_pCopy = new KAction(this); m_pCopy->setShortcut ( Qt::CTRL + Qt::Key_C ); m_pCopy->setText ( i18n("Copy") ); m_pCopy->setIcon ( KIcon("edit-copy") ); - m_pEmail = new KAction(this); + m_pEmail = new KAction(this); m_pEmail->setShortcut ( Qt::CTRL + Qt::Key_M ); m_pEmail->setText ( i18n("Send Email") ); m_pEmail->setIcon ( KIcon("mail-message-new") ); + m_pEmail->setEnabled ( false ); - m_pAddPhone = new KAction(this); + m_pAddPhone = new KAction(this); m_pAddPhone->setShortcut ( Qt::CTRL + Qt::Key_N ); m_pAddPhone->setText ( i18n("Add Phone Number") ); m_pAddPhone->setIcon ( KIcon("list-resource-add") ); + m_pEmail->setEnabled ( false ); - m_pBookmark = new KAction(this); + m_pBookmark = new KAction(this); m_pBookmark->setShortcut ( Qt::CTRL + Qt::Key_D ); m_pBookmark->setText ( i18n("Bookmark") ); m_pBookmark->setIcon ( KIcon("bookmarks") ); @@ -108,20 +112,19 @@ ContactItemWidget::ContactItemWidget(QWidget *parent) ///Destructor ContactItemWidget::~ContactItemWidget() { - /*delete m_pIconL ; - delete m_pContactNameL ; - delete m_pCallNumberL ; - delete m_pOrganizationL; - delete m_pEmailL ; - delete m_pItem ; - + if (m_pIconL) delete m_pIconL ; + if (m_pContactNameL) delete m_pContactNameL ; + if (m_pCallNumberL) delete m_pCallNumberL ; + if (m_pOrganizationL) delete m_pOrganizationL; + if (m_pEmailL) delete m_pEmailL ; + if (m_pMenu) delete m_pMenu ; + delete m_pCallAgain ; delete m_pEditContact ; delete m_pCopy ; delete m_pEmail ; delete m_pAddPhone ; delete m_pBookmark ; - delete m_pMenu ;*/ } @@ -199,6 +202,10 @@ void ContactItemWidget::setContact(Contact* contact) if (height < 48) height = 48; m_Size = QSize(0,height+8); + + if (!m_pContactKA->getPreferredEmail().isEmpty()) { + m_pEmail->setEnabled(true); + } } //setContact ///Set the model index @@ -355,6 +362,9 @@ void ContactItemWidget::showContext(const QPoint& pos) void ContactItemWidget::sendEmail() { kDebug() << "Sending email"; + QProcess *myProcess = new QProcess(this); + QStringList arguments; + myProcess->start("xdg-email", (arguments << m_pContactKA->getPreferredEmail())); } ///Call the same number again @@ -366,9 +376,14 @@ void ContactItemWidget::callAgain() QString number = showNumberSelector(ok); if (ok) { Call* call = SFLPhone::model()->addDialingCall(m_pContactKA->getFormattedName(), AccountList::getCurrentAccount()); - call->setCallNumber(number); - call->setPeerName(m_pContactKA->getFormattedName()); - call->actionPerformed(CALL_ACTION_ACCEPT); + if (call) { + call->setCallNumber(number); + call->setPeerName(m_pContactKA->getFormattedName()); + call->actionPerformed(CALL_ACTION_ACCEPT); + } + else { + HelperFunctions::displayNoAccountMessageBox(this); + } } } @@ -402,6 +417,12 @@ void ContactItemWidget::editContact() void ContactItemWidget::addPhone() { kDebug() << "Adding to contact"; + bool ok; + //QString number = QInputDialog::getText(0, i18n("Enter a new number"),i18n("New number:"),QLineEdit::Normal,QString(),ok,0); + QString text = QInputDialog::getText(this, i18n("Enter a new number"), i18n("New number:"), QLineEdit::Normal, QString(), &ok); + if (ok && !text.isEmpty()) { + AkonadiBackend::getInstance()->addPhoneNumber(m_pContactKA,text,"work"); + } } ///Add this contact to the bookmark list diff --git a/kde/src/widgets/HistoryDock.cpp b/kde/src/widgets/HistoryDock.cpp index 78d91201c87ed03a71a6cce2432f93d7ed30751f..f5244fb86c65faeca91e1caafac804327bb2d94f 100644 --- a/kde/src/widgets/HistoryDock.cpp +++ b/kde/src/widgets/HistoryDock.cpp @@ -267,7 +267,7 @@ void HistoryDock::newHistoryCall(Call* call) switch (CURRENT_SORTING_MODE) { case Date: { QString category = timeToHistoryCategory(QDateTime::fromTime_t(callItem->call()->getStartTimeStamp().toUInt()).date()); - QNumericTreeWidgetItem* item = m_pItemView->addItem<QNumericTreeWidgetItem>(category); + QNumericTreeWidgetItem* item = m_pItemView->addItem<QNumericTreeWidgetItem>(category,true); item->weight = -callItem->call()->getStopTimeStamp().toUInt(); item->widget = callItem; callItem->setItem(item); diff --git a/kde/src/widgets/HistoryTreeItem.cpp b/kde/src/widgets/HistoryTreeItem.cpp index 2576f60f13dd808f136c8ee07cdf60b871995b0b..044a2e3639d81e4d2a00dbdbc901e1177b72e942 100644 --- a/kde/src/widgets/HistoryTreeItem.cpp +++ b/kde/src/widgets/HistoryTreeItem.cpp @@ -37,6 +37,7 @@ #include <QtGui/QFontMetrics> #include <QtCore/QStringList> #include <QtCore/QFile> +#include <QtCore/QProcess> //KDE #include <KLocale> @@ -57,8 +58,14 @@ #include "SFLPhone.h" #include "widgets/BookmarkDock.h" #include "widgets/TranslucentButtons.h" +#include "klib/ConfigurationSkeleton.h" -const char * HistoryTreeItem::callStateIcons[12] = {ICON_INCOMING, ICON_RINGING, ICON_CURRENT, ICON_DIALING, ICON_HOLD, ICON_FAILURE, ICON_BUSY, ICON_TRANSFER, ICON_TRANSF_HOLD, "", "", ICON_CONFERENCE}; +static const char* icnPath[4] = { +/* INCOMING */ ICON_HISTORY_INCOMING, +/* OUTGOING */ ICON_HISTORY_OUTGOING, +/* MISSED */ ICON_HISTORY_MISSED, +/* NONE */ "", +}; ///PlayerWidget: A small widget to play call recording class PlayerWidget : public QWidget { @@ -76,9 +83,9 @@ protected: ///Constructor -HistoryTreeItem::HistoryTreeItem(QWidget *parent ,QString phone) +HistoryTreeItem::HistoryTreeItem(QWidget *parent ,QString phone,bool isBookmark) : QWidget(parent), m_pItemCall(0), m_pMenu(0) , m_pAudioSlider(0) , m_pTimeLeftL(0) , m_pTimePlayedL(0),m_pPlayer(0), - m_pContact(0) , m_pPause(0) , m_pStop(0) , m_pNote(0) , m_SeekPos(0) , m_Paused(false) + m_pContact(0) , m_pPause(0) , m_pStop(0) , m_pNote(0) , m_SeekPos(0) , m_Paused(false) ,m_IsBookmark(isBookmark) { setContextMenuPolicy(Qt::CustomContextMenu); setAcceptDrops(true); @@ -114,7 +121,14 @@ HistoryTreeItem::HistoryTreeItem(QWidget *parent ,QString phone) m_pBookmark->setShortcut ( Qt::CTRL + Qt::Key_D ); m_pBookmark->setText ( i18n("Bookmark") ); - m_pBookmark->setIcon ( KIcon("bookmarks") ); + if (!m_IsBookmark) { + m_pBookmark->setText ( i18n("Bookmark") ); + m_pBookmark->setIcon ( KIcon("bookmarks") ); + } + else { + m_pBookmark->setText ( i18n("Remove bookmark") ); + m_pBookmark->setIcon ( KIcon("edit-delete") ); + } m_pPlay = new QToolButton(this); @@ -239,8 +253,10 @@ void HistoryTreeItem::showContext(const QPoint& pos) ///Send an email void HistoryTreeItem::sendEmail() { - //TODO kDebug() << "Sending email"; + QProcess *myProcess = new QProcess(this); + QStringList arguments; + myProcess->start("xdg-email", (arguments << m_pContact->getPreferredEmail())); } ///Call the caller again @@ -250,9 +266,14 @@ void HistoryTreeItem::callAgain() kDebug() << "Calling "<< m_pItemCall->getPeerPhoneNumber(); } Call* call = SFLPhone::model()->addDialingCall(getName(), AccountList::getCurrentAccount()); - call->setCallNumber(m_PhoneNumber); - call->setPeerName(m_pPeerNameL->text()); - call->actionPerformed(CALL_ACTION_ACCEPT); + if (call) { + call->setCallNumber(m_PhoneNumber); + call->setPeerName(m_pPeerNameL->text()); + call->actionPerformed(CALL_ACTION_ACCEPT); + } + else { + HelperFunctions::displayNoAccountMessageBox(this); + } } ///Copy the call @@ -260,14 +281,18 @@ void HistoryTreeItem::copy() { kDebug() << "Copying contact"; QMimeData* mimeData = new QMimeData(); - mimeData->setData(MIME_CALLID, m_pItemCall->getCallId().toUtf8()); + if (m_pItemCall) + mimeData->setData(MIME_CALLID, m_pItemCall->getCallId().toUtf8()); + + mimeData->setData(MIME_PHONENUMBER, m_PhoneNumber.toUtf8()); + QString numbers; QString numbersHtml; if (m_pContact) { numbers = m_pContact->getFormattedName()+": "+m_PhoneNumber; numbersHtml = "<b>"+m_pContact->getFormattedName()+"</b><br />"+HelperFunctions::escapeHtmlEntities(m_PhoneNumber); } - else { + else if (m_pItemCall) { numbers = m_pItemCall->getPeerName()+": "+m_PhoneNumber; numbersHtml = "<b>"+m_pItemCall->getPeerName()+"</b><br />"+HelperFunctions::escapeHtmlEntities(m_PhoneNumber); } @@ -298,7 +323,10 @@ void HistoryTreeItem::addToContact() ///Bookmark this contact void HistoryTreeItem::bookmark() { - SFLPhone::app()->bookmarkDock()->addBookmark(m_PhoneNumber); + if (!m_IsBookmark) + SFLPhone::app()->bookmarkDock()->addBookmark(m_PhoneNumber); + else + SFLPhone::app()->bookmarkDock()->removeBookmark(m_PhoneNumber); } void HistoryTreeItem::removeRecording() @@ -508,6 +536,7 @@ void HistoryTreeItem::setItem(QTreeWidgetItem* item) ///Can a contact be associed with this call? bool HistoryTreeItem::getContactInfo(QString phoneNumber) { + QPixmap pxm; if (!m_pContact && !m_pItemCall) m_pContact = AkonadiBackend::getInstance()->getContactByPhone(phoneNumber,true); else if (m_pItemCall) @@ -515,36 +544,43 @@ bool HistoryTreeItem::getContactInfo(QString phoneNumber) if (m_pContact) { m_Name = m_pContact->getFormattedName(); m_pPeerNameL->setText("<b>"+m_Name+"</b>"); - if (m_pContact->getPhoto() != NULL) { - QPixmap pxm = (*m_pContact->getPhoto()); - if (m_pItemCall && !m_pItemCall->getRecordingPath().isEmpty()) { - QPainter painter(&pxm); - QPixmap status(KStandardDirs::locate("data","sflphone-client-kde/voicemail.png")); - status=status.scaled(QSize(24,24)); - painter.drawPixmap(pxm.width()-status.width(),pxm.height()-status.height(),status); - } - m_pIconL->setPixmap(pxm); - } - else if (m_pItemCall && !m_pItemCall->getRecordingPath().isEmpty()) - m_pIconL->setPixmap(QPixmap(KStandardDirs::locate("data","sflphone-client-kde/voicemail.png"))); + if (m_pContact->getPhoto() != NULL) + pxm = (*m_pContact->getPhoto()); else - m_pIconL->setPixmap(QPixmap(KIcon("user-identity").pixmap(QSize(48,48)))); - m_pContact = m_pContact; + pxm = QPixmap(KIcon("user-identity").pixmap(QSize(48,48))); + + //There is no point to add new contacts when there is already one + m_pAddToContact->setDisabled(true); + m_pAddContact->setDisabled(true); + + if (!m_pContact->getPreferredEmail().isEmpty()) + m_pEmail->setDisabled(false); } else { - if (m_pItemCall && !m_pItemCall->getRecordingPath().isEmpty()) - m_pIconL->setPixmap(QPixmap(KStandardDirs::locate("data","sflphone-client-kde/voicemail.png"))); - else - m_pIconL->setPixmap(QPixmap(KIcon("user-identity").pixmap(QSize(48,48)))); + pxm = QPixmap(KIcon("user-identity").pixmap(QSize(48,48))); if (!phoneNumber.isEmpty() && m_Name.isEmpty()) m_Name = phoneNumber; else if (m_Name.isEmpty()) m_Name = i18n("Unknown"); m_pPeerNameL->setText("<b>"+m_Name+"</b>"); - return false; } - return true; + + if (m_pItemCall && !m_pItemCall->getRecordingPath().isEmpty()) { + QPainter painter(&pxm); + QPixmap status(KStandardDirs::locate("data","sflphone-client-kde/voicemail.png")); + status=status.scaled(QSize(24,24)); + painter.drawPixmap(pxm.width()-status.width(),pxm.height()-status.height(),status); + } + else if (m_pItemCall && m_pItemCall->getHistoryState() != history_state::NONE && ConfigurationSkeleton::displayHistoryStatus()) { + QPainter painter(&pxm); + QPixmap status(icnPath[m_pItemCall->getHistoryState()]); + status=status.scaled(QSize(24,24)); + painter.drawPixmap(pxm.width()-status.width(),pxm.height()-status.height(),status); + } + m_pIconL->setPixmap(pxm); + + return m_pContact; } //getContactInfo ///Return the time stamp diff --git a/kde/src/widgets/HistoryTreeItem.h b/kde/src/widgets/HistoryTreeItem.h index f47a41c65a341e093a3dab30b48dadfcd8f77e13..462cab9eee78a3b26ecdb68d424ee5b3ccd90e34 100644 --- a/kde/src/widgets/HistoryTreeItem.h +++ b/kde/src/widgets/HistoryTreeItem.h @@ -55,7 +55,7 @@ class HistoryTreeItem : public QWidget Q_OBJECT public: //Constructor - HistoryTreeItem(QWidget* parent =0, QString phone = ""); + HistoryTreeItem(QWidget* parent =0, QString phone = "",bool isBookmark=false); ~HistoryTreeItem(); //Getters @@ -71,9 +71,6 @@ class HistoryTreeItem : public QWidget void setCall ( Call* call ); void setItem ( QTreeWidgetItem* item ); - //Const - static const char * callStateIcons[12]; - private: //Attributes Call* m_pItemCall ; @@ -117,6 +114,8 @@ class HistoryTreeItem : public QWidget TranslucentButtons* m_pBtnTrans; + bool m_IsBookmark; + protected: virtual void resizeEvent(QResizeEvent* event); virtual void mouseDoubleClickEvent(QMouseEvent* event); diff --git a/kde/src/widgets/SFLPhoneTray.cpp b/kde/src/widgets/SFLPhoneTray.cpp index 8ee116e03fb6c41bc82ccfd2000faa775c267df5..b53347681947deab3cf842d97cbc5426d1c50ca5 100755 --- a/kde/src/widgets/SFLPhoneTray.cpp +++ b/kde/src/widgets/SFLPhoneTray.cpp @@ -33,9 +33,10 @@ ///Constructor SFLPhoneTray::SFLPhoneTray(QIcon icon, QWidget *parent) : KSystemTrayIcon(icon, parent), - m_pTrayIconMenu(0), - m_Init(false) + m_pTrayIconMenu(0) { + m_pTrayIconMenu = new QMenu(parentWidget()); + setContextMenu(m_pTrayIconMenu); } ///Destructor @@ -44,22 +45,6 @@ SFLPhoneTray::~SFLPhoneTray() if (m_pTrayIconMenu) delete m_pTrayIconMenu; } -///Initializer -bool SFLPhoneTray::initialize() -{ - if ( m_Init ) { - kDebug() << "Already initialized."; - return false; - } - - m_pTrayIconMenu = new QMenu(parentWidget()); - setContextMenu(m_pTrayIconMenu); - - m_Init = true; - - return true; -} - /***************************************************************************** * * @@ -71,4 +56,10 @@ bool SFLPhoneTray::initialize() void SFLPhoneTray::addAction(KAction *action) { m_pTrayIconMenu->addAction(action); +} + +///Add a menu separator +void SFLPhoneTray::addSeparator() +{ + m_pTrayIconMenu->addSeparator(); } \ No newline at end of file diff --git a/kde/src/widgets/SFLPhoneTray.h b/kde/src/widgets/SFLPhoneTray.h index 9f0da19c270618050622f4c7ecb77da6891efae6..bdd811f4bf6bc4f93f8e66d2830a13e3b0a2359b 100755 --- a/kde/src/widgets/SFLPhoneTray.h +++ b/kde/src/widgets/SFLPhoneTray.h @@ -40,15 +40,14 @@ public: //Constructor SFLPhoneTray(QIcon icon, QWidget *parent = 0); ~SFLPhoneTray(); - bool initialize(); - + //Mutators void addAction(KAction *action); + void addSeparator(); private: //Attributes QMenu* m_pTrayIconMenu; - bool m_Init; }; #endif // SFLPHONETRAY_H diff --git a/kde/src/widgets/VideoDock.cpp b/kde/src/widgets/VideoDock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4cd47db09f675c5ecdf73ad1d2f9d19c37ee53dc --- /dev/null +++ b/kde/src/widgets/VideoDock.cpp @@ -0,0 +1,46 @@ +/************************************************************************************ + * Copyright (C) 2012 by Savoir-Faire Linux * + * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***********************************************************************************/ +#include "VideoDock.h" + +#include <QtGui/QSpacerItem> +#include <QtGui/QGridLayout> + +#include <KLocale> + +#include "VideoWidget.h" + +///Constructor +VideoDock::VideoDock(QWidget* parent) : QDockWidget(parent) +{ + setWindowTitle(i18n("Video")); + QWidget* wdg = new QWidget(this); + m_pVideoWidet = new VideoWidget(this); + auto l = new QGridLayout(wdg); + l->addWidget(m_pVideoWidet,1,1); + l->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding),0,0); + l->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding),0,1); + l->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding),2,0); + l->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding),0,2); + setWidget(wdg); +} + +void VideoDock::setRenderer(VideoRenderer* r) +{ + m_pVideoWidet->setRenderer(r); +} \ No newline at end of file diff --git a/kde/src/widgets/VideoDock.h b/kde/src/widgets/VideoDock.h new file mode 100644 index 0000000000000000000000000000000000000000..93d2ca04845db43b2e5cc129777d5266e13328b6 --- /dev/null +++ b/kde/src/widgets/VideoDock.h @@ -0,0 +1,45 @@ +/************************************************************************************ + * Copyright (C) 2012 by Savoir-Faire Linux * + * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***********************************************************************************/ +#ifndef VIDEO_DOCK_H +#define VIDEO_DOCK_H + +#include <QtGui/QDockWidget> + +//Qt +class QSpacerItem; + +//SFLPhone +class VideoWidget; +class VideoRenderer; + +///VideoDock: A dock hosting a VideoWidget or AcceleratedVideoWidget +class VideoDock : public QDockWidget { + Q_OBJECT +public: + VideoDock(QWidget* parent =0 ); + void setRenderer(VideoRenderer* r); + +private: + VideoWidget* m_pVideoWidet; + +private slots: + +}; + +#endif \ No newline at end of file diff --git a/kde/src/widgets/VideoWidget.cpp b/kde/src/widgets/VideoWidget.cpp index ef2ece8eac81ccfd8f6241546ffa886ce8453498..c7c7087bed58f1d5263329013552d05b10c7e5d0 100644 --- a/kde/src/widgets/VideoWidget.cpp +++ b/kde/src/widgets/VideoWidget.cpp @@ -17,35 +17,67 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***********************************************************************************/ #include "VideoWidget.h" +#include "../lib/VideoRenderer.h" #include <KDebug> -VideoWidget::VideoWidget(QWidget* parent) : QWidget(parent),m_Image(NULL) { +///Constructor +VideoWidget::VideoWidget(QWidget* parent ,VideoRenderer* renderer) : QWidget(parent),m_Image(nullptr),m_pRenderer(renderer) { setMinimumSize(200,200); - connect(VideoModel::getInstance(),SIGNAL(frameUpdated()),this,SLOT(repaint2())); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + connect(m_pRenderer,SIGNAL(frameUpdated()),this,SLOT(updateFrame())); + connect(VideoModel::getInstance(),SIGNAL(videoStopped()),this,SLOT(stop())); + connect(VideoModel::getInstance(),SIGNAL(videoCallInitiated(VideoRenderer*)),this,SLOT(setRenderer(VideoRenderer*))); } + +void VideoWidget::setRenderer(VideoRenderer* renderer) +{ + disconnect(m_pRenderer,SIGNAL(frameUpdated()),this,SLOT(updateFrame())); + m_pRenderer = renderer; + connect(m_pRenderer,SIGNAL(frameUpdated()),this,SLOT(updateFrame())); +} + +///Repaint the widget void VideoWidget::update() { QPainter painter(this); - painter.drawImage(QRect(0,0,width(),height()),*(m_Image)); + if (m_Image && m_pRenderer->isRendering()) + painter.drawImage(QRect(0,0,width(),height()),*(m_Image)); painter.end(); } +///Called when the widget need repainting void VideoWidget::paintEvent(QPaintEvent* event) { Q_UNUSED(event) - if (VideoModel::getInstance()->isPreviewing()) { - update(); - } + //if (VideoModel::getInstance()->isPreviewing()) { + update(); + //} } -void VideoWidget::repaint2() +///Called when a new frame is ready +void VideoWidget::updateFrame() { - QSize size(VideoModel::getInstance()->getActiveResolution().width, VideoModel::getInstance()->getActiveResolution().height); + QSize size(m_pRenderer->getActiveResolution().width, m_pRenderer->getActiveResolution().height); if (size != minimumSize()) setMinimumSize(size); - //if (m_Image) - // delete m_Image; - m_Image = new QImage(size,QImage::Format_ARGB32); - m_Image->loadFromData(VideoModel::getInstance()->getCurrentFrame(),"BMP"); + if (m_Image) + delete m_Image; + //if (!m_Image && VideoModel::getInstance()->isRendering()) + m_Image = new QImage((uchar*)m_pRenderer->rawData() , size.width(), size.height(), QImage::Format_ARGB32 ); + //This is the right way to do it, but it does not work +// if (!m_Image || (m_Image && m_Image->size() != size)) +// m_Image = new QImage((uchar*)VideoModel::getInstance()->rawData() , size.width(), size.height(), QImage::Format_ARGB32 ); +// if (!m_Image->loadFromData(VideoModel::getInstance()->getCurrentFrame())) { +// qDebug() << "Loading image failed"; +// } repaint(); +} + +///Prevent the painter to try to paint an invalid framebuffer +void VideoWidget::stop() +{ + if (m_Image) { + delete m_Image; + m_Image = nullptr; + } } \ No newline at end of file diff --git a/kde/src/widgets/VideoWidget.h b/kde/src/widgets/VideoWidget.h index 7c9e116005fe84fe187ed3ba8b9893e9b8e19f6e..2cd69b7b487cc6a486d14be5ec9cc33e90c3d63e 100644 --- a/kde/src/widgets/VideoWidget.h +++ b/kde/src/widgets/VideoWidget.h @@ -23,19 +23,25 @@ #include <QtGui/QPainter> #include "../lib/VideoModel.h" +class VideoRenderer; ///VideoWidget: A widget to display video from a framebuffer class VideoWidget : public QWidget { Q_OBJECT public: - VideoWidget(QWidget* parent =0 ); + VideoWidget(QWidget* parent =0, VideoRenderer* renderer = VideoModel::getInstance()->getPreviewRenderer()); private: - QImage* m_Image; + QImage* m_Image; + VideoRenderer* m_pRenderer; protected: virtual void paintEvent(QPaintEvent* event); private slots: void update(); - void repaint2(); + void updateFrame(); + void stop(); +public slots: + void setRenderer(VideoRenderer* renderer); + }; #endif \ No newline at end of file diff --git a/plugins/INSTALL b/plugins/INSTALL index 7d1c323beae76333f523f6df31c47a87f5597edb..a1e89e18ad20c227845f2099cb9894c799265d19 100644 --- a/plugins/INSTALL +++ b/plugins/INSTALL @@ -1,8 +1,8 @@ Installation Instructions ************************* -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006, 2007, 2008, 2009 Free Software Foundation, Inc. +Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation, +Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright @@ -226,6 +226,11 @@ order to use an ANSI C compiler: and if that doesn't work, install pre-built binaries of GCC for HP-UX. + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `<wchar.h>' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended diff --git a/plugins/addressbook/evolution/eds.c b/plugins/addressbook/evolution/eds.c index b1171fb4307d22d9c09ced1942bc23ad16fc6c62..a36efcbcdbaeb99ef9ae2b3ebcd21e82dee76039 100644 --- a/plugins/addressbook/evolution/eds.c +++ b/plugins/addressbook/evolution/eds.c @@ -37,7 +37,6 @@ */ #include <glib.h> -#include <glib/gstring.h> #include <string.h> #include <pango/pango.h> #include "eds.h"