From dd500fb1fdafac0c93efe0e26a553faf150d53a9 Mon Sep 17 00:00:00 2001 From: "Mark Palange (Mani)" Date: Thu, 7 Jan 2010 16:15:51 -0800 Subject: EXT-3780 Rewrote windows processor detection to support cpuid brandstring, and x64. Refactored the CProcessor class into LLProcessorInfo. Reviewed by brad --- indra/llcommon/llprocessor.cpp | 1674 ++++++++++++++++++++++++++-------------- indra/llcommon/llprocessor.h | 29 +- indra/llcommon/llsys.cpp | 26 +- 3 files changed, 1119 insertions(+), 610 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 469e544b16..010435f11a 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -30,6 +30,466 @@ * $/LicenseInfo$ */ +#include "linden_common.h" +#include "llprocessor.h" + +//#include + +#if LL_WINDOWS +# define WIN32_LEAN_AND_MEAN +# include +# include +#endif + +#include +#include + +#if LL_MSVC && _M_X64 +# define LL_X86_64 1 +# define LL_X86 1 +#elif LL_MSVC && _M_IX86 +# define LL_X86 1 +#elif LL_GNUC && ( defined(__amd64__) || defined(__x86_64__) ) +# define LL_X86_64 1 +# define LL_X86 1 +#elif LL_GNUC && ( defined(__i386__) ) +# define LL_X86 1 +#elif LL_GNUC && ( defined(__powerpc__) || defined(__ppc__) ) +# define LL_PPC 1 +#endif + +// The base calss for implementations. +// Each platform should override this class. +class LLProcessorInfoImpl +{ +public: + LLProcessorInfoImpl() {} + virtual ~LLProcessorInfoImpl() {} + + virtual F64 getCPUFrequency() const { return 0; } + virtual bool hasSSE() const { return false; } + virtual bool hasSSE2() const { return false; } + virtual bool hasAltivec() const { return false; } + virtual std::string getCPUFamilyName() const { return "Unknown"; } + virtual std::string getCPUBrandName() const { return "Unknown"; } + virtual std::string getCPUFeatureDescription() const { return "Unknown"; } +}; + +namespace +{ + // Pointer to the active impl. + boost::scoped_ptr gImpl; +} + +#ifdef LL_MSVC +// LL_MSVC and not LLWINDOWS because some of the following code +// uses the MSVC compiler intrinsics __cpuid() and __rdtsc(). + +// Delays for the specified amount of milliseconds +static void _Delay(unsigned int ms) +{ + LARGE_INTEGER freq, c1, c2; + __int64 x; + + // Get High-Res Timer frequency + if (!QueryPerformanceFrequency(&freq)) + return; + + // Convert ms to High-Res Timer value + x = freq.QuadPart/1000*ms; + + // Get first snapshot of High-Res Timer value + QueryPerformanceCounter(&c1); + do + { + // Get second snapshot + QueryPerformanceCounter(&c2); + }while(c2.QuadPart-c1.QuadPart < x); + // Loop while (second-first < x) +} + +static F64 calculate_cpu_frequency(U32 measure_msecs) +{ + if(measure_msecs == 0) + { + return 0; + } + + // After that we declare some vars and check the frequency of the high + // resolution timer for the measure process. + // If there's no high-res timer, we exit. + unsigned __int64 starttime, endtime, timedif, freq, start, end, dif; + if (!QueryPerformanceFrequency((LARGE_INTEGER *) &freq)) + { + return 0; + } + + // Now we can init the measure process. We set the process and thread priority + // to the highest available level (Realtime priority). Also we focus the + // first processor in the multiprocessor system. + HANDLE hProcess = GetCurrentProcess(); + HANDLE hThread = GetCurrentThread(); + unsigned long dwCurPriorityClass = GetPriorityClass(hProcess); + int iCurThreadPriority = GetThreadPriority(hThread); + unsigned long dwProcessMask, dwSystemMask, dwNewMask = 1; + GetProcessAffinityMask(hProcess, &dwProcessMask, &dwSystemMask); + + SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS); + SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL); + SetProcessAffinityMask(hProcess, dwNewMask); + + //// Now we call a CPUID to ensure, that all other prior called functions are + //// completed now (serialization) + //__asm cpuid + int cpu_info[4] = {-1}; + __cpuid(cpu_info, 0); + + // We ask the high-res timer for the start time + QueryPerformanceCounter((LARGE_INTEGER *) &starttime); + + // Then we get the current cpu clock and store it + start = __rdtsc(); + + // Now we wart for some msecs + _Delay(measure_msecs); + // Sleep(uiMeasureMSecs); + + // We ask for the end time + QueryPerformanceCounter((LARGE_INTEGER *) &endtime); + + // And also for the end cpu clock + end = __rdtsc(); + + // Now we can restore the default process and thread priorities + SetProcessAffinityMask(hProcess, dwProcessMask); + SetThreadPriority(hThread, iCurThreadPriority); + SetPriorityClass(hProcess, dwCurPriorityClass); + + // Then we calculate the time and clock differences + dif = end - start; + timedif = endtime - starttime; + + // And finally the frequency is the clock difference divided by the time + // difference. + F64 frequency = (F64)dif / (((F64)timedif) / freq); + + // At last we just return the frequency that is also stored in the call + // member var uqwFrequency + return frequency; +} + +static const char* cpu_feature_names[] = +{ + "x87 FPU On Chip", + "Virtual-8086 Mode Enhancement", + "Debugging Extensions", + "Page Size Extensions", + "Time Stamp Counter", + "RDMSR and WRMSR Support", + "Physical Address Extensions", + "Machine Check Exception", + "CMPXCHG8B Instruction", + "APIC On Chip", + "Unknown1", + "SYSENTER and SYSEXIT", + "Memory Type Range Registers", + "PTE Global Bit", + "Machine Check Architecture", + "Conditional Move/Compare Instruction", + "Page Attribute Table", + "Page Size Extension", + "Processor Serial Number", + "CFLUSH Extension", + "Unknown2", + "Debug Store", + "Thermal Monitor and Clock Ctrl", + "MMX Technology", + "FXSAVE/FXRSTOR", + "SSE Extensions", + "SSE2 Extensions", + "Self Snoop", + "Hyper-threading Technology", + "Thermal Monitor", + "Unknown4", + "Pend. Brk. EN." +}; + +// Windows implementation +class LLProcessorInfoWindowsImpl : public LLProcessorInfoImpl +{ +public: + LLProcessorInfoWindowsImpl() : + mCPUFrequency(0), + mSteppingID(0), + mModel(0), + mFamily(0), + mProcessorType(0), + mExtendedModel(0), + mExtendedFamily(0), + mBrandIndex(0), + mCLFLUSHCacheLineSize(0), + mAPICPhysicalID(0), + mCacheLineSize(0), + mL2Associativity(0), + mCacheSizeK(0), + + mFeatureInfo(0), + mSSE3NewInstructions(false), + mMONITOR_MWAIT(false), + mCPLQualifiedDebugStore(false), + mThermalMonitor2(false), + + mIds(0), + mExtIds(0) + + { + memset(&mCPUString, 0, 0x20); + memset(&mCPUBrandString, 0, 0x40); + + getCPUIDInfo(); + mCPUFrequency = calculate_cpu_frequency(50); + } + + F64 getCPUFrequency() const + { + return mCPUFrequency; + } + + bool hasSSE() const + { + // constant comes from the msdn docs for __cpuid + const int sse_feature_index = 25; + return mFeatureInfo & (1 << sse_feature_index); + } + + bool hasSSE2() const + { + // constant comes from the msdn docs for __cpuid + const int sse2_feature_index = 26; + return mFeatureInfo & (1 << sse2_feature_index); + } + + std::string getCPUFamilyName() const + { + const char* intel_string = "GenuineIntel"; + const char* amd_string = "AuthenticAMD"; + if(!strncmp(mCPUString, intel_string, strlen(intel_string))) + { + U32 composed_family = mFamily + mExtendedFamily; + switch(composed_family) + { + case 3: return "Intel i386"; + case 4: return "Intel i486"; + case 5: return "Intel Pentium"; + case 6: return "Intel Pentium Pro/2/3, Core"; + case 7: return "Intel Itanium (IA-64)"; + case 0xF: return "Intel Pentium 4"; + case 0x10: return "Intel Itanium 2 (IA-64)"; + default: return "Unknown"; + } + } + else if(!strncmp(mCPUString, amd_string, strlen(amd_string))) + { + U32 composed_family = (mFamily == 0xF) + ? mFamily + mExtendedFamily + : mFamily; + switch(composed_family) + { + case 4: return "AMD 80486/5x86"; + case 5: return "AMD K5/K6"; + case 6: return "AMD K7"; + case 0xF: return "AMD K8"; + case 0x10: return "AMD K8L"; + default: return "Unknown"; + } + } + return "Unknown"; + } + + std::string getCPUBrandName() const { return mCPUBrandString; } + std::string getCPUFeatureDescription() const + { + std::ostringstream out; + out << std::endl << std::endl; + out << "// CPU General Information" << std::endl; + out << "//////////////////////////" << std::endl; + out << "Processor Name: " << getCPUBrandName() << std::endl; + out << "Frequency: " << mCPUFrequency / (F64)1000000 << " MHz" << std::endl; + out << "Vendor: " << mCPUString << std::endl; + out << "Family: " << getCPUFamilyName() << " (" << mFamily << ")" << std::endl; + out << "Extended family: " << mExtendedFamily << std::endl; + out << "Model: " << mModel << std::endl; + out << "Extended model: " << mExtendedModel << std::endl; + out << "Type: " << mProcessorType << std::endl; + out << "Brand ID: " << mBrandIndex << std::endl; + out << std::endl; + out << "// CPU Configuration" << std::endl; + out << "//////////////////////////" << std::endl; + out << "Max Supported CPUID level = " << mIds << std::endl; + out << "Max Supported Ext. CPUID level = " << std::hex << mExtIds << std::dec << std::endl; + out << "CLFLUSH cache line size = " << mCLFLUSHCacheLineSize << std::endl; + out << "APIC Physical ID = " << mAPICPhysicalID << std::endl; + out << "Cache Line Size = " << mCacheLineSize << std::endl; + out << "L2 Associativity = " << mL2Associativity << std::endl; + out << "Cache Size = " << mCacheSizeK << "K" << std::endl; + out << std::endl; + out << "// CPU Extensions" << std::endl; + out << "//////////////////////////" << std::endl; + if(mSSE3NewInstructions) + { + out << " SSE3 New Instructions" << std::endl; + } + if(mMONITOR_MWAIT) + { + out << " MONITOR/MWAIT" << std::endl; + } + if(mCPLQualifiedDebugStore) + { + out << " CPL Qualified Debug Store" << std::endl; + } + if(mThermalMonitor2) + { + out << " Thermal Monitor 2" << std::endl; + } + + U32 index = 0; + U32 bit = 1; + while(index < (sizeof(cpu_feature_names)/sizeof(const char*))) + { + if(mFeatureInfo & bit) + { + out << " " << cpu_feature_names[index] << std::endl; + } + bit <<= 1; + ++index; + } + + return out.str(); + } + +private: + F64 mCPUFrequency; + char mCPUString[0x20]; + char mCPUBrandString[0x40]; + int mSteppingID; + int mModel; + int mFamily; + int mProcessorType; + int mExtendedModel; + int mExtendedFamily; + int mBrandIndex; + int mCLFLUSHCacheLineSize; + int mAPICPhysicalID; + int mCacheLineSize; + int mL2Associativity; + int mCacheSizeK; + + int mFeatureInfo; + bool mSSE3NewInstructions; + bool mMONITOR_MWAIT; + bool mCPLQualifiedDebugStore; + bool mThermalMonitor2; + + unsigned int mIds; + unsigned int mExtIds; + + void getCPUIDInfo() + { + // http://msdn.microsoft.com/en-us/library/hskdteyh(VS.80).aspx + + // __cpuid with an InfoType argument of 0 returns the number of + // valid Ids in cpu_info[0] and the CPU identification string in + // the other three array elements. The CPU identification string is + // not in linear order. The code below arranges the information + // in a human readable form. + int cpu_info[4] = {-1}; + __cpuid(cpu_info, 0); + unsigned int mIds = (unsigned int)cpu_info[0]; + *((int*)mCPUString) = cpu_info[1]; + *((int*)(mCPUString+4)) = cpu_info[3]; + *((int*)(mCPUString+8)) = cpu_info[2]; + + // Get the information associated with each valid Id + for(unsigned int i=0; i<=mIds; ++i) + { + __cpuid(cpu_info, i); + + // Interpret CPU feature information. + if (i == 1) + { + mSteppingID = cpu_info[0] & 0xf; + mModel = (cpu_info[0] >> 4) & 0xf; + mFamily = (cpu_info[0] >> 8) & 0xf; + mProcessorType = (cpu_info[0] >> 12) & 0x3; + mExtendedModel = (cpu_info[0] >> 16) & 0xf; + mExtendedFamily = (cpu_info[0] >> 20) & 0xff; + mBrandIndex = cpu_info[1] & 0xff; + mCLFLUSHCacheLineSize = ((cpu_info[1] >> 8) & 0xff) * 8; + mAPICPhysicalID = (cpu_info[1] >> 24) & 0xff; + mSSE3NewInstructions = (cpu_info[2] & 0x1) || false; + mMONITOR_MWAIT = (cpu_info[2] & 0x8) || false; + mCPLQualifiedDebugStore = (cpu_info[2] & 0x10) || false; + mThermalMonitor2 = (cpu_info[2] & 0x100) || false; + mFeatureInfo = cpu_info[3]; + } + } + + // Calling __cpuid with 0x80000000 as the InfoType argument + // gets the number of valid extended IDs. + __cpuid(cpu_info, 0x80000000); + mExtIds = cpu_info[0]; + memset(mCPUBrandString, 0, sizeof(mCPUBrandString)); + + // Get the information associated with each extended ID. + for(unsigned int i=0x80000000; i<=mExtIds; ++i) + { + __cpuid(cpu_info, i); + + // Interpret CPU brand string and cache information. + if (i == 0x80000002) + memcpy(mCPUBrandString, cpu_info, sizeof(cpu_info)); + else if (i == 0x80000003) + memcpy(mCPUBrandString + 16, cpu_info, sizeof(cpu_info)); + else if (i == 0x80000004) + memcpy(mCPUBrandString + 32, cpu_info, sizeof(cpu_info)); + else if (i == 0x80000006) + { + mCacheLineSize = cpu_info[2] & 0xff; + mL2Associativity = (cpu_info[2] >> 12) & 0xf; + mCacheSizeK = (cpu_info[2] >> 16) & 0xffff; + } + } + } +}; + +#endif // LL_MSVC + + + +// Interface implementation +LLProcessorInfo::LLProcessorInfo() +{ + // *NOTE:Mani - not thread safe. + if(gImpl == NULL) + { +#ifdef LL_MSVC + gImpl.reset(new LLProcessorInfoWindowsImpl); +#else + #error "Unimplemented" +#endif // LL_MSVC + } +} + +LLProcessorInfo::~LLProcessorInfo() {} +F64 LLProcessorInfo::getCPUFrequency() const { return gImpl->getCPUFrequency(); } +bool LLProcessorInfo::hasSSE() const { return gImpl->hasSSE(); } +bool LLProcessorInfo::hasSSE2() const { return gImpl->hasSSE2(); } +bool LLProcessorInfo::hasAltivec() const { return gImpl->hasAltivec(); } +std::string LLProcessorInfo::getCPUFamilyName() const { return gImpl->getCPUFamilyName(); } +std::string LLProcessorInfo::getCPUBrandName() const { return gImpl->getCPUBrandName(); } +std::string LLProcessorInfo::getCPUFeatureDescription() const { return gImpl->getCPUFeatureDescription(); } + +#if 0 // Filename: Processor.cpp // ======================= // Author: Benjamin Jurke @@ -42,7 +502,7 @@ // still need for CProcessor::GetCPUFrequency. // 06.03.2002 - My birthday (18th :-)) // - Replaced the '\r\n' line endings in function -// CProcessor::CPUInfoToText by '\n' +// CProcessor::cpu_infoToText by '\n' // - Replaced unsigned __int64 by signed __int64 for // solving some compiler conversion problems // - Fixed a bug at family=6, model=6 (Celeron -> P2) @@ -107,7 +567,7 @@ CProcessor::CProcessor() { uqwFrequency = 0; strCPUName[0] = 0; - memset(&CPUInfo, 0, sizeof(CPUInfo)); + memset(&cpu_info, 0, sizeof(cpu_info)); } // unsigned __int64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs) @@ -224,6 +684,8 @@ F64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs) ////////////////////////////////////////////////////////// bool CProcessor::AnalyzeIntelProcessor() { + // *NOTE:Mani - http://www.intel.com/Assets/PDF/appnote/241618.pdf + // According to the above doc, a lot of this what follows is wrong. #if LL_WINDOWS unsigned long eaxreg, ebxreg, edxreg; @@ -243,208 +705,247 @@ bool CProcessor::AnalyzeIntelProcessor() // Then get the cpu model, family, type, stepping and brand id by masking // the eax and ebx register - CPUInfo.uiStepping = eaxreg & 0xF; - CPUInfo.uiModel = (eaxreg >> 4) & 0xF; - CPUInfo.uiFamily = (eaxreg >> 8) & 0xF; - CPUInfo.uiType = (eaxreg >> 12) & 0x3; - CPUInfo.uiBrandID = ebxreg & 0xF; - - static const char* INTEL_BRAND[] = - { - /* 0x00 */ "", - /* 0x01 */ "0.18 micron Intel Celeron", - /* 0x02 */ "0.18 micron Intel Pentium III", - /* 0x03 */ "0.13 micron Intel Celeron", - /* 0x04 */ "0.13 micron Intel Pentium III", - /* 0x05 */ "", - /* 0x06 */ "0.13 micron Intel Pentium III Mobile", - /* 0x07 */ "0.13 micron Intel Celeron Mobile", - /* 0x08 */ "0.18 micron Intel Pentium 4", - /* 0x09 */ "0.13 micron Intel Pentium 4", - /* 0x0A */ "0.13 micron Intel Celeron", - /* 0x0B */ "0.13 micron Intel Pentium 4 Xeon", - /* 0x0C */ "Intel Xeon MP", - /* 0x0D */ "", - /* 0x0E */ "0.18 micron Intel Pentium 4 Xeon", - /* 0x0F */ "Mobile Intel Celeron", - /* 0x10 */ "", - /* 0x11 */ "Mobile Genuine Intel", - /* 0x12 */ "Intel Celeron M", - /* 0x13 */ "Mobile Intel Celeron", - /* 0x14 */ "Intel Celeron", - /* 0x15 */ "Mobile Genuine Intel", - /* 0x16 */ "Intel Pentium M", - /* 0x17 */ "Mobile Intel Celeron", - }; - - // Only override the brand if we have it in the lookup table. We should - // already have a string here from GetCPUInfo(). JC - if ( CPUInfo.uiBrandID < LL_ARRAY_SIZE(INTEL_BRAND) ) - { - strcpy(CPUInfo.strBrandID, INTEL_BRAND[CPUInfo.uiBrandID]); - - if (CPUInfo.uiBrandID == 3 && CPUInfo.uiModel == 6) + cpu_info.uiStepping = eaxreg & 0xF; + cpu_info.uiModel = (eaxreg >> 4) & 0xF; + cpu_info.uiFamily = (eaxreg >> 8) & 0xF; + cpu_info.uiType = (eaxreg >> 12) & 0x3; + cpu_info.uiBrandID = ebxreg & 0xF; + + // *NOTE:Mani - see http://www.intel.com/assets/pdf/appnote/241618.pdf + // These values are composed according to section 2.1.2.2 of the above doc. + cpu_info.uiExtendedFamily = ((eaxreg >> 20) & 0xFF) + cpu_info.uiFamily; + cpu_info.uiExtendedModel = (((eaxreg >> 16) & 0xFF) << 4) + cpu_info.uiModel; + + // Getting the Brand ID string if supported. + if (cpu_info.MaxSupportedExtendedLevel >= 0x80000004) + { + // If it supports the extended CPUID level 0x80000004 we read the data + char tmp[52]; /* Flawfinder: ignore */ + memset(tmp, 0, sizeof(tmp)); + __asm + { + mov eax, 0x80000002 + cpuid + mov dword ptr [tmp], eax + mov dword ptr [tmp+4], ebx + mov dword ptr [tmp+8], ecx + mov dword ptr [tmp+12], edx + mov eax, 0x80000003 + cpuid + mov dword ptr [tmp+16], eax + mov dword ptr [tmp+20], ebx + mov dword ptr [tmp+24], ecx + mov dword ptr [tmp+28], edx + mov eax, 0x80000004 + cpuid + mov dword ptr [tmp+32], eax + mov dword ptr [tmp+36], ebx + mov dword ptr [tmp+40], ecx + mov dword ptr [tmp+44], edx + } + // And copy it to the brand id string + strncpy(cpu_info.strBrandID, tmp,sizeof(cpu_info.strBrandID)-1); + cpu_info.strBrandID[sizeof(cpu_info.strBrandID)-1]='\0'; + } + else + { + static const char* INTEL_BRAND[] = + { + /* 0x00 */ "", + /* 0x01 */ "0.18 micron Intel Celeron", + /* 0x02 */ "0.18 micron Intel Pentium III", + /* 0x03 */ "0.13 micron Intel Celeron", + /* 0x04 */ "0.13 micron Intel Pentium III", + /* 0x05 */ "", + /* 0x06 */ "0.13 micron Intel Pentium III Mobile", + /* 0x07 */ "0.13 micron Intel Celeron Mobile", + /* 0x08 */ "0.18 micron Intel Pentium 4", + /* 0x09 */ "0.13 micron Intel Pentium 4", + /* 0x0A */ "0.13 micron Intel Celeron", + /* 0x0B */ "0.13 micron Intel Pentium 4 Xeon", + /* 0x0C */ "Intel Xeon MP", + /* 0x0D */ "", + /* 0x0E */ "0.18 micron Intel Pentium 4 Xeon", + /* 0x0F */ "Mobile Intel Celeron", + /* 0x10 */ "", + /* 0x11 */ "Mobile Genuine Intel", + /* 0x12 */ "Intel Celeron M", + /* 0x13 */ "Mobile Intel Celeron", + /* 0x14 */ "Intel Celeron", + /* 0x15 */ "Mobile Genuine Intel", + /* 0x16 */ "Intel Pentium M", + /* 0x17 */ "Mobile Intel Celeron", + }; + + // Only override the brand if we have it in the lookup table. We should + // already have a string here from Getcpu_info(). JC + if ( cpu_info.uiBrandID < LL_ARRAY_SIZE(INTEL_BRAND) ) { - strcpy(CPUInfo.strBrandID, "0.18 micron Intel Pentium III Xeon"); + strcpy(cpu_info.strBrandID, INTEL_BRAND[cpu_info.uiBrandID]); + + if (cpu_info.uiBrandID == 3 && cpu_info.uiModel == 6) + { + strcpy(cpu_info.strBrandID, "0.18 micron Intel Pentium III Xeon"); + } } } // Then we translate the cpu family - switch (CPUInfo.uiFamily) + switch (cpu_info.uiFamily) { case 3: // Family = 3: i386 (80386) processor family - strcpy(CPUInfo.strFamily, "Intel i386"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "Intel i386"); /* Flawfinder: ignore */ break; case 4: // Family = 4: i486 (80486) processor family - strcpy(CPUInfo.strFamily, "Intel i486"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "Intel i486"); /* Flawfinder: ignore */ break; case 5: // Family = 5: Pentium (80586) processor family - strcpy(CPUInfo.strFamily, "Intel Pentium"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "Intel Pentium"); /* Flawfinder: ignore */ break; case 6: // Family = 6: Pentium Pro (80686) processor family - strcpy(CPUInfo.strFamily, "Intel Pentium Pro/2/3, Core"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "Intel Pentium Pro/2/3, Core"); /* Flawfinder: ignore */ break; case 15: // Family = 15: Extended family specific // Masking the extended family - CPUInfo.uiExtendedFamily = (eaxreg >> 20) & 0xFF; - switch (CPUInfo.uiExtendedFamily) + cpu_info.uiExtendedFamily = (eaxreg >> 20) & 0xFF; + switch (cpu_info.uiExtendedFamily) { case 0: // Family = 15, Ext. Family = 0: Pentium 4 (80786 ??) processor family - strcpy(CPUInfo.strFamily, "Intel Pentium 4"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "Intel Pentium 4"); /* Flawfinder: ignore */ break; case 1: // Family = 15, Ext. Family = 1: McKinley (64-bit) processor family - strcpy(CPUInfo.strFamily, "Intel McKinley (IA-64)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "Intel McKinley (IA-64)"); /* Flawfinder: ignore */ break; default: // Sure is sure - strcpy(CPUInfo.strFamily, "Unknown Intel Pentium 4+"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "Unknown Intel Pentium 4+"); /* Flawfinder: ignore */ break; } break; default: // Failsave - strcpy(CPUInfo.strFamily, "Unknown"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "Unknown"); /* Flawfinder: ignore */ break; } // Now we come to the big deal, the exact model name - switch (CPUInfo.uiFamily) + switch (cpu_info.uiFamily) { case 3: // i386 (80386) processor family - strcpy(CPUInfo.strModel, "Unknown Intel i386"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Unknown Intel i386"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel i386", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; case 4: // i486 (80486) processor family - switch (CPUInfo.uiModel) + switch (cpu_info.uiModel) { case 0: // Model = 0: i486 DX-25/33 processor model - strcpy(CPUInfo.strModel, "Intel i486 DX-25/33"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel i486 DX-25/33"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel i486 DX-25/33", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; case 1: // Model = 1: i486 DX-50 processor model - strcpy(CPUInfo.strModel, "Intel i486 DX-50"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel i486 DX-50"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel i486 DX-50", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; case 2: // Model = 2: i486 SX processor model - strcpy(CPUInfo.strModel, "Intel i486 SX"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel i486 SX"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel i486 SX", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; case 3: // Model = 3: i486 DX2 (with i487 numeric coprocessor) processor model - strcpy(CPUInfo.strModel, "Intel i486 487/DX2"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel i486 487/DX2"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel i486 DX2 with i487 numeric coprocessor", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; case 4: // Model = 4: i486 SL processor model (never heard ?!?) - strcpy(CPUInfo.strModel, "Intel i486 SL"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel i486 SL"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel i486 SL", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; case 5: // Model = 5: i486 SX2 processor model - strcpy(CPUInfo.strModel, "Intel i486 SX2"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel i486 SX2"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel i486 SX2", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; case 7: // Model = 7: i486 write-back enhanced DX2 processor model - strcpy(CPUInfo.strModel, "Intel i486 write-back enhanced DX2"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel i486 write-back enhanced DX2"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel i486 write-back enhanced DX2", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; case 8: // Model = 8: i486 DX4 processor model - strcpy(CPUInfo.strModel, "Intel i486 DX4"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel i486 DX4"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel i486 DX4", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; case 9: // Model = 9: i486 write-back enhanced DX4 processor model - strcpy(CPUInfo.strModel, "Intel i486 write-back enhanced DX4"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel i486 write-back enhanced DX4"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel i486 DX4", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; default: // ... - strcpy(CPUInfo.strModel, "Unknown Intel i486"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Unknown Intel i486"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel i486 (Unknown model)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; } break; case 5: // Pentium (80586) processor family - switch (CPUInfo.uiModel) + switch (cpu_info.uiModel) { case 0: // Model = 0: Pentium (P5 A-Step) processor model - strcpy(CPUInfo.strModel, "Intel Pentium (P5 A-Step)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel Pentium (P5 A-Step)"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel Pentium (P5 A-Step core)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; // Famous for the DIV bug, as far as I know case 1: // Model = 1: Pentium 60/66 processor model - strcpy(CPUInfo.strModel, "Intel Pentium 60/66 (P5)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel Pentium 60/66 (P5)"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel Pentium 60/66 (P5 core)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; case 2: // Model = 2: Pentium 75-200 (P54C) processor model - strcpy(CPUInfo.strModel, "Intel Pentium 75-200 (P54C)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel Pentium 75-200 (P54C)"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel Pentium 75-200 (P54C core)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ break; case 3: // Model = 3: Pentium overdrive for 486 systems processor model - strcpy(CPUInfo.strModel, "Intel Pentium for 486 system (P24T Overdrive)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel Pentium for 486 system (P24T Overdrive)"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel Pentium for 486 (P24T overdrive core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; case 4: // Model = 4: Pentium MMX processor model - strcpy(CPUInfo.strModel, "Intel Pentium MMX (P55C)"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium MMX (P55C)"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium MMX (P55C core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; case 7: // Model = 7: Pentium processor model (don't know difference to Model=2) - strcpy(CPUInfo.strModel, "Intel Pentium (P54C)"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium (P54C)"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium (P54C core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; case 8: // Model = 8: Pentium MMX (0.25 micron) processor model - strcpy(CPUInfo.strModel, "Intel Pentium MMX (P55C), 0.25 micron"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium MMX (P55C), 0.25 micron"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium MMX (P55C core), 0.25 micron", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; default: // ... - strcpy(CPUInfo.strModel, "Unknown Intel Pentium"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Unknown Intel Pentium"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium (Unknown P5-model)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; } break; case 6: // Pentium Pro (80686) processor family - switch (CPUInfo.uiModel) + switch (cpu_info.uiModel) { case 0: // Model = 0: Pentium Pro (P6 A-Step) processor model - strcpy(CPUInfo.strModel, "Intel Pentium Pro (P6 A-Step)"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium Pro (P6 A-Step)"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium Pro (P6 A-Step core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; case 1: // Model = 1: Pentium Pro - strcpy(CPUInfo.strModel, "Intel Pentium Pro (P6)"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium Pro (P6)"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium Pro (P6 core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; case 3: // Model = 3: Pentium II (66 MHz FSB, I think) processor model - strcpy(CPUInfo.strModel, "Intel Pentium II Model 3, 0.28 micron"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium II Model 3, 0.28 micron"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium II (Model 3 core, 0.28 micron process)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; case 5: // Model = 5: Pentium II/Xeon/Celeron (0.25 micron) processor model - strcpy(CPUInfo.strModel, "Intel Pentium II Model 5/Xeon/Celeron, 0.25 micron"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium II Model 5/Xeon/Celeron, 0.25 micron"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium II/Xeon/Celeron (Model 5 core, 0.25 micron process)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; case 6: // Model = 6: Pentium II with internal L2 cache - strcpy(CPUInfo.strModel, "Intel Pentium II - internal L2 cache"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium II - internal L2 cache"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium II with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; case 7: // Model = 7: Pentium III/Xeon (extern L2 cache) processor model - strcpy(CPUInfo.strModel, "Intel Pentium III/Pentium III Xeon - external L2 cache, 0.25 micron"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium III/Pentium III Xeon - external L2 cache, 0.25 micron"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium III/Pentium III Xeon (0.25 micron process) with external L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; case 8: // Model = 8: Pentium III/Xeon/Celeron (256 KB on-die L2 cache) processor model - strcpy(CPUInfo.strModel, "Intel Pentium III/Celeron/Pentium III Xeon - internal L2 cache, 0.18 micron"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium III/Celeron/Pentium III Xeon - internal L2 cache, 0.18 micron"); /*Flawfinder: ignore*/ // We want to know it exactly: - switch (CPUInfo.uiBrandID) + switch (cpu_info.uiBrandID) { case 1: // Model = 8, Brand id = 1: Celeron (on-die L2 cache) processor model strncat(strCPUName, "Intel Celeron (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ @@ -461,13 +962,13 @@ bool CProcessor::AnalyzeIntelProcessor() } break; case 9: // Model = 9: Intel Pentium M processor, Intel Celeron M processor, model 9 - strcpy(CPUInfo.strModel, "Intel Pentium M Series Processor"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium M Series Processor"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium M Series Processor", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; case 0xA: // Model = 0xA: Pentium III/Xeon/Celeron (1 or 2 MB on-die L2 cache) processor model - strcpy(CPUInfo.strModel, "Intel Pentium III/Celeron/Pentium III Xeon - internal L2 cache, 0.18 micron"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium III/Celeron/Pentium III Xeon - internal L2 cache, 0.18 micron"); /*Flawfinder: ignore*/ // Exact detection: - switch (CPUInfo.uiBrandID) + switch (cpu_info.uiBrandID) { case 1: // Model = 0xA, Brand id = 1: Celeron (1 or 2 MB on-die L2 cache (does it exist??)) processor model strncat(strCPUName, "Intel Celeron (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ @@ -484,9 +985,9 @@ bool CProcessor::AnalyzeIntelProcessor() } break; case 0xB: // Model = 0xB: Pentium III/Xeon/Celeron (Tualatin core, on-die cache) processor model - strcpy(CPUInfo.strModel, "Intel Pentium III/Celeron/Pentium III Xeon - internal L2 cache, 0.13 micron"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium III/Celeron/Pentium III Xeon - internal L2 cache, 0.13 micron"); /*Flawfinder: ignore*/ // Omniscient: ;-) - switch (CPUInfo.uiBrandID) + switch (cpu_info.uiBrandID) { case 3: // Model = 0xB, Brand id = 3: Celeron (Tualatin core) processor model strncat(strCPUName, "Intel Celeron (Tualatin core, 0.13 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ @@ -503,72 +1004,72 @@ bool CProcessor::AnalyzeIntelProcessor() } break; case 0xD: // Model = 0xD: Intel Pentium M processor, Intel Celeron M processor, model D - strcpy(CPUInfo.strModel, "Intel Pentium M Series Processor"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium M Series Processor"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium M Series Processor", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; case 0xE: // Model = 0xE: Intel Core Duo processor, Intel Core Solo processor, model E - strcpy(CPUInfo.strModel, "Intel Core Series Processor"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Core Series Processor"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Core Series Processor", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; case 0xF: // Model = 0xF: Intel Core 2 Duo processor, model F - strcpy(CPUInfo.strModel, "Intel Core 2 Series Processor"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Core 2 Series Processor"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Core 2 Series Processor", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; default: // *more bored* - strcpy(CPUInfo.strModel, "Unknown Intel Pentium Pro/2/3, Core"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Unknown Intel Pentium Pro/2/3, Core"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium Pro/2/3, Core (Unknown model)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; } break; case 15: // Extended processor family // Masking the extended model - CPUInfo.uiExtendedModel = (eaxreg >> 16) & 0xFF; - switch (CPUInfo.uiModel) + cpu_info.uiExtendedModel = (eaxreg >> 16) & 0xFF; + switch (cpu_info.uiModel) { case 0: // Model = 0: Pentium 4 Willamette (A-Step) core - if ((CPUInfo.uiBrandID) == 8) // Brand id = 8: P4 Willamette + if ((cpu_info.uiBrandID) == 8) // Brand id = 8: P4 Willamette { - strcpy(CPUInfo.strModel, "Intel Pentium 4 Willamette (A-Step)"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strModel, "Intel Pentium 4 Willamette (A-Step)"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium 4 Willamette (A-Step)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ } else // else Xeon { - strcpy(CPUInfo.strModel, "Intel Pentium 4 Willamette Xeon (A-Step)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel Pentium 4 Willamette Xeon (A-Step)"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel Pentium 4 Willamette Xeon (A-Step)", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */ } break; case 1: // Model = 1: Pentium 4 Willamette core - if ((CPUInfo.uiBrandID) == 8) // Brand id = 8: P4 Willamette + if ((cpu_info.uiBrandID) == 8) // Brand id = 8: P4 Willamette { - strcpy(CPUInfo.strModel, "Intel Pentium 4 Willamette"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel Pentium 4 Willamette"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel Pentium 4 Willamette", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */ } else // else Xeon { - strcpy(CPUInfo.strModel, "Intel Pentium 4 Willamette Xeon"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel Pentium 4 Willamette Xeon"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel Pentium 4 Willamette Xeon", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */ } break; case 2: // Model = 2: Pentium 4 Northwood core - if (((CPUInfo.uiBrandID) == 9) || ((CPUInfo.uiBrandID) == 0xA)) // P4 Willamette + if (((cpu_info.uiBrandID) == 9) || ((cpu_info.uiBrandID) == 0xA)) // P4 Willamette { - strcpy(CPUInfo.strModel, "Intel Pentium 4 Northwood"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel Pentium 4 Northwood"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel Pentium 4 Northwood", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */ } else // Xeon { - strcpy(CPUInfo.strModel, "Intel Pentium 4 Northwood Xeon"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Intel Pentium 4 Northwood Xeon"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel Pentium 4 Northwood Xeon", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */ } break; default: // Silly stupid never used failsave option - strcpy(CPUInfo.strModel, "Unknown Intel Pentium 4"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Unknown Intel Pentium 4"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel Pentium 4 (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */ break; } break; default: // *grmpf* - strcpy(CPUInfo.strModel, "Unknown Intel model"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Unknown Intel model"); /* Flawfinder: ignore */ strncat(strCPUName, "Intel (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */ break; } @@ -576,7 +1077,7 @@ bool CProcessor::AnalyzeIntelProcessor() // After the long processor model block we now come to the processors serial // number. // First of all we check if the processor supports the serial number - if (CPUInfo.MaxSupportedLevel >= 3) + if (cpu_info.MaxSupportedLevel >= 3) { // If it supports the serial number CPUID level 0x00000003 we read the data unsigned long sig1, sig2, sig3; @@ -592,8 +1093,8 @@ bool CProcessor::AnalyzeIntelProcessor() } // Then we convert the data to a readable string snprintf( /* Flawfinder: ignore */ - CPUInfo.strProcessorSerial, - sizeof(CPUInfo.strProcessorSerial), + cpu_info.strProcessorSerial, + sizeof(cpu_info.strProcessorSerial), "%04lX-%04lX-%04lX-%04lX-%04lX-%04lX", sig1 >> 16, sig1 & 0xFFFF, @@ -605,8 +1106,8 @@ bool CProcessor::AnalyzeIntelProcessor() { // If there's no serial number support we just put "No serial number" snprintf( /* Flawfinder: ignore */ - CPUInfo.strProcessorSerial, - sizeof(CPUInfo.strProcessorSerial), + cpu_info.strProcessorSerial, + sizeof(cpu_info.strProcessorSerial), "No Processor Serial Number"); } @@ -649,13 +1150,13 @@ bool CProcessor::AnalyzeAMDProcessor() } // Then we mask the model, family, stepping and type (AMD does not support brand id) - CPUInfo.uiStepping = eaxreg & 0xF; - CPUInfo.uiModel = (eaxreg >> 4) & 0xF; - CPUInfo.uiFamily = (eaxreg >> 8) & 0xF; - CPUInfo.uiType = (eaxreg >> 12) & 0x3; + cpu_info.uiStepping = eaxreg & 0xF; + cpu_info.uiModel = (eaxreg >> 4) & 0xF; + cpu_info.uiFamily = (eaxreg >> 8) & 0xF; + cpu_info.uiType = (eaxreg >> 12) & 0x3; // Now we check if the processor supports the brand id string extended CPUID level - if (CPUInfo.MaxSupportedExtendedLevel >= 0x80000004) + if (cpu_info.MaxSupportedExtendedLevel >= 0x80000004) { // If it supports the extended CPUID level 0x80000004 we read the data char tmp[52]; /* Flawfinder: ignore */ @@ -682,181 +1183,181 @@ bool CProcessor::AnalyzeAMDProcessor() mov dword ptr [tmp+44], edx } // And copy it to the brand id string - strncpy(CPUInfo.strBrandID, tmp,sizeof(CPUInfo.strBrandID)-1); - CPUInfo.strBrandID[sizeof(CPUInfo.strBrandID)-1]='\0'; + strncpy(cpu_info.strBrandID, tmp,sizeof(cpu_info.strBrandID)-1); + cpu_info.strBrandID[sizeof(cpu_info.strBrandID)-1]='\0'; } else { // Or just tell there is no brand id string support - strcpy(CPUInfo.strBrandID, ""); /* Flawfinder: ignore */ + strcpy(cpu_info.strBrandID, ""); /* Flawfinder: ignore */ } // After that we translate the processor family - switch(CPUInfo.uiFamily) + switch(cpu_info.uiFamily) { case 4: // Family = 4: 486 (80486) or 5x86 (80486) processor family - switch (CPUInfo.uiModel) + switch (cpu_info.uiModel) { case 3: // Thanks to AMD for this nice form of family case 7: // detection.... *grmpf* case 8: case 9: - strcpy(CPUInfo.strFamily, "AMD 80486"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "AMD 80486"); /* Flawfinder: ignore */ break; case 0xE: case 0xF: - strcpy(CPUInfo.strFamily, "AMD 5x86"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "AMD 5x86"); /* Flawfinder: ignore */ break; default: - strcpy(CPUInfo.strFamily, "Unknown family"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "Unknown family"); /* Flawfinder: ignore */ break; } break; case 5: // Family = 5: K5 or K6 processor family - switch (CPUInfo.uiModel) + switch (cpu_info.uiModel) { case 0: case 1: case 2: case 3: - strcpy(CPUInfo.strFamily, "AMD K5"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "AMD K5"); /* Flawfinder: ignore */ break; case 6: case 7: case 8: case 9: - strcpy(CPUInfo.strFamily, "AMD K6"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "AMD K6"); /* Flawfinder: ignore */ break; default: - strcpy(CPUInfo.strFamily, "Unknown family"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "Unknown family"); /* Flawfinder: ignore */ break; } break; case 6: // Family = 6: K7 (Athlon, ...) processor family - strcpy(CPUInfo.strFamily, "AMD K7"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "AMD K7"); /* Flawfinder: ignore */ break; default: // For security - strcpy(CPUInfo.strFamily, "Unknown family"); /* Flawfinder: ignore */ + strcpy(cpu_info.strFamily, "Unknown family"); /* Flawfinder: ignore */ break; } // After the family detection we come to the specific processor model // detection - switch (CPUInfo.uiFamily) + switch (cpu_info.uiFamily) { case 4: // Family = 4: 486 (80486) or 5x85 (80486) processor family - switch (CPUInfo.uiModel) + switch (cpu_info.uiModel) { case 3: // Model = 3: 80486 DX2 - strcpy(CPUInfo.strModel, "AMD 80486 DX2"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD 80486 DX2"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD 80486 DX2", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 7: // Model = 7: 80486 write-back enhanced DX2 - strcpy(CPUInfo.strModel, "AMD 80486 write-back enhanced DX2"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD 80486 write-back enhanced DX2"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD 80486 write-back enhanced DX2", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 8: // Model = 8: 80486 DX4 - strcpy(CPUInfo.strModel, "AMD 80486 DX4"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD 80486 DX4"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD 80486 DX4", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 9: // Model = 9: 80486 write-back enhanced DX4 - strcpy(CPUInfo.strModel, "AMD 80486 write-back enhanced DX4"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD 80486 write-back enhanced DX4"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD 80486 write-back enhanced DX4", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 0xE: // Model = 0xE: 5x86 - strcpy(CPUInfo.strModel, "AMD 5x86"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD 5x86"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD 5x86", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 0xF: // Model = 0xF: 5x86 write-back enhanced (oh my god.....) - strcpy(CPUInfo.strModel, "AMD 5x86 write-back enhanced"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD 5x86 write-back enhanced"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD 5x86 write-back enhanced", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; default: // ... - strcpy(CPUInfo.strModel, "Unknown AMD 80486 or 5x86 model"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Unknown AMD 80486 or 5x86 model"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD 80486 or 5x86 (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; } break; case 5: // Family = 5: K5 / K6 processor family - switch (CPUInfo.uiModel) + switch (cpu_info.uiModel) { case 0: // Model = 0: K5 SSA 5 (Pentium Rating *ggg* 75, 90 and 100 Mhz) - strcpy(CPUInfo.strModel, "AMD K5 SSA5 (PR75, PR90, PR100)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD K5 SSA5 (PR75, PR90, PR100)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD K5 SSA5 (PR75, PR90, PR100)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 1: // Model = 1: K5 5k86 (PR 120 and 133 MHz) - strcpy(CPUInfo.strModel, "AMD K5 5k86 (PR120, PR133)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD K5 5k86 (PR120, PR133)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD K5 5k86 (PR120, PR133)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 2: // Model = 2: K5 5k86 (PR 166 MHz) - strcpy(CPUInfo.strModel, "AMD K5 5k86 (PR166)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD K5 5k86 (PR166)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD K5 5k86 (PR166)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 3: // Model = 3: K5 5k86 (PR 200 MHz) - strcpy(CPUInfo.strModel, "AMD K5 5k86 (PR200)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD K5 5k86 (PR200)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD K5 5k86 (PR200)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 6: // Model = 6: K6 - strcpy(CPUInfo.strModel, "AMD K6 (0.30 micron)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD K6 (0.30 micron)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD K6 (0.30 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 7: // Model = 7: K6 (0.25 micron) - strcpy(CPUInfo.strModel, "AMD K6 (0.25 micron)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD K6 (0.25 micron)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD K6 (0.25 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 8: // Model = 8: K6-2 - strcpy(CPUInfo.strModel, "AMD K6-2"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD K6-2"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD K6-2", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 9: // Model = 9: K6-III - strcpy(CPUInfo.strModel, "AMD K6-III"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD K6-III"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD K6-III", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 0xD: // Model = 0xD: K6-2+ / K6-III+ - strcpy(CPUInfo.strModel, "AMD K6-2+ or K6-III+ (0.18 micron)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD K6-2+ or K6-III+ (0.18 micron)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD K6-2+ or K6-III+ (0.18 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; default: // ... - strcpy(CPUInfo.strModel, "Unknown AMD K5 or K6 model"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Unknown AMD K5 or K6 model"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD K5 or K6 (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; } break; case 6: // Family = 6: K7 processor family (AMDs first good processors) - switch (CPUInfo.uiModel) + switch (cpu_info.uiModel) { case 1: // Athlon - strcpy(CPUInfo.strModel, "AMD Athlon (0.25 micron)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD Athlon (0.25 micron)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD Athlon (0.25 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 2: // Athlon (0.18 micron) - strcpy(CPUInfo.strModel, "AMD Athlon (0.18 micron)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD Athlon (0.18 micron)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD Athlon (0.18 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 3: // Duron (Spitfire core) - strcpy(CPUInfo.strModel, "AMD Duron (Spitfire)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD Duron (Spitfire)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD Duron (Spitfire core)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 4: // Athlon (Thunderbird core) - strcpy(CPUInfo.strModel, "AMD Athlon (Thunderbird)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD Athlon (Thunderbird)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD Athlon (Thunderbird core)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 6: // Athlon MP / Mobile Athlon (Palomino core) - strcpy(CPUInfo.strModel, "AMD Athlon MP/Mobile Athlon (Palomino)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD Athlon MP/Mobile Athlon (Palomino)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD Athlon MP/Mobile Athlon (Palomino core)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; case 7: // Mobile Duron (Morgan core) - strcpy(CPUInfo.strModel, "AMD Mobile Duron (Morgan)"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "AMD Mobile Duron (Morgan)"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD Mobile Duron (Morgan core)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; default: // ... - strcpy(CPUInfo.strModel, "Unknown AMD K7 model"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Unknown AMD K7 model"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD K7 (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; } break; default: // ... - strcpy(CPUInfo.strModel, "Unknown AMD model"); /* Flawfinder: ignore */ + strcpy(cpu_info.strModel, "Unknown AMD model"); /* Flawfinder: ignore */ strncat(strCPUName, "AMD (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */ break; } @@ -866,7 +1367,7 @@ bool CProcessor::AnalyzeAMDProcessor() GetStandardProcessorExtensions(); // Then we check if theres an extended CPUID level support - if (CPUInfo.MaxSupportedExtendedLevel >= 0x80000001) + if (cpu_info.MaxSupportedExtendedLevel >= 0x80000001) { // If we can access the extended CPUID level 0x80000001 we get the // edx register @@ -878,15 +1379,15 @@ bool CProcessor::AnalyzeAMDProcessor() } // Now we can mask some AMD specific cpu extensions - CPUInfo._Ext.EMMX_MultimediaExtensions = CheckBit(edxreg, 22); - CPUInfo._Ext.AA64_AMD64BitArchitecture = CheckBit(edxreg, 29); - CPUInfo._Ext._E3DNOW_InstructionExtensions = CheckBit(edxreg, 30); - CPUInfo._Ext._3DNOW_InstructionExtensions = CheckBit(edxreg, 31); + cpu_info._Ext.EMMX_MultimediaExtensions = CheckBit(edxreg, 22); + cpu_info._Ext.AA64_AMD64BitArchitecture = CheckBit(edxreg, 29); + cpu_info._Ext._E3DNOW_InstructionExtensions = CheckBit(edxreg, 30); + cpu_info._Ext._3DNOW_InstructionExtensions = CheckBit(edxreg, 31); } // After that we check if the processor supports the ext. CPUID level // 0x80000006 - if (CPUInfo.MaxSupportedExtendedLevel >= 0x80000006) + if (cpu_info.MaxSupportedExtendedLevel >= 0x80000006) { // If it's present, we read it out __asm @@ -902,68 +1403,68 @@ bool CProcessor::AnalyzeAMDProcessor() // Then we mask the L1 Data TLB information if ((ebxreg >> 16) && (eaxreg >> 16)) { - CPUInfo._Data.bPresent = true; - strcpy(CPUInfo._Data.strPageSize, "4 KB / 2 MB / 4MB"); /*Flawfinder: ignore*/ - CPUInfo._Data.uiAssociativeWays = (eaxreg >> 24) & 0xFF; - CPUInfo._Data.uiEntries = (eaxreg >> 16) & 0xFF; + cpu_info._Data.bPresent = true; + strcpy(cpu_info._Data.strPageSize, "4 KB / 2 MB / 4MB"); /*Flawfinder: ignore*/ + cpu_info._Data.uiAssociativeWays = (eaxreg >> 24) & 0xFF; + cpu_info._Data.uiEntries = (eaxreg >> 16) & 0xFF; } else if (eaxreg >> 16) { - CPUInfo._Data.bPresent = true; - strcpy(CPUInfo._Data.strPageSize, "2 MB / 4MB"); /*Flawfinder: ignore*/ - CPUInfo._Data.uiAssociativeWays = (eaxreg >> 24) & 0xFF; - CPUInfo._Data.uiEntries = (eaxreg >> 16) & 0xFF; + cpu_info._Data.bPresent = true; + strcpy(cpu_info._Data.strPageSize, "2 MB / 4MB"); /*Flawfinder: ignore*/ + cpu_info._Data.uiAssociativeWays = (eaxreg >> 24) & 0xFF; + cpu_info._Data.uiEntries = (eaxreg >> 16) & 0xFF; } else if (ebxreg >> 16) { - CPUInfo._Data.bPresent = true; - strcpy(CPUInfo._Data.strPageSize, "4 KB"); /*Flawfinder: ignore*/ - CPUInfo._Data.uiAssociativeWays = (ebxreg >> 24) & 0xFF; - CPUInfo._Data.uiEntries = (ebxreg >> 16) & 0xFF; + cpu_info._Data.bPresent = true; + strcpy(cpu_info._Data.strPageSize, "4 KB"); /*Flawfinder: ignore*/ + cpu_info._Data.uiAssociativeWays = (ebxreg >> 24) & 0xFF; + cpu_info._Data.uiEntries = (ebxreg >> 16) & 0xFF; } - if (CPUInfo._Data.uiAssociativeWays == 0xFF) - CPUInfo._Data.uiAssociativeWays = (unsigned int) -1; + if (cpu_info._Data.uiAssociativeWays == 0xFF) + cpu_info._Data.uiAssociativeWays = (unsigned int) -1; // Now the L1 Instruction/Code TLB information if ((ebxreg & 0xFFFF) && (eaxreg & 0xFFFF)) { - CPUInfo._Instruction.bPresent = true; - strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4MB"); /*Flawfinder: ignore*/ - CPUInfo._Instruction.uiAssociativeWays = (eaxreg >> 8) & 0xFF; - CPUInfo._Instruction.uiEntries = eaxreg & 0xFF; + cpu_info._Instruction.bPresent = true; + strcpy(cpu_info._Instruction.strPageSize, "4 KB / 2 MB / 4MB"); /*Flawfinder: ignore*/ + cpu_info._Instruction.uiAssociativeWays = (eaxreg >> 8) & 0xFF; + cpu_info._Instruction.uiEntries = eaxreg & 0xFF; } else if (eaxreg & 0xFFFF) { - CPUInfo._Instruction.bPresent = true; - strcpy(CPUInfo._Instruction.strPageSize, "2 MB / 4MB"); /*Flawfinder: ignore*/ - CPUInfo._Instruction.uiAssociativeWays = (eaxreg >> 8) & 0xFF; - CPUInfo._Instruction.uiEntries = eaxreg & 0xFF; + cpu_info._Instruction.bPresent = true; + strcpy(cpu_info._Instruction.strPageSize, "2 MB / 4MB"); /*Flawfinder: ignore*/ + cpu_info._Instruction.uiAssociativeWays = (eaxreg >> 8) & 0xFF; + cpu_info._Instruction.uiEntries = eaxreg & 0xFF; } else if (ebxreg & 0xFFFF) { - CPUInfo._Instruction.bPresent = true; - strcpy(CPUInfo._Instruction.strPageSize, "4 KB"); /*Flawfinder: ignore*/ - CPUInfo._Instruction.uiAssociativeWays = (ebxreg >> 8) & 0xFF; - CPUInfo._Instruction.uiEntries = ebxreg & 0xFF; + cpu_info._Instruction.bPresent = true; + strcpy(cpu_info._Instruction.strPageSize, "4 KB"); /*Flawfinder: ignore*/ + cpu_info._Instruction.uiAssociativeWays = (ebxreg >> 8) & 0xFF; + cpu_info._Instruction.uiEntries = ebxreg & 0xFF; } - if (CPUInfo._Instruction.uiAssociativeWays == 0xFF) - CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1; + if (cpu_info._Instruction.uiAssociativeWays == 0xFF) + cpu_info._Instruction.uiAssociativeWays = (unsigned int) -1; // Then we read the L1 data cache information if ((ecxreg >> 24) > 0) { - CPUInfo._L1.Data.bPresent = true; - snprintf(CPUInfo._L1.Data.strSize, sizeof(CPUInfo._L1.Data.strSize), "%d KB", ecxreg >> 24); /* Flawfinder: ignore */ - CPUInfo._L1.Data.uiAssociativeWays = (ecxreg >> 15) & 0xFF; - CPUInfo._L1.Data.uiLineSize = ecxreg & 0xFF; + cpu_info._L1.Data.bPresent = true; + snprintf(cpu_info._L1.Data.strSize, sizeof(cpu_info._L1.Data.strSize), "%d KB", ecxreg >> 24); /* Flawfinder: ignore */ + cpu_info._L1.Data.uiAssociativeWays = (ecxreg >> 15) & 0xFF; + cpu_info._L1.Data.uiLineSize = ecxreg & 0xFF; } // After that we read the L2 instruction/code cache information if ((edxreg >> 24) > 0) { - CPUInfo._L1.Instruction.bPresent = true; - snprintf(CPUInfo._L1.Instruction.strSize, sizeof(CPUInfo._L1.Instruction.strSize), "%d KB", edxreg >> 24); /* Flawfinder: ignore */ - CPUInfo._L1.Instruction.uiAssociativeWays = (edxreg >> 15) & 0xFF; - CPUInfo._L1.Instruction.uiLineSize = edxreg & 0xFF; + cpu_info._L1.Instruction.bPresent = true; + snprintf(cpu_info._L1.Instruction.strSize, sizeof(cpu_info._L1.Instruction.strSize), "%d KB", edxreg >> 24); /* Flawfinder: ignore */ + cpu_info._L1.Instruction.uiAssociativeWays = (edxreg >> 15) & 0xFF; + cpu_info._L1.Instruction.uiLineSize = edxreg & 0xFF; } // Note: I'm not absolutely sure that the L1 page size code (the @@ -984,33 +1485,33 @@ bool CProcessor::AnalyzeAMDProcessor() // L2 cache that is divided in data and code parts) if (((ecxreg >> 12) & 0xF) > 0) { - CPUInfo._L2.bPresent = true; - snprintf(CPUInfo._L2.strSize, sizeof(CPUInfo._L2.strSize), "%d KB", ecxreg >> 16); /* Flawfinder: ignore */ + cpu_info._L2.bPresent = true; + snprintf(cpu_info._L2.strSize, sizeof(cpu_info._L2.strSize), "%d KB", ecxreg >> 16); /* Flawfinder: ignore */ switch ((ecxreg >> 12) & 0xF) { case 1: - CPUInfo._L2.uiAssociativeWays = 1; + cpu_info._L2.uiAssociativeWays = 1; break; case 2: - CPUInfo._L2.uiAssociativeWays = 2; + cpu_info._L2.uiAssociativeWays = 2; break; case 4: - CPUInfo._L2.uiAssociativeWays = 4; + cpu_info._L2.uiAssociativeWays = 4; break; case 6: - CPUInfo._L2.uiAssociativeWays = 8; + cpu_info._L2.uiAssociativeWays = 8; break; case 8: - CPUInfo._L2.uiAssociativeWays = 16; + cpu_info._L2.uiAssociativeWays = 16; break; case 0xF: - CPUInfo._L2.uiAssociativeWays = (unsigned int) -1; + cpu_info._L2.uiAssociativeWays = (unsigned int) -1; break; default: - CPUInfo._L2.uiAssociativeWays = 0; + cpu_info._L2.uiAssociativeWays = 0; break; } - CPUInfo._L2.uiLineSize = ecxreg & 0xFF; + cpu_info._L2.uiLineSize = ecxreg & 0xFF; } } else @@ -1052,13 +1553,13 @@ bool CProcessor::AnalyzeUnknownProcessor() mov ebxreg, ebx } // Then we mask the processor model, family, type and stepping - CPUInfo.uiStepping = eaxreg & 0xF; - CPUInfo.uiModel = (eaxreg >> 4) & 0xF; - CPUInfo.uiFamily = (eaxreg >> 8) & 0xF; - CPUInfo.uiType = (eaxreg >> 12) & 0x3; + cpu_info.uiStepping = eaxreg & 0xF; + cpu_info.uiModel = (eaxreg >> 4) & 0xF; + cpu_info.uiFamily = (eaxreg >> 8) & 0xF; + cpu_info.uiType = (eaxreg >> 12) & 0x3; // To have complete information we also mask the brand id - CPUInfo.uiBrandID = ebxreg & 0xF; + cpu_info.uiBrandID = ebxreg & 0xF; // Then we get the standard processor extensions GetStandardProcessorExtensions(); @@ -1066,21 +1567,21 @@ bool CProcessor::AnalyzeUnknownProcessor() // Now we mark everything we do not know as unknown strcpy(strCPUName, "Unknown"); /*Flawfinder: ignore*/ - strcpy(CPUInfo._Data.strTLB, "Unknown"); /*Flawfinder: ignore*/ - strcpy(CPUInfo._Instruction.strTLB, "Unknown"); /*Flawfinder: ignore*/ + strcpy(cpu_info._Data.strTLB, "Unknown"); /*Flawfinder: ignore*/ + strcpy(cpu_info._Instruction.strTLB, "Unknown"); /*Flawfinder: ignore*/ - strcpy(CPUInfo._Trace.strCache, "Unknown"); /*Flawfinder: ignore*/ - strcpy(CPUInfo._L1.Data.strCache, "Unknown"); /*Flawfinder: ignore*/ - strcpy(CPUInfo._L1.Instruction.strCache, "Unknown"); /*Flawfinder: ignore*/ - strcpy(CPUInfo._L2.strCache, "Unknown"); /*Flawfinder: ignore*/ - strcpy(CPUInfo._L3.strCache, "Unknown"); /*Flawfinder: ignore*/ + strcpy(cpu_info._Trace.strCache, "Unknown"); /*Flawfinder: ignore*/ + strcpy(cpu_info._L1.Data.strCache, "Unknown"); /*Flawfinder: ignore*/ + strcpy(cpu_info._L1.Instruction.strCache, "Unknown"); /*Flawfinder: ignore*/ + strcpy(cpu_info._L2.strCache, "Unknown"); /*Flawfinder: ignore*/ + strcpy(cpu_info._L3.strCache, "Unknown"); /*Flawfinder: ignore*/ - strcpy(CPUInfo.strProcessorSerial, "Unknown / Not supported"); /*Flawfinder: ignore*/ + strcpy(cpu_info.strProcessorSerial, "Unknown / Not supported"); /*Flawfinder: ignore*/ // For the family, model and brand id we can only print the numeric value - snprintf(CPUInfo.strBrandID, sizeof(CPUInfo.strBrandID), "Brand-ID number %d", CPUInfo.uiBrandID); /* Flawfinder: ignore */ - snprintf(CPUInfo.strFamily, sizeof(CPUInfo.strFamily), "Family number %d", CPUInfo.uiFamily); /* Flawfinder: ignore */ - snprintf(CPUInfo.strModel, sizeof(CPUInfo.strModel), "Model number %d", CPUInfo.uiModel); /* Flawfinder: ignore */ + snprintf(cpu_info.strBrandID, sizeof(cpu_info.strBrandID), "Brand-ID number %d", cpu_info.uiBrandID); /* Flawfinder: ignore */ + snprintf(cpu_info.strFamily, sizeof(cpu_info.strFamily), "Family number %d", cpu_info.uiFamily); /* Flawfinder: ignore */ + snprintf(cpu_info.strModel, sizeof(cpu_info.strModel), "Model number %d", cpu_info.uiModel); /* Flawfinder: ignore */ // And thats it return true; @@ -1137,239 +1638,239 @@ void CProcessor::DecodeProcessorConfiguration(unsigned int cfg) case 0: // cfg = 0: Unused break; case 0x1: // cfg = 0x1: code TLB present, 4 KB pages, 4 ways, 32 entries - CPUInfo._Instruction.bPresent = true; - strcpy(CPUInfo._Instruction.strPageSize, "4 KB"); /*Flawfinder: ignore*/ - CPUInfo._Instruction.uiAssociativeWays = 4; - CPUInfo._Instruction.uiEntries = 32; + cpu_info._Instruction.bPresent = true; + strcpy(cpu_info._Instruction.strPageSize, "4 KB"); /*Flawfinder: ignore*/ + cpu_info._Instruction.uiAssociativeWays = 4; + cpu_info._Instruction.uiEntries = 32; break; case 0x2: // cfg = 0x2: code TLB present, 4 MB pages, fully associative, 2 entries - CPUInfo._Instruction.bPresent = true; - strcpy(CPUInfo._Instruction.strPageSize, "4 MB"); /*Flawfinder: ignore*/ - CPUInfo._Instruction.uiAssociativeWays = 4; - CPUInfo._Instruction.uiEntries = 2; + cpu_info._Instruction.bPresent = true; + strcpy(cpu_info._Instruction.strPageSize, "4 MB"); /*Flawfinder: ignore*/ + cpu_info._Instruction.uiAssociativeWays = 4; + cpu_info._Instruction.uiEntries = 2; break; case 0x3: // cfg = 0x3: data TLB present, 4 KB pages, 4 ways, 64 entries - CPUInfo._Data.bPresent = true; - strcpy(CPUInfo._Data.strPageSize, "4 KB"); /*Flawfinder: ignore*/ - CPUInfo._Data.uiAssociativeWays = 4; - CPUInfo._Data.uiEntries = 64; + cpu_info._Data.bPresent = true; + strcpy(cpu_info._Data.strPageSize, "4 KB"); /*Flawfinder: ignore*/ + cpu_info._Data.uiAssociativeWays = 4; + cpu_info._Data.uiEntries = 64; break; case 0x4: // cfg = 0x4: data TLB present, 4 MB pages, 4 ways, 8 entries - CPUInfo._Data.bPresent = true; - strcpy(CPUInfo._Data.strPageSize, "4 MB"); /*Flawfinder: ignore*/ - CPUInfo._Data.uiAssociativeWays = 4; - CPUInfo._Data.uiEntries = 8; + cpu_info._Data.bPresent = true; + strcpy(cpu_info._Data.strPageSize, "4 MB"); /*Flawfinder: ignore*/ + cpu_info._Data.uiAssociativeWays = 4; + cpu_info._Data.uiEntries = 8; break; case 0x6: // cfg = 0x6: code L1 cache present, 8 KB, 4 ways, 32 byte lines - CPUInfo._L1.Instruction.bPresent = true; - strcpy(CPUInfo._L1.Instruction.strSize, "8 KB"); /*Flawfinder: ignore*/ - CPUInfo._L1.Instruction.uiAssociativeWays = 4; - CPUInfo._L1.Instruction.uiLineSize = 32; + cpu_info._L1.Instruction.bPresent = true; + strcpy(cpu_info._L1.Instruction.strSize, "8 KB"); /*Flawfinder: ignore*/ + cpu_info._L1.Instruction.uiAssociativeWays = 4; + cpu_info._L1.Instruction.uiLineSize = 32; break; case 0x8: // cfg = 0x8: code L1 cache present, 16 KB, 4 ways, 32 byte lines - CPUInfo._L1.Instruction.bPresent = true; - strcpy(CPUInfo._L1.Instruction.strSize, "16 KB"); /*Flawfinder: ignore*/ - CPUInfo._L1.Instruction.uiAssociativeWays = 4; - CPUInfo._L1.Instruction.uiLineSize = 32; + cpu_info._L1.Instruction.bPresent = true; + strcpy(cpu_info._L1.Instruction.strSize, "16 KB"); /*Flawfinder: ignore*/ + cpu_info._L1.Instruction.uiAssociativeWays = 4; + cpu_info._L1.Instruction.uiLineSize = 32; break; case 0xA: // cfg = 0xA: data L1 cache present, 8 KB, 2 ways, 32 byte lines - CPUInfo._L1.Data.bPresent = true; - strcpy(CPUInfo._L1.Data.strSize, "8 KB"); /*Flawfinder: ignore*/ - CPUInfo._L1.Data.uiAssociativeWays = 2; - CPUInfo._L1.Data.uiLineSize = 32; + cpu_info._L1.Data.bPresent = true; + strcpy(cpu_info._L1.Data.strSize, "8 KB"); /*Flawfinder: ignore*/ + cpu_info._L1.Data.uiAssociativeWays = 2; + cpu_info._L1.Data.uiLineSize = 32; break; case 0xC: // cfg = 0xC: data L1 cache present, 16 KB, 4 ways, 32 byte lines - CPUInfo._L1.Data.bPresent = true; - strcpy(CPUInfo._L1.Data.strSize, "16 KB"); /*Flawfinder: ignore*/ - CPUInfo._L1.Data.uiAssociativeWays = 4; - CPUInfo._L1.Data.uiLineSize = 32; + cpu_info._L1.Data.bPresent = true; + strcpy(cpu_info._L1.Data.strSize, "16 KB"); /*Flawfinder: ignore*/ + cpu_info._L1.Data.uiAssociativeWays = 4; + cpu_info._L1.Data.uiLineSize = 32; break; case 0x22: // cfg = 0x22: code and data L3 cache present, 512 KB, 4 ways, 64 byte lines, sectored - CPUInfo._L3.bPresent = true; - strcpy(CPUInfo._L3.strSize, "512 KB"); /*Flawfinder: ignore*/ - CPUInfo._L3.uiAssociativeWays = 4; - CPUInfo._L3.uiLineSize = 64; - CPUInfo._L3.bSectored = true; + cpu_info._L3.bPresent = true; + strcpy(cpu_info._L3.strSize, "512 KB"); /*Flawfinder: ignore*/ + cpu_info._L3.uiAssociativeWays = 4; + cpu_info._L3.uiLineSize = 64; + cpu_info._L3.bSectored = true; break; case 0x23: // cfg = 0x23: code and data L3 cache present, 1024 KB, 8 ways, 64 byte lines, sectored - CPUInfo._L3.bPresent = true; - strcpy(CPUInfo._L3.strSize, "1024 KB"); /*Flawfinder: ignore*/ - CPUInfo._L3.uiAssociativeWays = 8; - CPUInfo._L3.uiLineSize = 64; - CPUInfo._L3.bSectored = true; + cpu_info._L3.bPresent = true; + strcpy(cpu_info._L3.strSize, "1024 KB"); /*Flawfinder: ignore*/ + cpu_info._L3.uiAssociativeWays = 8; + cpu_info._L3.uiLineSize = 64; + cpu_info._L3.bSectored = true; break; case 0x25: // cfg = 0x25: code and data L3 cache present, 2048 KB, 8 ways, 64 byte lines, sectored - CPUInfo._L3.bPresent = true; - strcpy(CPUInfo._L3.strSize, "2048 KB"); /*Flawfinder: ignore*/ - CPUInfo._L3.uiAssociativeWays = 8; - CPUInfo._L3.uiLineSize = 64; - CPUInfo._L3.bSectored = true; + cpu_info._L3.bPresent = true; + strcpy(cpu_info._L3.strSize, "2048 KB"); /*Flawfinder: ignore*/ + cpu_info._L3.uiAssociativeWays = 8; + cpu_info._L3.uiLineSize = 64; + cpu_info._L3.bSectored = true; break; case 0x29: // cfg = 0x29: code and data L3 cache present, 4096 KB, 8 ways, 64 byte lines, sectored - CPUInfo._L3.bPresent = true; - strcpy(CPUInfo._L3.strSize, "4096 KB"); /*Flawfinder: ignore*/ - CPUInfo._L3.uiAssociativeWays = 8; - CPUInfo._L3.uiLineSize = 64; - CPUInfo._L3.bSectored = true; + cpu_info._L3.bPresent = true; + strcpy(cpu_info._L3.strSize, "4096 KB"); /*Flawfinder: ignore*/ + cpu_info._L3.uiAssociativeWays = 8; + cpu_info._L3.uiLineSize = 64; + cpu_info._L3.bSectored = true; break; case 0x40: // cfg = 0x40: no integrated L2 cache (P6 core) or L3 cache (P4 core) break; case 0x41: // cfg = 0x41: code and data L2 cache present, 128 KB, 4 ways, 32 byte lines - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "128 KB"); /*Flawfinder: ignore*/ - CPUInfo._L2.uiAssociativeWays = 4; - CPUInfo._L2.uiLineSize = 32; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "128 KB"); /*Flawfinder: ignore*/ + cpu_info._L2.uiAssociativeWays = 4; + cpu_info._L2.uiLineSize = 32; break; case 0x42: // cfg = 0x42: code and data L2 cache present, 256 KB, 4 ways, 32 byte lines - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "256 KB"); /*Flawfinder: ignore*/ - CPUInfo._L2.uiAssociativeWays = 4; - CPUInfo._L2.uiLineSize = 32; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "256 KB"); /*Flawfinder: ignore*/ + cpu_info._L2.uiAssociativeWays = 4; + cpu_info._L2.uiLineSize = 32; break; case 0x43: // cfg = 0x43: code and data L2 cache present, 512 KB, 4 ways, 32 byte lines - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "512 KB"); /* Flawfinder: ignore */ - CPUInfo._L2.uiAssociativeWays = 4; - CPUInfo._L2.uiLineSize = 32; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "512 KB"); /* Flawfinder: ignore */ + cpu_info._L2.uiAssociativeWays = 4; + cpu_info._L2.uiLineSize = 32; break; case 0x44: // cfg = 0x44: code and data L2 cache present, 1024 KB, 4 ways, 32 byte lines - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "1 MB"); /* Flawfinder: ignore */ - CPUInfo._L2.uiAssociativeWays = 4; - CPUInfo._L2.uiLineSize = 32; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "1 MB"); /* Flawfinder: ignore */ + cpu_info._L2.uiAssociativeWays = 4; + cpu_info._L2.uiLineSize = 32; break; case 0x45: // cfg = 0x45: code and data L2 cache present, 2048 KB, 4 ways, 32 byte lines - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "2 MB"); /* Flawfinder: ignore */ - CPUInfo._L2.uiAssociativeWays = 4; - CPUInfo._L2.uiLineSize = 32; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "2 MB"); /* Flawfinder: ignore */ + cpu_info._L2.uiAssociativeWays = 4; + cpu_info._L2.uiLineSize = 32; break; case 0x50: // cfg = 0x50: code TLB present, 4 KB / 4 MB / 2 MB pages, fully associative, 64 entries - CPUInfo._Instruction.bPresent = true; - strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4 MB"); /* Flawfinder: ignore */ - CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1; - CPUInfo._Instruction.uiEntries = 64; + cpu_info._Instruction.bPresent = true; + strcpy(cpu_info._Instruction.strPageSize, "4 KB / 2 MB / 4 MB"); /* Flawfinder: ignore */ + cpu_info._Instruction.uiAssociativeWays = (unsigned int) -1; + cpu_info._Instruction.uiEntries = 64; break; case 0x51: // cfg = 0x51: code TLB present, 4 KB / 4 MB / 2 MB pages, fully associative, 128 entries - CPUInfo._Instruction.bPresent = true; - strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4 MB"); /* Flawfinder: ignore */ - CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1; - CPUInfo._Instruction.uiEntries = 128; + cpu_info._Instruction.bPresent = true; + strcpy(cpu_info._Instruction.strPageSize, "4 KB / 2 MB / 4 MB"); /* Flawfinder: ignore */ + cpu_info._Instruction.uiAssociativeWays = (unsigned int) -1; + cpu_info._Instruction.uiEntries = 128; break; case 0x52: // cfg = 0x52: code TLB present, 4 KB / 4 MB / 2 MB pages, fully associative, 256 entries - CPUInfo._Instruction.bPresent = true; - strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4 MB"); /* Flawfinder: ignore */ - CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1; - CPUInfo._Instruction.uiEntries = 256; + cpu_info._Instruction.bPresent = true; + strcpy(cpu_info._Instruction.strPageSize, "4 KB / 2 MB / 4 MB"); /* Flawfinder: ignore */ + cpu_info._Instruction.uiAssociativeWays = (unsigned int) -1; + cpu_info._Instruction.uiEntries = 256; break; case 0x5B: // cfg = 0x5B: data TLB present, 4 KB / 4 MB pages, fully associative, 64 entries - CPUInfo._Data.bPresent = true; - strcpy(CPUInfo._Data.strPageSize, "4 KB / 4 MB"); /* Flawfinder: ignore */ - CPUInfo._Data.uiAssociativeWays = (unsigned int) -1; - CPUInfo._Data.uiEntries = 64; + cpu_info._Data.bPresent = true; + strcpy(cpu_info._Data.strPageSize, "4 KB / 4 MB"); /* Flawfinder: ignore */ + cpu_info._Data.uiAssociativeWays = (unsigned int) -1; + cpu_info._Data.uiEntries = 64; break; case 0x5C: // cfg = 0x5C: data TLB present, 4 KB / 4 MB pages, fully associative, 128 entries - CPUInfo._Data.bPresent = true; - strcpy(CPUInfo._Data.strPageSize, "4 KB / 4 MB"); /* Flawfinder: ignore */ - CPUInfo._Data.uiAssociativeWays = (unsigned int) -1; - CPUInfo._Data.uiEntries = 128; + cpu_info._Data.bPresent = true; + strcpy(cpu_info._Data.strPageSize, "4 KB / 4 MB"); /* Flawfinder: ignore */ + cpu_info._Data.uiAssociativeWays = (unsigned int) -1; + cpu_info._Data.uiEntries = 128; break; case 0x5d: // cfg = 0x5D: data TLB present, 4 KB / 4 MB pages, fully associative, 256 entries - CPUInfo._Data.bPresent = true; - strcpy(CPUInfo._Data.strPageSize, "4 KB / 4 MB"); /* Flawfinder: ignore */ - CPUInfo._Data.uiAssociativeWays = (unsigned int) -1; - CPUInfo._Data.uiEntries = 256; + cpu_info._Data.bPresent = true; + strcpy(cpu_info._Data.strPageSize, "4 KB / 4 MB"); /* Flawfinder: ignore */ + cpu_info._Data.uiAssociativeWays = (unsigned int) -1; + cpu_info._Data.uiEntries = 256; break; case 0x66: // cfg = 0x66: data L1 cache present, 8 KB, 4 ways, 64 byte lines, sectored - CPUInfo._L1.Data.bPresent = true; - strcpy(CPUInfo._L1.Data.strSize, "8 KB"); /* Flawfinder: ignore */ - CPUInfo._L1.Data.uiAssociativeWays = 4; - CPUInfo._L1.Data.uiLineSize = 64; + cpu_info._L1.Data.bPresent = true; + strcpy(cpu_info._L1.Data.strSize, "8 KB"); /* Flawfinder: ignore */ + cpu_info._L1.Data.uiAssociativeWays = 4; + cpu_info._L1.Data.uiLineSize = 64; break; case 0x67: // cfg = 0x67: data L1 cache present, 16 KB, 4 ways, 64 byte lines, sectored - CPUInfo._L1.Data.bPresent = true; - strcpy(CPUInfo._L1.Data.strSize, "16 KB"); /* Flawfinder: ignore */ - CPUInfo._L1.Data.uiAssociativeWays = 4; - CPUInfo._L1.Data.uiLineSize = 64; + cpu_info._L1.Data.bPresent = true; + strcpy(cpu_info._L1.Data.strSize, "16 KB"); /* Flawfinder: ignore */ + cpu_info._L1.Data.uiAssociativeWays = 4; + cpu_info._L1.Data.uiLineSize = 64; break; case 0x68: // cfg = 0x68: data L1 cache present, 32 KB, 4 ways, 64 byte lines, sectored - CPUInfo._L1.Data.bPresent = true; - strcpy(CPUInfo._L1.Data.strSize, "32 KB"); /* Flawfinder: ignore */ - CPUInfo._L1.Data.uiAssociativeWays = 4; - CPUInfo._L1.Data.uiLineSize = 64; + cpu_info._L1.Data.bPresent = true; + strcpy(cpu_info._L1.Data.strSize, "32 KB"); /* Flawfinder: ignore */ + cpu_info._L1.Data.uiAssociativeWays = 4; + cpu_info._L1.Data.uiLineSize = 64; break; case 0x70: // cfg = 0x70: trace L1 cache present, 12 KuOPs, 4 ways - CPUInfo._Trace.bPresent = true; - strcpy(CPUInfo._Trace.strSize, "12 K-micro-ops"); /* Flawfinder: ignore */ - CPUInfo._Trace.uiAssociativeWays = 4; + cpu_info._Trace.bPresent = true; + strcpy(cpu_info._Trace.strSize, "12 K-micro-ops"); /* Flawfinder: ignore */ + cpu_info._Trace.uiAssociativeWays = 4; break; case 0x71: // cfg = 0x71: trace L1 cache present, 16 KuOPs, 4 ways - CPUInfo._Trace.bPresent = true; - strcpy(CPUInfo._Trace.strSize, "16 K-micro-ops"); /* Flawfinder: ignore */ - CPUInfo._Trace.uiAssociativeWays = 4; + cpu_info._Trace.bPresent = true; + strcpy(cpu_info._Trace.strSize, "16 K-micro-ops"); /* Flawfinder: ignore */ + cpu_info._Trace.uiAssociativeWays = 4; break; case 0x72: // cfg = 0x72: trace L1 cache present, 32 KuOPs, 4 ways - CPUInfo._Trace.bPresent = true; - strcpy(CPUInfo._Trace.strSize, "32 K-micro-ops"); /* Flawfinder: ignore */ - CPUInfo._Trace.uiAssociativeWays = 4; + cpu_info._Trace.bPresent = true; + strcpy(cpu_info._Trace.strSize, "32 K-micro-ops"); /* Flawfinder: ignore */ + cpu_info._Trace.uiAssociativeWays = 4; break; case 0x79: // cfg = 0x79: code and data L2 cache present, 128 KB, 8 ways, 64 byte lines, sectored - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "128 KB"); /* Flawfinder: ignore */ - CPUInfo._L2.uiAssociativeWays = 8; - CPUInfo._L2.uiLineSize = 64; - CPUInfo._L2.bSectored = true; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "128 KB"); /* Flawfinder: ignore */ + cpu_info._L2.uiAssociativeWays = 8; + cpu_info._L2.uiLineSize = 64; + cpu_info._L2.bSectored = true; break; case 0x7A: // cfg = 0x7A: code and data L2 cache present, 256 KB, 8 ways, 64 byte lines, sectored - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "256 KB"); /* Flawfinder: ignore */ - CPUInfo._L2.uiAssociativeWays = 8; - CPUInfo._L2.uiLineSize = 64; - CPUInfo._L2.bSectored = true; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "256 KB"); /* Flawfinder: ignore */ + cpu_info._L2.uiAssociativeWays = 8; + cpu_info._L2.uiLineSize = 64; + cpu_info._L2.bSectored = true; break; case 0x7B: // cfg = 0x7B: code and data L2 cache present, 512 KB, 8 ways, 64 byte lines, sectored - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "512 KB"); /* Flawfinder: ignore */ - CPUInfo._L2.uiAssociativeWays = 8; - CPUInfo._L2.uiLineSize = 64; - CPUInfo._L2.bSectored = true; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "512 KB"); /* Flawfinder: ignore */ + cpu_info._L2.uiAssociativeWays = 8; + cpu_info._L2.uiLineSize = 64; + cpu_info._L2.bSectored = true; break; case 0x7C: // cfg = 0x7C: code and data L2 cache present, 1024 KB, 8 ways, 64 byte lines, sectored - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "1 MB"); /* Flawfinder: ignore */ - CPUInfo._L2.uiAssociativeWays = 8; - CPUInfo._L2.uiLineSize = 64; - CPUInfo._L2.bSectored = true; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "1 MB"); /* Flawfinder: ignore */ + cpu_info._L2.uiAssociativeWays = 8; + cpu_info._L2.uiLineSize = 64; + cpu_info._L2.bSectored = true; break; case 0x81: // cfg = 0x81: code and data L2 cache present, 128 KB, 8 ways, 32 byte lines - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "128 KB"); /* Flawfinder: ignore */ - CPUInfo._L2.uiAssociativeWays = 8; - CPUInfo._L2.uiLineSize = 32; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "128 KB"); /* Flawfinder: ignore */ + cpu_info._L2.uiAssociativeWays = 8; + cpu_info._L2.uiLineSize = 32; break; case 0x82: // cfg = 0x82: code and data L2 cache present, 256 KB, 8 ways, 32 byte lines - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "256 KB"); /* Flawfinder: ignore */ - CPUInfo._L2.uiAssociativeWays = 8; - CPUInfo._L2.uiLineSize = 32; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "256 KB"); /* Flawfinder: ignore */ + cpu_info._L2.uiAssociativeWays = 8; + cpu_info._L2.uiLineSize = 32; break; case 0x83: // cfg = 0x83: code and data L2 cache present, 512 KB, 8 ways, 32 byte lines - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "512 KB"); /* Flawfinder: ignore */ - CPUInfo._L2.uiAssociativeWays = 8; - CPUInfo._L2.uiLineSize = 32; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "512 KB"); /* Flawfinder: ignore */ + cpu_info._L2.uiAssociativeWays = 8; + cpu_info._L2.uiLineSize = 32; break; case 0x84: // cfg = 0x84: code and data L2 cache present, 1024 KB, 8 ways, 32 byte lines - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "1 MB"); /* Flawfinder: ignore */ - CPUInfo._L2.uiAssociativeWays = 8; - CPUInfo._L2.uiLineSize = 32; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "1 MB"); /* Flawfinder: ignore */ + cpu_info._L2.uiAssociativeWays = 8; + cpu_info._L2.uiLineSize = 32; break; case 0x85: // cfg = 0x85: code and data L2 cache present, 2048 KB, 8 ways, 32 byte lines - CPUInfo._L2.bPresent = true; - strcpy(CPUInfo._L2.strSize, "2 MB"); /* Flawfinder: ignore */ - CPUInfo._L2.uiAssociativeWays = 8; - CPUInfo._L2.uiLineSize = 32; + cpu_info._L2.bPresent = true; + strcpy(cpu_info._L2.strSize, "2 MB"); /* Flawfinder: ignore */ + cpu_info._L2.uiAssociativeWays = 8; + cpu_info._L2.uiLineSize = 32; break; } } @@ -1428,15 +1929,15 @@ FORCEINLINE static void TranslateCache(ProcessorCache *cache) void CProcessor::TranslateProcessorConfiguration() { // We just call the small functions defined above - TranslateTLB(&CPUInfo._Data); - TranslateTLB(&CPUInfo._Instruction); + TranslateTLB(&cpu_info._Data); + TranslateTLB(&cpu_info._Instruction); - TranslateCache(&CPUInfo._Trace); + TranslateCache(&cpu_info._Trace); - TranslateCache(&CPUInfo._L1.Instruction); - TranslateCache(&CPUInfo._L1.Data); - TranslateCache(&CPUInfo._L2); - TranslateCache(&CPUInfo._L3); + TranslateCache(&cpu_info._L1.Instruction); + TranslateCache(&cpu_info._L1.Data); + TranslateCache(&cpu_info._L2); + TranslateCache(&cpu_info._L3); } // void CProcessor::GetStandardProcessorConfiguration() @@ -1454,7 +1955,7 @@ void CProcessor::GetStandardProcessorConfiguration() // First we check if the processor supports the standard // CPUID level 0x00000002 - if (CPUInfo.MaxSupportedLevel >= 2) + if (cpu_info.MaxSupportedLevel >= 2) { // Now we go read the std. CPUID level 0x00000002 the first time unsigned long count, num = 255; @@ -1529,48 +2030,48 @@ void CProcessor::GetStandardProcessorExtensions() } // Then we mask some bits - CPUInfo._Ext.FPU_FloatingPointUnit = CheckBit(edxreg, 0); - CPUInfo._Ext.VME_Virtual8086ModeEnhancements = CheckBit(edxreg, 1); - CPUInfo._Ext.DE_DebuggingExtensions = CheckBit(edxreg, 2); - CPUInfo._Ext.PSE_PageSizeExtensions = CheckBit(edxreg, 3); - CPUInfo._Ext.TSC_TimeStampCounter = CheckBit(edxreg, 4); - CPUInfo._Ext.MSR_ModelSpecificRegisters = CheckBit(edxreg, 5); - CPUInfo._Ext.PAE_PhysicalAddressExtension = CheckBit(edxreg, 6); - CPUInfo._Ext.MCE_MachineCheckException = CheckBit(edxreg, 7); - CPUInfo._Ext.CX8_COMPXCHG8B_Instruction = CheckBit(edxreg, 8); - CPUInfo._Ext.APIC_AdvancedProgrammableInterruptController = CheckBit(edxreg, 9); - CPUInfo._Ext.APIC_ID = (ebxreg >> 24) & 0xFF; - CPUInfo._Ext.SEP_FastSystemCall = CheckBit(edxreg, 11); - CPUInfo._Ext.MTRR_MemoryTypeRangeRegisters = CheckBit(edxreg, 12); - CPUInfo._Ext.PGE_PTE_GlobalFlag = CheckBit(edxreg, 13); - CPUInfo._Ext.MCA_MachineCheckArchitecture = CheckBit(edxreg, 14); - CPUInfo._Ext.CMOV_ConditionalMoveAndCompareInstructions = CheckBit(edxreg, 15); - CPUInfo._Ext.FGPAT_PageAttributeTable = CheckBit(edxreg, 16); - CPUInfo._Ext.PSE36_36bitPageSizeExtension = CheckBit(edxreg, 17); - CPUInfo._Ext.PN_ProcessorSerialNumber = CheckBit(edxreg, 18); - CPUInfo._Ext.CLFSH_CFLUSH_Instruction = CheckBit(edxreg, 19); - CPUInfo._Ext.CLFLUSH_InstructionCacheLineSize = (ebxreg >> 8) & 0xFF; - CPUInfo._Ext.DS_DebugStore = CheckBit(edxreg, 21); - CPUInfo._Ext.ACPI_ThermalMonitorAndClockControl = CheckBit(edxreg, 22); - CPUInfo._Ext.MMX_MultimediaExtensions = CheckBit(edxreg, 23); - CPUInfo._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore = CheckBit(edxreg, 24); - CPUInfo._Ext.SSE_StreamingSIMD_Extensions = CheckBit(edxreg, 25); - CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = CheckBit(edxreg, 26); - CPUInfo._Ext.Altivec_Extensions = false; - CPUInfo._Ext.SS_SelfSnoop = CheckBit(edxreg, 27); - CPUInfo._Ext.HT_HyperThreading = CheckBit(edxreg, 28); - CPUInfo._Ext.HT_HyterThreadingSiblings = (ebxreg >> 16) & 0xFF; - CPUInfo._Ext.TM_ThermalMonitor = CheckBit(edxreg, 29); - CPUInfo._Ext.IA64_Intel64BitArchitecture = CheckBit(edxreg, 30); + cpu_info._Ext.FPU_FloatingPointUnit = CheckBit(edxreg, 0); + cpu_info._Ext.VME_Virtual8086ModeEnhancements = CheckBit(edxreg, 1); + cpu_info._Ext.DE_DebuggingExtensions = CheckBit(edxreg, 2); + cpu_info._Ext.PSE_PageSizeExtensions = CheckBit(edxreg, 3); + cpu_info._Ext.TSC_TimeStampCounter = CheckBit(edxreg, 4); + cpu_info._Ext.MSR_ModelSpecificRegisters = CheckBit(edxreg, 5); + cpu_info._Ext.PAE_PhysicalAddressExtension = CheckBit(edxreg, 6); + cpu_info._Ext.MCE_MachineCheckException = CheckBit(edxreg, 7); + cpu_info._Ext.CX8_COMPXCHG8B_Instruction = CheckBit(edxreg, 8); + cpu_info._Ext.APIC_AdvancedProgrammableInterruptController = CheckBit(edxreg, 9); + cpu_info._Ext.APIC_ID = (ebxreg >> 24) & 0xFF; + cpu_info._Ext.SEP_FastSystemCall = CheckBit(edxreg, 11); + cpu_info._Ext.MTRR_MemoryTypeRangeRegisters = CheckBit(edxreg, 12); + cpu_info._Ext.PGE_PTE_GlobalFlag = CheckBit(edxreg, 13); + cpu_info._Ext.MCA_MachineCheckArchitecture = CheckBit(edxreg, 14); + cpu_info._Ext.CMOV_ConditionalMoveAndCompareInstructions = CheckBit(edxreg, 15); + cpu_info._Ext.FGPAT_PageAttributeTable = CheckBit(edxreg, 16); + cpu_info._Ext.PSE36_36bitPageSizeExtension = CheckBit(edxreg, 17); + cpu_info._Ext.PN_ProcessorSerialNumber = CheckBit(edxreg, 18); + cpu_info._Ext.CLFSH_CFLUSH_Instruction = CheckBit(edxreg, 19); + cpu_info._Ext.CLFLUSH_InstructionCacheLineSize = (ebxreg >> 8) & 0xFF; + cpu_info._Ext.DS_DebugStore = CheckBit(edxreg, 21); + cpu_info._Ext.ACPI_ThermalMonitorAndClockControl = CheckBit(edxreg, 22); + cpu_info._Ext.MMX_MultimediaExtensions = CheckBit(edxreg, 23); + cpu_info._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore = CheckBit(edxreg, 24); + cpu_info._Ext.SSE_StreamingSIMD_Extensions = CheckBit(edxreg, 25); + cpu_info._Ext.SSE2_StreamingSIMD2_Extensions = CheckBit(edxreg, 26); + cpu_info._Ext.Altivec_Extensions = false; + cpu_info._Ext.SS_SelfSnoop = CheckBit(edxreg, 27); + cpu_info._Ext.HT_HyperThreading = CheckBit(edxreg, 28); + cpu_info._Ext.HT_HyterThreadingSiblings = (ebxreg >> 16) & 0xFF; + cpu_info._Ext.TM_ThermalMonitor = CheckBit(edxreg, 29); + cpu_info._Ext.IA64_Intel64BitArchitecture = CheckBit(edxreg, 30); #endif } -// const ProcessorInfo *CProcessor::GetCPUInfo() +// const ProcessorInfo *CProcessor::Getcpu_info() // ============================================= // Calls all the other detection function to create an detailed // processor information /////////////////////////////////////////////////////////////// -const ProcessorInfo *CProcessor::GetCPUInfo() +const ProcessorInfo *CProcessor::Getcpu_info() { #if LL_WINDOWS unsigned long eaxreg, ebxreg, ecxreg, edxreg; @@ -1591,14 +2092,14 @@ const ProcessorInfo *CProcessor::GetCPUInfo() mov ecxreg, ecx } // Then we connect the single register values to the vendor string - *((unsigned long *) CPUInfo.strVendor) = ebxreg; - *((unsigned long *) (CPUInfo.strVendor+4)) = edxreg; - *((unsigned long *) (CPUInfo.strVendor+8)) = ecxreg; + *((unsigned long *) cpu_info.strVendor) = ebxreg; + *((unsigned long *) (cpu_info.strVendor+4)) = edxreg; + *((unsigned long *) (cpu_info.strVendor+8)) = ecxreg; // Null terminate for string comparisons below. - CPUInfo.strVendor[12] = 0; + cpu_info.strVendor[12] = 0; // We can also read the max. supported standard CPUID level - CPUInfo.MaxSupportedLevel = eaxreg & 0xFFFF; + cpu_info.MaxSupportedLevel = eaxreg & 0xFFFF; // Then we read the ext. CPUID level 0x80000000 __asm @@ -1608,48 +2109,48 @@ const ProcessorInfo *CProcessor::GetCPUInfo() mov eaxreg, eax } // ...to check the max. supportted extended CPUID level - CPUInfo.MaxSupportedExtendedLevel = eaxreg; + cpu_info.MaxSupportedExtendedLevel = eaxreg; // Then we switch to the specific processor vendors // See http://www.sandpile.org/ia32/cpuid.htm - if (!strcmp(CPUInfo.strVendor, "GenuineIntel")) + if (!strcmp(cpu_info.strVendor, "GenuineIntel")) { AnalyzeIntelProcessor(); } - else if (!strcmp(CPUInfo.strVendor, "AuthenticAMD")) + else if (!strcmp(cpu_info.strVendor, "AuthenticAMD")) { AnalyzeAMDProcessor(); } - else if (!strcmp(CPUInfo.strVendor, "UMC UMC UMC")) + else if (!strcmp(cpu_info.strVendor, "UMC UMC UMC")) { AnalyzeUnknownProcessor(); } - else if (!strcmp(CPUInfo.strVendor, "CyrixInstead")) + else if (!strcmp(cpu_info.strVendor, "CyrixInstead")) { AnalyzeUnknownProcessor(); } - else if (!strcmp(CPUInfo.strVendor, "NexGenDriven")) + else if (!strcmp(cpu_info.strVendor, "NexGenDriven")) { AnalyzeUnknownProcessor(); } - else if (!strcmp(CPUInfo.strVendor, "CentaurHauls")) + else if (!strcmp(cpu_info.strVendor, "CentaurHauls")) { AnalyzeUnknownProcessor(); } - else if (!strcmp(CPUInfo.strVendor, "RiseRiseRise")) + else if (!strcmp(cpu_info.strVendor, "RiseRiseRise")) { AnalyzeUnknownProcessor(); } - else if (!strcmp(CPUInfo.strVendor, "SiS SiS SiS")) + else if (!strcmp(cpu_info.strVendor, "SiS SiS SiS")) { AnalyzeUnknownProcessor(); } - else if (!strcmp(CPUInfo.strVendor, "GenuineTMx86")) + else if (!strcmp(cpu_info.strVendor, "GenuineTMx86")) { // Transmeta AnalyzeUnknownProcessor(); } - else if (!strcmp(CPUInfo.strVendor, "Geode by NSC")) + else if (!strcmp(cpu_info.strVendor, "Geode by NSC")) { AnalyzeUnknownProcessor(); } @@ -1658,8 +2159,8 @@ const ProcessorInfo *CProcessor::GetCPUInfo() AnalyzeUnknownProcessor(); } #endif - // After all we return the class CPUInfo member var - return (&CPUInfo); + // After all we return the class cpu_info member var + return (&cpu_info); } #elif LL_SOLARIS @@ -1676,7 +2177,7 @@ CProcessor::CProcessor() { uqwFrequency = 0; strCPUName[0] = 0; - memset(&CPUInfo, 0, sizeof(CPUInfo)); + memset(&cpu_info, 0, sizeof(cpu_info)); } // unsigned __int64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs) @@ -1686,18 +2187,18 @@ CProcessor::CProcessor() F64 CProcessor::GetCPUFrequency(unsigned int /*uiMeasureMSecs*/) { if(uqwFrequency == 0){ - GetCPUInfo(); + Getcpu_info(); } return uqwFrequency; } -// const ProcessorInfo *CProcessor::GetCPUInfo() +// const ProcessorInfo *CProcessor::Getcpu_info() // ============================================= // Calls all the other detection function to create an detailed // processor information /////////////////////////////////////////////////////////////// -const ProcessorInfo *CProcessor::GetCPUInfo() +const ProcessorInfo *CProcessor::Getcpu_info() { // In Solaris the CPU info is in the kstats // try "psrinfo" or "kstat cpu_info" to see all @@ -1712,7 +2213,7 @@ const ProcessorInfo *CProcessor::GetCPUInfo() if((int)kc == -1){ llwarns << "kstat_open(0 failed!" << llendl; - return (&CPUInfo); + return (&cpu_info); } for (ks = kc->kc_chain; ks != NULL; ks = ks->ks_next) { @@ -1723,7 +2224,7 @@ const ProcessorInfo *CProcessor::GetCPUInfo() if(ncpus < 1){ llwarns << "No cpus found in kstats!" << llendl; - return (&CPUInfo); + return (&cpu_info); } for (ks = kc->kc_chain; ks; ks = ks->ks_next) { @@ -1746,10 +2247,10 @@ const ProcessorInfo *CProcessor::GetCPUInfo() if(!strcmp(ksi->name, "brand")){ strncat(strCPUName, (char *)KSTAT_NAMED_STR_PTR(ksi), sizeof(strCPUName)-strlen(strCPUName)-1); - strncat(CPUInfo.strFamily, (char *)KSTAT_NAMED_STR_PTR(ksi), - sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); - strncpy(CPUInfo.strBrandID, strCPUName,sizeof(CPUInfo.strBrandID)-1); - CPUInfo.strBrandID[sizeof(CPUInfo.strBrandID)-1]='\0'; + strncat(cpu_info.strFamily, (char *)KSTAT_NAMED_STR_PTR(ksi), + sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); + strncpy(cpu_info.strBrandID, strCPUName,sizeof(cpu_info.strBrandID)-1); + cpu_info.strBrandID[sizeof(cpu_info.strBrandID)-1]='\0'; // DEBUG llinfos << "CPU brand: " << strCPUName << llendl; continue; } @@ -1767,8 +2268,8 @@ const ProcessorInfo *CProcessor::GetCPUInfo() #if defined(__i386) if(!strcmp(ksi->name, "vendor_id")){ - strncpy(CPUInfo.strVendor, (char *)KSTAT_NAMED_STR_PTR(ksi), sizeof(CPUInfo.strVendor)-1); - // DEBUG llinfos << "CPU vendor: " << CPUInfo.strVendor << llendl; + strncpy(cpu_info.strVendor, (char *)KSTAT_NAMED_STR_PTR(ksi), sizeof(cpu_info.strVendor)-1); + // DEBUG llinfos << "CPU vendor: " << cpu_info.strVendor << llendl; continue; } #endif @@ -1777,7 +2278,7 @@ const ProcessorInfo *CProcessor::GetCPUInfo() kstat_close(kc); #if defined(__sparc) // SPARC does not define a vendor string in kstat - strncpy(CPUInfo.strVendor, "Sun Microsystems, Inc.", sizeof(CPUInfo.strVendor)-1); + strncpy(cpu_info.strVendor, "Sun Microsystems, Inc.", sizeof(cpu_info.strVendor)-1); #endif // DEBUG llinfo << "The system has " << ncpus << " CPUs with a clock rate of " << uqwFrequency << "MHz." << llendl; @@ -1791,29 +2292,29 @@ const ProcessorInfo *CProcessor::GetCPUInfo() (void) getisax(&ui, 1); if(ui & AV_386_FPU) - CPUInfo._Ext.FPU_FloatingPointUnit = true; + cpu_info._Ext.FPU_FloatingPointUnit = true; if(ui & AV_386_CX8) - CPUInfo._Ext.CX8_COMPXCHG8B_Instruction = true; + cpu_info._Ext.CX8_COMPXCHG8B_Instruction = true; if(ui & AV_386_MMX) - CPUInfo._Ext.MMX_MultimediaExtensions = true; + cpu_info._Ext.MMX_MultimediaExtensions = true; if(ui & AV_386_AMD_MMX) - CPUInfo._Ext.MMX_MultimediaExtensions = true; + cpu_info._Ext.MMX_MultimediaExtensions = true; if(ui & AV_386_FXSR) - CPUInfo._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore = true; + cpu_info._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore = true; if(ui & AV_386_SSE) - CPUInfo._Ext.SSE_StreamingSIMD_Extensions = true; + cpu_info._Ext.SSE_StreamingSIMD_Extensions = true; if(ui & AV_386_SSE2) - CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = true; + cpu_info._Ext.SSE2_StreamingSIMD2_Extensions = true; /* Left these here since they may get used later if(ui & AV_386_SSE3) - CPUInfo._Ext.... = true; + cpu_info._Ext.... = true; if(ui & AV_386_AMD_3DNow) - CPUInfo._Ext.... = true; + cpu_info._Ext.... = true; if(ui & AV_386_AMD_3DNowx) - CPUInfo._Ext.... = true; + cpu_info._Ext.... = true; */ #endif - return (&CPUInfo); + return (&cpu_info); } #else @@ -1876,15 +2377,15 @@ static void TranslateCache(ProcessorCache *cache) void CProcessor::TranslateProcessorConfiguration() { // We just call the small functions defined above - TranslateTLB(&CPUInfo._Data); - TranslateTLB(&CPUInfo._Instruction); + TranslateTLB(&cpu_info._Data); + TranslateTLB(&cpu_info._Instruction); - TranslateCache(&CPUInfo._Trace); + TranslateCache(&cpu_info._Trace); - TranslateCache(&CPUInfo._L1.Instruction); - TranslateCache(&CPUInfo._L1.Data); - TranslateCache(&CPUInfo._L2); - TranslateCache(&CPUInfo._L3); + TranslateCache(&cpu_info._L1.Instruction); + TranslateCache(&cpu_info._L1.Data); + TranslateCache(&cpu_info._L2); + TranslateCache(&cpu_info._L3); } // CProcessor::CProcessor @@ -1895,7 +2396,7 @@ CProcessor::CProcessor() { uqwFrequency = 0; strCPUName[0] = 0; - memset(&CPUInfo, 0, sizeof(CPUInfo)); + memset(&cpu_info, 0, sizeof(cpu_info)); } // unsigned __int64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs) @@ -1930,12 +2431,12 @@ static bool hasFeature(const char *name) return result; } -// const ProcessorInfo *CProcessor::GetCPUInfo() +// const ProcessorInfo *CProcessor::Getcpu_info() // ============================================= // Calls all the other detection function to create an detailed // processor information /////////////////////////////////////////////////////////////// -const ProcessorInfo *CProcessor::GetCPUInfo() +const ProcessorInfo *CProcessor::Getcpu_info() { int pagesize = 0; int cachelinesize = 0; @@ -2011,52 +2512,52 @@ const ProcessorInfo *CProcessor::GetCPUInfo() { case CPU_SUBTYPE_POWERPC_601:// ((cpu_subtype_t) 1) strncat(strCPUName, "PowerPC 601", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ - strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */ + strncat(cpu_info.strFamily, "PowerPC", sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); /* Flawfinder: ignore */ break; case CPU_SUBTYPE_POWERPC_602:// ((cpu_subtype_t) 2) strncat(strCPUName, "PowerPC 602", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ - strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */ + strncat(cpu_info.strFamily, "PowerPC", sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); /* Flawfinder: ignore */ break; case CPU_SUBTYPE_POWERPC_603:// ((cpu_subtype_t) 3) strncat(strCPUName, "PowerPC 603", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ - strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */ + strncat(cpu_info.strFamily, "PowerPC", sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); /* Flawfinder: ignore */ break; case CPU_SUBTYPE_POWERPC_603e:// ((cpu_subtype_t) 4) strncat(strCPUName, "PowerPC 603e", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ - strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */ + strncat(cpu_info.strFamily, "PowerPC", sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); /* Flawfinder: ignore */ break; case CPU_SUBTYPE_POWERPC_603ev:// ((cpu_subtype_t) 5) strncat(strCPUName, "PowerPC 603ev", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ - strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */ + strncat(cpu_info.strFamily, "PowerPC", sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); /* Flawfinder: ignore */ break; case CPU_SUBTYPE_POWERPC_604:// ((cpu_subtype_t) 6) strncat(strCPUName, "PowerPC 604", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ - strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */ + strncat(cpu_info.strFamily, "PowerPC", sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); /* Flawfinder: ignore */ break; case CPU_SUBTYPE_POWERPC_604e:// ((cpu_subtype_t) 7) strncat(strCPUName, "PowerPC 604e", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ - strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */ + strncat(cpu_info.strFamily, "PowerPC", sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); /* Flawfinder: ignore */ break; case CPU_SUBTYPE_POWERPC_620:// ((cpu_subtype_t) 8) strncat(strCPUName, "PowerPC 620", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ - strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */ + strncat(cpu_info.strFamily, "PowerPC", sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); /* Flawfinder: ignore */ break; case CPU_SUBTYPE_POWERPC_750:// ((cpu_subtype_t) 9) strncat(strCPUName, "PowerPC 750", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ - strncat(CPUInfo.strFamily, "PowerPC G3", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */ + strncat(cpu_info.strFamily, "PowerPC G3", sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); /* Flawfinder: ignore */ break; case CPU_SUBTYPE_POWERPC_7400:// ((cpu_subtype_t) 10) strncat(strCPUName, "PowerPC 7400", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ - strncat(CPUInfo.strFamily, "PowerPC G4", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */ + strncat(cpu_info.strFamily, "PowerPC G4", sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); /* Flawfinder: ignore */ break; case CPU_SUBTYPE_POWERPC_7450:// ((cpu_subtype_t) 11) strncat(strCPUName, "PowerPC 7450", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ - strncat(CPUInfo.strFamily, "PowerPC G4", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */ + strncat(cpu_info.strFamily, "PowerPC G4", sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); /* Flawfinder: ignore */ break; case CPU_SUBTYPE_POWERPC_970:// ((cpu_subtype_t) 100) strncat(strCPUName, "PowerPC 970", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */ - strncat(CPUInfo.strFamily, "PowerPC G5", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */ + strncat(cpu_info.strFamily, "PowerPC G5", sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); /* Flawfinder: ignore */ break; default: @@ -2064,12 +2565,12 @@ const ProcessorInfo *CProcessor::GetCPUInfo() break; } - CPUInfo._Ext.EMMX_MultimediaExtensions = - CPUInfo._Ext.MMX_MultimediaExtensions = - CPUInfo._Ext.SSE_StreamingSIMD_Extensions = - CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = false; + cpu_info._Ext.EMMX_MultimediaExtensions = + cpu_info._Ext.MMX_MultimediaExtensions = + cpu_info._Ext.SSE_StreamingSIMD_Extensions = + cpu_info._Ext.SSE2_StreamingSIMD2_Extensions = false; - CPUInfo._Ext.Altivec_Extensions = hasFeature("hw.optional.altivec"); + cpu_info._Ext.Altivec_Extensions = hasFeature("hw.optional.altivec"); #endif @@ -2082,54 +2583,54 @@ const ProcessorInfo *CProcessor::GetCPUInfo() break; } - CPUInfo._Ext.EMMX_MultimediaExtensions = hasFeature("hw.optional.mmx"); // MBW -- XXX -- this may be wrong... - CPUInfo._Ext.MMX_MultimediaExtensions = hasFeature("hw.optional.mmx"); - CPUInfo._Ext.SSE_StreamingSIMD_Extensions = hasFeature("hw.optional.sse"); - CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = hasFeature("hw.optional.sse2"); - CPUInfo._Ext.Altivec_Extensions = false; - CPUInfo._Ext.AA64_AMD64BitArchitecture = hasFeature("hw.optional.x86_64"); + cpu_info._Ext.EMMX_MultimediaExtensions = hasFeature("hw.optional.mmx"); // MBW -- XXX -- this may be wrong... + cpu_info._Ext.MMX_MultimediaExtensions = hasFeature("hw.optional.mmx"); + cpu_info._Ext.SSE_StreamingSIMD_Extensions = hasFeature("hw.optional.sse"); + cpu_info._Ext.SSE2_StreamingSIMD2_Extensions = hasFeature("hw.optional.sse2"); + cpu_info._Ext.Altivec_Extensions = false; + cpu_info._Ext.AA64_AMD64BitArchitecture = hasFeature("hw.optional.x86_64"); #endif // Terse CPU info uses this string... - strncpy(CPUInfo.strBrandID, strCPUName,sizeof(CPUInfo.strBrandID)-1); /* Flawfinder: ignore */ - CPUInfo.strBrandID[sizeof(CPUInfo.strBrandID)-1]='\0'; + strncpy(cpu_info.strBrandID, strCPUName,sizeof(cpu_info.strBrandID)-1); /* Flawfinder: ignore */ + cpu_info.strBrandID[sizeof(cpu_info.strBrandID)-1]='\0'; // Fun cache config stuff... if(l1dcachesize != 0) { - CPUInfo._L1.Data.bPresent = true; - snprintf(CPUInfo._L1.Data.strSize, sizeof(CPUInfo._L1.Data.strSize), "%d KB", l1dcachesize / 1024); /* Flawfinder: ignore */ -// CPUInfo._L1.Data.uiAssociativeWays = ???; - CPUInfo._L1.Data.uiLineSize = cachelinesize; + cpu_info._L1.Data.bPresent = true; + snprintf(cpu_info._L1.Data.strSize, sizeof(cpu_info._L1.Data.strSize), "%d KB", l1dcachesize / 1024); /* Flawfinder: ignore */ +// cpu_info._L1.Data.uiAssociativeWays = ???; + cpu_info._L1.Data.uiLineSize = cachelinesize; } if(l1icachesize != 0) { - CPUInfo._L1.Instruction.bPresent = true; - snprintf(CPUInfo._L1.Instruction.strSize, sizeof(CPUInfo._L1.Instruction.strSize), "%d KB", l1icachesize / 1024); /* Flawfinder: ignore */ -// CPUInfo._L1.Instruction.uiAssociativeWays = ???; - CPUInfo._L1.Instruction.uiLineSize = cachelinesize; + cpu_info._L1.Instruction.bPresent = true; + snprintf(cpu_info._L1.Instruction.strSize, sizeof(cpu_info._L1.Instruction.strSize), "%d KB", l1icachesize / 1024); /* Flawfinder: ignore */ +// cpu_info._L1.Instruction.uiAssociativeWays = ???; + cpu_info._L1.Instruction.uiLineSize = cachelinesize; } if(l2cachesize != 0) { - CPUInfo._L2.bPresent = true; - snprintf(CPUInfo._L2.strSize, sizeof(CPUInfo._L2.strSize), "%d KB", l2cachesize / 1024); /* Flawfinder: ignore */ -// CPUInfo._L2.uiAssociativeWays = ???; - CPUInfo._L2.uiLineSize = cachelinesize; + cpu_info._L2.bPresent = true; + snprintf(cpu_info._L2.strSize, sizeof(cpu_info._L2.strSize), "%d KB", l2cachesize / 1024); /* Flawfinder: ignore */ +// cpu_info._L2.uiAssociativeWays = ???; + cpu_info._L2.uiLineSize = cachelinesize; } if(l3cachesize != 0) { - CPUInfo._L2.bPresent = true; - snprintf(CPUInfo._L2.strSize, sizeof(CPUInfo._L2.strSize), "%d KB", l3cachesize / 1024); /* Flawfinder: ignore */ -// CPUInfo._L2.uiAssociativeWays = ???; - CPUInfo._L2.uiLineSize = cachelinesize; + cpu_info._L2.bPresent = true; + snprintf(cpu_info._L2.strSize, sizeof(cpu_info._L2.strSize), "%d KB", l3cachesize / 1024); /* Flawfinder: ignore */ +// cpu_info._L2.uiAssociativeWays = ???; + cpu_info._L2.uiLineSize = cachelinesize; } - CPUInfo._Ext.FPU_FloatingPointUnit = hasFeature("hw.optional.floatingpoint"); + cpu_info._Ext.FPU_FloatingPointUnit = hasFeature("hw.optional.floatingpoint"); // printf("pagesize = 0x%x\n", pagesize); // printf("cachelinesize = 0x%x\n", cachelinesize); @@ -2143,17 +2644,17 @@ const ProcessorInfo *CProcessor::GetCPUInfo() // After reading we translate the configuration to strings TranslateProcessorConfiguration(); - // After all we return the class CPUInfo member var - return (&CPUInfo); + // After all we return the class cpu_info member var + return (&cpu_info); } #endif // LL_DARWIN -// bool CProcessor::CPUInfoToText(char *strBuffer, unsigned int uiMaxLen) +// bool CProcessor::cpu_infoToText(char *strBuffer, unsigned int uiMaxLen) // ====================================================================== // Gets the frequency and processor information and writes it to a string ///////////////////////////////////////////////////////////////////////// -bool CProcessor::CPUInfoToText(char *strBuffer, unsigned int uiMaxLen) +bool CProcessor::cpu_infoToText(char *strBuffer, unsigned int uiMaxLen) { #define LENCHECK len = (unsigned int) strlen(buf); if (len >= uiMaxLen) return false; strcpy(strBuffer, buf); strBuffer += len; /*Flawfinder: ignore*/ #define COPYADD(str) strcpy(buf, str); LENCHECK; /* Flawfinder: ignore */ @@ -2167,7 +2668,7 @@ bool CProcessor::CPUInfoToText(char *strBuffer, unsigned int uiMaxLen) GetCPUFrequency(50); // Then we get the processor information - GetCPUInfo(); + Getcpu_info(); // Now we construct the string (see the macros at function beginning) strBuffer[0] = 0; @@ -2175,16 +2676,16 @@ bool CProcessor::CPUInfoToText(char *strBuffer, unsigned int uiMaxLen) COPYADD("// CPU General Information\n//////////////////////////\n"); FORMATADD("Processor name: %s\n", strCPUName); FORMATADD("Frequency: %.2f MHz\n\n", (float) uqwFrequency / 1000000.0f); - FORMATADD("Vendor: %s\n", CPUInfo.strVendor); - FORMATADD("Family: %s\n", CPUInfo.strFamily); - FORMATADD("Extended family: %d\n", CPUInfo.uiExtendedFamily); - FORMATADD("Model: %s\n", CPUInfo.strModel); - FORMATADD("Extended model: %d\n", CPUInfo.uiExtendedModel); - FORMATADD("Type: %s\n", CPUInfo.strType); - FORMATADD("Brand ID: %s\n", CPUInfo.strBrandID); - if (CPUInfo._Ext.PN_ProcessorSerialNumber) + FORMATADD("Vendor: %s\n", cpu_info.strVendor); + FORMATADD("Family: %s\n", cpu_info.strFamily); + FORMATADD("Extended family: %d\n", cpu_info.uiExtendedFamily); + FORMATADD("Model: %s\n", cpu_info.strModel); + FORMATADD("Extended model: %d\n", cpu_info.uiExtendedModel); + FORMATADD("Type: %s\n", cpu_info.strType); + FORMATADD("Brand ID: %s\n", cpu_info.strBrandID); + if (cpu_info._Ext.PN_ProcessorSerialNumber) { - FORMATADD("Processor Serial: %s\n", CPUInfo.strProcessorSerial); + FORMATADD("Processor Serial: %s\n", cpu_info.strProcessorSerial); } else { @@ -2193,60 +2694,60 @@ bool CProcessor::CPUInfoToText(char *strBuffer, unsigned int uiMaxLen) #if !LL_SOLARIS // NOTE: Why bother printing all this when it's irrelavent COPYADD("\n\n// CPU Configuration\n////////////////////\n"); - FORMATADD("L1 instruction cache: %s\n", CPUInfo._L1.Instruction.strCache); - FORMATADD("L1 data cache: %s\n", CPUInfo._L1.Data.strCache); - FORMATADD("L2 cache: %s\n", CPUInfo._L2.strCache); - FORMATADD("L3 cache: %s\n", CPUInfo._L3.strCache); - FORMATADD("Trace cache: %s\n", CPUInfo._Trace.strCache); - FORMATADD("Instruction TLB: %s\n", CPUInfo._Instruction.strTLB); - FORMATADD("Data TLB: %s\n", CPUInfo._Data.strTLB); - FORMATADD("Max Supported CPUID-Level: 0x%08lX\n", CPUInfo.MaxSupportedLevel); - FORMATADD("Max Supported Ext. CPUID-Level: 0x%08lX\n", CPUInfo.MaxSupportedExtendedLevel); + FORMATADD("L1 instruction cache: %s\n", cpu_info._L1.Instruction.strCache); + FORMATADD("L1 data cache: %s\n", cpu_info._L1.Data.strCache); + FORMATADD("L2 cache: %s\n", cpu_info._L2.strCache); + FORMATADD("L3 cache: %s\n", cpu_info._L3.strCache); + FORMATADD("Trace cache: %s\n", cpu_info._Trace.strCache); + FORMATADD("Instruction TLB: %s\n", cpu_info._Instruction.strTLB); + FORMATADD("Data TLB: %s\n", cpu_info._Data.strTLB); + FORMATADD("Max Supported CPUID-Level: 0x%08lX\n", cpu_info.MaxSupportedLevel); + FORMATADD("Max Supported Ext. CPUID-Level: 0x%08lX\n", cpu_info.MaxSupportedExtendedLevel); COPYADD("\n\n// CPU Extensions\n/////////////////\n"); - BOOLADD("AA64 AMD 64-bit Architecture: ", CPUInfo._Ext.AA64_AMD64BitArchitecture); - BOOLADD("ACPI Thermal Monitor And Clock Control: ", CPUInfo._Ext.ACPI_ThermalMonitorAndClockControl); - BOOLADD("APIC Advanced Programmable Interrupt Controller: ", CPUInfo._Ext.APIC_AdvancedProgrammableInterruptController); - FORMATADD(" APIC-ID: %d\n", CPUInfo._Ext.APIC_ID); - BOOLADD("CLFSH CLFLUSH Instruction Presence: ", CPUInfo._Ext.CLFSH_CFLUSH_Instruction); - FORMATADD(" CLFLUSH Instruction Cache Line Size: %d\n", CPUInfo._Ext.CLFLUSH_InstructionCacheLineSize); - BOOLADD("CMOV Conditional Move And Compare Instructions: ", CPUInfo._Ext.CMOV_ConditionalMoveAndCompareInstructions); - BOOLADD("CX8 COMPXCHG8B Instruction: ", CPUInfo._Ext.CX8_COMPXCHG8B_Instruction); - BOOLADD("DE Debugging Extensions: ", CPUInfo._Ext.DE_DebuggingExtensions); - BOOLADD("DS Debug Store: ", CPUInfo._Ext.DS_DebugStore); - BOOLADD("FGPAT Page Attribute Table: ", CPUInfo._Ext.FGPAT_PageAttributeTable); - BOOLADD("FPU Floating Point Unit: ", CPUInfo._Ext.FPU_FloatingPointUnit); - BOOLADD("FXSR Fast Streaming SIMD Extensions Save/Restore:", CPUInfo._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore); - BOOLADD("HT Hyper Threading: ", CPUInfo._Ext.HT_HyperThreading); - BOOLADD("IA64 Intel 64-Bit Architecture: ", CPUInfo._Ext.IA64_Intel64BitArchitecture); - BOOLADD("MCA Machine Check Architecture: ", CPUInfo._Ext.MCA_MachineCheckArchitecture); - BOOLADD("MCE Machine Check Exception: ", CPUInfo._Ext.MCE_MachineCheckException); - BOOLADD("MMX Multimedia Extensions: ", CPUInfo._Ext.MMX_MultimediaExtensions); - BOOLADD("MMX+ Multimedia Extensions: ", CPUInfo._Ext.EMMX_MultimediaExtensions); - BOOLADD("MSR Model Specific Registers: ", CPUInfo._Ext.MSR_ModelSpecificRegisters); - BOOLADD("MTRR Memory Type Range Registers: ", CPUInfo._Ext.MTRR_MemoryTypeRangeRegisters); - BOOLADD("PAE Physical Address Extension: ", CPUInfo._Ext.PAE_PhysicalAddressExtension); - BOOLADD("PGE PTE Global Flag: ", CPUInfo._Ext.PGE_PTE_GlobalFlag); - if (CPUInfo._Ext.PN_ProcessorSerialNumber) - { - FORMATADD("PN Processor Serial Number: %s\n", CPUInfo.strProcessorSerial); + BOOLADD("AA64 AMD 64-bit Architecture: ", cpu_info._Ext.AA64_AMD64BitArchitecture); + BOOLADD("ACPI Thermal Monitor And Clock Control: ", cpu_info._Ext.ACPI_ThermalMonitorAndClockControl); + BOOLADD("APIC Advanced Programmable Interrupt Controller: ", cpu_info._Ext.APIC_AdvancedProgrammableInterruptController); + FORMATADD(" APIC-ID: %d\n", cpu_info._Ext.APIC_ID); + BOOLADD("CLFSH CLFLUSH Instruction Presence: ", cpu_info._Ext.CLFSH_CFLUSH_Instruction); + FORMATADD(" CLFLUSH Instruction Cache Line Size: %d\n", cpu_info._Ext.CLFLUSH_InstructionCacheLineSize); + BOOLADD("CMOV Conditional Move And Compare Instructions: ", cpu_info._Ext.CMOV_ConditionalMoveAndCompareInstructions); + BOOLADD("CX8 COMPXCHG8B Instruction: ", cpu_info._Ext.CX8_COMPXCHG8B_Instruction); + BOOLADD("DE Debugging Extensions: ", cpu_info._Ext.DE_DebuggingExtensions); + BOOLADD("DS Debug Store: ", cpu_info._Ext.DS_DebugStore); + BOOLADD("FGPAT Page Attribute Table: ", cpu_info._Ext.FGPAT_PageAttributeTable); + BOOLADD("FPU Floating Point Unit: ", cpu_info._Ext.FPU_FloatingPointUnit); + BOOLADD("FXSR Fast Streaming SIMD Extensions Save/Restore:", cpu_info._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore); + BOOLADD("HT Hyper Threading: ", cpu_info._Ext.HT_HyperThreading); + BOOLADD("IA64 Intel 64-Bit Architecture: ", cpu_info._Ext.IA64_Intel64BitArchitecture); + BOOLADD("MCA Machine Check Architecture: ", cpu_info._Ext.MCA_MachineCheckArchitecture); + BOOLADD("MCE Machine Check Exception: ", cpu_info._Ext.MCE_MachineCheckException); + BOOLADD("MMX Multimedia Extensions: ", cpu_info._Ext.MMX_MultimediaExtensions); + BOOLADD("MMX+ Multimedia Extensions: ", cpu_info._Ext.EMMX_MultimediaExtensions); + BOOLADD("MSR Model Specific Registers: ", cpu_info._Ext.MSR_ModelSpecificRegisters); + BOOLADD("MTRR Memory Type Range Registers: ", cpu_info._Ext.MTRR_MemoryTypeRangeRegisters); + BOOLADD("PAE Physical Address Extension: ", cpu_info._Ext.PAE_PhysicalAddressExtension); + BOOLADD("PGE PTE Global Flag: ", cpu_info._Ext.PGE_PTE_GlobalFlag); + if (cpu_info._Ext.PN_ProcessorSerialNumber) + { + FORMATADD("PN Processor Serial Number: %s\n", cpu_info.strProcessorSerial); } else { COPYADD("PN Processor Serial Number: Disabled\n"); } - BOOLADD("PSE Page Size Extensions: ", CPUInfo._Ext.PSE_PageSizeExtensions); - BOOLADD("PSE36 36-bit Page Size Extension: ", CPUInfo._Ext.PSE36_36bitPageSizeExtension); - BOOLADD("SEP Fast System Call: ", CPUInfo._Ext.SEP_FastSystemCall); - BOOLADD("SS Self Snoop: ", CPUInfo._Ext.SS_SelfSnoop); - BOOLADD("SSE Streaming SIMD Extensions: ", CPUInfo._Ext.SSE_StreamingSIMD_Extensions); - BOOLADD("SSE2 Streaming SIMD 2 Extensions: ", CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions); - BOOLADD("ALTVEC Altivec Extensions: ", CPUInfo._Ext.Altivec_Extensions); - BOOLADD("TM Thermal Monitor: ", CPUInfo._Ext.TM_ThermalMonitor); - BOOLADD("TSC Time Stamp Counter: ", CPUInfo._Ext.TSC_TimeStampCounter); - BOOLADD("VME Virtual 8086 Mode Enhancements: ", CPUInfo._Ext.VME_Virtual8086ModeEnhancements); - BOOLADD("3DNow! Instructions: ", CPUInfo._Ext._3DNOW_InstructionExtensions); - BOOLADD("Enhanced 3DNow! Instructions: ", CPUInfo._Ext._E3DNOW_InstructionExtensions); + BOOLADD("PSE Page Size Extensions: ", cpu_info._Ext.PSE_PageSizeExtensions); + BOOLADD("PSE36 36-bit Page Size Extension: ", cpu_info._Ext.PSE36_36bitPageSizeExtension); + BOOLADD("SEP Fast System Call: ", cpu_info._Ext.SEP_FastSystemCall); + BOOLADD("SS Self Snoop: ", cpu_info._Ext.SS_SelfSnoop); + BOOLADD("SSE Streaming SIMD Extensions: ", cpu_info._Ext.SSE_StreamingSIMD_Extensions); + BOOLADD("SSE2 Streaming SIMD 2 Extensions: ", cpu_info._Ext.SSE2_StreamingSIMD2_Extensions); + BOOLADD("ALTVEC Altivec Extensions: ", cpu_info._Ext.Altivec_Extensions); + BOOLADD("TM Thermal Monitor: ", cpu_info._Ext.TM_ThermalMonitor); + BOOLADD("TSC Time Stamp Counter: ", cpu_info._Ext.TSC_TimeStampCounter); + BOOLADD("VME Virtual 8086 Mode Enhancements: ", cpu_info._Ext.VME_Virtual8086ModeEnhancements); + BOOLADD("3DNow! Instructions: ", cpu_info._Ext._3DNOW_InstructionExtensions); + BOOLADD("Enhanced 3DNow! Instructions: ", cpu_info._Ext._E3DNOW_InstructionExtensions); #endif // Yippie!!! return true; @@ -2254,7 +2755,7 @@ bool CProcessor::CPUInfoToText(char *strBuffer, unsigned int uiMaxLen) // bool CProcessor::WriteInfoTextFile(const std::string& strFilename) // =========================================================== -// Takes use of CProcessor::CPUInfoToText and saves the string to a +// Takes use of CProcessor::cpu_infoToText and saves the string to a // file /////////////////////////////////////////////////////////////////// bool CProcessor::WriteInfoTextFile(const std::string& strFilename) @@ -2262,7 +2763,7 @@ bool CProcessor::WriteInfoTextFile(const std::string& strFilename) char buf[16384]; /* Flawfinder: ignore */ // First we get the string - if (!CPUInfoToText(buf, 16383)) + if (!cpu_infoToText(buf, 16383)) return false; // Then we create a new file (CREATE_ALWAYS) @@ -2281,3 +2782,4 @@ bool CProcessor::WriteInfoTextFile(const std::string& strFilename) // Done return true; } +#endif diff --git a/indra/llcommon/llprocessor.h b/indra/llcommon/llprocessor.h index 746d007a7f..1cea6245a3 100644 --- a/indra/llcommon/llprocessor.h +++ b/indra/llcommon/llprocessor.h @@ -30,14 +30,30 @@ * $/LicenseInfo$ */ -// Author: Benjamin Jurke -// File history: 27.02.2002 File created. -/////////////////////////////////////////// - #ifndef LLPROCESSOR_H #define LLPROCESSOR_H +class LLProcessorInfo +{ +public: + LLProcessorInfo(); + ~LLProcessorInfo(); + + F64 getCPUFrequency() const; + bool hasSSE() const; + bool hasSSE2() const; + bool hasAltivec() const; + std::string getCPUFamilyName() const; + std::string getCPUBrandName() const; + std::string getCPUFeatureDescription() const; +}; + +# if 0 +// Author: Benjamin Jurke +// File history: 27.02.2002 File created. +/////////////////////////////////////////// + // Options: /////////// #if LL_WINDOWS @@ -166,7 +182,7 @@ public: // Private vars: //////////////// -public: +private: F64 uqwFrequency; char strCPUName[128]; /* Flawfinder: ignore */ ProcessorInfo CPUInfo; @@ -192,5 +208,6 @@ public: bool WriteInfoTextFile(const std::string& strFilename); }; +#endif // 0 -#endif +#endif // LLPROCESSOR_H diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 0272c55db2..fc3ce52aa8 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -513,18 +513,17 @@ U32 LLOSInfo::getProcessResidentSizeKB() LLCPUInfo::LLCPUInfo() { std::ostringstream out; - CProcessor proc; - const ProcessorInfo* info = proc.GetCPUInfo(); + LLProcessorInfo proc; // proc.WriteInfoTextFile("procInfo.txt"); - mHasSSE = info->_Ext.SSE_StreamingSIMD_Extensions; - mHasSSE2 = info->_Ext.SSE2_StreamingSIMD2_Extensions; - mHasAltivec = info->_Ext.Altivec_Extensions; - mCPUMhz = (S32)(proc.GetCPUFrequency(50)/1000000.0); - mFamily.assign( info->strFamily ); + mHasSSE = proc.hasSSE(); + mHasSSE2 = proc.hasSSE2(); + mHasAltivec = proc.hasAltivec(); + mCPUMhz = (S32)(proc.getCPUFrequency()/1000000.0); + mFamily = proc.getCPUFamilyName(); mCPUString = "Unknown"; #if LL_WINDOWS || LL_DARWIN || LL_SOLARIS - out << proc.strCPUName; + out << proc.getCPUBrandName(); if (200 < mCPUMhz && mCPUMhz < 10000) // *NOTE: cpu speed is often way wrong, do a sanity check { out << " (" << mCPUMhz << " MHz)"; @@ -609,16 +608,7 @@ void LLCPUInfo::stream(std::ostream& s) const { #if LL_WINDOWS || LL_DARWIN || LL_SOLARIS // gather machine information. - char proc_buf[CPUINFO_BUFFER_SIZE]; /* Flawfinder: ignore */ - CProcessor proc; - if(proc.CPUInfoToText(proc_buf, CPUINFO_BUFFER_SIZE)) - { - s << proc_buf; - } - else - { - s << "Unable to collect processor information" << std::endl; - } + s << LLProcessorInfo().getCPUFeatureDescription(); #else // *NOTE: This works on linux. What will it do on other systems? LLFILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, "rb"); -- cgit v1.3 From 03104e6998a71a806614b0a6e8234d7dcfb8291a Mon Sep 17 00:00:00 2001 From: palange Date: Mon, 11 Jan 2010 16:43:06 -0800 Subject: intermediate commit, to pass to windows machine --- indra/llcommon/CMakeLists.txt | 1 - indra/llcommon/llprocessor.cpp | 481 ++++++++++++++++++++--------------------- 2 files changed, 236 insertions(+), 246 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index ac7cc2cdac..ab8c9b6f9a 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -230,7 +230,6 @@ set(llcommon_HEADER_FILES metaclasst.h metaproperty.h metapropertyt.h - processor.h reflective.h reflectivet.h roles_constants.h diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 010435f11a..8ac0cdf4e3 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -39,10 +39,11 @@ # define WIN32_LEAN_AND_MEAN # include # include +# include #endif #include -#include +#include "llsd.h" #if LL_MSVC && _M_X64 # define LL_X86_64 1 @@ -58,7 +59,66 @@ # define LL_PPC 1 #endif -// The base calss for implementations. +class LLProcessorInfoImpl; // foward declaration for the gImpl; + +namespace +{ + static const char* cpu_feature_names[] = + { + "x87 FPU On Chip", + "Virtual-8086 Mode Enhancement", + "Debugging Extensions", + "Page Size Extensions", + "Time Stamp Counter", + "RDMSR and WRMSR Support", + "Physical Address Extensions", + "Machine Check Exception", + "CMPXCHG8B Instruction", + "APIC On Chip", + "Unknown1", + "SYSENTER and SYSEXIT", + "Memory Type Range Registers", + "PTE Global Bit", + "Machine Check Architecture", + "Conditional Move/Compare Instruction", + "Page Attribute Table", + "Page Size Extension", + "Processor Serial Number", + "CFLUSH Extension", + "Unknown2", + "Debug Store", + "Thermal Monitor and Clock Ctrl", + "MMX Technology", + "FXSAVE/FXRSTOR", + "SSE Extensions", + "SSE2 Extensions", + "Self Snoop", + "Hyper-threading Technology", + "Thermal Monitor", + "Unknown4", + "Pend. Brk. EN.", // End of FeatureInfo bits + + "SSE3 New Instructions", // 32 + "MONITOR/MWAIT", + "CPL Qualified Debug Store", + "Thermal Monitor 2" + }; + + enum cpu_features + { + eSSE_Ext=25, + eSSE2_Ext=26, + eSSE3_Features=32, + eMONTIOR_MWAIT=33, + eCPLDebugStore=34, + eThermalMonitor2=35 + }; + + // Pointer to the active impl. + boost::scoped_ptr gImpl; +} + +// The base class for implementations. // Each platform should override this class. class LLProcessorInfoImpl { @@ -66,20 +126,90 @@ public: LLProcessorInfoImpl() {} virtual ~LLProcessorInfoImpl() {} - virtual F64 getCPUFrequency() const { return 0; } - virtual bool hasSSE() const { return false; } - virtual bool hasSSE2() const { return false; } - virtual bool hasAltivec() const { return false; } - virtual std::string getCPUFamilyName() const { return "Unknown"; } - virtual std::string getCPUBrandName() const { return "Unknown"; } - virtual std::string getCPUFeatureDescription() const { return "Unknown"; } + F64 getCPUFrequency() const + { + return getInfo("Frequency", 0).asReal(); + } + + bool hasSSE() const + { + return hasExtension(cpu_feature_names[eSSE_Ext]); + } + + bool hasSSE2() const + { + return hasExtension(cpu_feature_names[eSSE2_Ext]); + } + + bool hasAltivec() const + { + return hasExtension("Altivec"); + } + + std::string getCPUFamilyName() const { return getInfo("FamilyName", "Unknown").asString(); } + std::string getCPUBrandName() const { return getInfo("BrandName", "Unknown").asString(); } + std::string getCPUFeatureDescription() const + { + std::ostringstream out; + out << std::endl << std::endl; + out << "// CPU General Information" << std::endl; + out << "//////////////////////////" << std::endl; + out << "Processor Name: " << getCPUBrandName() << std::endl; + out << "Frequency: " << getCPUFrequency() / (F64)1000000 << " MHz" << std::endl; + out << "Vendor: " << getInfo("Vendor", "Unknown").asString() << std::endl; + out << "Family: " << getCPUFamilyName() << " (" << getInfo("Family", 0) << ")" << std::endl; + out << "Extended family: " << getInfo("ExtendedFamily", 0) << std::endl; + out << "Model: " << getInfo("Model", 0) << std::endl; + out << "Extended model: " << getInfo("ExtendedModel", 0) << std::endl; + out << "Type: " << getInfo("Type", 0) << std::endl; + out << "Brand ID: " << getInfo("BrandID", 0) << std::endl; + out << std::endl; + out << "// CPU Configuration" << std::endl; + out << "//////////////////////////" << std::endl; + out << "Max Supported CPUID level = " << getConfig("MaxID", 0) << std::endl; + out << "Max Supported Ext. CPUID level = " << std::hex << getConfig("MaxExtID", 0) << std::dec << std::endl; + out << "CLFLUSH cache line size = " << getConfig("CLFLUSHCacheLineSize", 0) << std::endl; + out << "APIC Physical ID = " << getConfig("APICPhysicalID", 0) << std::endl; + out << "Cache Line Size = " << getConfig("CacheLineSize", 0) << std::endl; + out << "L2 Associativity = " << getConfig("L2Associativity", 0) << std::endl; + out << "Cache Size = " << getConfig("CacheSizeK", 0) << "K" << std::endl; + out << std::endl; + out << "// CPU Extensions" << std::endl; + out << "//////////////////////////" << std::endl; + + for(LLSD::map_const_iterator itr = mProcessorInfo.beginMap(); itr != mProcessorInfo.endMap(); ++itr) + { + out << " " << itr->first << std::endl; + } + return out.str(); + } + +protected: + void setInfo(const std::string& name, const LLSD& value) { mProcessorInfo["info"][name]=value; } + void setConfig(const std::string& name, const LLSD& value) { mProcessorInfo["config"][name]=value; } + void setExtension(const std::string& name) { mProcessorInfo["extension"][name] = "true"; } + + LLSD getInfo(const std::string& name, const LLSD& defaultVal) const + { + LLSD r = mProcessorInfo["info"].get(name); + return r.isDefined() ? r : defaultVal; + } + + LLSD getConfig(const std::string& name, const LLSD& defaultVal) const + { + LLSD r = mProcessorInfo["config"].get(name); + return r.isDefined() ? r : defaultVal; + } + + bool hasExtension(const std::string& name) const + { + return mProcessorInfo["extension"].has(name); + } + +private: + LLSD mProcessorInfo; }; -namespace -{ - // Pointer to the active impl. - boost::scoped_ptr gImpl; -} #ifdef LL_MSVC // LL_MSVC and not LLWINDOWS because some of the following code @@ -178,98 +308,90 @@ static F64 calculate_cpu_frequency(U32 measure_msecs) return frequency; } -static const char* cpu_feature_names[] = -{ - "x87 FPU On Chip", - "Virtual-8086 Mode Enhancement", - "Debugging Extensions", - "Page Size Extensions", - "Time Stamp Counter", - "RDMSR and WRMSR Support", - "Physical Address Extensions", - "Machine Check Exception", - "CMPXCHG8B Instruction", - "APIC On Chip", - "Unknown1", - "SYSENTER and SYSEXIT", - "Memory Type Range Registers", - "PTE Global Bit", - "Machine Check Architecture", - "Conditional Move/Compare Instruction", - "Page Attribute Table", - "Page Size Extension", - "Processor Serial Number", - "CFLUSH Extension", - "Unknown2", - "Debug Store", - "Thermal Monitor and Clock Ctrl", - "MMX Technology", - "FXSAVE/FXRSTOR", - "SSE Extensions", - "SSE2 Extensions", - "Self Snoop", - "Hyper-threading Technology", - "Thermal Monitor", - "Unknown4", - "Pend. Brk. EN." -}; - // Windows implementation class LLProcessorInfoWindowsImpl : public LLProcessorInfoImpl { public: LLProcessorInfoWindowsImpl() : - mCPUFrequency(0), - mSteppingID(0), - mModel(0), - mFamily(0), - mProcessorType(0), - mExtendedModel(0), - mExtendedFamily(0), - mBrandIndex(0), - mCLFLUSHCacheLineSize(0), - mAPICPhysicalID(0), - mCacheLineSize(0), - mL2Associativity(0), - mCacheSizeK(0), - - mFeatureInfo(0), - mSSE3NewInstructions(false), - mMONITOR_MWAIT(false), - mCPLQualifiedDebugStore(false), - mThermalMonitor2(false), - - mIds(0), - mExtIds(0) - - { - memset(&mCPUString, 0, 0x20); - memset(&mCPUBrandString, 0, 0x40); - + { getCPUIDInfo(); - mCPUFrequency = calculate_cpu_frequency(50); - } - - F64 getCPUFrequency() const - { - return mCPUFrequency; + AddInfoItem("Frequency", calculate_cpu_frequency(50)); } - bool hasSSE() const +private: + void getCPUIDInfo() { - // constant comes from the msdn docs for __cpuid - const int sse_feature_index = 25; - return mFeatureInfo & (1 << sse_feature_index); - } + // http://msdn.microsoft.com/en-us/library/hskdteyh(VS.80).aspx + + // __cpuid with an InfoType argument of 0 returns the number of + // valid Ids in cpu_info[0] and the CPU identification string in + // the other three array elements. The CPU identification string is + // not in linear order. The code below arranges the information + // in a human readable form. + int cpu_info[4] = {-1}; + __cpuid(cpu_info, 0); + unsigned int ids = (unsigned int)cpu_info[0]; + setInfo("MaxIDs", ids); + + char cpu_vendor[0x20]; + memset(cpu_vendor, 0, sizeof(cpu_vendor)); + *((int*)cpu_vendor) = cpu_info[1]; + *((int*)(cpu_vendor+4)) = cpu_info[3]; + *((int*)(cpu_vendor+8)) = cpu_info[2]; + + // Get the information associated with each valid Id + for(unsigned int i=0; i<=mIds; ++i) + { + __cpuid(cpu_info, i); - bool hasSSE2() const - { - // constant comes from the msdn docs for __cpuid - const int sse2_feature_index = 26; - return mFeatureInfo & (1 << sse2_feature_index); + // Interpret CPU feature information. + if (i == 1) + { + mSteppingID = cpu_info[0] & 0xf; + mModel = (cpu_info[0] >> 4) & 0xf; + mFamily = (cpu_info[0] >> 8) & 0xf; + mProcessorType = (cpu_info[0] >> 12) & 0x3; + mExtendedModel = (cpu_info[0] >> 16) & 0xf; + mExtendedFamily = (cpu_info[0] >> 20) & 0xff; + mBrandIndex = cpu_info[1] & 0xff; + mCLFLUSHCacheLineSize = ((cpu_info[1] >> 8) & 0xff) * 8; + mAPICPhysicalID = (cpu_info[1] >> 24) & 0xff; + mSSE3NewInstructions = (cpu_info[2] & 0x1) || false; + mMONITOR_MWAIT = (cpu_info[2] & 0x8) || false; + mCPLQualifiedDebugStore = (cpu_info[2] & 0x10) || false; + mThermalMonitor2 = (cpu_info[2] & 0x100) || false; + mFeatureInfo = cpu_info[3]; + } + } + + // Calling __cpuid with 0x80000000 as the InfoType argument + // gets the number of valid extended IDs. + __cpuid(cpu_info, 0x80000000); + mExtIds = cpu_info[0]; + memset(mCPUBrandString, 0, sizeof(mCPUBrandString)); + + // Get the information associated with each extended ID. + for(unsigned int i=0x80000000; i<=mExtIds; ++i) + { + __cpuid(cpu_info, i); + + // Interpret CPU brand string and cache information. + if (i == 0x80000002) + memcpy(mCPUBrandString, cpu_info, sizeof(cpu_info)); + else if (i == 0x80000003) + memcpy(mCPUBrandString + 16, cpu_info, sizeof(cpu_info)); + else if (i == 0x80000004) + memcpy(mCPUBrandString + 32, cpu_info, sizeof(cpu_info)); + else if (i == 0x80000006) + { + mCacheLineSize = cpu_info[2] & 0xff; + mL2Associativity = (cpu_info[2] >> 12) & 0xf; + mCacheSizeK = (cpu_info[2] >> 16) & 0xffff; + } + } } - std::string getCPUFamilyName() const + std::string computeCPUFamilyName() const { const char* intel_string = "GenuineIntel"; const char* amd_string = "AuthenticAMD"; @@ -306,160 +428,27 @@ public: return "Unknown"; } - std::string getCPUBrandName() const { return mCPUBrandString; } - std::string getCPUFeatureDescription() const - { - std::ostringstream out; - out << std::endl << std::endl; - out << "// CPU General Information" << std::endl; - out << "//////////////////////////" << std::endl; - out << "Processor Name: " << getCPUBrandName() << std::endl; - out << "Frequency: " << mCPUFrequency / (F64)1000000 << " MHz" << std::endl; - out << "Vendor: " << mCPUString << std::endl; - out << "Family: " << getCPUFamilyName() << " (" << mFamily << ")" << std::endl; - out << "Extended family: " << mExtendedFamily << std::endl; - out << "Model: " << mModel << std::endl; - out << "Extended model: " << mExtendedModel << std::endl; - out << "Type: " << mProcessorType << std::endl; - out << "Brand ID: " << mBrandIndex << std::endl; - out << std::endl; - out << "// CPU Configuration" << std::endl; - out << "//////////////////////////" << std::endl; - out << "Max Supported CPUID level = " << mIds << std::endl; - out << "Max Supported Ext. CPUID level = " << std::hex << mExtIds << std::dec << std::endl; - out << "CLFLUSH cache line size = " << mCLFLUSHCacheLineSize << std::endl; - out << "APIC Physical ID = " << mAPICPhysicalID << std::endl; - out << "Cache Line Size = " << mCacheLineSize << std::endl; - out << "L2 Associativity = " << mL2Associativity << std::endl; - out << "Cache Size = " << mCacheSizeK << "K" << std::endl; - out << std::endl; - out << "// CPU Extensions" << std::endl; - out << "//////////////////////////" << std::endl; - if(mSSE3NewInstructions) - { - out << " SSE3 New Instructions" << std::endl; - } - if(mMONITOR_MWAIT) - { - out << " MONITOR/MWAIT" << std::endl; - } - if(mCPLQualifiedDebugStore) - { - out << " CPL Qualified Debug Store" << std::endl; - } - if(mThermalMonitor2) - { - out << " Thermal Monitor 2" << std::endl; - } - - U32 index = 0; - U32 bit = 1; - while(index < (sizeof(cpu_feature_names)/sizeof(const char*))) - { - if(mFeatureInfo & bit) - { - out << " " << cpu_feature_names[index] << std::endl; - } - bit <<= 1; - ++index; - } - - return out.str(); - } +}; -private: - F64 mCPUFrequency; - char mCPUString[0x20]; - char mCPUBrandString[0x40]; - int mSteppingID; - int mModel; - int mFamily; - int mProcessorType; - int mExtendedModel; - int mExtendedFamily; - int mBrandIndex; - int mCLFLUSHCacheLineSize; - int mAPICPhysicalID; - int mCacheLineSize; - int mL2Associativity; - int mCacheSizeK; - - int mFeatureInfo; - bool mSSE3NewInstructions; - bool mMONITOR_MWAIT; - bool mCPLQualifiedDebugStore; - bool mThermalMonitor2; - - unsigned int mIds; - unsigned int mExtIds; +#elif LL_DARWIN - void getCPUIDInfo() +class LLProcessorInfoDarwinImpl +{ +public: + LLProcessorInfoDarwinImpl() { - // http://msdn.microsoft.com/en-us/library/hskdteyh(VS.80).aspx - - // __cpuid with an InfoType argument of 0 returns the number of - // valid Ids in cpu_info[0] and the CPU identification string in - // the other three array elements. The CPU identification string is - // not in linear order. The code below arranges the information - // in a human readable form. - int cpu_info[4] = {-1}; - __cpuid(cpu_info, 0); - unsigned int mIds = (unsigned int)cpu_info[0]; - *((int*)mCPUString) = cpu_info[1]; - *((int*)(mCPUString+4)) = cpu_info[3]; - *((int*)(mCPUString+8)) = cpu_info[2]; - - // Get the information associated with each valid Id - for(unsigned int i=0; i<=mIds; ++i) - { - __cpuid(cpu_info, i); - - // Interpret CPU feature information. - if (i == 1) - { - mSteppingID = cpu_info[0] & 0xf; - mModel = (cpu_info[0] >> 4) & 0xf; - mFamily = (cpu_info[0] >> 8) & 0xf; - mProcessorType = (cpu_info[0] >> 12) & 0x3; - mExtendedModel = (cpu_info[0] >> 16) & 0xf; - mExtendedFamily = (cpu_info[0] >> 20) & 0xff; - mBrandIndex = cpu_info[1] & 0xff; - mCLFLUSHCacheLineSize = ((cpu_info[1] >> 8) & 0xff) * 8; - mAPICPhysicalID = (cpu_info[1] >> 24) & 0xff; - mSSE3NewInstructions = (cpu_info[2] & 0x1) || false; - mMONITOR_MWAIT = (cpu_info[2] & 0x8) || false; - mCPLQualifiedDebugStore = (cpu_info[2] & 0x10) || false; - mThermalMonitor2 = (cpu_info[2] & 0x100) || false; - mFeatureInfo = cpu_info[3]; - } - } - - // Calling __cpuid with 0x80000000 as the InfoType argument - // gets the number of valid extended IDs. - __cpuid(cpu_info, 0x80000000); - mExtIds = cpu_info[0]; - memset(mCPUBrandString, 0, sizeof(mCPUBrandString)); - - // Get the information associated with each extended ID. - for(unsigned int i=0x80000000; i<=mExtIds; ++i) - { - __cpuid(cpu_info, i); - - // Interpret CPU brand string and cache information. - if (i == 0x80000002) - memcpy(mCPUBrandString, cpu_info, sizeof(cpu_info)); - else if (i == 0x80000003) - memcpy(mCPUBrandString + 16, cpu_info, sizeof(cpu_info)); - else if (i == 0x80000004) - memcpy(mCPUBrandString + 32, cpu_info, sizeof(cpu_info)); - else if (i == 0x80000006) - { - mCacheLineSize = cpu_info[2] & 0xff; - mL2Associativity = (cpu_info[2] >> 12) & 0xf; - mCacheSizeK = (cpu_info[2] >> 16) & 0xffff; - } - } + } + + virtual ~LLProcessorInfoDarwinImpl() {} + + virtual F64 getCPUFrequency() const { return 0; } + virtual bool hasSSE() const { return false; } + virtual bool hasSSE2() const { return false; } + virtual bool hasAltivec() const { return false; } + virtual std::string getCPUFamilyName() const { return "Unknown"; } + virtual std::string getCPUBrandName() const { return "Unknown"; } + virtual std::string getCPUFeatureDescription() const { return "Unknown"; } }; #endif // LL_MSVC @@ -474,7 +463,9 @@ LLProcessorInfo::LLProcessorInfo() { #ifdef LL_MSVC gImpl.reset(new LLProcessorInfoWindowsImpl); -#else +#elif LL_DARWIN + gImpl.reset(new LLProcessorInfoDarwinImpl); +#else #error "Unimplemented" #endif // LL_MSVC } -- cgit v1.3 From 6abecf1228010d83c2df8d14057950fb51e37320 Mon Sep 17 00:00:00 2001 From: palange Date: Mon, 1 Feb 2010 11:23:30 -0800 Subject: pushing to private repo to move to windows development. --- indra/llcommon/llprocessor.cpp | 544 +++++++++++++++++++++++++++++------------ indra/llcommon/llprocessor.h | 3 + indra/newview/llappviewer.cpp | 4 - 3 files changed, 388 insertions(+), 163 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 8ac0cdf4e3..09a7004913 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -33,6 +33,8 @@ #include "linden_common.h" #include "llprocessor.h" +#include "llerror.h" + //#include #if LL_WINDOWS @@ -42,28 +44,86 @@ # include #endif -#include #include "llsd.h" -#if LL_MSVC && _M_X64 -# define LL_X86_64 1 -# define LL_X86 1 -#elif LL_MSVC && _M_IX86 -# define LL_X86 1 -#elif LL_GNUC && ( defined(__amd64__) || defined(__x86_64__) ) -# define LL_X86_64 1 -# define LL_X86 1 -#elif LL_GNUC && ( defined(__i386__) ) -# define LL_X86 1 -#elif LL_GNUC && ( defined(__powerpc__) || defined(__ppc__) ) -# define LL_PPC 1 -#endif - -class LLProcessorInfoImpl; // foward declaration for the gImpl; +class LLProcessorInfoImpl; // foward declaration for the mImpl; namespace { - static const char* cpu_feature_names[] = + enum cpu_info + { + eBrandName = 0, + eFrequency, + eVendor, + eStepping, + eFamily, + eExtendedFamily, + eModel, + eExtendedModel, + eType, + eBrandID, + eFamilyName + }; + + + const char* cpu_info_names[] = + { + "Processor Name", + "Frequency", + "Vendor", + "Stepping", + "Family", + "Extended Family", + "Model", + "Extended Model", + "Type", + "Brand ID", + "Family Name" + }; + + enum cpu_config + { + eMaxID, + eMaxExtID, + eCLFLUSHCacheLineSize, + eAPICPhysicalID, + eCacheLineSize, + eL2Associativity, + eCacheSizeK, + eFeatureBits, + eExtFeatureBits + }; + + const char* cpu_config_names[] = + { + "Max Supported CPUID level", + "Max Supported Ext. CPUID level", + "CLFLUSH cache line size", + "APIC Physical ID", + "Cache Line Size", + "L2 Associativity", + "Cache Size", + "Feature Bits", + "Ext. Feature Bits" + }; + + + + // *NOTE:Mani - this contains the elements we reference directly and extensions beyond the first 32. + // The rest of the names are referenced by bit maks returned from cpuid. + enum cpu_features + { + eSSE_Ext=25, + eSSE2_Ext=26, + + eSSE3_Features=32, + eMONTIOR_MWAIT=33, + eCPLDebugStore=34, + eThermalMonitor2=35, + eAltivec=36 + }; + + const char* cpu_feature_names[] = { "x87 FPU On Chip", "Virtual-8086 Mode Enhancement", @@ -96,39 +156,70 @@ namespace "Hyper-threading Technology", "Thermal Monitor", "Unknown4", - "Pend. Brk. EN.", // End of FeatureInfo bits + "Pend. Brk. EN.", // 31 End of FeatureInfo bits "SSE3 New Instructions", // 32 "MONITOR/MWAIT", "CPL Qualified Debug Store", - "Thermal Monitor 2" - }; + "Thermal Monitor 2", - enum cpu_features - { - eSSE_Ext=25, - eSSE2_Ext=26, - eSSE3_Features=32, - eMONTIOR_MWAIT=33, - eCPLDebugStore=34, - eThermalMonitor2=35 + "Altivec" }; - // Pointer to the active impl. - boost::scoped_ptr gImpl; -} + std::string compute_CPUFamilyName(const char* cpu_vendor, int family, int ext_family) + { + const char* intel_string = "GenuineIntel"; + const char* amd_string = "AuthenticAMD"; + if(!strncmp(cpu_vendor, intel_string, strlen(intel_string))) + { + U32 composed_family = family + ext_family; + switch(composed_family) + { + case 3: return "Intel i386"; + case 4: return "Intel i486"; + case 5: return "Intel Pentium"; + case 6: return "Intel Pentium Pro/2/3, Core"; + case 7: return "Intel Itanium (IA-64)"; + case 0xF: return "Intel Pentium 4"; + case 0x10: return "Intel Itanium 2 (IA-64)"; + default: return "Unknown"; + } + } + else if(!strncmp(cpu_vendor, amd_string, strlen(amd_string))) + { + U32 composed_family = (family == 0xF) + ? family + ext_family + : family; + switch(composed_family) + { + case 4: return "AMD 80486/5x86"; + case 5: return "AMD K5/K6"; + case 6: return "AMD K7"; + case 0xF: return "AMD K8"; + case 0x10: return "AMD K8L"; + default: return "Unknown"; + } + } + return "Unknown"; + } +} // end unnamed namespace // The base class for implementations. // Each platform should override this class. class LLProcessorInfoImpl { public: - LLProcessorInfoImpl() {} + LLProcessorInfoImpl() + { + mProcessorInfo["info"] = LLSD::emptyMap(); + mProcessorInfo["config"] = LLSD::emptyMap(); + mProcessorInfo["extension"] = LLSD::emptyMap(); + } virtual ~LLProcessorInfoImpl() {} F64 getCPUFrequency() const { - return getInfo("Frequency", 0).asReal(); + return getInfo(eFrequency, 0).asReal(); } bool hasSSE() const @@ -146,8 +237,9 @@ public: return hasExtension("Altivec"); } - std::string getCPUFamilyName() const { return getInfo("FamilyName", "Unknown").asString(); } - std::string getCPUBrandName() const { return getInfo("BrandName", "Unknown").asString(); } + std::string getCPUFamilyName() const { return getInfo(eFamilyName, "Unknown").asString(); } + std::string getCPUBrandName() const { return getInfo(eBrandName, "Unknown").asString(); } + std::string getCPUFeatureDescription() const { std::ostringstream out; @@ -156,28 +248,29 @@ public: out << "//////////////////////////" << std::endl; out << "Processor Name: " << getCPUBrandName() << std::endl; out << "Frequency: " << getCPUFrequency() / (F64)1000000 << " MHz" << std::endl; - out << "Vendor: " << getInfo("Vendor", "Unknown").asString() << std::endl; - out << "Family: " << getCPUFamilyName() << " (" << getInfo("Family", 0) << ")" << std::endl; - out << "Extended family: " << getInfo("ExtendedFamily", 0) << std::endl; - out << "Model: " << getInfo("Model", 0) << std::endl; - out << "Extended model: " << getInfo("ExtendedModel", 0) << std::endl; - out << "Type: " << getInfo("Type", 0) << std::endl; - out << "Brand ID: " << getInfo("BrandID", 0) << std::endl; + out << "Vendor: " << getInfo(eVendor, "Unknown").asString() << std::endl; + out << "Family: " << getCPUFamilyName() << " (" << getInfo(eFamily, 0) << ")" << std::endl; + out << "Extended family: " << getInfo(eExtendedFamily, 0) << std::endl; + out << "Model: " << getInfo(eModel, 0) << std::endl; + out << "Extended model: " << getInfo(eExtendedModel, 0) << std::endl; + out << "Type: " << getInfo(eType, 0) << std::endl; + out << "Brand ID: " << getInfo(eBrandID, 0) << std::endl; out << std::endl; out << "// CPU Configuration" << std::endl; out << "//////////////////////////" << std::endl; - out << "Max Supported CPUID level = " << getConfig("MaxID", 0) << std::endl; - out << "Max Supported Ext. CPUID level = " << std::hex << getConfig("MaxExtID", 0) << std::dec << std::endl; - out << "CLFLUSH cache line size = " << getConfig("CLFLUSHCacheLineSize", 0) << std::endl; - out << "APIC Physical ID = " << getConfig("APICPhysicalID", 0) << std::endl; - out << "Cache Line Size = " << getConfig("CacheLineSize", 0) << std::endl; - out << "L2 Associativity = " << getConfig("L2Associativity", 0) << std::endl; - out << "Cache Size = " << getConfig("CacheSizeK", 0) << "K" << std::endl; + + // Iterate through the dictionary of configuration options. + LLSD configs = mProcessorInfo["config"]; + for(LLSD::map_const_iterator cfgItr = configs.beginMap(); cfgItr != configs.endMap(); ++cfgItr) + { + out << cfgItr->first << " = " << cfgItr->second << std::endl; + } out << std::endl; + out << "// CPU Extensions" << std::endl; out << "//////////////////////////" << std::endl; - for(LLSD::map_const_iterator itr = mProcessorInfo.beginMap(); itr != mProcessorInfo.endMap(); ++itr) + for(LLSD::map_const_iterator itr = mProcessorInfo["extension"].beginMap(); itr != mProcessorInfo["extension"].endMap(); ++itr) { out << " " << itr->first << std::endl; } @@ -185,28 +278,46 @@ public: } protected: - void setInfo(const std::string& name, const LLSD& value) { mProcessorInfo["info"][name]=value; } - void setConfig(const std::string& name, const LLSD& value) { mProcessorInfo["config"][name]=value; } + void setInfo(cpu_info info_type, const LLSD& value) + { + setInfo(cpu_info_names[info_type], value); + } + LLSD getInfo(cpu_info info_type, const LLSD& defaultVal) const + { + return getInfo(cpu_info_names[info_type], defaultVal); + } + + void setConfig(cpu_config config_type, const LLSD& value) + { + setConfig(cpu_config_names[config_type], value); + } + LLSD getConfig(cpu_config config_type, const LLSD& defaultVal) const + { + return getConfig(cpu_config_names[config_type], defaultVal); + } + void setExtension(const std::string& name) { mProcessorInfo["extension"][name] = "true"; } + bool hasExtension(const std::string& name) const + { + return mProcessorInfo["extension"].has(name); + } +private: + void setInfo(const std::string& name, const LLSD& value) { mProcessorInfo["info"][name]=value; } LLSD getInfo(const std::string& name, const LLSD& defaultVal) const { LLSD r = mProcessorInfo["info"].get(name); return r.isDefined() ? r : defaultVal; } - + void setConfig(const std::string& name, const LLSD& value) { mProcessorInfo["config"][name]=value; } LLSD getConfig(const std::string& name, const LLSD& defaultVal) const { LLSD r = mProcessorInfo["config"].get(name); return r.isDefined() ? r : defaultVal; } - bool hasExtension(const std::string& name) const - { - return mProcessorInfo["extension"].has(name); - } - private: + LLSD mProcessorInfo; }; @@ -247,7 +358,7 @@ static F64 calculate_cpu_frequency(U32 measure_msecs) // After that we declare some vars and check the frequency of the high // resolution timer for the measure process. - // If there's no high-res timer, we exit. + // If there"s no high-res timer, we exit. unsigned __int64 starttime, endtime, timedif, freq, start, end, dif; if (!QueryPerformanceFrequency((LARGE_INTEGER *) &freq)) { @@ -271,8 +382,8 @@ static F64 calculate_cpu_frequency(U32 measure_msecs) //// Now we call a CPUID to ensure, that all other prior called functions are //// completed now (serialization) //__asm cpuid - int cpu_info[4] = {-1}; - __cpuid(cpu_info, 0); + int cpu_info[4] = {-1}; + __cpuid(cpu_info, 0); // We ask the high-res timer for the start time QueryPerformanceCounter((LARGE_INTEGER *) &starttime); @@ -331,44 +442,77 @@ private: int cpu_info[4] = {-1}; __cpuid(cpu_info, 0); unsigned int ids = (unsigned int)cpu_info[0]; - setInfo("MaxIDs", ids); + setInfo(eMaxID, ids); char cpu_vendor[0x20]; memset(cpu_vendor, 0, sizeof(cpu_vendor)); *((int*)cpu_vendor) = cpu_info[1]; *((int*)(cpu_vendor+4)) = cpu_info[3]; *((int*)(cpu_vendor+8)) = cpu_info[2]; + setInfo(eVendor, cpu_vendor); // Get the information associated with each valid Id - for(unsigned int i=0; i<=mIds; ++i) + for(unsigned int i=0; i<=ids; ++i) { __cpuid(cpu_info, i); // Interpret CPU feature information. if (i == 1) { - mSteppingID = cpu_info[0] & 0xf; - mModel = (cpu_info[0] >> 4) & 0xf; - mFamily = (cpu_info[0] >> 8) & 0xf; - mProcessorType = (cpu_info[0] >> 12) & 0x3; - mExtendedModel = (cpu_info[0] >> 16) & 0xf; - mExtendedFamily = (cpu_info[0] >> 20) & 0xff; - mBrandIndex = cpu_info[1] & 0xff; - mCLFLUSHCacheLineSize = ((cpu_info[1] >> 8) & 0xff) * 8; - mAPICPhysicalID = (cpu_info[1] >> 24) & 0xff; - mSSE3NewInstructions = (cpu_info[2] & 0x1) || false; - mMONITOR_MWAIT = (cpu_info[2] & 0x8) || false; - mCPLQualifiedDebugStore = (cpu_info[2] & 0x10) || false; - mThermalMonitor2 = (cpu_info[2] & 0x100) || false; - mFeatureInfo = cpu_info[3]; + setInfo(eStepping, cpu_info[0] & 0xf); + setInfo(eModel, (cpu_info[0] >> 4) & 0xf); + int family = (cpu_info[0] >> 8) & 0xf; + setInfo(eFamily, family); + setInfo(eType, (cpu_info[0] >> 12) & 0x3); + setInfo(eExtendedModel, (cpu_info[0] >> 16) & 0xf); + int ext_family = (cpu_info[0] >> 20) & 0xff; + setInfo(eExtendedFamily, ext_family); + setInfo(eBrandIndex, cpu_info[1] & 0xff); + + setInfo(eFamilyName, compute_CPUFamilyName(family, ext_family)); + + setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8); + setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff); + + if(cpu_info[2] & 0x1) + { + setExtension(cpu_feature_names[eSSE3_Features]); + } + + if(cpu_info[2] & 0x8) + { + setExtension(cpu_feature_names[eMONTIOR_MWAIT]); + } + + if(cpu_info[2] & 0x10) || false; + { + setExtension(cpu_feature_names[eCPLDebugStore]); + } + + if(cpu_info[2] & 0x100) + { + setExtension(cpu_feature_names[eThermalMonitor2]); + } + + unsigned int feature_info = (unsigned int) cpu_info[3]; + for(unsigned int index = 0, bit = 1; index < eSSE3_Features; ++index, bit <<= 1) + { + if(feature_info & bit) + { + setExtension(cpu_feature_names[index]); + } + } } } // Calling __cpuid with 0x80000000 as the InfoType argument // gets the number of valid extended IDs. __cpuid(cpu_info, 0x80000000); - mExtIds = cpu_info[0]; - memset(mCPUBrandString, 0, sizeof(mCPUBrandString)); + unsigned int ext_ids = cpu_info[0]; + setConfig(eMaxExtID, 0); + + char cpu_brand_string[0x40]; + memset(cpu_brand_string, 0, sizeof(cpu_brand_string)); // Get the information associated with each extended ID. for(unsigned int i=0x80000000; i<=mExtIds; ++i) @@ -377,67 +521,35 @@ private: // Interpret CPU brand string and cache information. if (i == 0x80000002) - memcpy(mCPUBrandString, cpu_info, sizeof(cpu_info)); + memcpy(cpu_brand_string, cpu_info, sizeof(cpu_info)); else if (i == 0x80000003) - memcpy(mCPUBrandString + 16, cpu_info, sizeof(cpu_info)); + memcpy(cpu_brand_string + 16, cpu_info, sizeof(cpu_info)); else if (i == 0x80000004) - memcpy(mCPUBrandString + 32, cpu_info, sizeof(cpu_info)); - else if (i == 0x80000006) - { - mCacheLineSize = cpu_info[2] & 0xff; - mL2Associativity = (cpu_info[2] >> 12) & 0xf; - mCacheSizeK = (cpu_info[2] >> 16) & 0xffff; - } - } - } - - std::string computeCPUFamilyName() const - { - const char* intel_string = "GenuineIntel"; - const char* amd_string = "AuthenticAMD"; - if(!strncmp(mCPUString, intel_string, strlen(intel_string))) - { - U32 composed_family = mFamily + mExtendedFamily; - switch(composed_family) { - case 3: return "Intel i386"; - case 4: return "Intel i486"; - case 5: return "Intel Pentium"; - case 6: return "Intel Pentium Pro/2/3, Core"; - case 7: return "Intel Itanium (IA-64)"; - case 0xF: return "Intel Pentium 4"; - case 0x10: return "Intel Itanium 2 (IA-64)"; - default: return "Unknown"; + memcpy(cpu_brand_string + 32, cpu_info, sizeof(cpu_info)); + setInfo(eBrandName, cpu_brand_string); } - } - else if(!strncmp(mCPUString, amd_string, strlen(amd_string))) - { - U32 composed_family = (mFamily == 0xF) - ? mFamily + mExtendedFamily - : mFamily; - switch(composed_family) + else if (i == 0x80000006) { - case 4: return "AMD 80486/5x86"; - case 5: return "AMD K5/K6"; - case 6: return "AMD K7"; - case 0xF: return "AMD K8"; - case 0x10: return "AMD K8L"; - default: return "Unknown"; + setConfig(eCacheLineSize, cpu_info[2] & 0xff); + setConfig(eL2Associativity, (cpu_info[2] >> 12) & 0xf); + setConfig(eCacheSizeK, (cpu_info[2] >> 16) & 0xffff); } } - return "Unknown"; } - }; #elif LL_DARWIN -class LLProcessorInfoDarwinImpl +#include +#include + +class LLProcessorInfoDarwinImpl : public LLProcessorInfoImpl { public: LLProcessorInfoDarwinImpl() { - + getCPUIDInfo(); } virtual ~LLProcessorInfoDarwinImpl() {} @@ -449,22 +561,136 @@ public: virtual std::string getCPUFamilyName() const { return "Unknown"; } virtual std::string getCPUBrandName() const { return "Unknown"; } virtual std::string getCPUFeatureDescription() const { return "Unknown"; } -}; -#endif // LL_MSVC +private: + int getSysctlInt(const char* name) + { + int result = 0; + size_t len = sizeof(int); + int error = sysctlbyname(name, (void*)&result, &len, NULL, 0); + return error == -1 ? 0 : result; + } + + uint64_t getSysctlInt64(const char* name) + { + uint64_t value = 0; + size_t size = sizeof(value); + int result = sysctlbyname(name, (void*)&value, &size, NULL, 0); + if ( result == 0 ) + { + if ( size == sizeof( uint64_t ) ) + ; + else if ( size == sizeof( uint32_t ) ) + value = (uint64_t)(( uint32_t *)&value); + else if ( size == sizeof( uint16_t ) ) + value = (uint64_t)(( uint16_t *)&value); + else if ( size == sizeof( uint8_t ) ) + value = (uint64_t)(( uint8_t *)&value); + else + { + LL_ERRS("Unknown type returned from sysctl!") << LL_ENDL; + } + } + + return result == -1 ? 0 : value; + } + + void getCPUIDInfo() + { + size_t len = 0; + + char cpu_brand_string[0x40]; + len = sizeof(cpu_brand_string); + memset(cpu_brand_string, 0, len); + sysctlbyname("machdep.cpu.brand_string", (void*)cpu_brand_string, &len, NULL, 0); + cpu_brand_string[0x3f] = 0; + setInfo(eBrandName, cpu_brand_string); + + char cpu_vendor[0x20]; + len = sizeof(cpu_vendor); + memset(cpu_vendor, 0, len); + sysctlbyname("machdep.cpu.vendor", (void*)cpu_vendor, &len, NULL, 0); + cpu_vendor[0x1f] = 0; + setInfo(eVendor, cpu_vendor); + + setInfo(eStepping, getSysctlInt("machdep.cpu.stepping")); + setInfo(eModel, getSysctlInt("machdep.cpu.model")); + int family = getSysctlInt("machdep.cpu.family"); + int ext_family = getSysctlInt("machdep.cpu.extfamily"); + setInfo(eFamily, family); + setInfo(eExtendedFamily, ext_family); + setInfo(eFamilyName, compute_CPUFamilyName(cpu_vendor, family, ext_family)); + setInfo(eExtendedModel, getSysctlInt("machdep.cpu.extmodel")); + setInfo(eBrandID, getSysctlInt("machdep.cpu.brand")); + setInfo(eType, 0); // ? where to find this? + + //setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8); + //setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff); + setConfig(eCacheLineSize, getSysctlInt("machdep.cpu.cache.linesize")); + setConfig(eL2Associativity, getSysctlInt("machdep.cpu.cache.L2_associativity")); + setConfig(eCacheSizeK, getSysctlInt("machdep.cpu.cache.size")); + + uint64_t feature_info = getSysctlInt64("machdep.cpu.feature_bits"); + S32 *feature_infos = (S32*)(&feature_info); + + setConfig(eFeatureBits, feature_infos[0]); + + for(unsigned int index = 0, bit = 1; index < eSSE3_Features; ++index, bit <<= 1) + { + if(feature_info & bit) + { + setExtension(cpu_feature_names[index]); + } + } + + // *NOTE:Mani - I didn't find any docs that assure me that machdep.cpu.feature_bits will always be + // The feature bits I think it is. Here's a test: +#ifndef LL_RELEASE_FOR_DOWNLOAD + #if defined(__i386__) && defined(__PIC__) + /* %ebx may be the PIC register. */ + #define __cpuid(level, a, b, c, d) \ + __asm__ ("xchgl\t%%ebx, %1\n\t" \ + "cpuid\n\t" \ + "xchgl\t%%ebx, %1\n\t" \ + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ + : "0" (level)) + #else + #define __cpuid(level, a, b, c, d) \ + __asm__ ("cpuid\n\t" \ + : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \ + : "0" (level)) + #endif + + unsigned int eax, ebx, ecx, edx; + __cpuid(0x1, eax, ebx, ecx, edx); + if(feature_infos[0] != (S32)edx) + { + llerrs << "machdep.cpu.feature_bits doesn't match expected cpuid result!" << llendl; + } +#endif // LL_RELEASE_FOR_DOWNLOAD + + uint64_t ext_feature_info = getSysctlInt64("machdep.cpu.extfeature_bits"); + S32 *ext_feature_infos = (S32*)(&ext_feature_info); + setConfig(eExtFeatureBits, ext_feature_infos[0]); + } +}; +#endif // LL_MSVC -// Interface implementation -LLProcessorInfo::LLProcessorInfo() +////////////////////////////////////////////////////// +// Interface definition +LLProcessorInfo::LLProcessorInfo() : mImpl(NULL) { // *NOTE:Mani - not thread safe. - if(gImpl == NULL) + if(!mImpl) { #ifdef LL_MSVC - gImpl.reset(new LLProcessorInfoWindowsImpl); + static LLProcessorInfoWindowsImpl the_impl; + mImpl = &the_impl; #elif LL_DARWIN - gImpl.reset(new LLProcessorInfoDarwinImpl); + static LLProcessorInfoDarwinImpl the_impl; + mImpl = &the_impl; #else #error "Unimplemented" #endif // LL_MSVC @@ -472,13 +698,13 @@ LLProcessorInfo::LLProcessorInfo() } LLProcessorInfo::~LLProcessorInfo() {} -F64 LLProcessorInfo::getCPUFrequency() const { return gImpl->getCPUFrequency(); } -bool LLProcessorInfo::hasSSE() const { return gImpl->hasSSE(); } -bool LLProcessorInfo::hasSSE2() const { return gImpl->hasSSE2(); } -bool LLProcessorInfo::hasAltivec() const { return gImpl->hasAltivec(); } -std::string LLProcessorInfo::getCPUFamilyName() const { return gImpl->getCPUFamilyName(); } -std::string LLProcessorInfo::getCPUBrandName() const { return gImpl->getCPUBrandName(); } -std::string LLProcessorInfo::getCPUFeatureDescription() const { return gImpl->getCPUFeatureDescription(); } +F64 LLProcessorInfo::getCPUFrequency() const { return mImpl->getCPUFrequency(); } +bool LLProcessorInfo::hasSSE() const { return mImpl->hasSSE(); } +bool LLProcessorInfo::hasSSE2() const { return mImpl->hasSSE2(); } +bool LLProcessorInfo::hasAltivec() const { return mImpl->hasAltivec(); } +std::string LLProcessorInfo::getCPUFamilyName() const { return mImpl->getCPUFamilyName(); } +std::string LLProcessorInfo::getCPUBrandName() const { return mImpl->getCPUBrandName(); } +std::string LLProcessorInfo::getCPUFeatureDescription() const { return mImpl->getCPUFeatureDescription(); } #if 0 // Filename: Processor.cpp @@ -492,14 +718,14 @@ std::string LLProcessorInfo::getCPUFeatureDescription() const { return gImpl->ge // - Optional include of the windows.h header which is // still need for CProcessor::GetCPUFrequency. // 06.03.2002 - My birthday (18th :-)) -// - Replaced the '\r\n' line endings in function -// CProcessor::cpu_infoToText by '\n' +// - Replaced the "\r\n" line endings in function +// CProcessor::cpu_infoToText by "\n" // - Replaced unsigned __int64 by signed __int64 for // solving some compiler conversion problems // - Fixed a bug at family=6, model=6 (Celeron -> P2) ////////////////////////////////////////////////////////////////////////////////// -#include "linden_common.h" + #include "processor.h" @@ -571,7 +797,7 @@ F64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs) return 0; #else // If there are invalid measure time parameters, zero msecs for example, - // we've to exit the function + // we"ve to exit the function if (uiMeasureMSecs < 1) { // If theres already a measured frequency available, we return it @@ -601,7 +827,7 @@ F64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs) // After that we declare some vars and check the frequency of the high // resolution timer for the measure process. - // If there's no high-res timer, we exit. + // If there"s no high-res timer, we exit. __int64 starttime, endtime, timedif, freq, start, end, dif; if (!QueryPerformanceFrequency((LARGE_INTEGER *) &freq)) return 0; @@ -736,7 +962,7 @@ bool CProcessor::AnalyzeIntelProcessor() } // And copy it to the brand id string strncpy(cpu_info.strBrandID, tmp,sizeof(cpu_info.strBrandID)-1); - cpu_info.strBrandID[sizeof(cpu_info.strBrandID)-1]='\0'; + cpu_info.strBrandID[sizeof(cpu_info.strBrandID)-1]="\0"; } else { @@ -892,7 +1118,7 @@ bool CProcessor::AnalyzeIntelProcessor() strcpy(cpu_info.strModel, "Intel Pentium MMX (P55C)"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium MMX (P55C core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; - case 7: // Model = 7: Pentium processor model (don't know difference to Model=2) + case 7: // Model = 7: Pentium processor model (don"t know difference to Model=2) strcpy(cpu_info.strModel, "Intel Pentium (P54C)"); /*Flawfinder: ignore*/ strncat(strCPUName, "Intel Pentium (P54C core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/ break; @@ -1095,7 +1321,7 @@ bool CProcessor::AnalyzeIntelProcessor() } else { - // If there's no serial number support we just put "No serial number" + // If there"s no serial number support we just put "No serial number" snprintf( /* Flawfinder: ignore */ cpu_info.strProcessorSerial, sizeof(cpu_info.strProcessorSerial), @@ -1175,7 +1401,7 @@ bool CProcessor::AnalyzeAMDProcessor() } // And copy it to the brand id string strncpy(cpu_info.strBrandID, tmp,sizeof(cpu_info.strBrandID)-1); - cpu_info.strBrandID[sizeof(cpu_info.strBrandID)-1]='\0'; + cpu_info.strBrandID[sizeof(cpu_info.strBrandID)-1]="\0"; } else { @@ -1380,7 +1606,7 @@ bool CProcessor::AnalyzeAMDProcessor() // 0x80000006 if (cpu_info.MaxSupportedExtendedLevel >= 0x80000006) { - // If it's present, we read it out + // If it"s present, we read it out __asm { mov eax, 0x80000005 @@ -1458,8 +1684,8 @@ bool CProcessor::AnalyzeAMDProcessor() cpu_info._L1.Instruction.uiLineSize = edxreg & 0xFF; } - // Note: I'm not absolutely sure that the L1 page size code (the - // 'if/else if/else if' structs above) really detects the real page + // Note: I"m not absolutely sure that the L1 page size code (the + // "if/else if/else if" structs above) really detects the real page // size for the TLB. Somebody should check it.... // Now we read the ext. CPUID level 0x80000006 @@ -1591,8 +1817,8 @@ bool CProcessor::CheckCPUIDPresence() #if LL_WINDOWS unsigned long BitChanged; - // We've to check if we can toggle the flag register bit 21 - // If we can't the processor does not support the CPUID command + // We"ve to check if we can toggle the flag register bit 21 + // If we can"t the processor does not support the CPUID command __asm { pushfd @@ -1620,7 +1846,7 @@ bool CProcessor::CheckCPUIDPresence() ////////////////////////////////////////////////////////////////////////////////// void CProcessor::DecodeProcessorConfiguration(unsigned int cfg) { - // First we ensure that there's only one single byte + // First we ensure that there"s only one single byte cfg &= 0xFF; // Then we do a big switch @@ -1961,7 +2187,7 @@ void CProcessor::GetStandardProcessorConfiguration() mov ecxreg, ecx mov edxreg, edx } - // We have to repeat this reading for 'num' times + // We have to repeat this reading for "num" times num = eaxreg & 0xFF; // Then we call the big decode switch function @@ -2193,7 +2419,7 @@ const ProcessorInfo *CProcessor::Getcpu_info() { // In Solaris the CPU info is in the kstats // try "psrinfo" or "kstat cpu_info" to see all - // that's available + // that"s available int ncpus=0, i; kstat_ctl_t *kc; kstat_t *ks; @@ -2241,7 +2467,7 @@ const ProcessorInfo *CProcessor::Getcpu_info() strncat(cpu_info.strFamily, (char *)KSTAT_NAMED_STR_PTR(ksi), sizeof(cpu_info.strFamily)-strlen(cpu_info.strFamily)-1); strncpy(cpu_info.strBrandID, strCPUName,sizeof(cpu_info.strBrandID)-1); - cpu_info.strBrandID[sizeof(cpu_info.strBrandID)-1]='\0'; + cpu_info.strBrandID[sizeof(cpu_info.strBrandID)-1]="\0"; // DEBUG llinfos << "CPU brand: " << strCPUName << llendl; continue; } @@ -2274,7 +2500,7 @@ const ProcessorInfo *CProcessor::Getcpu_info() // DEBUG llinfo << "The system has " << ncpus << " CPUs with a clock rate of " << uqwFrequency << "MHz." << llendl; -#if defined (__i386) // we really don't care about the CPU extensions on SPARC but on x86... +#if defined (__i386) // we really don"t care about the CPU extensions on SPARC but on x86... // Now get cpu extensions @@ -2585,7 +2811,7 @@ const ProcessorInfo *CProcessor::Getcpu_info() // Terse CPU info uses this string... strncpy(cpu_info.strBrandID, strCPUName,sizeof(cpu_info.strBrandID)-1); /* Flawfinder: ignore */ - cpu_info.strBrandID[sizeof(cpu_info.strBrandID)-1]='\0'; + cpu_info.strBrandID[sizeof(cpu_info.strBrandID)-1]="\0"; // Fun cache config stuff... @@ -2682,7 +2908,7 @@ bool CProcessor::cpu_infoToText(char *strBuffer, unsigned int uiMaxLen) { COPYADD("Processor Serial: Disabled\n"); } -#if !LL_SOLARIS // NOTE: Why bother printing all this when it's irrelavent +#if !LL_SOLARIS // NOTE: Why bother printing all this when it"s irrelavent COPYADD("\n\n// CPU Configuration\n////////////////////\n"); FORMATADD("L1 instruction cache: %s\n", cpu_info._L1.Instruction.strCache); diff --git a/indra/llcommon/llprocessor.h b/indra/llcommon/llprocessor.h index 1cea6245a3..e33af77143 100644 --- a/indra/llcommon/llprocessor.h +++ b/indra/llcommon/llprocessor.h @@ -33,6 +33,7 @@ #ifndef LLPROCESSOR_H #define LLPROCESSOR_H +class LLProcessorInfoImpl; class LLProcessorInfo { @@ -47,6 +48,8 @@ public: std::string getCPUFamilyName() const; std::string getCPUBrandName() const; std::string getCPUFeatureDescription() const; +private: + LLProcessorInfoImpl* mImpl; }; # if 0 diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index becc30832d..21d908035f 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -324,10 +324,6 @@ const char *VFS_INDEX_FILE_BASE = "index.db2.x."; static std::string gWindowTitle; -std::string gLoginPage; -std::vector gLoginURIs; -static std::string gHelperURI; - LLAppViewer::LLUpdaterInfo *LLAppViewer::sUpdaterInfo = NULL ; void idle_afk_check() -- cgit v1.3 From 4665ef7b9c997f4ac550a910b536021b7a87d811 Mon Sep 17 00:00:00 2001 From: palange Date: Mon, 1 Feb 2010 17:00:21 -0800 Subject: Added cpu frequency. puching to move to windows. again. --- indra/llcommon/llprocessor.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra') diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 09a7004913..6407c82138 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -550,6 +550,8 @@ public: LLProcessorInfoDarwinImpl() { getCPUIDInfo(); + uint64_t frequency = getSysctlInt64("hw.cpufrequency"); + setInfo(eFrequency, (F64)frequency); } virtual ~LLProcessorInfoDarwinImpl() {} -- cgit v1.3 From 0f07ec0c692c94f60d9d834660362c4fc697bd1c Mon Sep 17 00:00:00 2001 From: "Mark Palange (Mani)" Date: Tue, 2 Feb 2010 14:42:32 -0800 Subject: Pushing processor id windows fixes to working branch. --- indra/llcommon/llprocessor.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 6407c82138..459d57e155 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -423,10 +423,10 @@ static F64 calculate_cpu_frequency(U32 measure_msecs) class LLProcessorInfoWindowsImpl : public LLProcessorInfoImpl { public: - LLProcessorInfoWindowsImpl() : + LLProcessorInfoWindowsImpl() { getCPUIDInfo(); - AddInfoItem("Frequency", calculate_cpu_frequency(50)); + setInfo(eFrequency, calculate_cpu_frequency(50)); } private: @@ -442,7 +442,7 @@ private: int cpu_info[4] = {-1}; __cpuid(cpu_info, 0); unsigned int ids = (unsigned int)cpu_info[0]; - setInfo(eMaxID, ids); + setConfig(eMaxID, (S32)ids); char cpu_vendor[0x20]; memset(cpu_vendor, 0, sizeof(cpu_vendor)); @@ -467,9 +467,9 @@ private: setInfo(eExtendedModel, (cpu_info[0] >> 16) & 0xf); int ext_family = (cpu_info[0] >> 20) & 0xff; setInfo(eExtendedFamily, ext_family); - setInfo(eBrandIndex, cpu_info[1] & 0xff); + setInfo(eBrandID, cpu_info[1] & 0xff); - setInfo(eFamilyName, compute_CPUFamilyName(family, ext_family)); + setInfo(eFamilyName, compute_CPUFamilyName(cpu_vendor, family, ext_family)); setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8); setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff); @@ -484,7 +484,7 @@ private: setExtension(cpu_feature_names[eMONTIOR_MWAIT]); } - if(cpu_info[2] & 0x10) || false; + if(cpu_info[2] & 0x10) { setExtension(cpu_feature_names[eCPLDebugStore]); } @@ -515,7 +515,7 @@ private: memset(cpu_brand_string, 0, sizeof(cpu_brand_string)); // Get the information associated with each extended ID. - for(unsigned int i=0x80000000; i<=mExtIds; ++i) + for(unsigned int i=0x80000000; i<=ext_ids; ++i) { __cpuid(cpu_info, i); -- cgit v1.3 From 58f813a136a5a889e01b39e96714af91402321ba Mon Sep 17 00:00:00 2001 From: "Mark Palange (Mani)" Date: Tue, 2 Feb 2010 14:45:42 -0800 Subject: merge --- etc/message.xml | 2 +- indra/lib/python/indra/util/llmanifest.py | 1 + indra/llaudio/llstreamingaudio_fmod.cpp | 2 +- indra/llcommon/llfasttimer.h | 194 ++++++++------ indra/llmessage/llcurl.cpp | 20 +- indra/llmessage/llcurl.h | 11 + indra/llmessage/llhttpclient.cpp | 2 +- indra/llmessage/llurlrequest.cpp | 1 + indra/llplugin/llpluginclassmedia.cpp | 35 ++- indra/llplugin/llpluginclassmedia.h | 18 +- indra/llplugin/llpluginprocesschild.cpp | 10 +- indra/llrender/llfontgl.cpp | 21 +- indra/llrender/llfontgl.h | 8 +- indra/llrender/llgl.cpp | 37 +++ indra/llrender/llglstates.h | 2 + indra/llrender/llimagegl.cpp | 84 +++--- indra/llrender/llimagegl.h | 2 +- indra/llrender/llrender.cpp | 2 + indra/llrender/llvertexbuffer.cpp | 42 +-- indra/llui/CMakeLists.txt | 4 + indra/llui/llcheckboxctrl.cpp | 2 +- indra/llui/llconsole.cpp | 10 +- indra/llui/lldockablefloater.cpp | 9 + indra/llui/lldockablefloater.h | 17 ++ indra/llui/lldockcontrol.cpp | 12 +- indra/llui/llflatlistview.cpp | 12 +- indra/llui/llfloater.cpp | 64 +++-- indra/llui/llfloater.h | 3 +- indra/llui/lllayoutstack.h | 5 + indra/llui/lllineeditor.cpp | 3 +- indra/llui/llmenubutton.h | 1 + indra/llui/llmenugl.cpp | 61 +++-- indra/llui/llmultislider.cpp | 2 +- indra/llui/llpanel.cpp | 61 ++++- indra/llui/llpanel.h | 3 + indra/llui/llresizehandle.cpp | 81 +++--- indra/llui/llscrollcontainer.cpp | 5 + indra/llui/llscrolllistcell.cpp | 2 +- indra/llui/llscrolllistctrl.cpp | 5 +- indra/llui/llscrolllistctrl.h | 3 + indra/llui/lltextbase.cpp | 21 +- indra/llui/lltexteditor.cpp | 3 +- indra/llui/lluictrl.cpp | 8 + indra/llui/llurlentry.cpp | 10 +- indra/llui/tests/llurlentry_test.cpp | 66 +++++ indra/llvfs/lldir.cpp | 15 +- indra/llvfs/lldir.h | 2 +- indra/llvfs/lldir_linux.cpp | 3 +- indra/llvfs/lldir_solaris.cpp | 2 +- indra/llwindow/llkeyboardwin32.cpp | 2 +- indra/llwindow/llwindowwin32.cpp | 24 +- indra/media_plugins/webkit/media_plugin_webkit.cpp | 57 +++- indra/newview/CMakeLists.txt | 38 +-- indra/newview/app_settings/ignorable_dialogs.xml | 15 +- indra/newview/app_settings/settings.xml | 87 +++---- indra/newview/character/avatar_lad.xml | 12 +- indra/newview/llagent.cpp | 27 +- indra/newview/llagent.h | 1 + indra/newview/llagentwearables.cpp | 286 ++++++++++++++++++--- indra/newview/llappearancemgr.cpp | 174 +++++++++++-- indra/newview/llappearancemgr.h | 17 +- indra/newview/llappviewer.cpp | 59 +++-- indra/newview/llavataractions.cpp | 10 +- indra/newview/llavatariconctrl.cpp | 4 +- indra/newview/llavatarlist.cpp | 19 +- indra/newview/llavatarlist.h | 4 + indra/newview/llavatarlistitem.cpp | 266 ++++++++++++------- indra/newview/llavatarlistitem.h | 71 +++-- indra/newview/llbottomtray.cpp | 96 ++++++- indra/newview/llbottomtray.h | 17 +- indra/newview/llcallfloater.cpp | 164 ++++-------- indra/newview/llcallfloater.h | 23 +- indra/newview/llchatbar.cpp | 3 +- indra/newview/llchathistory.cpp | 65 +++-- indra/newview/llchathistory.h | 1 + indra/newview/llchatitemscontainerctrl.cpp | 64 +++-- indra/newview/llchatitemscontainerctrl.h | 1 + indra/newview/llchiclet.cpp | 67 ++++- indra/newview/llchiclet.h | 21 +- indra/newview/llcompilequeue.cpp | 15 +- indra/newview/lldateutil.cpp | 7 +- indra/newview/lldrawpoolbump.cpp | 4 +- indra/newview/lldrawpoolwlsky.cpp | 21 +- indra/newview/lldrawpoolwlsky.h | 2 +- indra/newview/llfasttimerview.cpp | 6 +- indra/newview/llfavoritesbar.cpp | 38 +-- indra/newview/llfavoritesbar.h | 11 +- indra/newview/llfirstuse.cpp | 15 +- indra/newview/llfirstuse.h | 7 + indra/newview/llfloaterabout.cpp | 19 +- indra/newview/llfloaterbulkpermission.cpp | 1 - indra/newview/llfloatercamera.cpp | 25 +- indra/newview/llfloatercamera.h | 4 +- indra/newview/llfloaterchat.cpp | 33 +-- indra/newview/llfloaterchat.h | 2 +- indra/newview/llfloaterchatterbox.cpp | 28 +- indra/newview/llfloatergesture.cpp | 12 +- indra/newview/llfloatergesture.h | 2 +- indra/newview/llfloatergroups.cpp | 9 +- indra/newview/llfloaterinventory.cpp | 4 +- indra/newview/llfloaterland.cpp | 7 +- indra/newview/llfloaterland.h | 1 - indra/newview/llfloatermediasettings.cpp | 10 +- indra/newview/llfloaterpreference.cpp | 60 ++++- indra/newview/llfloaterpreference.h | 6 +- indra/newview/llfloaterregioninfo.cpp | 7 +- indra/newview/llfloaterscriptlimits.cpp | 101 +++++++- indra/newview/llfloaterscriptlimits.h | 14 +- indra/newview/llfloatersearch.cpp | 39 +++ indra/newview/llfloatersettingsdebug.cpp | 2 +- indra/newview/llfloatersnapshot.cpp | 4 +- indra/newview/llfloatertools.cpp | 60 +++-- indra/newview/llfloatervoicedevicesettings.cpp | 38 +-- indra/newview/llfloaterworldmap.cpp | 76 ++---- indra/newview/llfloaterworldmap.h | 5 +- indra/newview/llfolderview.cpp | 36 +-- indra/newview/llfolderview.h | 2 +- indra/newview/llfoldervieweventlistener.h | 1 + indra/newview/llfolderviewitem.cpp | 133 +++++----- indra/newview/llfolderviewitem.h | 20 +- indra/newview/llgesturemgr.cpp | 10 + indra/newview/llgesturemgr.h | 2 + indra/newview/llgroupactions.cpp | 10 +- indra/newview/llgrouplist.cpp | 5 +- indra/newview/llhudtext.cpp | 6 +- indra/newview/llimfloater.cpp | 130 ++++++---- indra/newview/llimfloater.h | 5 +- indra/newview/llimfloatercontainer.cpp | 11 + indra/newview/llimfloatercontainer.h | 6 +- indra/newview/llimpanel.cpp | 2 +- indra/newview/llimview.cpp | 112 +++++--- indra/newview/llimview.h | 17 +- indra/newview/llinspect.h | 2 +- indra/newview/llinspectavatar.cpp | 103 +++++--- indra/newview/llinspectobject.cpp | 20 +- indra/newview/llinventorybridge.cpp | 143 ++++------- indra/newview/llinventorybridge.h | 1 + indra/newview/llinventoryfunctions.cpp | 3 +- indra/newview/llinventorymodel.cpp | 106 +++++++- indra/newview/llinventorymodel.h | 33 +++ indra/newview/llinventoryobserver.cpp | 35 ++- indra/newview/llinventorypanel.cpp | 53 ++-- indra/newview/llinventorypanel.h | 2 +- indra/newview/lllandmarklist.cpp | 7 + indra/newview/lllocationhistory.cpp | 6 + indra/newview/lllogchat.cpp | 4 - indra/newview/llmanip.cpp | 2 +- indra/newview/llmediactrl.cpp | 272 +++++++++++--------- indra/newview/llmediactrl.h | 1 - indra/newview/llmenucommands.cpp | 3 +- indra/newview/llmorphview.cpp | 4 +- indra/newview/llmutelist.cpp | 7 +- indra/newview/llnamebox.cpp | 35 ++- indra/newview/llnamebox.h | 6 + indra/newview/llnamelistctrl.cpp | 80 ++++-- indra/newview/llnamelistctrl.h | 5 +- indra/newview/llnearbychat.cpp | 28 +- indra/newview/llnearbychatbar.cpp | 252 +++++++++++++----- indra/newview/llnearbychatbar.h | 47 +++- indra/newview/llnearbychathandler.cpp | 23 +- indra/newview/llnotificationofferhandler.cpp | 4 +- indra/newview/lloutputmonitorctrl.cpp | 65 ++++- indra/newview/lloutputmonitorctrl.h | 29 ++- indra/newview/llpanelavatar.cpp | 20 +- indra/newview/llpanelclassified.cpp | 15 +- indra/newview/llpanelclassified.h | 1 + indra/newview/llpanelgroup.cpp | 79 +++++- indra/newview/llpanelgroup.h | 4 + indra/newview/llpanelgroupgeneral.cpp | 11 +- indra/newview/llpanelgroupnotices.cpp | 2 +- indra/newview/llpanelgrouproles.cpp | 61 ++--- indra/newview/llpanelgrouproles.h | 13 +- indra/newview/llpanelhome.cpp | 2 +- indra/newview/llpanelimcontrolpanel.cpp | 9 +- indra/newview/llpanellandaudio.cpp | 62 ++--- indra/newview/llpanellandaudio.h | 4 +- indra/newview/llpanellandmarkinfo.cpp | 7 + indra/newview/llpanellandmarks.cpp | 57 +++- indra/newview/llpanellandmarks.h | 16 ++ indra/newview/llpanelmaininventory.cpp | 6 +- indra/newview/llpanelobject.cpp | 4 +- indra/newview/llpanelobjectinventory.cpp | 2 +- indra/newview/llpaneloutfitsinventory.cpp | 175 +++++++++++-- indra/newview/llpaneloutfitsinventory.h | 6 +- indra/newview/llpanelpeople.cpp | 27 +- indra/newview/llpanelpeople.h | 2 +- indra/newview/llpanelpick.cpp | 100 +++---- indra/newview/llpanelpick.h | 23 +- indra/newview/llpanelpicks.cpp | 14 +- indra/newview/llpanelpicks.h | 1 + indra/newview/llpanelplaces.cpp | 31 +++ indra/newview/llpanelplaces.h | 6 + indra/newview/llpanelprimmediacontrols.cpp | 120 +++++---- indra/newview/llpanelprimmediacontrols.h | 4 +- indra/newview/llpanelteleporthistory.cpp | 14 +- indra/newview/llpanelvolume.cpp | 4 +- indra/newview/llparticipantlist.cpp | 23 +- indra/newview/llparticipantlist.h | 2 +- indra/newview/llpreviewgesture.cpp | 6 + indra/newview/llpreviewnotecard.cpp | 1 - indra/newview/llpreviewscript.cpp | 1 - 201 files changed, 4193 insertions(+), 2010 deletions(-) (limited to 'indra') diff --git a/etc/message.xml b/etc/message.xml index da08e12aa1..c17ae3656d 100644 --- a/etc/message.xml +++ b/etc/message.xml @@ -50,7 +50,7 @@ OpenCircuit flavor - template + llsd trusted-sender false diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index 8c05210618..7e5b86c53f 100644 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -119,6 +119,7 @@ ARGUMENTS=[ On Linux this would try to use Linux_i686Manifest.""", default=""), dict(name='build', description='Build directory.', default=DEFAULT_SRCTREE), + dict(name='buildtype', description='Build type (i.e. Debug, Release, RelWithDebInfo).', default=None), dict(name='configuration', description="""The build configuration used.""", default="Release"), diff --git a/indra/llaudio/llstreamingaudio_fmod.cpp b/indra/llaudio/llstreamingaudio_fmod.cpp index a71a87203c..a4620fa13c 100644 --- a/indra/llaudio/llstreamingaudio_fmod.cpp +++ b/indra/llaudio/llstreamingaudio_fmod.cpp @@ -174,7 +174,7 @@ void LLStreamingAudio_FMOD::update() break; case -3: // failed to open, file not found, perhaps - llwarns << "InternetSteam - failed to open" << llendl; + llwarns << "InternetStream - failed to open" << llendl; stop(); return; case -4: diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index f5c90291b8..8af79c90fd 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -1,11 +1,11 @@ -/** +/** * @file llfasttimer.h * @brief Declaration of a fast timer. * * $LicenseInfo:firstyear=2004&license=viewergpl$ - * + * * Copyright (c) 2004-2009, Linden Research, Inc. - * + * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 @@ -13,17 +13,17 @@ * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * + * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * + * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. - * + * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. @@ -39,13 +39,37 @@ #define TIME_FAST_TIMERS 0 #if LL_WINDOWS +#define LL_INLINE __forceinline + +// +// NOTE: put back in when we aren't using platform sdk anymore +// +// because MS has different signatures for these functions in winnt.h +// need to rename them to avoid conflicts +//#define _interlockedbittestandset _renamed_interlockedbittestandset +//#define _interlockedbittestandreset _renamed_interlockedbittestandreset +//#include +//#undef _interlockedbittestandset +//#undef _interlockedbittestandreset + +//inline U32 get_cpu_clock_count_32() +//{ +// U64 time_stamp = __rdtsc(); +// return (U32)(time_stamp >> 8); +//} +// +//// return full timer value, *not* shifted by 8 bits +//inline U64 get_cpu_clock_count_64() +//{ +// return __rdtsc(); +//} // shift off lower 8 bits for lower resolution but longer term timing // on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing inline U32 get_cpu_clock_count_32() { U32 ret_val; - __asm + __asm { _emit 0x0f _emit 0x31 @@ -61,7 +85,7 @@ inline U32 get_cpu_clock_count_32() inline U64 get_cpu_clock_count_64() { U64 ret_val; - __asm + __asm { _emit 0x0f _emit 0x31 @@ -72,19 +96,20 @@ inline U64 get_cpu_clock_count_64() } return ret_val; } - -#endif // LL_WINDOWS +#else +#define LL_INLINE +#endif #if (LL_LINUX || LL_SOLARIS || LL_DARWIN) && (defined(__i386__) || defined(__amd64__)) inline U32 get_cpu_clock_count_32() -{ - U64 x; - __asm__ volatile (".byte 0x0f, 0x31": "=A"(x)); - return (U32)x >> 8; +{ + U64 x; + __asm__ volatile (".byte 0x0f, 0x31": "=A"(x)); + return (U32)x >> 8; } inline U32 get_cpu_clock_count_64() -{ +{ U64 x; __asm__ volatile (".byte 0x0f, 0x31": "=A"(x)); return x >> 8; @@ -103,7 +128,7 @@ inline U32 get_cpu_clock_count_32() } inline U32 get_cpu_clock_count_64() -{ +{ return get_clock_count(); } #endif @@ -113,12 +138,27 @@ class LLMutex; #include #include "llsd.h" - class LL_COMMON_API LLFastTimer { public: + + class NamedTimer; + + struct LL_COMMON_API FrameState + { + FrameState(NamedTimer* timerp); + + U32 mSelfTimeCounter; + U32 mCalls; + FrameState* mParent; // info for caller timer + FrameState* mLastCaller; // used to bootstrap tree construction + NamedTimer* mTimer; + U16 mActiveCount; // number of timers with this ID active on stack + bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame + }; + // stores a "named" timer instance to be reused via multiple LLFastTimer stack instances - class LL_COMMON_API NamedTimer + class LL_COMMON_API NamedTimer : public LLInstanceTracker { friend class DeclareTimer; @@ -149,25 +189,11 @@ public: static NamedTimer& getRootNamedTimer(); - struct FrameState - { - FrameState(NamedTimer* timerp); - - U32 mSelfTimeCounter; - U32 mCalls; - FrameState* mParent; // info for caller timer - FrameState* mLastCaller; // used to bootstrap tree construction - NamedTimer* mTimer; - U16 mActiveCount; // number of timers with this ID active on stack - bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame - }; - S32 getFrameStateIndex() const { return mFrameStateIndex; } FrameState& getFrameState() const; - - private: + private: friend class LLFastTimer; friend class NamedTimerFactory; @@ -178,7 +204,7 @@ public: // recursive call to gather total time from children static void accumulateTimings(); - // updates cumulative times and hierarchy, + // updates cumulative times and hierarchy, // can be called multiple times in a frame, at any point static void processTimes(); @@ -186,7 +212,6 @@ public: static void resetFrame(); static void reset(); - // // members // @@ -207,58 +232,47 @@ public: std::vector mChildren; bool mCollapsed; // don't show children bool mNeedsSorting; // sort children whenever child added - }; // used to statically declare a new named timer class LL_COMMON_API DeclareTimer : public LLInstanceTracker { + friend class LLFastTimer; public: DeclareTimer(const std::string& name, bool open); DeclareTimer(const std::string& name); static void updateCachedPointers(); - // convertable to NamedTimer::FrameState for convenient usage of LLFastTimer(declared_timer) - operator NamedTimer::FrameState&() { return *mFrameState; } private: - NamedTimer& mTimer; - NamedTimer::FrameState* mFrameState; + NamedTimer& mTimer; + FrameState* mFrameState; }; - public: - static LLMutex* sLogLock; - static std::queue sLogQueue; - static BOOL sLog; - static BOOL sMetricLog; - - typedef std::vector info_list_t; - static info_list_t& getFrameStateList(); + LLFastTimer(LLFastTimer::FrameState* state); - enum RootTimerMarker { ROOT }; - LLFastTimer(RootTimerMarker); - - LLFastTimer(NamedTimer::FrameState& timer) - : mFrameState(&timer) + LL_INLINE LLFastTimer(LLFastTimer::DeclareTimer& timer) + : mFrameState(timer.mFrameState) { #if TIME_FAST_TIMERS U64 timer_start = get_cpu_clock_count_64(); #endif #if FAST_TIMER_ON - NamedTimer::FrameState* frame_state = &timer; - U32 cur_time = get_cpu_clock_count_32(); - mStartSelfTime = cur_time; - mStartTotalTime = cur_time; + LLFastTimer::FrameState* frame_state = mFrameState; + mStartTime = get_cpu_clock_count_32(); frame_state->mActiveCount++; frame_state->mCalls++; // keep current parent as long as it is active when we are frame_state->mMoveUpTree |= (frame_state->mParent->mActiveCount == 0); - - mLastTimer = sCurTimer; - sCurTimer = this; + + LLFastTimer::CurTimerData* cur_timer_data = &LLFastTimer::sCurTimerData; + mLastTimerData = *cur_timer_data; + cur_timer_data->mCurTimer = this; + cur_timer_data->mFrameState = frame_state; + cur_timer_data->mChildTime = 0; #endif #if TIME_FAST_TIMERS U64 timer_end = get_cpu_clock_count_64(); @@ -266,41 +280,54 @@ public: #endif } - ~LLFastTimer() + LL_INLINE ~LLFastTimer() { #if TIME_FAST_TIMERS U64 timer_start = get_cpu_clock_count_64(); #endif #if FAST_TIMER_ON - NamedTimer::FrameState* frame_state = mFrameState; - U32 cur_time = get_cpu_clock_count_32(); - frame_state->mSelfTimeCounter += cur_time - mStartSelfTime; + LLFastTimer::FrameState* frame_state = mFrameState; + U32 total_time = get_cpu_clock_count_32() - mStartTime; + frame_state->mSelfTimeCounter += total_time - LLFastTimer::sCurTimerData.mChildTime; frame_state->mActiveCount--; - LLFastTimer* last_timer = mLastTimer; - sCurTimer = last_timer; // store last caller to bootstrap tree creation - frame_state->mLastCaller = last_timer->mFrameState; + // do this in the destructor in case of recursion to get topmost caller + frame_state->mLastCaller = mLastTimerData.mFrameState; // we are only tracking self time, so subtract our total time delta from parents - U32 total_time = cur_time - mStartTotalTime; - last_timer->mStartSelfTime += total_time; + mLastTimerData.mChildTime += total_time; + + LLFastTimer::sCurTimerData = mLastTimerData; #endif #if TIME_FAST_TIMERS U64 timer_end = get_cpu_clock_count_64(); sTimerCycles += timer_end - timer_start; sTimerCalls++; -#endif +#endif } +public: + static LLMutex* sLogLock; + static std::queue sLogQueue; + static BOOL sLog; + static BOOL sMetricLog; + static bool sPauseHistory; + static bool sResetHistory; + static U64 sTimerCycles; + static U32 sTimerCalls; + + typedef std::vector info_list_t; + static info_list_t& getFrameStateList(); + // call this once a frame to reset timers static void nextFrame(); // dumps current cumulative frame stats to log // call nextFrame() to reset timers - static void dumpCurTimes(); + static void dumpCurTimes(); // call this to reset timer hierarchy, averages, etc. static void reset(); @@ -312,23 +339,26 @@ public: static void writeLog(std::ostream& os); static const NamedTimer* getTimerByName(const std::string& name); -public: - static bool sPauseHistory; - static bool sResetHistory; - static U64 sTimerCycles; - static U32 sTimerCalls; - + struct CurTimerData + { + LLFastTimer* mCurTimer; + FrameState* mFrameState; + U32 mChildTime; + }; + static CurTimerData sCurTimerData; + private: - static LLFastTimer* sCurTimer; static S32 sCurFrameIndex; static S32 sLastFrameIndex; static U64 sLastFrameTime; static info_list_t* sTimerInfos; - U32 mStartSelfTime; // start time + time of all child timers - U32 mStartTotalTime; // start time + time of all child timers - NamedTimer::FrameState* mFrameState; - LLFastTimer* mLastTimer; + U32 mStartTime; + LLFastTimer::FrameState* mFrameState; + LLFastTimer::CurTimerData mLastTimerData; + }; +typedef class LLFastTimer LLFastTimer; + #endif // LL_LLFASTTIMER_H diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index dc02367a62..024e17a777 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -89,6 +89,10 @@ S32 gCurlMultiCount = 0; std::vector LLCurl::sSSLMutex; std::string LLCurl::sCAPath; std::string LLCurl::sCAFile; +// Verify SSL certificates by default (matches libcurl default). The ability +// to alter this flag is only to allow us to suppress verification if it's +// broken for some reason. +bool LLCurl::sSSLVerify = true; //static void LLCurl::setCAPath(const std::string& path) @@ -102,6 +106,18 @@ void LLCurl::setCAFile(const std::string& file) sCAFile = file; } +//static +void LLCurl::setSSLVerify(bool verify) +{ + sSSLVerify = verify; +} + +//static +bool LLCurl::getSSLVerify() +{ + return sSSLVerify; +} + //static std::string LLCurl::getVersionString() { @@ -465,7 +481,8 @@ void LLCurl::Easy::prepRequest(const std::string& url, setErrorBuffer(); setCA(); - setopt(CURLOPT_SSL_VERIFYPEER, true); + setopt(CURLOPT_SSL_VERIFYPEER, LLCurl::getSSLVerify()); + setopt(CURLOPT_SSL_VERIFYHOST, LLCurl::getSSLVerify()? 2 : 0); setopt(CURLOPT_TIMEOUT, CURL_REQUEST_TIMEOUT); setoptString(CURLOPT_URL, url); @@ -1044,4 +1061,3 @@ void LLCurl::cleanupClass() #endif curl_global_cleanup(); } - diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index 1bc1767966..caf02cccd9 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -157,6 +157,16 @@ public: */ static const std::string& getCAPath() { return sCAPath; } + /** + * @ brief Set flag controlling whether to verify HTTPS certs. + */ + static void setSSLVerify(bool verify); + + /** + * @ brief Get flag controlling whether to verify HTTPS certs. + */ + static bool getSSLVerify(); + /** * @ brief Initialize LLCurl class */ @@ -182,6 +192,7 @@ public: private: static std::string sCAPath; static std::string sCAFile; + static bool sSSLVerify; }; namespace boost diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 12ecbb36eb..dd56e18caf 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -222,7 +222,7 @@ static void request( LLPumpIO::chain_t chain; LLURLRequest* req = new LLURLRequest(method, url); - req->checkRootCertificate(true); + req->checkRootCertificate(LLCurl::getSSLVerify()); lldebugs << LLURLRequest::actionAsVerb(method) << " " << url << " " diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 81b7761ed5..4e7ceff984 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -163,6 +163,7 @@ void LLURLRequest::setBodyLimit(U32 size) void LLURLRequest::checkRootCertificate(bool check) { mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE)); + mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, (check? 2 : 0)); mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); } diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 1a382643da..3d2eaed5c5 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -37,6 +37,8 @@ #include "llpluginclassmedia.h" #include "llpluginmessageclasses.h" +#include "llqtwebkit.h" + static int LOW_PRIORITY_TEXTURE_SIZE_DEFAULT = 256; static int nextPowerOf2( int value ) @@ -124,7 +126,8 @@ void LLPluginClassMedia::reset() mCanPaste = false; mMediaName.clear(); mMediaDescription.clear(); - + mBackgroundColor = LLColor4(1.0f, 1.0f, 1.0f, 1.0f); + // media_browser class mNavigateURI.clear(); mNavigateResultCode = -1; @@ -133,6 +136,9 @@ void LLPluginClassMedia::reset() mHistoryForwardAvailable = false; mStatusText.clear(); mProgressPercent = 0; + mClickURL.clear(); + mClickTarget.clear(); + mClickTargetType = TARGET_NONE; // media_time class mCurrentTime = 0.0f; @@ -234,6 +240,10 @@ void LLPluginClassMedia::idle(void) message.setValueS32("height", mRequestedMediaHeight); message.setValueS32("texture_width", mRequestedTextureWidth); message.setValueS32("texture_height", mRequestedTextureHeight); + message.setValueReal("background_r", mBackgroundColor.mV[VX]); + message.setValueReal("background_g", mBackgroundColor.mV[VY]); + message.setValueReal("background_b", mBackgroundColor.mV[VZ]); + message.setValueReal("background_a", mBackgroundColor.mV[VW]); mPlugin->sendMessage(message); // DO NOT just use sendMessage() here -- we want this to jump ahead of the queue. LL_DEBUGS("Plugin") << "Sending size_change" << LL_ENDL; @@ -664,6 +674,26 @@ void LLPluginClassMedia::paste() sendMessage(message); } +LLPluginClassMedia::ETargetType getTargetTypeFromLLQtWebkit(int target_type) +{ + // convert a LinkTargetType value from llqtwebkit to an ETargetType + // so that we don't expose the llqtwebkit header in viewer code + switch (target_type) + { + case LinkTargetType::LTT_TARGET_NONE: + return LLPluginClassMedia::TARGET_NONE; + + case LinkTargetType::LTT_TARGET_BLANK: + return LLPluginClassMedia::TARGET_BLANK; + + case LinkTargetType::LTT_TARGET_EXTERNAL: + return LLPluginClassMedia::TARGET_EXTERNAL; + + default: + return LLPluginClassMedia::TARGET_OTHER; + } +} + /* virtual */ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) { @@ -916,12 +946,15 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) { mClickURL = message.getValue("uri"); mClickTarget = message.getValue("target"); + U32 target_type = message.getValueU32("target_type"); + mClickTargetType = ::getTargetTypeFromLLQtWebkit(target_type); mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLICK_LINK_HREF); } else if(message_name == "click_nofollow") { mClickURL = message.getValue("uri"); mClickTarget.clear(); + mClickTargetType = TARGET_NONE; mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLICK_LINK_NOFOLLOW); } else diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index b58067733b..ebb9099576 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -39,7 +39,7 @@ #include "llrect.h" #include "llpluginclassmediaowner.h" #include - +#include "v4color.h" class LLPluginClassMedia : public LLPluginProcessParentOwner { @@ -86,6 +86,8 @@ public: void setSize(int width, int height); void setAutoScale(bool auto_scale); + void setBackgroundColor(LLColor4 color) { mBackgroundColor = color; }; + // Returns true if all of the texture parameters (depth, format, size, and texture size) are set up and consistent. // This will initially be false, and will also be false for some time after setSize while the resize is processed. // Note that if this returns true, it is safe to use all the get() functions above without checking for invalid return values @@ -212,6 +214,17 @@ public: // This is valid after MEDIA_EVENT_CLICK_LINK_HREF std::string getClickTarget() const { return mClickTarget; }; + typedef enum + { + TARGET_NONE, // empty href target string + TARGET_BLANK, // target to open link in user's preferred browser + TARGET_EXTERNAL, // target to open link in external browser + TARGET_OTHER // nonempty and unsupported target type + }ETargetType; + + // This is valid after MEDIA_EVENT_CLICK_LINK_HREF + ETargetType getClickTargetType() const { return mClickTargetType; }; + std::string getMediaName() const { return mMediaName; }; std::string getMediaDescription() const { return mMediaDescription; }; @@ -328,6 +341,8 @@ protected: std::string mMediaName; std::string mMediaDescription; + LLColor4 mBackgroundColor; + ///////////////////////////////////////// // media_browser class std::string mNavigateURI; @@ -340,6 +355,7 @@ protected: std::string mLocation; std::string mClickURL; std::string mClickTarget; + ETargetType mClickTargetType; ///////////////////////////////////////// // media_time class diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp index ccf6dab942..07fc82c770 100644 --- a/indra/llplugin/llpluginprocesschild.cpp +++ b/indra/llplugin/llpluginprocesschild.cpp @@ -54,8 +54,14 @@ LLPluginProcessChild::~LLPluginProcessChild() if(mInstance != NULL) { sendMessageToPlugin(LLPluginMessage("base", "cleanup")); - delete mInstance; - mInstance = NULL; + + // IMPORTANT: under some (unknown) circumstances the apr_dso_unload() triggered when mInstance is deleted + // appears to fail and lock up which means that a given instance of the slplugin process never exits. + // This is bad, especially when users try to update their version of SL - it fails because the slplugin + // process as well as a bunch of plugin specific files are locked and cannot be overwritten. + exit( 0 ); + //delete mInstance; + //mInstance = NULL; } } diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 37a28ac721..1de1d6ded4 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -472,7 +472,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars } // Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels -S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars, BOOL end_on_word_boundary) const +S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars, EWordWrapStyle end_on_word_boundary) const { if (!wchars || !wchars[0] || max_chars == 0) { @@ -562,9 +562,24 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch drawn_x = cur_x; } - if( clip && end_on_word_boundary && (start_of_last_word != 0) ) + if( clip ) { - i = start_of_last_word; + switch (end_on_word_boundary) + { + case ONLY_WORD_BOUNDARIES: + i = start_of_last_word; + break; + case WORD_BOUNDARY_IF_POSSIBLE: + if (start_of_last_word != 0) + { + i = start_of_last_word; + } + break; + default: + case ANYWHERE: + // do nothing + break; + } } return i; } diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index ea8eee7690..dfa4cf8ce5 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -122,7 +122,13 @@ public: // The following are called often, frequently with large buffers, so do not use a string interface // Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels - S32 maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars = S32_MAX, BOOL end_on_word_boundary = FALSE) const; + typedef enum e_word_wrap_style + { + ONLY_WORD_BOUNDARIES, + WORD_BOUNDARY_IF_POSSIBLE, + ANYWHERE + } EWordWrapStyle ; + S32 maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars = S32_MAX, EWordWrapStyle end_on_word_boundary = ANYWHERE) const; // Returns the index of the first complete characters from text that can be drawn in max_pixels // given that the character at start_pos should be the last character (or as close to last as possible). diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 3400a72385..187a9a984e 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -1919,6 +1919,16 @@ LLGLDepthTest::LLGLDepthTest(GLboolean depth_enabled, GLboolean write_enabled, G : mPrevDepthEnabled(sDepthEnabled), mPrevDepthFunc(sDepthFunc), mPrevWriteEnabled(sWriteEnabled) { stop_glerror(); + + checkState(); + + if (!depth_enabled) + { // always disable depth writes if depth testing is disabled + // GL spec defines this as a requirement, but some implementations allow depth writes with testing disabled + // The proper way to write to depth buffer with testing disabled is to enable testing and use a depth_func of GL_ALWAYS + write_enabled = FALSE; + } + if (depth_enabled != sDepthEnabled) { gGL.flush(); @@ -1942,6 +1952,7 @@ LLGLDepthTest::LLGLDepthTest(GLboolean depth_enabled, GLboolean write_enabled, G LLGLDepthTest::~LLGLDepthTest() { + checkState(); if (sDepthEnabled != mPrevDepthEnabled ) { gGL.flush(); @@ -1963,6 +1974,32 @@ LLGLDepthTest::~LLGLDepthTest() } } +void LLGLDepthTest::checkState() +{ + if (gDebugGL) + { + GLint func = 0; + GLboolean mask = FALSE; + + glGetIntegerv(GL_DEPTH_FUNC, &func); + glGetBooleanv(GL_DEPTH_WRITEMASK, &mask); + + if (glIsEnabled(GL_DEPTH_TEST) != sDepthEnabled || + sWriteEnabled != mask || + sDepthFunc != func) + { + if (gDebugSession) + { + gFailLog << "Unexpected depth testing state." << std::endl; + } + else + { + LL_GL_ERRS << "Unexpected depth testing state." << LL_ENDL; + } + } + } +} + LLGLClampToFarClip::LLGLClampToFarClip(glh::matrix4f P) { for (U32 i = 0; i < 4; i++) diff --git a/indra/llrender/llglstates.h b/indra/llrender/llglstates.h index 4a51cac438..968a37cab0 100644 --- a/indra/llrender/llglstates.h +++ b/indra/llrender/llglstates.h @@ -46,6 +46,8 @@ public: ~LLGLDepthTest(); + void checkState(); + GLboolean mPrevDepthEnabled; GLenum mPrevDepthFunc; GLboolean mPrevWriteEnabled; diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index f8d7ea00e0..cd493481d5 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -428,49 +428,56 @@ LLImageGL::~LLImageGL() void LLImageGL::init(BOOL usemipmaps) { -#ifdef DEBUG_MISS - mMissed = FALSE; -#endif + // keep these members in the same order as declared in llimagehl.h + // so that it is obvious by visual inspection if we forgot to + // init a field. + + mTextureMemory = 0; + mLastBindTime = 0.f; + + mPickMask = NULL; + mUseMipMaps = usemipmaps; + mHasExplicitFormat = FALSE; + mAutoGenMips = FALSE; + + mIsMask = FALSE; + mNeedsAlphaAndPickMask = TRUE ; + mAlphaStride = 0 ; + mAlphaOffset = 0 ; - mPickMask = NULL; - mTextureMemory = 0; - mLastBindTime = 0.f; + mGLTextureCreated = FALSE ; + mTexName = 0; + mWidth = 0; + mHeight = 0; + mCurrentDiscardLevel = -1; + + mDiscardLevelInAtlas = -1 ; + mTexelsInAtlas = 0 ; + mTexelsInGLTexture = 0 ; - mTarget = GL_TEXTURE_2D; - mBindTarget = LLTexUnit::TT_TEXTURE; - mUseMipMaps = usemipmaps; - mHasMipMaps = false; - mAutoGenMips = FALSE; - mTexName = 0; - mIsResident = 0; + mTarget = GL_TEXTURE_2D; + mBindTarget = LLTexUnit::TT_TEXTURE; + mHasMipMaps = false; + + mIsResident = 0; + + mComponents = 0; + mMaxDiscardLevel = MAX_DISCARD_LEVEL; mTexOptionsDirty = true; mAddressMode = LLTexUnit::TAM_WRAP; mFilterOption = LLTexUnit::TFO_ANISOTROPIC; - mWidth = 0; - mHeight = 0; - mComponents = 0; - - mMaxDiscardLevel = MAX_DISCARD_LEVEL; - mCurrentDiscardLevel = -1; mFormatInternal = -1; mFormatPrimary = (LLGLenum) 0; mFormatType = GL_UNSIGNED_BYTE; mFormatSwapBytes = FALSE; - mHasExplicitFormat = FALSE; - - mGLTextureCreated = FALSE ; - mIsMask = FALSE; - mCategory = -1 ; - mAlphaStride = 0 ; - mAlphaOffset = 0 ; - mNeedsAlphaAndPickMask = TRUE ; +#ifdef DEBUG_MISS + mMissed = FALSE; +#endif - mDiscardLevelInAtlas = -1 ; - mTexelsInAtlas = 0 ; - mTexelsInGLTexture = 0 ; + mCategory = -1; } void LLImageGL::cleanup() @@ -1669,7 +1676,7 @@ void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in) } if (mFormatType != GL_UNSIGNED_BYTE || - mFormatPrimary != GL_RGBA) + mFormatPrimary != GL_RGBA) { //cannot generate a pick mask for this texture delete [] mPickMask; @@ -1729,12 +1736,25 @@ BOOL LLImageGL::getMask(const LLVector2 &tc) if (u < 0.f || u > 1.f || v < 0.f || v > 1.f) { - llerrs << "WTF?" << llendl; + LL_WARNS_ONCE("render") << "Ugh, u/v out of range in image mask pick" << LL_ENDL; + u = v = 0.f; + llassert(false); } S32 x = (S32)(u * width); S32 y = (S32)(v * height); + if (x >= width) + { + LL_WARNS_ONCE("render") << "Ooh, width overrun on pick mask read, that coulda been bad." << LL_ENDL; + x = llmax(0, width-1); + } + if (y >= height) + { + LL_WARNS_ONCE("render") << "Ooh, height overrun on pick mask read, that woulda been bad." << LL_ENDL; + y = llmax(0, height-1); + } + S32 idx = y*width+x; S32 offset = idx%8; diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 937065043c..facfb7bd62 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -224,7 +224,7 @@ protected: bool mTexOptionsDirty; LLTexUnit::eTextureAddressMode mAddressMode; // Defaults to TAM_WRAP - LLTexUnit::eTextureFilterOptions mFilterOption; // Defaults to TFO_TRILINEAR + LLTexUnit::eTextureFilterOptions mFilterOption; // Defaults to TFO_ANISOTROPIC LLGLint mFormatInternal; // = GL internalformat LLGLenum mFormatPrimary; // = GL format (pixel data format) diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index fc45df8153..f97d81126e 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -162,6 +162,8 @@ void LLTexUnit::enable(eTextureType type) disable(); // Force a disable of a previous texture type if it's enabled. } mCurrTexType = type; + + gGL.flush(); glEnable(sGLTextureType[type]); } } diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index db4189dfea..572ae13909 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -1061,17 +1061,20 @@ void LLVertexBuffer::setBuffer(U32 data_mask) } } - glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff); - if ((GLuint)buff != mGLIndices) + if (mGLIndices) { - if (gDebugSession) - { - error = TRUE; - gFailLog << "Invalid GL index buffer bound: " << buff << std::endl; - } - else + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff); + if ((GLuint)buff != mGLIndices) { - llerrs << "Invalid GL index buffer bound: " << buff << llendl; + if (gDebugSession) + { + error = TRUE; + gFailLog << "Invalid GL index buffer bound: " << buff << std::endl; + } + else + { + llerrs << "Invalid GL index buffer bound: " << buff << llendl; + } } } } @@ -1095,17 +1098,20 @@ void LLVertexBuffer::setBuffer(U32 data_mask) } } - glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff); - if ((GLuint)buff != mGLIndices) + if (mGLIndices != 0) { - if (gDebugSession) - { - error = TRUE; - gFailLog << "Invalid GL index buffer bound: "<< std::endl; - } - else + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff); + if ((GLuint)buff != mGLIndices) { - llerrs << "Invalid GL index buffer bound: " << buff << llendl; + if (gDebugSession) + { + error = TRUE; + gFailLog << "Invalid GL index buffer bound: "<< std::endl; + } + else + { + llerrs << "Invalid GL index buffer bound: " << buff << llendl; + } } } } diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 82ec02d2eb..ce068618e2 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -26,6 +26,8 @@ include_directories( ) set(llui_SOURCE_FILES + llaccordionctrl.cpp + llaccordionctrltab.cpp llbutton.cpp llcheckboxctrl.cpp llclipboard.cpp @@ -111,6 +113,8 @@ set(llui_SOURCE_FILES set(llui_HEADER_FILES CMakeLists.txt + llaccordionctrl.h + llaccordionctrltab.h llbutton.h llcallbackmap.h llcheckboxctrl.h diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index cd10dfdb1c..3d32157406 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -107,8 +107,8 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p) { tbparams.font(p.font); } + tbparams.text_color( p.enabled() ? p.text_enabled_color() : p.text_disabled_color() ); mLabel = LLUICtrlFactory::create (tbparams); - addChild(mLabel); // Button diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp index fa0abd55d0..59499f987b 100644 --- a/indra/llui/llconsole.cpp +++ b/indra/llui/llconsole.cpp @@ -180,7 +180,7 @@ void LLConsole::draw() // draw remaining lines F32 y_pos = 0.f; - LLUIImagePtr imagep = LLUI::getUIImage("rounded_square.tga"); + LLUIImagePtr imagep = LLUI::getUIImage("Rounded_Square"); // F32 console_opacity = llclamp(gSavedSettings.getF32("ConsoleBackgroundOpacity"), 0.f, 1.f); F32 console_opacity = llclamp(LLUI::sSettingGroups["config"]->getF32("ConsoleBackgroundOpacity"), 0.f, 1.f); @@ -330,7 +330,7 @@ void LLConsole::Paragraph::updateLines(F32 screen_width, const LLFontGL* font, b skip_chars = 0; } - U32 drawable = font->maxDrawableChars(mParagraphText.c_str()+paragraph_offset, screen_width, line_end - paragraph_offset, TRUE); + U32 drawable = font->maxDrawableChars(mParagraphText.c_str()+paragraph_offset, screen_width, line_end - paragraph_offset, LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); if (drawable != 0) { @@ -392,4 +392,10 @@ void LLConsole::addLine(const LLWString& wline, F32 size, const LLColor4 &color) Paragraph paragraph(wline, color, mTimer.getElapsedTimeF32(), mFont, (F32)getRect().getWidth() ); mParagraphs.push_back ( paragraph ); + + // remove old paragraphs which can't possibly be visible any more. ::draw() will do something similar but more conservative - we do this here because ::draw() isn't guaranteed to ever be called! (i.e. the console isn't visible) + while ((S32)mParagraphs.size() > llmax((S32)0, (S32)(mMaxLines))) + { + mParagraphs.pop_front(); + } } diff --git a/indra/llui/lldockablefloater.cpp b/indra/llui/lldockablefloater.cpp index 6a5b475134..74438b184a 100644 --- a/indra/llui/lldockablefloater.cpp +++ b/indra/llui/lldockablefloater.cpp @@ -57,11 +57,20 @@ LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, , mOverlapsScreenChannel(false) { init(this); + mUseTongue = true; } LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, const LLSD& key, const Params& params) : LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(uniqueDocking) +{ + init(this); + mUseTongue = true; +} + +LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, + bool useTongue, const LLSD& key, const Params& params) : + LLFloater(key, params), mDockControl(dockControl), mUseTongue(useTongue), mUniqueDocking(uniqueDocking) { init(this); } diff --git a/indra/llui/lldockablefloater.h b/indra/llui/lldockablefloater.h index ae4f99e205..2b1ce99ae2 100644 --- a/indra/llui/lldockablefloater.h +++ b/indra/llui/lldockablefloater.h @@ -62,6 +62,20 @@ public: */ LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, const LLSD& key, const Params& params = getDefaultParams()); + + /** + * Constructor. + * @param dockControl a pointer to the doc control instance + * @param uniqueDocking - a flag defines is docking should work as tab(at one + * moment only one docked floater can be shown). + * @praram useTongue - a flag defines is dock tongue should be used. + * @params key a floater key. + * @params params a floater parameters + */ + LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking, + bool useTongue, const LLSD& key, + const Params& params = getDefaultParams()); + virtual ~LLDockableFloater(); static LLHandle getInstanceHandle() { return sInstanceHandle; } @@ -104,6 +118,7 @@ public: virtual void setOverlapsScreenChannel(bool overlaps) { mOverlapsScreenChannel = overlaps; } bool getUniqueDocking() { return mUniqueDocking; } + bool getUseTongue() { return mUseTongue; } private: /** * Provides unique of dockable floater. @@ -125,6 +140,8 @@ private: */ bool mUniqueDocking; + bool mUseTongue; + bool mOverlapsScreenChannel; }; diff --git a/indra/llui/lldockcontrol.cpp b/indra/llui/lldockcontrol.cpp index 1c3c8449c5..0d8e54aa48 100644 --- a/indra/llui/lldockcontrol.cpp +++ b/indra/llui/lldockcontrol.cpp @@ -182,12 +182,12 @@ void LLDockControl::moveDockable() LLRect rootRect; mGetAllowedRectCallback(rootRect); - bool unique_docking = false; + bool use_tongue = false; LLDockableFloater* dockable_floater = dynamic_cast (mDockableFloater); if (dockable_floater != NULL) { - unique_docking = dockable_floater->getUniqueDocking(); + use_tongue = dockable_floater->getUseTongue(); } LLRect dockableRect = mDockableFloater->calcScreenRect(); @@ -218,7 +218,7 @@ void LLDockControl::moveDockable() x = dockRect.getCenterX() - dockableRect.getWidth() / 2; y = dockRect.mTop + dockableRect.getHeight(); // unique docking used with dock tongue, so add tongue height o the Y coordinate - if (unique_docking) + if (use_tongue) { y += mDockTongue->getHeight(); } @@ -287,15 +287,15 @@ void LLDockControl::forceRecalculatePosition() void LLDockControl::drawToungue() { - bool unique_docking = false; + bool use_tongue = false; LLDockableFloater* dockable_floater = dynamic_cast (mDockableFloater); if (dockable_floater != NULL) { - unique_docking = dockable_floater->getUniqueDocking(); + use_tongue = dockable_floater->getUseTongue(); } - if (mEnabled && unique_docking) + if (mEnabled && use_tongue) { mDockTongue->draw(mDockTongueX, mDockTongueY); } diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index 3754d155cf..3694ecd4f4 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -711,19 +711,12 @@ void LLFlatListView::selectLastItem () void LLFlatListView::ensureSelectedVisible() { - LLRect visible_rc = getVisibleContentRect(); LLRect selected_rc = getLastSelectedItemRect(); - if ( !visible_rc.contains (selected_rc) ) + if ( selected_rc.isValid() ) { - // But scroll in Items panel coordinates scrollToShowRect(selected_rc); } - - // In case we are in accordion tab notify parent to show selected rectangle - LLRect screen_rc; - localRectToScreen(selected_rc, &screen_rc); - notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue())); } @@ -906,7 +899,8 @@ void LLFlatListView::notifyParentItemsRectChanged() params["width"] = req_rect.getWidth(); params["height"] = req_rect.getHeight(); - getParent()->notifyParent(params); + if (getParent()) // dummy widgets don't have a parent + getParent()->notifyParent(params); } void LLFlatListView::setNoItemsCommentVisible(bool visible) const diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 845203b420..79d8f90fec 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -233,6 +233,7 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) mAutoFocus(TRUE), // automatically take focus when opened mCanDock(false), mDocked(false), + mTornOff(false), mHasBeenDraggedWhileMinimized(FALSE), mPreviousMinimizedBottom(0), mPreviousMinimizedLeft(0) @@ -878,9 +879,11 @@ void LLFloater::setSnappedTo(const LLView* snap_view) else { //RN: assume it's a floater as it must be a sibling to our parent floater - LLFloater* floaterp = (LLFloater*)snap_view; - - setSnapTarget(floaterp->getHandle()); + const LLFloater* floaterp = dynamic_cast(snap_view); + if (floaterp) + { + setSnapTarget(floaterp->getHandle()); + } } } @@ -1065,10 +1068,6 @@ void LLFloater::setMinimized(BOOL minimize) reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), TRUE ); } - // don't show the help button while minimized - it's - // not very useful when minimized and uses up space - mButtonsEnabled[BUTTON_HELP] = !minimize; - applyTitle (); make_ui_sound("UISndWindowClose"); @@ -1361,6 +1360,7 @@ void LLFloater::bringToFront( S32 x, S32 y ) // virtual void LLFloater::setVisibleAndFrontmost(BOOL take_focus) { + gFocusMgr.setTopCtrl(NULL); setVisible(TRUE); setFrontmost(take_focus); } @@ -1458,6 +1458,7 @@ void LLFloater::onClickTearOff(LLFloater* self) } self->setTornOff(false); } + self->updateButtons(); } // static @@ -1741,14 +1742,32 @@ void LLFloater::updateButtons() S32 button_count = 0; for (S32 i = 0; i < BUTTON_COUNT; i++) { - if(!mButtons[i]) continue; - mButtons[i]->setEnabled(mButtonsEnabled[i]); + if (!mButtons[i]) + { + continue; + } + + bool enabled = mButtonsEnabled[i]; + if (i == BUTTON_HELP) + { + // don't show the help button if the floater is minimized + // or if it is a docked tear-off floater + if (isMinimized() || (mButtonsEnabled[BUTTON_TEAR_OFF] && ! mTornOff)) + { + enabled = false; + } + } + if (i == BUTTON_CLOSE && mButtonScale != 1.f) + { + //*HACK: always render close button for hosted floaters so + //that users don't accidentally hit the button when + //closing multiple windows in the chatterbox + enabled = true; + } - if (mButtonsEnabled[i] - //*HACK: always render close button for hosted floaters - // so that users don't accidentally hit the button when closing multiple windows - // in the chatterbox - || (i == BUTTON_CLOSE && mButtonScale != 1.f)) + mButtons[i]->setEnabled(enabled); + + if (enabled) { button_count++; @@ -1775,7 +1794,7 @@ void LLFloater::updateButtons() // the restore button should have a tab stop so that it takes action when you Ctrl-Tab to a minimized floater mButtons[i]->setTabStop(i == BUTTON_RESTORE); } - else if (mButtons[i]) + else { mButtons[i]->setVisible(FALSE); } @@ -2344,7 +2363,7 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out LLRect::tCoordType screen_width = getSnapRect().getWidth(); LLRect::tCoordType screen_height = getSnapRect().getHeight(); - + // only automatically resize non-minimized, resizable floaters if( floater->isResizable() && !floater->isMinimized() ) { @@ -2369,16 +2388,13 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out new_width = llmax(new_width, min_width); new_height = llmax(new_height, min_height); + LLRect new_rect; + new_rect.setLeftTopAndSize(view_rect.mLeft,view_rect.mTop,new_width, new_height); + floater->reshape( new_width, new_height, TRUE ); - if (floater->followsRight()) - { - floater->translate(old_width - new_width, 0); - } + floater->setRect(new_rect); - if (floater->followsTop()) - { - floater->translate(0, old_height - new_height); - } + floater->translateIntoRect( getLocalRect(), false ); } } diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index daf558de24..f70495c0f0 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -256,7 +256,7 @@ public: bool isDocked() const { return mDocked; } virtual void setDocked(bool docked, bool pop_on_undock = true); - virtual void setTornOff(bool torn_off) {} + virtual void setTornOff(bool torn_off) { mTornOff = torn_off; } // Return a closeable floater, if any, given the current focus. static LLFloater* getClosableFloaterFromFocus(); @@ -387,6 +387,7 @@ private: bool mCanDock; bool mDocked; + bool mTornOff; static LLMultiFloater* sHostp; static BOOL sQuitting; diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index cde383b047..c4f10038f8 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -91,6 +91,11 @@ public: bool getPanelMinSize(const std::string& panel_name, S32* min_widthp, S32* min_heightp); void updateLayout(BOOL force_resize = FALSE); + + S32 getPanelSpacing() const { return mPanelSpacing; } + BOOL getAnimate () const { return mAnimate; } + void setAnimate (BOOL animate) { mAnimate = animate; } + static void updateClass(); protected: diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 8a21155cc3..73e4d126f3 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -1583,7 +1583,6 @@ void LLLineEditor::draw() F32 alpha = getDrawContext().mAlpha; S32 text_len = mText.length(); static LLUICachedControl lineeditor_cursor_thickness ("UILineEditorCursorThickness", 0); - static LLUICachedControl lineeditor_v_pad ("UILineEditorVPad", 0); static LLUICachedControl preedit_marker_brightness ("UIPreeditMarkerBrightness", 0); static LLUICachedControl preedit_marker_gap ("UIPreeditMarkerGap", 0); static LLUICachedControl preedit_marker_position ("UIPreeditMarkerPosition", 0); @@ -1609,6 +1608,8 @@ void LLLineEditor::draw() LLRect background( 0, getRect().getHeight(), getRect().getWidth(), 0 ); background.stretch( -mBorderThickness ); + S32 lineeditor_v_pad = llround((background.getHeight() - mGLFont->getLineHeight())/2); + drawBackground(); // draw text diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h index 02eb9d3806..d0e99d9f40 100644 --- a/indra/llui/llmenubutton.h +++ b/indra/llui/llmenubutton.h @@ -55,6 +55,7 @@ public: /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask ); void hideMenu(); + LLMenuGL* getMenu() { return mMenu; } protected: friend class LLUICtrlFactory; diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 2648cbf08d..c172a2b714 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -46,6 +46,7 @@ #include "llmenugl.h" +#include "llgl.h" #include "llmath.h" #include "llrender.h" #include "llfocusmgr.h" @@ -98,7 +99,7 @@ const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10; const S32 MENU_ITEM_PADDING = 4; const std::string BOOLEAN_TRUE_PREFIX( "\xE2\x9C\x94" ); // U+2714 HEAVY CHECK MARK -const std::string BRANCH_SUFFIX( ">" ); +const std::string BRANCH_SUFFIX( "\xE2\x96\xB6" ); // U+25B6 BLACK RIGHT-POINTING TRIANGLE const std::string ARROW_UP ("^^^^^^^"); const std::string ARROW_DOWN("vvvvvvv"); @@ -477,6 +478,7 @@ void LLMenuItemGL::draw( void ) if (dynamic_cast(this)) debug_count++; gGL.color4fv( mHighlightBackground.get().mV ); + gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); } @@ -1143,37 +1145,41 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask ) if (!branch) return LLMenuItemGL::handleKeyHere(key, mask); - if (getMenu()->getVisible() && branch->getVisible() && key == KEY_LEFT) + // an item is highlighted, my menu is open, and I have an active sub menu or we are in + // keyboard navigation mode + if (getHighlight() + && getMenu()->isOpen() + && (isActive() || LLMenuGL::getKeyboardMode())) { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); - - BOOL handled = branch->clearHoverItem(); - if (branch->getTornOff()) - { - ((LLFloater*)branch->getParent())->setFocus(FALSE); - } - if (handled && getMenu()->getTornOff()) + if (branch->getVisible() && key == KEY_LEFT) { - ((LLFloater*)getMenu()->getParent())->setFocus(TRUE); - } - return handled; - } + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(TRUE); - if (getHighlight() && - getMenu()->isOpen() && - key == KEY_RIGHT && !branch->getHighlightedItem()) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + BOOL handled = branch->clearHoverItem(); + if (branch->getTornOff()) + { + ((LLFloater*)branch->getParent())->setFocus(FALSE); + } + if (handled && getMenu()->getTornOff()) + { + ((LLFloater*)getMenu()->getParent())->setFocus(TRUE); + } + return handled; + } - LLMenuItemGL* itemp = branch->highlightNextItem(NULL); - if (itemp) + if (key == KEY_RIGHT && !branch->getHighlightedItem()) { - return TRUE; + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(TRUE); + + LLMenuItemGL* itemp = branch->highlightNextItem(NULL); + if (itemp) + { + return TRUE; + } } } - return LLMenuItemGL::handleKeyHere(key, mask); } @@ -1431,7 +1437,7 @@ BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask) { BOOL menu_open = getBranch()->getVisible(); // don't do keyboard navigation of top-level menus unless in keyboard mode, or menu expanded - if (getHighlight() && getMenu()->getVisible() && (isActive() || LLMenuGL::getKeyboardMode())) + if (getHighlight() && getMenu()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode())) { if (key == KEY_LEFT) { @@ -2836,6 +2842,7 @@ BOOL LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks ) return TRUE; } + void LLMenuGL::draw( void ) { if (mNeedsArrange) @@ -2959,7 +2966,7 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) LLUI::getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y); LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y); - const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); + const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getRect(); const S32 HPAD = 2; LLRect rect = menu->getRect(); diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp index aea7c5f87c..27a727fdf5 100644 --- a/indra/llui/llmultislider.cpp +++ b/indra/llui/llmultislider.cpp @@ -490,7 +490,7 @@ void LLMultiSlider::draw() F32 opacity = getEnabled() ? 1.f : 0.3f; // Track - LLUIImagePtr thumb_imagep = LLUI::getUIImage("rounded_square.tga"); + LLUIImagePtr thumb_imagep = LLUI::getUIImage("Rounded_Square"); static LLUICachedControl multi_track_height ("UIMultiTrackHeight", 0); S32 height_offset = (getRect().getHeight() - multi_track_height) / 2; diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index db32882438..7f23fe2671 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -43,6 +43,7 @@ #include "llerror.h" #include "lltimer.h" +#include "llaccordionctrltab.h" #include "llbutton.h" #include "llmenugl.h" //#include "llstatusbar.h" @@ -851,14 +852,26 @@ static LLPanel *childGetVisibleTabWithHelp(LLView *parent) // look through immediate children first for an active tab with help for (child = parent->getFirstChild(); child; child = parent->findNextSibling(child)) { + LLPanel *curTabPanel = NULL; + + // do we have a tab container? LLTabContainer *tab = dynamic_cast(child); if (tab && tab->getVisible()) { - LLPanel *curTabPanel = tab->getCurrentPanel(); - if (curTabPanel && !curTabPanel->getHelpTopic().empty()) - { - return curTabPanel; - } + curTabPanel = tab->getCurrentPanel(); + } + + // do we have an accordion tab? + LLAccordionCtrlTab* accordion = dynamic_cast(child); + if (accordion && accordion->getDisplayChildren()) + { + curTabPanel = dynamic_cast(accordion->getAccordionView()); + } + + // if we found a valid tab, does it have a help topic? + if (curTabPanel && !curTabPanel->getHelpTopic().empty()) + { + return curTabPanel; } } @@ -885,6 +898,44 @@ LLPanel *LLPanel::childGetVisibleTabWithHelp() return ::childGetVisibleTabWithHelp(this); } +static LLPanel *childGetVisiblePanelWithHelp(LLView *parent) +{ + LLView *child; + + // look through immediate children first for an active panel with help + for (child = parent->getFirstChild(); child; child = parent->findNextSibling(child)) + { + // do we have a panel with a help topic? + LLPanel *panel = dynamic_cast(child); + if (panel && panel->getVisible() && !panel->getHelpTopic().empty()) + { + return panel; + } + } + + // then try a bit harder and recurse through all children + for (child = parent->getFirstChild(); child; child = parent->findNextSibling(child)) + { + if (child->getVisible()) + { + LLPanel* panel = ::childGetVisiblePanelWithHelp(child); + if (panel) + { + return panel; + } + } + } + + // couldn't find any active panels with a help topic string + return NULL; +} + +LLPanel *LLPanel::childGetVisiblePanelWithHelp() +{ + // find a visible tab with a help topic (to determine help context) + return ::childGetVisiblePanelWithHelp(this); +} + void LLPanel::childSetPrevalidate(const std::string& id, BOOL (*func)(const LLWString &) ) { LLLineEditor* child = findChild(id); diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index d0986a06d3..6de83fe3a7 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -214,7 +214,10 @@ public: // LLTabContainer void childShowTab(const std::string& id, const std::string& tabname, bool visible = true); LLPanel *childGetVisibleTab(const std::string& id) const; + + // Find a child with a nonempty Help topic LLPanel *childGetVisibleTabWithHelp(); + LLPanel *childGetVisiblePanelWithHelp(); // LLTextBox/LLTextEditor/LLLineEditor void childSetText(const std::string& id, const LLStringExplicit& text) { childSetValue(id, LLSD(text)); } diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp index 6239a8f721..3df09d124a 100644 --- a/indra/llui/llresizehandle.cpp +++ b/indra/llui/llresizehandle.cpp @@ -124,7 +124,7 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) { // Make sure the mouse in still over the application. We don't want to make the parent // so big that we can't see the resize handle any more. - + S32 screen_x; S32 screen_y; localPointToScreen(x, y, &screen_x, &screen_y); @@ -146,68 +146,61 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) LLRect scaled_rect = orig_rect; S32 delta_x = screen_x - mDragLastScreenX; S32 delta_y = screen_y - mDragLastScreenY; + + if(delta_x == 0 && delta_y == 0) + return FALSE; + LLCoordGL mouse_dir; // use hysteresis on mouse motion to preserve user intent when mouse stops moving mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX; mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY; + mLastMouseScreenX = screen_x; mLastMouseScreenY = screen_y; mLastMouseDir = mouse_dir; - S32 x_multiple = 1; - S32 y_multiple = 1; - switch( mCorner ) - { - case LEFT_TOP: - x_multiple = -1; - y_multiple = 1; - break; - case LEFT_BOTTOM: - x_multiple = -1; - y_multiple = -1; - break; - case RIGHT_TOP: - x_multiple = 1; - y_multiple = 1; - break; - case RIGHT_BOTTOM: - x_multiple = 1; - y_multiple = -1; - break; - } + S32 new_width = orig_rect.getWidth(); + S32 new_height = orig_rect.getHeight(); - S32 new_width = orig_rect.getWidth() + x_multiple * delta_x; - if( new_width < mMinWidth ) - { - new_width = mMinWidth; - delta_x = x_multiple * (mMinWidth - orig_rect.getWidth()); - } - - S32 new_height = orig_rect.getHeight() + y_multiple * delta_y; - if( new_height < mMinHeight ) - { - new_height = mMinHeight; - delta_y = y_multiple * (mMinHeight - orig_rect.getHeight()); - } + S32 new_pos_x = orig_rect.mLeft; + S32 new_pos_y = orig_rect.mTop; switch( mCorner ) { - case LEFT_TOP: - scaled_rect.translate(delta_x, 0); + case LEFT_TOP: + new_width-=delta_x; + new_height+=delta_y; + new_pos_x+=delta_x; + new_pos_y+=delta_y; break; case LEFT_BOTTOM: - scaled_rect.translate(delta_x, delta_y); + new_width-=delta_x; + new_height-=delta_y; + new_pos_x+=delta_x; break; case RIGHT_TOP: + new_width+=delta_x; + new_height+=delta_y; + new_pos_y+=delta_y; break; case RIGHT_BOTTOM: - scaled_rect.translate(0, delta_y); + new_width+=delta_x; + new_height-=delta_y; break; } + new_width = llmax(new_width,mMinWidth); + new_height = llmax(new_height,mMinHeight); + + LLRect::tCoordType screen_width = resizing_view->getParent()->getSnapRect().getWidth(); + LLRect::tCoordType screen_height = resizing_view->getParent()->getSnapRect().getHeight(); + + new_width = llmin(new_width, screen_width); + new_height = llmin(new_height, screen_height); + // temporarily set new parent rect - scaled_rect.mRight = scaled_rect.mLeft + new_width; - scaled_rect.mTop = scaled_rect.mBottom + new_height; + scaled_rect.setLeftTopAndSize(new_pos_x,new_pos_y,new_width,new_height); + resizing_view->setRect(scaled_rect); LLView* snap_view = NULL; @@ -258,7 +251,11 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask) resizing_view->setRect(orig_rect); // translate and scale to new shape - resizing_view->setShape(scaled_rect, true); + resizing_view->reshape(scaled_rect.getWidth(),scaled_rect.getHeight()); + resizing_view->setRect(scaled_rect); + //set shape to handle dependent floaters... + resizing_view->handleReshape(scaled_rect, false); + // update last valid mouse cursor position based on resized view's actual size LLRect new_rect = resizing_view->getRect(); diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index a5e47e8547..94465a67ce 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -668,6 +668,11 @@ void LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLRect& const // propagate scroll to document updateScroll(); + + // In case we are in accordion tab notify parent to show selected rectangle + LLRect screen_rc; + localRectToScreen(rect_to_constrain, &screen_rc); + notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue())); } void LLScrollContainer::pageUp(S32 overlap) diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp index 7238d903a3..3cc92baa8d 100644 --- a/indra/llui/llscrolllistcell.cpp +++ b/indra/llui/llscrolllistcell.cpp @@ -188,7 +188,7 @@ LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p) // initialize rounded rect image if (!mRoundedRectImage) { - mRoundedRectImage = LLUI::getUIImage("rounded_square.tga"); + mRoundedRectImage = LLUI::getUIImage("Rounded_Square"); } } diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index a53a30b501..4e84013db0 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -142,6 +142,7 @@ LLScrollListCtrl::Params::Params() contents(""), scroll_bar_bg_visible("scroll_bar_bg_visible"), scroll_bar_bg_color("scroll_bar_bg_color") + , border("border") { name = "scroll_list"; mouse_opaque = true; @@ -231,10 +232,8 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) if (p.has_border) { LLRect border_rect = getLocalRect(); - LLViewBorder::Params params; - params.name("dig border"); + LLViewBorder::Params params = p.border; params.rect(border_rect); - params.bevel_style(LLViewBorder::BEVEL_IN); mBorder = LLUICtrlFactory::create (params); addChild(mBorder); } diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 78bc60db6e..907dc90bea 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -51,6 +51,7 @@ #include "lldate.h" #include "llscrolllistitem.h" #include "llscrolllistcolumn.h" +#include "llviewborder.h" class LLScrollListCell; class LLTextBox; @@ -109,6 +110,8 @@ public: scroll_bar_bg_color; Optional contents; + + Optional border; Params(); }; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 7447a984ac..17aecaf32f 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -346,7 +346,8 @@ void LLTextBase::drawSelectionBackground() S32 segment_line_start = segmentp->getStart() + segment_offset; S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd); - S32 segment_width, segment_height; + S32 segment_width = 0; + S32 segment_height = 0; // if selection after beginning of segment if(selection_left >= segment_line_start) @@ -433,7 +434,8 @@ void LLTextBase::drawCursor() if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) { - S32 segment_width, segment_height; + S32 segment_width = 0; + S32 segment_height = 0; segmentp->getDimensions(mCursorPos - segmentp->getStart(), 1, segment_width, segment_height); S32 width = llmax(CURSOR_THICKNESS, segment_width); cursor_rect.mRight = cursor_rect.mLeft + width; @@ -2443,10 +2445,12 @@ void LLNormalTextSegment::setToolTip(const std::string& tooltip) bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const { - height = mFontHeight; + height = 0; + width = 0; bool force_newline = false; if (num_chars > 0) { + height = mFontHeight; LLWString text = mEditor.getWText(); // if last character is a newline, then return true, forcing line break llwchar last_char = text[mStart + first_char + num_chars - 1]; @@ -2461,10 +2465,6 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); } } - else - { - width = 0; - } LLUIImagePtr image = mStyle->getImage(); if( image.notNull()) @@ -2509,10 +2509,15 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin // set max characters to length of segment, or to first newline max_chars = llmin(max_chars, last_char - (mStart + segment_offset)); + // if no character yet displayed on this line, don't require word wrapping since + // we can just move to the next line, otherwise insist on it so we make forward progress + LLFontGL::EWordWrapStyle word_wrap_style = (line_offset == 0) + ? LLFontGL::WORD_BOUNDARY_IF_POSSIBLE + : LLFontGL::ONLY_WORD_BOUNDARIES; S32 num_chars = mStyle->getFont()->maxDrawableChars(text.c_str() + segment_offset + mStart, (F32)num_pixels, max_chars, - TRUE); + word_wrap_style); if (num_chars == 0 && line_offset == 0 diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index e8fc9475a5..f2c3879a6c 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -308,7 +308,8 @@ LLTextEditor::~LLTextEditor() // Scrollbar is deleted by LLView std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); - delete mContextMenu; + // context menu is owned by menu holder, not us + //delete mContextMenu; } //////////////////////////////////////////////////////////// diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 6044908ca7..3ade46d367 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -868,6 +868,14 @@ bool LLUICtrl::findHelpTopic(std::string& help_topic_out) if (panel) { + // does the panel have a sub-panel with a help topic? + LLPanel *subpanel = panel->childGetVisiblePanelWithHelp(); + if (subpanel) + { + help_topic_out = subpanel->getHelpTopic(); + return true; // success (subpanel) + } + // does the panel have an active tab with a help topic? LLPanel *tab = panel->childGetVisibleTabWithHelp(); if (tab) diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 983f0a2d49..4927e57a52 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -101,7 +101,7 @@ std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) { start++; } - return url.substr(start, url.size()-start-1); + return unescapeUrl(url.substr(start, url.size()-start-1)); } std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) @@ -201,8 +201,12 @@ std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) // LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol() { - mPattern = boost::regex("(\\bwww\\.\\S+\\.\\S+|\\b[^ \t\n\r\f\v:/]+.com\\S*|\\b[^ \t\n\r\f\v:/]+.net\\S*|\\b[^ \t\n\r\f\v:/]+.edu\\S*|\\b[^ \t\n\r\f\v:/]+.org\\S*)", - boost::regex::perl|boost::regex::icase); + mPattern = boost::regex("(" + "\\bwww\\.\\S+\\.\\S+" // i.e. www.FOO.BAR + "|" // or + "(? template<> + void object::test<11>() + { + // + // test LLUrlEntryHTTPNoProtocol - general URLs without a protocol + // + LLUrlEntryHTTPNoProtocol url; + boost::regex r = url.getPattern(); + + testRegex("naked .com URL", r, + "see google.com", + "google.com"); + + testRegex("naked .org URL", r, + "see en.wikipedia.org for details", + "en.wikipedia.org"); + + testRegex("naked .net URL", r, + "example.net", + "example.net"); + + testRegex("naked .edu URL (2 instances)", r, + "MIT web site is at web.mit.edu and also www.mit.edu", + "web.mit.edu"); + + testRegex("don't match e-mail addresses", r, + "test@lindenlab.com", + ""); + + testRegex(".com URL with path", r, + "see secondlife.com/status for grid status", + "secondlife.com/status"); + + testRegex(".com URL with port", r, + "secondlife.com:80", + "secondlife.com:80"); + + testRegex(".com URL with port and path", r, + "see secondlife.com:80/status", + "secondlife.com:80/status"); + + testRegex("www.*.com URL with port and path", r, + "see www.secondlife.com:80/status", + "www.secondlife.com:80/status"); + + testRegex("invalid .com URL [1]", r, + "..com", + ""); + + testRegex("invalid .com URL [2]", r, + "you.come", + ""); + + testRegex("invalid .com URL [3]", r, + "recommended", + ""); + + testRegex("invalid .edu URL", r, + "hi there scheduled maitenance has begun", + ""); + + testRegex("invalid .net URL", r, + "foo.netty", + ""); + } } diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index b2b17fdd56..da4abde451 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -200,6 +200,11 @@ const std::string &LLDir::getOSUserAppDir() const const std::string &LLDir::getLindenUserDir() const { + if (mLindenUserDir.empty()) + { + lldebugs << "getLindenUserDir() called early, we don't have the user name yet - returning empty string to caller" << llendl; + } + return mLindenUserDir; } @@ -337,7 +342,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_CACHE: - prefix = getCacheDir(); + prefix = getCacheDir(); break; case LL_PATH_USER_SETTINGS: @@ -348,6 +353,11 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd case LL_PATH_PER_SL_ACCOUNT: prefix = getLindenUserDir(); + if (prefix.empty()) + { + // if we're asking for the per-SL-account directory but we haven't logged in yet (or otherwise don't know the account name from which to build this string), then intentionally return a blank string to the caller and skip the below warning about a blank prefix. + return std::string(); + } break; case LL_PATH_CHAT_LOGS: @@ -557,7 +567,7 @@ std::string LLDir::getForbiddenFileChars() void LLDir::setLindenUserDir(const std::string &first, const std::string &last) { - // if both first and last aren't set, assume we're grabbing the cached dir + // if both first and last aren't set, that's bad. if (!first.empty() && !last.empty()) { // some platforms have case-sensitive filesystems, so be @@ -571,6 +581,7 @@ void LLDir::setLindenUserDir(const std::string &first, const std::string &last) mLindenUserDir += firstlower; mLindenUserDir += "_"; mLindenUserDir += lastlower; + llinfos << "Got name for LLDir::setLindenUserDir(first='" << first << "', last='" << last << "')" << llendl; } else { diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index 206e3223e3..9067d75bac 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -44,7 +44,7 @@ typedef enum ELLPath LL_PATH_NONE = 0, LL_PATH_USER_SETTINGS = 1, LL_PATH_APP_SETTINGS = 2, - LL_PATH_PER_SL_ACCOUNT = 3, + LL_PATH_PER_SL_ACCOUNT = 3, // returns/expands to blank string if we don't know the account name yet LL_PATH_CACHE = 4, LL_PATH_CHARACTER = 5, LL_PATH_HELP = 6, diff --git a/indra/llvfs/lldir_linux.cpp b/indra/llvfs/lldir_linux.cpp index ee902d1de7..a9736560ec 100644 --- a/indra/llvfs/lldir_linux.cpp +++ b/indra/llvfs/lldir_linux.cpp @@ -112,9 +112,10 @@ LLDir_Linux::LLDir_Linux() // ...normal installation running mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins"; } + mOSUserDir = getCurrentUserHome(tmp_str); mOSUserAppDir = ""; - mLindenUserDir = tmp_str; + mLindenUserDir = ""; char path [32]; /* Flawfinder: ignore */ diff --git a/indra/llvfs/lldir_solaris.cpp b/indra/llvfs/lldir_solaris.cpp index a8fad8e5bd..8ac5a41e93 100644 --- a/indra/llvfs/lldir_solaris.cpp +++ b/indra/llvfs/lldir_solaris.cpp @@ -100,7 +100,7 @@ LLDir_Solaris::LLDir_Solaris() mAppRODataDir = strdup(tmp_str); mOSUserDir = getCurrentUserHome(tmp_str); mOSUserAppDir = ""; - mLindenUserDir = tmp_str; + mLindenUserDir = ""; char path [LL_MAX_PATH]; /* Flawfinder: ignore */ diff --git a/indra/llwindow/llkeyboardwin32.cpp b/indra/llwindow/llkeyboardwin32.cpp index 35a3e7621a..ab5ecb4e63 100644 --- a/indra/llwindow/llkeyboardwin32.cpp +++ b/indra/llwindow/llkeyboardwin32.cpp @@ -80,7 +80,7 @@ LLKeyboardWin32::LLKeyboardWin32() mTranslateKeyMap[VK_OEM_COMMA] = ','; mTranslateKeyMap[VK_OEM_MINUS] = '-'; mTranslateKeyMap[VK_OEM_PERIOD] = '.'; - mTranslateKeyMap[VK_OEM_2] = KEY_PAD_DIVIDE; + mTranslateKeyMap[VK_OEM_2] = '/';//This used to be KEY_PAD_DIVIDE, but that breaks typing into text fields in media prims mTranslateKeyMap[VK_OEM_3] = '`'; mTranslateKeyMap[VK_OEM_4] = '['; mTranslateKeyMap[VK_OEM_5] = '\\'; diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 3b9c840e72..b591111b75 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -2184,7 +2184,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_MBUTTONUP"); LLFastTimer t2(FTM_MOUSEHANDLER); - // Because we move the cursor position in tllviewerhe app, we need to query + // Because we move the cursor position in the llviewer app, we need to query // to find out where the cursor at the time the event is handled. // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks @@ -2214,7 +2214,27 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_MOUSEWHEEL"); static short z_delta = 0; - z_delta += HIWORD(w_param); + RECT client_rect; + + // eat scroll events that occur outside our window, since we use mouse position to direct scroll + // instead of keyboard focus + // NOTE: mouse_coord is in *window* coordinates for scroll events + POINT mouse_coord = {(S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param)}; + + if (ScreenToClient(window_imp->mWindowHandle, &mouse_coord) + && GetClientRect(window_imp->mWindowHandle, &client_rect)) + { + // we have a valid mouse point and client rect + if (mouse_coord.x < client_rect.left || client_rect.right < mouse_coord.x + || mouse_coord.y < client_rect.top || client_rect.bottom < mouse_coord.y) + { + // mouse is outside of client rect, so don't do anything + return 0; + } + } + + S16 incoming_z_delta = HIWORD(w_param); + z_delta += incoming_z_delta; // cout << "z_delta " << z_delta << endl; // current mouse wheels report changes in increments of zDelta (+120, -120) diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp index 4b6da552cf..42d680ade6 100644 --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -84,6 +84,7 @@ private: INIT_STATE_NAVIGATING, // Browser instance has been set up and initial navigate to about:blank has been issued INIT_STATE_NAVIGATE_COMPLETE, // initial navigate to about:blank has completed INIT_STATE_WAIT_REDRAW, // First real navigate begin has been received, waiting for page changed event to start handling redraws + INIT_STATE_WAIT_COMPLETE, // Waiting for first real navigate complete event INIT_STATE_RUNNING // All initialization gymnastics are complete. }; int mBrowserWindowId; @@ -97,6 +98,9 @@ private: int mLastMouseX; int mLastMouseY; bool mFirstFocus; + F32 mBackgroundR; + F32 mBackgroundG; + F32 mBackgroundB; void setInitState(int state) { @@ -122,7 +126,7 @@ private: } } - if ( (mInitState == INIT_STATE_RUNNING) && mNeedsUpdate ) + if ( (mInitState > INIT_STATE_WAIT_REDRAW) && mNeedsUpdate ) { const unsigned char* browser_pixels = LLQtWebKit::getInstance()->grabBrowserWindow( mBrowserWindowId ); @@ -171,6 +175,15 @@ private: } std::string application_dir = std::string( cwd ); +#if LL_DARWIN + // When running under the Xcode debugger, there's a setting called "Break on Debugger()/DebugStr()" which defaults to being turned on. + // This causes the environment variable USERBREAK to be set to 1, which causes these legacy calls to break into the debugger. + // This wouldn't cause any problems except for the fact that the current release version of the Flash plugin has a call to Debugger() in it + // which gets hit when the plugin is probed by webkit. + // Unsetting the environment variable here works around this issue. + unsetenv("USERBREAK"); +#endif + #if LL_WINDOWS //*NOTE:Mani - On windows, at least, the component path is the // location of this dll's image file. @@ -236,8 +249,9 @@ private: // don't flip bitmap LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, true ); - // set background color to be black - mostly for initial login page - LLQtWebKit::getInstance()->setBackgroundColor( mBrowserWindowId, 0x00, 0x00, 0x00 ); + // set background color + // convert background color channels from [0.0, 1.0] to [0, 255]; + LLQtWebKit::getInstance()->setBackgroundColor( mBrowserWindowId, int(mBackgroundR * 255.0f), int(mBackgroundG * 255.0f), int(mBackgroundB * 255.0f) ); // Set state _before_ starting the navigate, since onNavigateBegin might get called before this call returns. setInitState(INIT_STATE_NAVIGATING); @@ -245,7 +259,21 @@ private: // Don't do this here -- it causes the dreaded "white flash" when loading a browser instance. // FIXME: Re-added this because navigating to a "page" initializes things correctly - especially // for the HTTP AUTH dialog issues (DEV-41731). Will fix at a later date. - LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" ); + // Build a data URL like this: "data:text/html,%3Chtml%3E%3Cbody bgcolor=%22#RRGGBB%22%3E%3C/body%3E%3C/html%3E" + // where RRGGBB is the background color in HTML style + std::stringstream url; + + url << "data:text/html,%3Chtml%3E%3Cbody%20bgcolor=%22#"; + // convert background color channels from [0.0, 1.0] to [0, 255]; + url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundR * 255.0f); + url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundG * 255.0f); + url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundB * 255.0f); + url << "%22%3E%3C/body%3E%3C/html%3E"; + + lldebugs << "data url is: " << url.str() << llendl; + + LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, url.str() ); +// LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" ); return true; }; @@ -295,7 +323,7 @@ private: { if(mInitState == INIT_STATE_WAIT_REDRAW) { - setInitState(INIT_STATE_RUNNING); + setInitState(INIT_STATE_WAIT_COMPLETE); } // flag that an update is required @@ -317,7 +345,9 @@ private: if(mInitState == INIT_STATE_NAVIGATE_COMPLETE) { - setInitState(INIT_STATE_WAIT_REDRAW); + // Skip the WAIT_REDRAW state now -- with the right background color set, it should no longer be necessary. +// setInitState(INIT_STATE_WAIT_REDRAW); + setInitState(INIT_STATE_WAIT_COMPLETE); } } @@ -328,6 +358,14 @@ private: { if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE) { + if(mInitState < INIT_STATE_RUNNING) + { + setInitState(INIT_STATE_RUNNING); + + // Clear the history, so the "back" button doesn't take you back to "about:blank". + LLQtWebKit::getInstance()->clearHistory(mBrowserWindowId); + } + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete"); message.setValue("uri", event.getEventUri()); message.setValueS32("result_code", event.getIntValue()); @@ -400,6 +438,7 @@ private: LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_href"); message.setValue("uri", event.getStringValue()); message.setValue("target", event.getStringValue2()); + message.setValueU32("target_type", event.getLinkType()); sendMessage(message); } @@ -695,7 +734,11 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) S32 height = message_in.getValueS32("height"); S32 texture_width = message_in.getValueS32("texture_width"); S32 texture_height = message_in.getValueS32("texture_height"); - + mBackgroundR = message_in.getValueReal("background_r"); + mBackgroundG = message_in.getValueReal("background_g"); + mBackgroundB = message_in.getValueReal("background_b"); +// mBackgroundA = message_in.setValueReal("background_a"); // Ignore any alpha + if(!name.empty()) { // Find the shared memory region with this name diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 8918fc3018..5373556c20 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -63,8 +63,6 @@ include_directories( ) set(viewer_SOURCE_FILES - llaccordionctrl.cpp - llaccordionctrltab.cpp llagent.cpp llagentaccess.cpp llagentdata.cpp @@ -158,7 +156,6 @@ set(viewer_SOURCE_FILES llfloaterbuycurrency.cpp llfloaterbuyland.cpp llfloatercamera.cpp - llfloaterchat.cpp llfloaterchatterbox.cpp llfloatercolorpicker.cpp llfloatercustomize.cpp @@ -214,7 +211,6 @@ set(viewer_SOURCE_FILES llfloaterurldisplay.cpp llfloaterurlentry.cpp llfloatervoicedevicesettings.cpp - llfloatervolumepulldown.cpp llfloaterwater.cpp llfloaterwhitelistentry.cpp llfloaterwindlight.cpp @@ -252,6 +248,7 @@ set(viewer_SOURCE_FILES llinspectgroup.cpp llinspectobject.cpp llinspectremoteobject.cpp + llinspecttoast.cpp llinventorybridge.cpp llinventoryclipboard.cpp llinventoryfilter.cpp @@ -349,6 +346,7 @@ set(viewer_SOURCE_FILES llpanelshower.cpp llpanelteleporthistory.cpp llpanelvolume.cpp + llpanelvolumepulldown.cpp llparcelselection.cpp llparticipantlist.cpp llpatchvertexarray.cpp @@ -387,6 +385,7 @@ set(viewer_SOURCE_FILES llspatialpartition.cpp llspeakbutton.cpp llspeakers.cpp + llspeakingindicatormanager.cpp llsplitbutton.cpp llsprite.cpp llstartup.cpp @@ -569,8 +568,6 @@ endif (LINUX) set(viewer_HEADER_FILES CMakeLists.txt ViewerInstall.cmake - llaccordionctrl.h - llaccordionctrltab.h llagent.h llagentaccess.h llagentdata.h @@ -667,7 +664,6 @@ set(viewer_HEADER_FILES llfloaterbuycurrency.h llfloaterbuyland.h llfloatercamera.h - llfloaterchat.h llfloaterchatterbox.h llfloatercolorpicker.h llfloatercustomize.h @@ -759,6 +755,7 @@ set(viewer_HEADER_FILES llinspectgroup.h llinspectobject.h llinspectremoteobject.h + llinspecttoast.h llinventorybridge.h llinventoryclipboard.h llinventoryfilter.h @@ -852,6 +849,7 @@ set(viewer_HEADER_FILES llpanelshower.h llpanelteleporthistory.h llpanelvolume.h + llpanelvolumepulldown.h llparcelselection.h llparticipantlist.h llpatchvertexarray.h @@ -892,6 +890,7 @@ set(viewer_HEADER_FILES llspatialpartition.h llspeakbutton.h llspeakers.h + llspeakingindicatormanager.h llsplitbutton.h llsprite.h llstartup.h @@ -1406,11 +1405,11 @@ if (WINDOWS) # Note the need to specify multiple names explicitly. set(GOOGLE_PERF_TOOLS_SOURCE ${SHARED_LIB_STAGING_DIR}/Release/libtcmalloc_minimal.dll - ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libtcmalloc_minimal.dll - ${SHARED_LIB_STAGING_DIR}/Debug/libtcmalloc_minimal-debug.dll - ) + ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libtcmalloc_minimal.dll + ${SHARED_LIB_STAGING_DIR}/Debug/libtcmalloc_minimal-debug.dll + ) endif(USE_GOOGLE_PERFTOOLS) - + set(COPY_INPUT_DEPENDECIES # The following commented dependencies are determined at variably at build time. Can't do this here. @@ -1499,15 +1498,16 @@ if (WINDOWS) --actions=copy --artwork=${ARTWORK_DIR} --build=${CMAKE_CURRENT_BINARY_DIR} + --buildtype=${CMAKE_BUILD_TYPE} --configuration=${CMAKE_CFG_INTDIR} --dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} --grid=${GRID} --source=${CMAKE_CURRENT_SOURCE_DIR} - --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/copy_touched.bat + --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/copy_touched.bat DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py - stage_third_party_libs - ${COPY_INPUT_DEPENDECIES} + ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py + stage_third_party_libs + ${COPY_INPUT_DEPENDECIES} COMMENT "Performing viewer_manifest copy" ) @@ -1569,6 +1569,7 @@ if (WINDOWS) ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py --artwork=${ARTWORK_DIR} --build=${CMAKE_CURRENT_BINARY_DIR} + --buildtype=${CMAKE_BUILD_TYPE} --channel=${VIEWER_CHANNEL} --configuration=${CMAKE_CFG_INTDIR} --dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} @@ -1650,6 +1651,7 @@ if (LINUX) --arch=${ARCH} --artwork=${ARTWORK_DIR} --build=${CMAKE_CURRENT_BINARY_DIR} + --buildtype=${CMAKE_BUILD_TYPE} --channel=${VIEWER_CHANNEL} --configuration=${CMAKE_CFG_INTDIR} --dest=${CMAKE_CURRENT_BINARY_DIR}/packaged @@ -1695,6 +1697,7 @@ if (DARWIN) --actions=copy --artwork=${ARTWORK_DIR} --build=${CMAKE_CURRENT_BINARY_DIR} + --buildtype=${CMAKE_BUILD_TYPE} --configuration=${CMAKE_CFG_INTDIR} --dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${product}.app --grid=${GRID} @@ -1715,6 +1718,7 @@ if (DARWIN) ARGS ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py --grid=${GRID} + --buildtype=${CMAKE_BUILD_TYPE} --configuration=${CMAKE_CFG_INTDIR} --channel=${VIEWER_CHANNEL} --login_channel=${VIEWER_LOGIN_CHANNEL} @@ -1734,6 +1738,7 @@ if (DARWIN) ARGS ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py --grid=${GRID} + --buildtype=${CMAKE_BUILD_TYPE} --configuration=${CMAKE_CFG_INTDIR} --channel=${VIEWER_CHANNEL} --login_channel=${VIEWER_LOGIN_CHANNEL} @@ -1761,9 +1766,10 @@ if (LL_TESTS) llagentaccess.cpp lldateutil.cpp llmediadataclient.cpp - llviewerhelputil.cpp lllogininstance.cpp + llviewerhelputil.cpp ) + ################################################## # DISABLING PRECOMPILED HEADERS USAGE FOR TESTS ################################################## diff --git a/indra/newview/app_settings/ignorable_dialogs.xml b/indra/newview/app_settings/ignorable_dialogs.xml index ab18febccc..e825f13e82 100644 --- a/indra/newview/app_settings/ignorable_dialogs.xml +++ b/indra/newview/app_settings/ignorable_dialogs.xml @@ -177,21 +177,10 @@ Value 1 - FirstStreamingMusic + FirstStreamingMedia Comment - Enables FirstStreamingMusic warning dialog - Persist - 1 - Type - Boolean - Value - 1 - - FirstStreamingVideo - - Comment - Enables FirstStreamingVideo warning dialog + Enables FirstStreamingMedia warning dialog Persist 1 Type diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index e24e1a8605..a4fc095727 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -276,7 +276,7 @@ Value 0.5 - AudioSteamingMedia + AudioStreamingMedia Comment Enable streaming @@ -285,7 +285,7 @@ Type Boolean Value - 0 + 1 AudioStreamingMusic @@ -296,7 +296,7 @@ Type Boolean Value - 0 + 1 AudioStreamingVideo @@ -307,7 +307,7 @@ Type Boolean Value - 0 + 1 AuditTexture @@ -408,6 +408,17 @@ Value 1 + AvalinePhoneSeparator + + Comment + Separator of phone parts to have Avaline numbers human readable in Voice Control Panel + Persist + 1 + Type + String + Value + - + AvatarAxisDeadZone0 Comment @@ -1130,9 +1141,9 @@ Persist 1 Type - Boolean + S32 Value - 1 + 5 CameraAngle @@ -3246,17 +3257,6 @@ Value 0.75 - FolderIndentation - - Comment - Number of pixels to indent subfolders in inventory - Persist - 1 - Type - S32 - Value - 8 - FolderLoadingMessageWaitTime Comment @@ -3596,7 +3596,7 @@ Type String Value - http://viewer-help.secondlife.com/[LANGUAGE]/[CHANNEL]/[VERSION]/[TOPIC] + http://viewer-help.secondlife.com/[LANGUAGE]/[CHANNEL]/[VERSION]/[TOPIC][DEBUG_MODE] HomeSidePanelURL @@ -3607,7 +3607,7 @@ Type String Value - http://lecs.viewer-sidebar.secondlife.com.s3.amazonaws.com/sidebar.html + http://lecs.viewer-sidebar.secondlife.com.s3.amazonaws.com/sidebar.html?p=[AUTH_TOKEN]&lang=[LANGUAGE]&channel=[CHANNEL]&version=[VERSION]&major=[VERSION_MAJOR]&minor=[VERSION_MINOR] SearchURL @@ -3618,7 +3618,7 @@ Type String Value - http://int.searchwww-phx0.damballah.lindenlab.com/viewer/[CATEGORY]?q=[QUERY]&p=[AUTH_TOKEN]&r=[MATURITY]&lang=[LANGUAGE]&g=[GODLIKE]&sid=[SESSION_ID]&rid=[REGION_ID]&pid=[PARCEL_ID] + http://search.secondlife.com/viewer/[CATEGORY]?q=[QUERY]&p=[AUTH_TOKEN]&r=[MATURITY]&lang=[LANGUAGE]&g=[GODLIKE]&sid=[SESSION_ID]&rid=[REGION_ID]&pid=[PARCEL_ID] HighResSnapshot @@ -3653,17 +3653,6 @@ Value - IMInChat - - Comment - Copy IM into chat console - Persist - 1 - Type - Boolean - Value - 0 - IMShowTimestamps Comment @@ -4809,6 +4798,17 @@ Value 0 + MyOutfitsAutofill + + Comment + Always autofill My Outfits from library when empty (else happens just once). + Persist + 1 + Type + Boolean + Value + 0 + NearMeRange Comment @@ -5040,6 +5040,18 @@ Value 5 + ToastButtonWidth + + Comment + Default width of buttons in the toast. + Note if required width will be less then this one, a button will be reshaped to default size , otherwise to required + Persist + 1 + Type + S32 + Value + 90 + ChannelBottomPanelMargin Comment @@ -9320,18 +9332,7 @@ S32 Value 2 - - UILineEditorVPad - - Comment - UI Line Editor Vertical Pad - Persist - 1 - Type - S32 - Value - 5 - + UIMaxComboWidth Comment diff --git a/indra/newview/character/avatar_lad.xml b/indra/newview/character/avatar_lad.xml index ae89eb4413..448e20b382 100644 --- a/indra/newview/character/avatar_lad.xml +++ b/indra/newview/character/avatar_lad.xml @@ -5608,9 +5608,7 @@ + height="512"> + + + @@ -6620,6 +6625,7 @@ render_pass="bump"> local_texture="head_tattoo" /> + diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index d2a56f65dd..da0e9238d6 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -43,7 +43,7 @@ #include "llcallingcard.h" #include "llchannelmanager.h" #include "llconsole.h" -#include "llfirstuse.h" +//#include "llfirstuse.h" #include "llfloatercamera.h" #include "llfloatercustomize.h" #include "llfloaterreg.h" @@ -514,6 +514,8 @@ void LLAgent::resetView(BOOL reset_camera, BOOL change_camera) } setFocusOnAvatar(TRUE, ANIMATE); + + mCameraFOVZoomFactor = 0.f; } mHUDTargetZoom = 1.f; @@ -2804,6 +2806,7 @@ void LLAgent::endAnimationUpdateUI() gStatusBar->setVisibleForMouselook(true); LLBottomTray::getInstance()->setVisible(TRUE); + LLBottomTray::getInstance()->onMouselookModeOut(); LLSideTray::getInstance()->getButtonsPanel()->setVisible(TRUE); LLSideTray::getInstance()->updateSidetrayVisibility(); @@ -2812,7 +2815,7 @@ void LLAgent::endAnimationUpdateUI() LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); - LLFloaterCamera::toPrevModeIfInAvatarViewMode(); + LLFloaterCamera::onLeavingMouseLook(); // Only pop if we have pushed... if (TRUE == mViewsPushed) @@ -2902,6 +2905,7 @@ void LLAgent::endAnimationUpdateUI() LLNavigationBar::getInstance()->setVisible(FALSE); gStatusBar->setVisibleForMouselook(false); + LLBottomTray::getInstance()->onMouselookModeIn(); LLBottomTray::getInstance()->setVisible(FALSE); LLSideTray::getInstance()->getButtonsPanel()->setVisible(FALSE); @@ -2915,10 +2919,6 @@ void LLAgent::endAnimationUpdateUI() // JC - Added for always chat in third person option gFocusMgr.setKeyboardFocus(NULL); - //Making sure Camera Controls floater is in the right state - //when entering Mouse Look using wheel scrolling - LLFloaterCamera::updateIfNotInAvatarViewMode(); - LLToolMgr::getInstance()->setCurrentToolset(gMouselookToolset); mViewsPushed = TRUE; @@ -3588,7 +3588,7 @@ F32 LLAgent::calcCameraFOVZoomFactor() { return 0.f; } - else if (mFocusObject.notNull() && !mFocusObject->isAvatar()) + else if (mFocusObject.notNull() && !mFocusObject->isAvatar() && !mFocusOnAvatar) { // don't FOV zoom on mostly transparent objects LLVector3 focus_offset = mFocusObjectOffset; @@ -5161,6 +5161,11 @@ BOOL LLAgent::setUserGroupFlags(const LLUUID& group_id, BOOL accept_notices, BOO return FALSE; } +BOOL LLAgent::canJoinGroups() const +{ + return mGroups.count() < MAX_AGENT_GROUPS; +} + LLQuaternion LLAgent::getHeadRotation() { if (mAvatarObject.isNull() || !mAvatarObject->mPelvisp || !mAvatarObject->mHeadp) @@ -5694,10 +5699,10 @@ void LLAgent::processScriptControlChange(LLMessageSystem *msg, void **) } // Any control taken? If so, might be first time. - if (total_count > 0) - { - LLFirstUse::useOverrideKeys(); - } + //if (total_count > 0) + //{ + //LLFirstUse::useOverrideKeys(); + //} } else { diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 2e95dc72be..beede7fbe3 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -972,6 +972,7 @@ public: BOOL setGroupContribution(const LLUUID& group_id, S32 contribution); BOOL setUserGroupFlags(const LLUUID& group_id, BOOL accept_notices, BOOL list_in_profile); const std::string &getGroupName() const { return mGroupName; } + BOOL canJoinGroups() const; private: std::string mGroupName; LLUUID mGroupID; diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index dc1598aacd..c21cdf9508 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -95,19 +95,38 @@ public: enum ELibraryOutfitFetchStep { LOFS_FOLDER = 0, LOFS_OUTFITS, + LOFS_LIBRARY, + LOFS_IMPORTED, LOFS_CONTENTS }; - LLLibraryOutfitsFetch() : mCurrFetchStep(LOFS_FOLDER), mOutfitsPopulated(false) {} + LLLibraryOutfitsFetch() : mCurrFetchStep(LOFS_FOLDER), mOutfitsPopulated(false) + { + mMyOutfitsID = LLUUID::null; + mClothingID = LLUUID::null; + mLibraryClothingID = LLUUID::null; + mImportedClothingID = LLUUID::null; + mImportedClothingName = "Imported Library Clothing"; + } ~LLLibraryOutfitsFetch() {} - virtual void done(); + virtual void done(); void doneIdle(); + LLUUID mMyOutfitsID; + void importedFolderFetch(); protected: void folderDone(void); void outfitsDone(void); + void libraryDone(void); + void importedFolderDone(void); void contentsDone(void); enum ELibraryOutfitFetchStep mCurrFetchStep; - std::vector< std::pair< LLUUID, std::string > > mOutfits; + typedef std::vector< std::pair< LLUUID, std::string > > cloth_folder_vec_t; + cloth_folder_vec_t mLibraryClothingFolders; + cloth_folder_vec_t mImportedClothingFolders; bool mOutfitsPopulated; + LLUUID mClothingID; + LLUUID mLibraryClothingID; + LLUUID mImportedClothingID; + std::string mImportedClothingName; }; LLAgentWearables gAgentWearables; @@ -116,6 +135,39 @@ BOOL LLAgentWearables::mInitialWearablesUpdateReceived = FALSE; using namespace LLVOAvatarDefines; +// HACK: For EXT-3923: Pants item shows in inventory with skin icon and messes with "current look" +// Some db items are corrupted, have inventory flags = 0, implying wearable type = shape, even though +// wearable type stored in asset is some other value. +// Calling this function whenever a wearable is added to increase visibility if this problem +// turns up in other inventories. +void checkWearableAgainstInventory(LLWearable *wearable) +{ + if (wearable->getItemID().isNull()) + return; + + // Check for wearable type consistent with inventory item wearable type. + LLViewerInventoryItem *item = gInventory.getItem(wearable->getItemID()); + if (item) + { + if (!item->isWearableType()) + { + llwarns << "wearable associated with non-wearable item" << llendl; + } + if (item->getWearableType() != wearable->getType()) + { + llwarns << "type mismatch: wearable " << wearable->getName() + << " has type " << wearable->getType() + << " but inventory item " << item->getName() + << " has type " << item->getWearableType() << llendl; + } + } + else + { + llwarns << "wearable inventory item not found" << wearable->getName() + << " itemID " << wearable->getItemID().asString() << llendl; + } +} + void LLAgentWearables::dump() { llinfos << "LLAgentWearablesDump" << llendl; @@ -657,6 +709,7 @@ LLWearable* LLAgentWearables::getWearable(const EWearableType type, U32 index) void LLAgentWearables::setWearable(const EWearableType type, U32 index, LLWearable *wearable) { + LLWearable *old_wearable = getWearable(type,index); if (!old_wearable) { @@ -680,6 +733,7 @@ void LLAgentWearables::setWearable(const EWearableType type, U32 index, LLWearab wearable_vec[index] = wearable; old_wearable->setLabelUpdated(); wearableUpdated(wearable); + checkWearableAgainstInventory(wearable); } } @@ -695,6 +749,7 @@ U32 LLAgentWearables::pushWearable(const EWearableType type, LLWearable *wearabl { mWearableDatas[type].push_back(wearable); wearableUpdated(wearable); + checkWearableAgainstInventory(wearable); return mWearableDatas[type].size()-1; } return MAX_WEARABLES_PER_TYPE; @@ -875,7 +930,7 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs // If this is the very first time the user has logged into viewer2+ (from a legacy viewer, or new account) // then auto-populate outfits from the library into the My Outfits folder. - if (LLInventoryModel::getIsFirstTimeInViewer2()) + if (LLInventoryModel::getIsFirstTimeInViewer2() || gSavedSettings.getBOOL("MyOutfitsAutofill")) { gAgentWearables.populateMyOutfitsFolder(); } @@ -1309,15 +1364,15 @@ void LLAgentWearables::makeNewOutfit(const std::string& new_folder_name, } } -class LLAutoRenameFolder: public LLInventoryCallback +class LLShowCreatedOutfit: public LLInventoryCallback { public: - LLAutoRenameFolder(LLUUID& folder_id): + LLShowCreatedOutfit(LLUUID& folder_id): mFolderID(folder_id) { } - virtual ~LLAutoRenameFolder() + virtual ~LLShowCreatedOutfit() { LLSD key; LLSideTray::getInstance()->showPanel("panel_outfits_inventory", key); @@ -1327,13 +1382,15 @@ public: { outfit_panel->getRootFolder()->clearSelection(); outfit_panel->getRootFolder()->setSelectionByID(mFolderID, TRUE); - outfit_panel->getRootFolder()->setNeedsAutoRename(TRUE); } LLAccordionCtrlTab* tab_outfits = outfit_panel ? outfit_panel->findChild("tab_outfits") : 0; if (tab_outfits && !tab_outfits->getDisplayChildren()) { tab_outfits->changeOpenClose(tab_outfits->getDisplayChildren()); } + + LLAppearanceManager::instance().updateIsDirty(); + LLAppearanceManager::instance().updatePanelOutfitName(""); } virtual void fire(const LLUUID&) @@ -1358,9 +1415,10 @@ LLUUID LLAgentWearables::makeNewOutfitLinks(const std::string& new_folder_name) LLFolderType::FT_OUTFIT, new_folder_name); - LLPointer cb = new LLAutoRenameFolder(folder_id); + LLPointer cb = new LLShowCreatedOutfit(folder_id); LLAppearanceManager::instance().shallowCopyCategory(LLAppearanceManager::instance().getCOF(),folder_id, cb); - + LLAppearanceManager::instance().createBaseOutfitLink(folder_id, cb); + return folder_id; } @@ -1705,6 +1763,7 @@ void LLAgentWearables::setWearableFinal(LLInventoryItem* new_item, LLWearable* n mWearableDatas[type].push_back(new_wearable); llinfos << "Added additional wearable for type " << type << " size is now " << mWearableDatas[type].size() << llendl; + checkWearableAgainstInventory(new_wearable); } else { @@ -2088,11 +2147,15 @@ void LLAgentWearables::populateMyOutfitsFolder(void) // Get the complete information on the items in the inventory and // setup an observer that will wait for that to happen. LLInventoryFetchDescendentsObserver::folder_ref_t folders; - const LLUUID my_outfits_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + outfits->mMyOutfitsID = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); - folders.push_back(my_outfits_id); + folders.push_back(outfits->mMyOutfitsID); gInventory.addObserver(outfits); outfits->fetchDescendents(folders); + if (outfits->isEverythingComplete()) + { + outfits->done(); + } } void LLLibraryOutfitsFetch::done() @@ -2106,13 +2169,24 @@ void LLLibraryOutfitsFetch::done() void LLLibraryOutfitsFetch::doneIdle() { gInventory.addObserver(this); // Add this back in since it was taken out during ::done() + switch (mCurrFetchStep) { case LOFS_FOLDER: folderDone(); + mCurrFetchStep = LOFS_OUTFITS; break; case LOFS_OUTFITS: outfitsDone(); + mCurrFetchStep = LOFS_LIBRARY; + break; + case LOFS_LIBRARY: + libraryDone(); + mCurrFetchStep = LOFS_IMPORTED; + break; + case LOFS_IMPORTED: + importedFolderDone(); + mCurrFetchStep = LOFS_CONTENTS; break; case LOFS_CONTENTS: contentsDone(); @@ -2134,67 +2208,217 @@ void LLLibraryOutfitsFetch::doneIdle() void LLLibraryOutfitsFetch::folderDone(void) { - // Early out if we already have items in My Outfits. LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t wearable_array; - gInventory.collectDescendents(mCompleteFolders.front(), cat_array, wearable_array, + gInventory.collectDescendents(mMyOutfitsID, cat_array, wearable_array, LLInventoryModel::EXCLUDE_TRASH); + + // Early out if we already have items in My Outfits. if (cat_array.count() > 0 || wearable_array.count() > 0) { mOutfitsPopulated = true; return; } - // Get the UUID of the library's clothing folder - const LLUUID library_clothing_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING, false, true); + mClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING); + mLibraryClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING, false, true); mCompleteFolders.clear(); // Get the complete information on the items in the inventory. LLInventoryFetchDescendentsObserver::folder_ref_t folders; - folders.push_back(library_clothing_id); - mCurrFetchStep = LOFS_OUTFITS; + folders.push_back(mClothingID); + folders.push_back(mLibraryClothingID); fetchDescendents(folders); + if (isEverythingComplete()) + { + done(); + } } void LLLibraryOutfitsFetch::outfitsDone(void) { LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t wearable_array; - gInventory.collectDescendents(mCompleteFolders.front(), cat_array, wearable_array, - LLInventoryModel::EXCLUDE_TRASH); - LLInventoryFetchDescendentsObserver::folder_ref_t folders; + // Collect the contents of the Library's Clothing folder + gInventory.collectDescendents(mLibraryClothingID, cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH); + llassert(cat_array.count() > 0); for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin(); iter != cat_array.end(); ++iter) { const LLViewerInventoryCategory *cat = iter->get(); + + // Get the names and id's of every outfit in the library, except for ruth and other "misc" outfits. if (cat->getName() != "More Outfits" && cat->getName() != "Ruth") { + // Get the name of every outfit in the library folders.push_back(cat->getUUID()); - mOutfits.push_back(std::make_pair(cat->getUUID(), cat->getName())); + mLibraryClothingFolders.push_back(std::make_pair(cat->getUUID(), cat->getName())); } } + + // Collect the contents of your Inventory Clothing folder + cat_array.clear(); + wearable_array.clear(); + gInventory.collectDescendents(mClothingID, cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH); + + // Check if you already have an "Imported Library Clothing" folder + for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin(); + iter != cat_array.end(); + ++iter) + { + const LLViewerInventoryCategory *cat = iter->get(); + if (cat->getName() == mImportedClothingName) + { + mImportedClothingID = cat->getUUID(); + } + } + mCompleteFolders.clear(); + + fetchDescendents(folders); + if (isEverythingComplete()) + { + done(); + } +} - mCurrFetchStep = LOFS_CONTENTS; +class LLLibraryOutfitsCopyDone: public LLInventoryCallback +{ +public: + LLLibraryOutfitsCopyDone(LLLibraryOutfitsFetch * fetcher): + mFireCount(0), mLibraryOutfitsFetcher(fetcher) + { + } + + virtual ~LLLibraryOutfitsCopyDone() + { + if (mLibraryOutfitsFetcher) + { + gInventory.addObserver(mLibraryOutfitsFetcher); + mLibraryOutfitsFetcher->done(); + } + } + + /* virtual */ void fire(const LLUUID& inv_item) + { + mFireCount++; + } +private: + U32 mFireCount; + LLLibraryOutfitsFetch * mLibraryOutfitsFetcher; +}; + +void LLLibraryOutfitsFetch::libraryDone(void) +{ + // Copy the clothing folders from the library into the imported clothing folder if necessary. + if (mImportedClothingID == LLUUID::null) + { + gInventory.removeObserver(this); + LLPointer copy_waiter = new LLLibraryOutfitsCopyDone(this); + mImportedClothingID = gInventory.createNewCategory(mClothingID, + LLFolderType::FT_NONE, + mImportedClothingName); + + for (cloth_folder_vec_t::const_iterator iter = mLibraryClothingFolders.begin(); + iter != mLibraryClothingFolders.end(); + ++iter) + { + LLUUID folder_id = gInventory.createNewCategory(mImportedClothingID, + LLFolderType::FT_NONE, + iter->second); + LLAppearanceManager::getInstance()->shallowCopyCategory(iter->first, folder_id, copy_waiter); + } + } + else + { + // Skip straight to fetching the contents of the imported folder + importedFolderFetch(); + } +} + +void LLLibraryOutfitsFetch::importedFolderFetch(void) +{ + // Fetch the contents of the Imported Clothing Folder + LLInventoryFetchDescendentsObserver::folder_ref_t folders; + folders.push_back(mImportedClothingID); + + mCompleteFolders.clear(); + fetchDescendents(folders); + if (isEverythingComplete()) + { + done(); + } } -void LLLibraryOutfitsFetch::contentsDone(void) +void LLLibraryOutfitsFetch::importedFolderDone(void) { - for(S32 i = 0; i < (S32)mOutfits.size(); ++i) + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t wearable_array; + LLInventoryFetchDescendentsObserver::folder_ref_t folders; + + // Collect the contents of the Imported Clothing folder + gInventory.collectDescendents(mImportedClothingID, cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH); + + for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin(); + iter != cat_array.end(); + ++iter) + { + const LLViewerInventoryCategory *cat = iter->get(); + + // Get the name of every imported outfit + folders.push_back(cat->getUUID()); + mImportedClothingFolders.push_back(std::make_pair(cat->getUUID(), cat->getName())); + } + + mCompleteFolders.clear(); + fetchDescendents(folders); + if (isEverythingComplete()) + { + done(); + } +} + +void LLLibraryOutfitsFetch::contentsDone(void) +{ + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t wearable_array; + + for (cloth_folder_vec_t::const_iterator folder_iter = mImportedClothingFolders.begin(); + folder_iter != mImportedClothingFolders.end(); + ++folder_iter) { // First, make a folder in the My Outfits directory. - const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); - LLUUID folder_id = gInventory.createNewCategory(parent_id, - LLFolderType::FT_OUTFIT, - mOutfits[i].second); - LLAppearanceManager::getInstance()->shallowCopyCategory(mOutfits[i].first, folder_id, NULL); + LLUUID new_outfit_folder_id = gInventory.createNewCategory(mMyOutfitsID, LLFolderType::FT_OUTFIT, folder_iter->second); + + cat_array.clear(); + wearable_array.clear(); + // Collect the contents of each imported clothing folder, so we can create new outfit links for it + gInventory.collectDescendents(folder_iter->first, cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH); + + for (LLInventoryModel::item_array_t::const_iterator wearable_iter = wearable_array.begin(); + wearable_iter != wearable_array.end(); + ++wearable_iter) + { + const LLViewerInventoryItem *item = wearable_iter->get(); + link_inventory_item(gAgent.getID(), + item->getLinkedUUID(), + new_outfit_folder_id, + item->getName(), + LLAssetType::AT_LINK, + NULL); + } } + mOutfitsPopulated = true; } @@ -2236,6 +2460,8 @@ void LLInitialWearablesFetch::processContents() } else { + // if we're constructing the COF from the wearables message, we don't have a proper outfit link + LLAppearanceManager::instance().setOutfitDirty(true); processWearablesMessage(); } delete this; diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 43b2f34ecd..748d8bdfbf 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -364,7 +364,7 @@ LLUUID LLAppearanceManager::getCOF() } -const LLViewerInventoryItem* LLAppearanceManager::getCurrentOutfitLink() +const LLViewerInventoryItem* LLAppearanceManager::getBaseOutfitLink() { const LLUUID& current_outfit_cat = getCOF(); LLInventoryModel::cat_array_t cat_array; @@ -392,6 +392,21 @@ const LLViewerInventoryItem* LLAppearanceManager::getCurrentOutfitLink() return NULL; } +bool LLAppearanceManager::getBaseOutfitName(std::string& name) +{ + const LLViewerInventoryItem* outfit_link = getBaseOutfitLink(); + if(outfit_link) + { + const LLViewerInventoryCategory *cat = outfit_link->getLinkedCategory(); + if (cat) + { + name = cat->getName(); + return true; + } + } + return false; +} + // Update appearance from outfit folder. void LLAppearanceManager::changeOutfit(bool proceed, const LLUUID& category, bool append) { @@ -444,6 +459,28 @@ void LLAppearanceManager::shallowCopyCategory(const LLUUID& src_id, const LLUUID } } +void LLAppearanceManager::purgeBaseOutfitLink(const LLUUID& category) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(category, cats, items, + LLInventoryModel::EXCLUDE_TRASH); + for (S32 i = 0; i < items.count(); ++i) + { + LLViewerInventoryItem *item = items.get(i); + if (item->getActualType() != LLAssetType::AT_LINK_FOLDER) + continue; + if (item->getIsLinkType()) + { + LLViewerInventoryCategory* catp = item->getLinkedCategory(); + if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT) + { + gInventory.purgeObject(item->getUUID()); + } + } + } +} + void LLAppearanceManager::purgeCategory(const LLUUID& category, bool keep_outfit_links) { LLInventoryModel::cat_array_t cats; @@ -578,17 +615,9 @@ void LLAppearanceManager::updateCOF(const LLUUID& category, bool append) linkAll(cof, gest_items, link_waiter); // Add link to outfit if category is an outfit. - LLViewerInventoryCategory* catp = gInventory.getCategory(category); if (!append) { - std::string new_outfit_name = ""; - if (catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT) - { - link_inventory_item(gAgent.getID(), category, cof, catp->getName(), - LLAssetType::AT_LINK_FOLDER, link_waiter); - new_outfit_name = catp->getName(); - } - updatePanelOutfitName(new_outfit_name); + createBaseOutfitLink(category, link_waiter); } } @@ -602,6 +631,23 @@ void LLAppearanceManager::updatePanelOutfitName(const std::string& name) } } +void LLAppearanceManager::createBaseOutfitLink(const LLUUID& category, LLPointer link_waiter) +{ + const LLUUID cof = getCOF(); + LLViewerInventoryCategory* catp = gInventory.getCategory(category); + std::string new_outfit_name = ""; + + purgeBaseOutfitLink(cof); + + if (catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT) + { + link_inventory_item(gAgent.getID(), category, cof, catp->getName(), + LLAssetType::AT_LINK_FOLDER, link_waiter); + new_outfit_name = catp->getName(); + } + + updatePanelOutfitName(new_outfit_name); +} void LLAppearanceManager::updateAgentWearables(LLWearableHoldingPattern* holder, bool append) { @@ -644,6 +690,10 @@ void LLAppearanceManager::updateAgentWearables(LLWearableHoldingPattern* holder, void LLAppearanceManager::updateAppearanceFromCOF() { + // update dirty flag to see if the state of the COF matches + // the saved outfit stored as a folder link + updateIsDirty(); + dumpCat(getCOF(),"COF, start"); bool follow_folder_links = true; @@ -693,14 +743,30 @@ void LLAppearanceManager::updateAppearanceFromCOF() LLDynamicArray found_container; for(S32 i = 0; i < wear_items.count(); ++i) { - found = new LLFoundData(wear_items.get(i)->getLinkedUUID(), // Wear the base item, not the link - wear_items.get(i)->getAssetUUID(), - wear_items.get(i)->getName(), - wear_items.get(i)->getType()); - holder->mFoundList.push_front(found); - found_container.put(found); + LLViewerInventoryItem *item = wear_items.get(i); + LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL; + if (item && linked_item) + { + found = new LLFoundData(linked_item->getUUID(), + linked_item->getAssetUUID(), + linked_item->getName(), + linked_item->getType()); + holder->mFoundList.push_front(found); + found_container.put(found); + } + else + { + if (!item) + { + llwarns << "attempt to wear a null item " << llendl; + } + else if (!linked_item) + { + llwarns << "attempt to wear a broken link " << item->getName() << llendl; + } + } } - for(S32 i = 0; i < wear_items.count(); ++i) + for(S32 i = 0; i < found_container.count(); ++i) { holder->append = false; found = found_container.get(i); @@ -959,7 +1025,9 @@ void LLAppearanceManager::addCOFItemLink(const LLInventoryItem *item, bool do_up if (linked_already) { if (do_update) + { LLAppearanceManager::updateAppearanceFromCOF(); + } return; } else @@ -1013,6 +1081,75 @@ void LLAppearanceManager::removeCOFItemLinks(const LLUUID& item_id, bool do_upda } } +void LLAppearanceManager::updateIsDirty() +{ + LLUUID cof = getCOF(); + LLUUID base_outfit; + + // find base outfit link + const LLViewerInventoryItem* base_outfit_item = getBaseOutfitLink(); + LLViewerInventoryCategory* catp = NULL; + if (base_outfit_item && base_outfit_item->getIsLinkType()) + { + catp = base_outfit_item->getLinkedCategory(); + } + if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT) + { + base_outfit = catp->getUUID(); + } + + if(base_outfit.isNull()) + { + // no outfit link found, display "unsaved outfit" + mOutfitIsDirty = true; + } + else + { + LLInventoryModel::cat_array_t cof_cats; + LLInventoryModel::item_array_t cof_items; + gInventory.collectDescendents(cof, cof_cats, cof_items, + LLInventoryModel::EXCLUDE_TRASH); + + LLInventoryModel::cat_array_t outfit_cats; + LLInventoryModel::item_array_t outfit_items; + gInventory.collectDescendents(base_outfit, outfit_cats, outfit_items, + LLInventoryModel::EXCLUDE_TRASH); + + if(outfit_items.count() != cof_items.count() -1) + { + // Current outfit folder should have one more item than the outfit folder. + // this one item is the link back to the outfit folder itself. + mOutfitIsDirty = true; + } + else + { + typedef std::set item_set_t; + item_set_t cof_set; + item_set_t outfit_set; + + // sort COF items by UUID + for (S32 i = 0; i < cof_items.count(); ++i) + { + LLViewerInventoryItem *item = cof_items.get(i); + // don't add the base outfit link to the list of objects we're comparing + if(item != base_outfit_item) + { + cof_set.insert(item->getLinkedUUID()); + } + } + + // sort outfit folder by UUID + for (S32 i = 0; i < outfit_items.count(); ++i) + { + LLViewerInventoryItem *item = outfit_items.get(i); + outfit_set.insert(item->getLinkedUUID()); + } + + mOutfitIsDirty = (outfit_set != cof_set); + } + } +} + //#define DUMP_CAT_VERBOSE void LLAppearanceManager::dumpCat(const LLUUID& cat_id, const std::string& msg) @@ -1049,7 +1186,8 @@ void LLAppearanceManager::dumpItemArray(const LLInventoryModel::item_array_t& it } LLAppearanceManager::LLAppearanceManager(): - mAttachmentInvLinkEnabled(false) + mAttachmentInvLinkEnabled(false), + mOutfitIsDirty(false) { } diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index b625d42a50..20745b70e4 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -62,11 +62,14 @@ public: LLUUID getCOF(); // Finds the folder link to the currently worn outfit - const LLViewerInventoryItem *getCurrentOutfitLink(); + const LLViewerInventoryItem *getBaseOutfitLink(); + bool getBaseOutfitName(std::string &name); // Update the displayed outfit name in UI. void updatePanelOutfitName(const std::string& name); + void createBaseOutfitLink(const LLUUID& category, LLPointer link_waiter); + void updateAgentWearables(LLWearableHoldingPattern* holder, bool append); // For debugging - could be moved elsewhere. @@ -94,6 +97,16 @@ public: // Add COF link to ensemble folder. void addEnsembleLink(LLInventoryCategory* item, bool do_update = true); + //has the current outfit changed since it was loaded? + bool isOutfitDirty() { return mOutfitIsDirty; } + + // set false if you just loaded the outfit, true otherwise + void setOutfitDirty(bool isDirty) { mOutfitIsDirty = isDirty; } + + // manually compare ouftit folder link to COF to see if outfit has changed. + // should only be necessary to do on initial login. + void updateIsDirty(); + protected: LLAppearanceManager(); ~LLAppearanceManager(); @@ -114,9 +127,11 @@ private: bool follow_folder_links); void purgeCategory(const LLUUID& category, bool keep_outfit_links); + void purgeBaseOutfitLink(const LLUUID& category); std::set mRegisteredAttachments; bool mAttachmentInvLinkEnabled; + bool mOutfitIsDirty; }; #define SUPPORT_ENSEMBLES 0 diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 21d908035f..8507c62d27 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -71,7 +71,7 @@ #include "lluicolortable.h" #include "llurldispatcher.h" #include "llurlhistory.h" -#include "llfirstuse.h" +//#include "llfirstuse.h" #include "llrender.h" #include "llteleporthistory.h" #include "lllocationhistory.h" @@ -313,6 +313,7 @@ void init_default_trans_args() { default_trans_args.insert("SECOND_LIFE"); // World default_trans_args.insert("APP_NAME"); + default_trans_args.insert("CAPITALIZED_APP_NAME"); default_trans_args.insert("SECOND_LIFE_GRID"); default_trans_args.insert("SUPPORT_SITE"); } @@ -921,7 +922,6 @@ bool LLAppViewer::mainLoop() { LLMemType mt1(LLMemType::MTYPE_MAIN); mMainloopTimeout = new LLWatchdogTimeout(); - // *FIX:Mani - Make this a setting, once new settings exist in this branch. //------------------------------------------- // Run main loop until time to quit @@ -931,12 +931,13 @@ bool LLAppViewer::mainLoop() gServicePump = new LLPumpIO(gAPRPoolp); LLHTTPClient::setPump(*gServicePump); LLCurl::setCAFile(gDirUtilp->getCAFile()); + LLCurl::setSSLVerify(! gSavedSettings.getBOOL("NoVerifySSLCert")); // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated. LLVoiceChannel::initClass(); LLVoiceClient::init(gServicePump); - + LLTimer frameTimer,idleTimer; LLTimer debugTime; LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); @@ -1371,7 +1372,7 @@ bool LLAppViewer::cleanup() if( gViewerWindow) gViewerWindow->shutdownViews(); - llinfos << "Cleaning up Inevntory" << llendflush; + llinfos << "Cleaning up Inventory" << llendflush; // Cleanup Inventory after the UI since it will delete any remaining observers // (Deleted observers should have already removed themselves) @@ -1473,10 +1474,17 @@ bool LLAppViewer::cleanup() LLUIColorTable::instance().saveUserSettings(); - // PerAccountSettingsFile should be empty if no use has been logged on. + // PerAccountSettingsFile should be empty if no user has been logged on. // *FIX:Mani This should get really saved in a "logoff" mode. - gSavedPerAccountSettings.saveToFile(gSavedSettings.getString("PerAccountSettingsFile"), TRUE); - llinfos << "Saved settings" << llendflush; + if (gSavedSettings.getString("PerAccountSettingsFile").empty()) + { + llinfos << "Not saving per-account settings; don't know the account name yet." << llendl; + } + else + { + gSavedPerAccountSettings.saveToFile(gSavedSettings.getString("PerAccountSettingsFile"), TRUE); + llinfos << "Saved settings" << llendflush; + } std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); // save all settings, even if equals defaults @@ -1910,26 +1918,25 @@ bool LLAppViewer::initConfiguration() // These are warnings that appear on the first experience of that condition. // They are already set in the settings_default.xml file, but still need to be added to LLFirstUse // for disable/reset ability - LLFirstUse::addConfigVariable("FirstBalanceIncrease"); - LLFirstUse::addConfigVariable("FirstBalanceDecrease"); - LLFirstUse::addConfigVariable("FirstSit"); - LLFirstUse::addConfigVariable("FirstMap"); - LLFirstUse::addConfigVariable("FirstGoTo"); - LLFirstUse::addConfigVariable("FirstBuild"); +// LLFirstUse::addConfigVariable("FirstBalanceIncrease"); +// LLFirstUse::addConfigVariable("FirstBalanceDecrease"); +// LLFirstUse::addConfigVariable("FirstSit"); +// LLFirstUse::addConfigVariable("FirstMap"); +// LLFirstUse::addConfigVariable("FirstGoTo"); +// LLFirstUse::addConfigVariable("FirstBuild"); // LLFirstUse::addConfigVariable("FirstLeftClickNoHit"); - LLFirstUse::addConfigVariable("FirstTeleport"); - LLFirstUse::addConfigVariable("FirstOverrideKeys"); - LLFirstUse::addConfigVariable("FirstAttach"); - LLFirstUse::addConfigVariable("FirstAppearance"); - LLFirstUse::addConfigVariable("FirstInventory"); - LLFirstUse::addConfigVariable("FirstSandbox"); - LLFirstUse::addConfigVariable("FirstFlexible"); - LLFirstUse::addConfigVariable("FirstDebugMenus"); - LLFirstUse::addConfigVariable("FirstStreamingMusic"); - LLFirstUse::addConfigVariable("FirstStreamingVideo"); - LLFirstUse::addConfigVariable("FirstSculptedPrim"); - LLFirstUse::addConfigVariable("FirstVoice"); - LLFirstUse::addConfigVariable("FirstMedia"); +// LLFirstUse::addConfigVariable("FirstTeleport"); +// LLFirstUse::addConfigVariable("FirstOverrideKeys"); +// LLFirstUse::addConfigVariable("FirstAttach"); +// LLFirstUse::addConfigVariable("FirstAppearance"); +// LLFirstUse::addConfigVariable("FirstInventory"); +// LLFirstUse::addConfigVariable("FirstSandbox"); +// LLFirstUse::addConfigVariable("FirstFlexible"); +// LLFirstUse::addConfigVariable("FirstDebugMenus"); +// LLFirstUse::addConfigVariable("FirstStreamingMedia"); +// LLFirstUse::addConfigVariable("FirstSculptedPrim"); +// LLFirstUse::addConfigVariable("FirstVoice"); +// LLFirstUse::addConfigVariable("FirstMedia"); // - read command line settings. LLControlGroupCLP clp; diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 2a8c55e5db..40c9bb6afa 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -168,8 +168,10 @@ void LLAvatarActions::offerTeleport(const LLUUID& invitee) // static void LLAvatarActions::offerTeleport(const std::vector& ids) { - if (ids.size() > 0) - handle_lure(ids); + if (ids.size() == 0) + return; + + handle_lure(ids); } // static @@ -595,9 +597,11 @@ void LLAvatarActions::requestFriendship(const LLUUID& target_id, const std::stri LLSD args; args["TO_NAME"] = target_name; + LLSD payload; + payload["from_id"] = target_id; payload["SESSION_NAME"] = target_name; - payload["SUPPRES_TOST"] = true; + payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("FriendshipOffered", args, payload); } diff --git a/indra/newview/llavatariconctrl.cpp b/indra/newview/llavatariconctrl.cpp index 42ae122ff9..87b8d807c4 100644 --- a/indra/newview/llavatariconctrl.cpp +++ b/indra/newview/llavatariconctrl.cpp @@ -64,7 +64,7 @@ void LLAvatarIconIDCache::load () llinfos << "Loading avatar icon id cache." << llendl; // build filename for each user - std::string resolved_filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, mFilename); + std::string resolved_filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, mFilename); llifstream file(resolved_filename); if (!file.is_open()) @@ -97,7 +97,7 @@ void LLAvatarIconIDCache::load () void LLAvatarIconIDCache::save () { - std::string resolved_filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, mFilename); + std::string resolved_filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, mFilename); // open a file for writing llofstream file (resolved_filename); diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 5df73a994e..6784e6693b 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -82,7 +82,7 @@ void LLAvatarList::setSpeakingIndicatorsVisible(bool visible) getItems(items); for( std::vector::const_iterator it = items.begin(); it != items.end(); it++) { - static_cast(*it)->setSpeakingIndicatorVisible(mShowSpeakingIndicator); + static_cast(*it)->showSpeakingIndicator(mShowSpeakingIndicator); } } @@ -320,21 +320,25 @@ boost::signals2::connection LLAvatarList::setRefreshCompleteCallback(const commi return mRefreshCompleteSignal.connect(cb); } +boost::signals2::connection LLAvatarList::setItemDoubleClickCallback(const mouse_signal_t::slot_type& cb) +{ + return mItemDoubleClickSignal.connect(cb); +} + void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, BOOL is_online, EAddPosition pos) { LLAvatarListItem* item = new LLAvatarListItem(); - item->showInfoBtn(true); - item->showSpeakingIndicator(true); item->setName(name); item->setAvatarId(id, mIgnoreOnlineStatus); item->setOnline(mIgnoreOnlineStatus ? true : is_online); item->showLastInteractionTime(mShowLastInteractionTime); - item->childSetVisible("info_btn", false); item->setAvatarIconVisible(mShowIcons); item->setShowInfoBtn(mShowInfoBtn); item->setShowProfileBtn(mShowProfileBtn); - item->setSpeakingIndicatorVisible(mShowSpeakingIndicator); + item->showSpeakingIndicator(mShowSpeakingIndicator); + + item->setDoubleClickCallback(boost::bind(&LLAvatarList::onItemDoucleClicked, this, _1, _2, _3, _4)); addItem(item, id, pos); } @@ -403,6 +407,11 @@ void LLAvatarList::updateLastInteractionTimes() } } +void LLAvatarList::onItemDoucleClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask) +{ + mItemDoubleClickSignal(ctrl, x, y, mask); +} + bool LLAvatarItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const { const LLAvatarListItem* avatar_item1 = dynamic_cast(item1); diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index 0d2ce884ae..a58a562378 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -92,6 +92,8 @@ public: boost::signals2::connection setRefreshCompleteCallback(const commit_signal_t::slot_type& cb); + boost::signals2::connection setItemDoubleClickCallback(const mouse_signal_t::slot_type& cb); + protected: void refresh(); @@ -101,6 +103,7 @@ protected: std::vector& vadded, std::vector& vremoved); void updateLastInteractionTimes(); + void onItemDoucleClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask); private: @@ -120,6 +123,7 @@ private: LLAvatarListItem::ContextMenu* mContextMenu; commit_signal_t mRefreshCompleteSignal; + mouse_signal_t mItemDoubleClickSignal; }; /** Abstract comparator for avatar items */ diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 6945ac6932..66ab32f3e8 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -44,10 +44,10 @@ #include "llbutton.h" bool LLAvatarListItem::sStaticInitialized = false; -S32 LLAvatarListItem::sIconWidth = 0; -S32 LLAvatarListItem::sInfoBtnWidth = 0; -S32 LLAvatarListItem::sProfileBtnWidth = 0; -S32 LLAvatarListItem::sSpeakingIndicatorWidth = 0; +S32 LLAvatarListItem::sLeftPadding = 0; +S32 LLAvatarListItem::sRightNamePadding = 0; +S32 LLAvatarListItem::sChildrenWidths[LLAvatarListItem::ALIC_COUNT]; + LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) : LLPanel(), @@ -91,43 +91,25 @@ BOOL LLAvatarListItem::postBuild() mProfileBtn->setVisible(false); mProfileBtn->setClickedCallback(boost::bind(&LLAvatarListItem::onProfileBtnClick, this)); - // Remember avatar icon width including its padding from the name text box, - // so that we can hide and show the icon again later. if (!sStaticInitialized) { - sIconWidth = mAvatarName->getRect().mLeft - mAvatarIcon->getRect().mLeft; - sInfoBtnWidth = mInfoBtn->getRect().mRight - mSpeakingIndicator->getRect().mRight; - sProfileBtnWidth = mProfileBtn->getRect().mRight - mInfoBtn->getRect().mRight; - sSpeakingIndicatorWidth = mSpeakingIndicator->getRect().mRight - mAvatarName->getRect().mRight; + // Remember children widths including their padding from the next sibling, + // so that we can hide and show them again later. + initChildrenWidths(this); sStaticInitialized = true; } -/* - if(!p.buttons.profile) - { - delete mProfile; - mProfile = NULL; - - LLRect rect; - - rect.setLeftTopAndSize(mName->getRect().mLeft, mName->getRect().mTop, mName->getRect().getWidth() + 30, mName->getRect().getHeight()); - mName->setRect(rect); - - if(mLocator) - { - rect.setLeftTopAndSize(mLocator->getRect().mLeft + 30, mLocator->getRect().mTop, mLocator->getRect().getWidth(), mLocator->getRect().getHeight()); - mLocator->setRect(rect); - } + return TRUE; +} - if(mInfo) - { - rect.setLeftTopAndSize(mInfo->getRect().mLeft + 30, mInfo->getRect().mTop, mInfo->getRect().getWidth(), mInfo->getRect().getHeight()); - mInfo->setRect(rect); - } +S32 LLAvatarListItem::notifyParent(const LLSD& info) +{ + if (info.has("visibility_changed")) + { + updateChildren(); } -*/ - return TRUE; + return 0; } void LLAvatarListItem::onMouseEnter(S32 x, S32 y, MASK mask) @@ -137,6 +119,8 @@ void LLAvatarListItem::onMouseEnter(S32 x, S32 y, MASK mask) mProfileBtn->setVisible(mShowProfileBtn); LLPanel::onMouseEnter(x, y, mask); + + updateChildren(); } void LLAvatarListItem::onMouseLeave(S32 x, S32 y, MASK mask) @@ -146,6 +130,8 @@ void LLAvatarListItem::onMouseLeave(S32 x, S32 y, MASK mask) mProfileBtn->setVisible(false); LLPanel::onMouseLeave(x, y, mask); + + updateChildren(); } // virtual, called by LLAvatarTracker @@ -215,12 +201,8 @@ void LLAvatarListItem::showLastInteractionTime(bool show) if (show) return; - LLRect name_rect = mAvatarName->getRect(); - LLRect time_rect = mLastInteractionTime->getRect(); - mLastInteractionTime->setVisible(false); - name_rect.mRight += (time_rect.mRight - name_rect.mRight); - mAvatarName->setRect(name_rect); + updateChildren(); } void LLAvatarListItem::setLastInteractionTime(U32 secs_since) @@ -234,12 +216,6 @@ void LLAvatarListItem::setShowInfoBtn(bool show) if(mShowInfoBtn == show) return; mShowInfoBtn = show; - S32 width_delta = show ? - sInfoBtnWidth : sInfoBtnWidth; - - //Translating speaking indicator - mSpeakingIndicator->translate(width_delta, 0); - //Reshaping avatar name - mAvatarName->reshape(mAvatarName->getRect().getWidth() + width_delta, mAvatarName->getRect().getHeight()); } void LLAvatarListItem::setShowProfileBtn(bool show) @@ -248,24 +224,17 @@ void LLAvatarListItem::setShowProfileBtn(bool show) if(mShowProfileBtn == show) return; mShowProfileBtn = show; - S32 width_delta = show ? - sProfileBtnWidth : sProfileBtnWidth; - - //Translating speaking indicator - mSpeakingIndicator->translate(width_delta, 0); - //Reshaping avatar name - mAvatarName->reshape(mAvatarName->getRect().getWidth() + width_delta, mAvatarName->getRect().getHeight()); } -void LLAvatarListItem::setSpeakingIndicatorVisible(bool visible) +void LLAvatarListItem::showSpeakingIndicator(bool visible) { // Already done? Then do nothing. if (mSpeakingIndicator->getVisible() == (BOOL)visible) return; - mSpeakingIndicator->setVisible(visible); - S32 width_delta = visible ? - sSpeakingIndicatorWidth : sSpeakingIndicatorWidth; - - //Reshaping avatar name - mAvatarName->reshape(mAvatarName->getRect().getWidth() + width_delta, mAvatarName->getRect().getHeight()); +// Disabled to not contradict with SpeakingIndicatorManager functionality. EXT-3976 +// probably this method should be totally removed. +// mSpeakingIndicator->setVisible(visible); +// updateChildren(); } void LLAvatarListItem::setAvatarIconVisible(bool visible) @@ -276,36 +245,12 @@ void LLAvatarListItem::setAvatarIconVisible(bool visible) // Show/hide avatar icon. mAvatarIcon->setVisible(visible); - - // Move the avatar name horizontally by icon size + its distance from the avatar name. - LLRect name_rect = mAvatarName->getRect(); - name_rect.mLeft += visible ? sIconWidth : -sIconWidth; - mAvatarName->setRect(name_rect); + updateChildren(); } void LLAvatarListItem::onInfoBtnClick() { LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mAvatarId)); - - /* TODO fix positioning of inspector - localPointToScreen(mXPos, mYPos, &mXPos, &mYPos); - - - LLRect rect; - - // *TODO Vadim: rewrite this. "+= -" looks weird. - S32 delta = mYPos - inspector->getRect().getHeight(); - if(delta < 0) - { - mYPos += -delta; - } - - rect.setLeftTopAndSize(mXPos, mYPos, - inspector->getRect().getWidth(), inspector->getRect().getHeight()); - inspector->setRect(rect); - inspector->setFrontmost(true); - inspector->setVisible(true); - */ } void LLAvatarListItem::onProfileBtnClick() @@ -313,6 +258,21 @@ void LLAvatarListItem::onProfileBtnClick() LLAvatarActions::showProfile(mAvatarId); } +BOOL LLAvatarListItem::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + if(mInfoBtn->getRect().pointInRect(x, y)) + { + onInfoBtnClick(); + return TRUE; + } + if(mProfileBtn->getRect().pointInRect(x, y)) + { + onProfileBtnClick(); + return TRUE; + } + return LLPanel::handleDoubleClick(x, y, mask); +} + void LLAvatarListItem::setValue( const LLSD& value ) { if (!value.isMap()) return;; @@ -344,21 +304,6 @@ void LLAvatarListItem::onNameCache(const std::string& first_name, const std::str setName(name); } -void LLAvatarListItem::reshapeAvatarName() -{ - S32 width_delta = 0; - width_delta += mShowProfileBtn ? sProfileBtnWidth : 0; - width_delta += mSpeakingIndicator->getVisible() ? sSpeakingIndicatorWidth : 0; - width_delta += mAvatarIcon->getVisible() ? sIconWidth : 0; - width_delta += mShowInfoBtn ? sInfoBtnWidth : 0; - width_delta += mLastInteractionTime->getVisible() ? mLastInteractionTime->getRect().getWidth() : 0; - - S32 height = mAvatarName->getRect().getHeight(); - S32 width = getRect().getWidth() - width_delta; - - mAvatarName->reshape(width, height); -} - // Convert given number of seconds to a string like "23 minutes", "15 hours" or "3 years", // taking i18n into account. The format string to use is taken from the panel XML. std::string LLAvatarListItem::formatSeconds(U32 secs) @@ -492,4 +437,133 @@ LLAvatarListItem::icon_color_map_t& LLAvatarListItem::getItemIconColorMap() return item_icon_color_map; } +// static +void LLAvatarListItem::initChildrenWidths(LLAvatarListItem* avatar_item) +{ + //profile btn width + padding + S32 profile_btn_width = avatar_item->getRect().getWidth() - avatar_item->mProfileBtn->getRect().mLeft; + + //info btn width + padding + S32 info_btn_width = avatar_item->mProfileBtn->getRect().mLeft - avatar_item->mInfoBtn->getRect().mLeft; + + //speaking indicator width + padding + S32 speaking_indicator_width = avatar_item->mInfoBtn->getRect().mLeft - avatar_item->mSpeakingIndicator->getRect().mLeft; + + // last interaction time textbox width + padding + S32 last_interaction_time_width = avatar_item->mSpeakingIndicator->getRect().mLeft - avatar_item->mLastInteractionTime->getRect().mLeft; + + // icon width + padding + S32 icon_width = avatar_item->mAvatarName->getRect().mLeft - avatar_item->mAvatarIcon->getRect().mLeft; + + sLeftPadding = avatar_item->mAvatarIcon->getRect().mLeft; + sRightNamePadding = avatar_item->mLastInteractionTime->getRect().mLeft - avatar_item->mAvatarName->getRect().mRight; + + S32 index = ALIC_COUNT; + sChildrenWidths[--index] = icon_width; + sChildrenWidths[--index] = 0; // for avatar name we don't need its width, it will be calculated as "left available space" + sChildrenWidths[--index] = last_interaction_time_width; + sChildrenWidths[--index] = speaking_indicator_width; + sChildrenWidths[--index] = info_btn_width; + sChildrenWidths[--index] = profile_btn_width; +} + +void LLAvatarListItem::updateChildren() +{ + LL_DEBUGS("AvatarItemReshape") << LL_ENDL; + LL_DEBUGS("AvatarItemReshape") << "Updating for: " << getAvatarName() << LL_ENDL; + + S32 name_new_width = getRect().getWidth(); + S32 ctrl_new_left = name_new_width; + S32 name_new_left = sLeftPadding; + + // iterate through all children and set them into correct position depend on each child visibility + // assume that child indexes are in back order: the first in Enum is the last (right) in the item + // iterate & set child views starting from right to left + for (S32 i = 0; i < ALIC_COUNT; ++i) + { + // skip "name" textbox, it will be processed out of loop later + if (ALIC_NAME == i) continue; + + LLView* control = getItemChildView((EAvatarListItemChildIndex)i); + + LL_DEBUGS("AvatarItemReshape") << "Processing control: " << control->getName() << LL_ENDL; + // skip invisible views + if (!control->getVisible()) continue; + + S32 ctrl_width = sChildrenWidths[i]; // including space between current & left controls + + // decrease available for + name_new_width -= ctrl_width; + LL_DEBUGS("AvatarItemReshape") << "width: " << ctrl_width << ", name_new_width: " << name_new_width << LL_ENDL; + + LLRect control_rect = control->getRect(); + LL_DEBUGS("AvatarItemReshape") << "rect before: " << control_rect << LL_ENDL; + + if (ALIC_ICON == i) + { + // assume that this is the last iteration, + // so it is not necessary to save "ctrl_new_left" value calculated on previous iterations + ctrl_new_left = sLeftPadding; + name_new_left = ctrl_new_left + ctrl_width; + } + else + { + ctrl_new_left -= ctrl_width; + } + + LL_DEBUGS("AvatarItemReshape") << "ctrl_new_left: " << ctrl_new_left << LL_ENDL; + + control_rect.setLeftTopAndSize( + ctrl_new_left, + control_rect.mTop, + control_rect.getWidth(), + control_rect.getHeight()); + + LL_DEBUGS("AvatarItemReshape") << "rect after: " << control_rect << LL_ENDL; + control->setShape(control_rect); + } + + // set size and position of the "name" child + LLView* name_view = getItemChildView(ALIC_NAME); + LLRect name_view_rect = name_view->getRect(); + LL_DEBUGS("AvatarItemReshape") << "name rect before: " << name_view_rect << LL_ENDL; + + // apply paddings + name_new_width -= sLeftPadding; + name_new_width -= sRightNamePadding; + + name_view_rect.setLeftTopAndSize( + name_new_left, + name_view_rect.mTop, + name_new_width, + name_view_rect.getHeight()); + + name_view->setShape(name_view_rect); + + LL_DEBUGS("AvatarItemReshape") << "name rect after: " << name_view_rect << LL_ENDL; +} + +LLView* LLAvatarListItem::getItemChildView(EAvatarListItemChildIndex child_view_index) +{ + LLView* child_view = mAvatarName; + if (child_view_index < 0 || ALIC_COUNT <= child_view_index) + { + LL_WARNS("AvatarItemReshape") << "Child view index is out of range: " << child_view_index << LL_ENDL; + return child_view; + } + switch (child_view_index) + { + case ALIC_ICON: child_view = mAvatarIcon; break; + case ALIC_NAME: child_view = mAvatarName; break; + case ALIC_INTERACTION_TIME: child_view = mLastInteractionTime; break; + case ALIC_SPEAKER_INDICATOR: child_view = mSpeakingIndicator; break; + case ALIC_INFO_BUTTON: child_view = mInfoBtn; break; + case ALIC_PROFILE_BUTTON: child_view = mProfileBtn; break; + default: + LL_WARNS("AvatarItemReshape") << "Unexpected child view index is passed: " << child_view_index << LL_ENDL; + } + + return child_view; +} + // EOF diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index 96097bc9b5..479a4833cb 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -74,6 +74,11 @@ public: virtual ~LLAvatarListItem(); virtual BOOL postBuild(); + + /** + * Processes notification from speaker indicator to update children when indicator's visibility is changed. + */ + virtual S32 notifyParent(const LLSD& info); virtual void onMouseLeave(S32 x, S32 y, MASK mask); virtual void onMouseEnter(S32 x, S32 y, MASK mask); virtual void setValue(const LLSD& value); @@ -88,7 +93,8 @@ public: //Show/hide profile/info btn, translating speaker indicator and avatar name coordinates accordingly void setShowProfileBtn(bool show); void setShowInfoBtn(bool show); - void setSpeakingIndicatorVisible(bool visible); + void showSpeakingIndicator(bool show); + void showLastInteractionTime(bool show); void setAvatarIconVisible(bool visible); const LLUUID& getAvatarId() const; @@ -97,16 +103,7 @@ public: void onInfoBtnClick(); void onProfileBtnClick(); - void showSpeakingIndicator(bool show) { mSpeakingIndicator->setVisible(show); } - void showInfoBtn(bool show_info_btn) {mInfoBtn->setVisible(show_info_btn); } - void showLastInteractionTime(bool show); - - /** - * This method was added to fix EXT-2364 (Items in group/ad-hoc IM participant list (avatar names) should be reshaped when adding/removing the "(Moderator)" label) - * But this is a *HACK. The real reason of it was in incorrect logic while hiding profile/info/speaker buttons - * *TODO: new reshape method should be provided in lieu of this one to be called when visibility if those buttons is changed - */ - void reshapeAvatarName(); + /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); protected: /** @@ -124,6 +121,23 @@ private: E_UNKNOWN, } EOnlineStatus; + /** + * Enumeration of item elements in order from right to left. + * + * updateChildren() assumes that indexes are in the such order to process avatar icon easier. + * + * @see updateChildren() + */ + typedef enum e_avatar_item_child { + ALIC_PROFILE_BUTTON, + ALIC_INFO_BUTTON, + ALIC_SPEAKER_INDICATOR, + ALIC_INTERACTION_TIME, + ALIC_NAME, + ALIC_ICON, + ALIC_COUNT, + } EAvatarListItemChildIndex; + void setNameInternal(const std::string& name, const std::string& highlight); void onNameCache(const std::string& first_name, const std::string& last_name); @@ -135,6 +149,26 @@ private: typedef std::map icon_color_map_t; static icon_color_map_t& getItemIconColorMap(); + /** + * Initializes widths of all children to use them while changing visibility of any of them. + * + * @see updateChildren() + */ + static void initChildrenWidths(LLAvatarListItem* self); + + /** + * Updates position and rectangle of visible children to fit all available item's width. + */ + void updateChildren(); + + /** + * Gets child view specified by index. + * + * This method implemented via switch by all EAvatarListItemChildIndex values. + * It is used to not store children in array or vector to avoid of increasing memory usage. + */ + LLView* getItemChildView(EAvatarListItemChildIndex child_index); + LLTextBox* mAvatarName; LLTextBox* mLastInteractionTime; LLStyle::Params mAvatarNameStyle; @@ -151,10 +185,17 @@ private: bool mShowProfileBtn; static bool sStaticInitialized; // this variable is introduced to improve code readability - static S32 sIconWidth; // icon width + padding - static S32 sInfoBtnWidth; //info btn width + padding - static S32 sProfileBtnWidth; //profile btn width + padding - static S32 sSpeakingIndicatorWidth; //speaking indicator width + padding + static S32 sLeftPadding; // padding to first left visible child (icon or name) + static S32 sRightNamePadding; // right padding from name to next visible child + + /** + * Contains widths of each child specified by EAvatarListItemChildIndex + * including padding to the next right one. + * + * @see initChildrenWidths() + */ + static S32 sChildrenWidths[ALIC_COUNT]; + }; #endif //LL_LLAVATARLISTITEM_H diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index 976b312509..bd68d52868 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -48,10 +48,20 @@ #include "llsyswellwindow.h" #include "llfloatercamera.h" #include "lltexteditor.h" +#include "llnotifications.h" // Build time optimization, generate extern template once in .cpp file template class LLBottomTray* LLSingleton::getInstance(); +namespace +{ + const std::string& PANEL_CHICLET_NAME = "chiclet_list_panel"; + const std::string& PANEL_CHATBAR_NAME = "chat_bar"; + const std::string& PANEL_MOVEMENT_NAME = "movement_panel"; + const std::string& PANEL_CAMERA_NAME = "cam_panel"; + const std::string& PANEL_GESTURE_NAME = "gesture_panel"; +} + LLBottomTray::LLBottomTray(const LLSD&) : mChicletPanel(NULL), mSpeakPanel(NULL), @@ -161,6 +171,9 @@ void LLBottomTray::sessionAdded(const LLUUID& session_id, const std::string& nam { chiclet->setIMSessionName(name); chiclet->setOtherParticipantId(other_participant_id); + + LLIMFloater::onIMChicletCreated(session_id); + } else { @@ -233,6 +246,61 @@ void LLBottomTray::onFocusLost() } } +void LLBottomTray::savePanelsShape() +{ + mSavedShapeList.clear(); + for (child_list_const_iter_t + child_it = mToolbarStack->beginChild(), + child_it_end = mToolbarStack->endChild(); + child_it != child_it_end; ++child_it) + { + mSavedShapeList.push_back( (*child_it)->getRect() ); + } +} + +void LLBottomTray::restorePanelsShape() +{ + if (mSavedShapeList.size() != mToolbarStack->getChildCount()) + return; + int i = 0; + for (child_list_const_iter_t + child_it = mToolbarStack->beginChild(), + child_it_end = mToolbarStack->endChild(); + child_it != child_it_end; ++child_it) + { + (*child_it)->setShape(mSavedShapeList[i++]); + } +} + +void LLBottomTray::onMouselookModeOut() +{ + // Apply the saved settings when we are not in mouselook mode, see EXT-3988. + { + setTrayButtonVisibleIfPossible (RS_BUTTON_GESTURES, gSavedSettings.getBOOL("ShowGestureButton"), false); + setTrayButtonVisibleIfPossible (RS_BUTTON_MOVEMENT, gSavedSettings.getBOOL("ShowMoveButton"), false); + setTrayButtonVisibleIfPossible (RS_BUTTON_CAMERA, gSavedSettings.getBOOL("ShowCameraButton"), false); + setTrayButtonVisibleIfPossible (RS_BUTTON_SNAPSHOT, gSavedSettings.getBOOL("ShowSnapshotButton"),false); + } + // HACK: To avoid usage the LLLayoutStack logic of resizing, we force the updateLayout + // and then restore children saved shapes. See EXT-4309. + BOOL saved_anim = mToolbarStack->getAnimate(); + mToolbarStack->updatePanelAutoResize(PANEL_CHATBAR_NAME, FALSE); + // Disable animation to prevent layout updating in several frames. + mToolbarStack->setAnimate(FALSE); + // Force the updating of layout to reset panels collapse factor. + mToolbarStack->updateLayout(); + // Restore animate state. + mToolbarStack->setAnimate(saved_anim); + // Restore saved shapes. + restorePanelsShape(); +} + +void LLBottomTray::onMouselookModeIn() +{ + savePanelsShape(); + mToolbarStack->updatePanelAutoResize(PANEL_CHATBAR_NAME, TRUE); +} + //virtual // setVisible used instead of onVisibilityChange, since LLAgent calls it on entering/leaving mouselook mode. // If bottom tray is already visible in mouselook mode, then onVisibilityChange will not be called from setVisible(true), @@ -251,8 +319,10 @@ void LLBottomTray::setVisible(BOOL visible) { LLView* viewp = *child_it; std::string name = viewp->getName(); - - if ("chat_bar" == name || "movement_panel" == name || "cam_panel" == name || "snapshot_panel" == name || "gesture_panel" == name) + + // Chat bar and gesture button are shown even in mouselook mode. + // But the move, camera and snapshot buttons shouldn't be displayed. See EXT-3988. + if ("chat_bar" == name || "gesture_panel" == name || (visibility && ("movement_panel" == name || "cam_panel" == name || "snapshot_panel" == name))) continue; else { @@ -260,6 +330,11 @@ void LLBottomTray::setVisible(BOOL visible) } } } + + if(visible) + gFloaterView->setSnapOffsetBottom(getRect().getHeight()); + else + gFloaterView->setSnapOffsetBottom(0); } void LLBottomTray::showBottomTrayContextMenu(S32 x, S32 y, MASK mask) @@ -324,15 +399,6 @@ void LLBottomTray::showSnapshotButton(BOOL visible) setTrayButtonVisibleIfPossible(RS_BUTTON_SNAPSHOT, visible); } -namespace -{ - const std::string& PANEL_CHICLET_NAME = "chiclet_list_panel"; - const std::string& PANEL_CHATBAR_NAME = "chat_bar"; - const std::string& PANEL_MOVEMENT_NAME = "movement_panel"; - const std::string& PANEL_CAMERA_NAME = "cam_panel"; - const std::string& PANEL_GESTURE_NAME = "gesture_panel"; -} - BOOL LLBottomTray::postBuild() { @@ -1005,7 +1071,7 @@ void LLBottomTray::setTrayButtonVisible(EResizeState shown_object_type, bool vis panel->setVisible(visible); } -void LLBottomTray::setTrayButtonVisibleIfPossible(EResizeState shown_object_type, bool visible) +void LLBottomTray::setTrayButtonVisibleIfPossible(EResizeState shown_object_type, bool visible, bool raise_notification) { bool can_be_set = true; @@ -1045,7 +1111,11 @@ void LLBottomTray::setTrayButtonVisibleIfPossible(EResizeState shown_object_type { // mark this button to show it while future bottom tray extending mResizeState |= shown_object_type; - LLNotificationsUtil::add("BottomTrayButtonCanNotBeShown"); + if ( raise_notification ) + LLNotificationsUtil::add("BottomTrayButtonCanNotBeShown", + LLSD(), + LLSD(), + LLNotificationFunctorRegistry::instance().DONOTHING); } } diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index 9be0e5810f..562ee56912 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -92,7 +92,10 @@ public: void showMoveButton(BOOL visible); void showCameraButton(BOOL visible); void showSnapshotButton(BOOL visible); - + + void onMouselookModeIn(); + void onMouselookModeOut(); + /** * Creates IM Chiclet based on session type (IM chat or Group chat) */ @@ -167,7 +170,14 @@ private: * - if hidden via context menu button should be shown but there is no enough room for now * it will be shown while extending. */ - void setTrayButtonVisibleIfPossible(EResizeState shown_object_type, bool visible); + void setTrayButtonVisibleIfPossible(EResizeState shown_object_type, bool visible, bool raise_notification = true); + + /** + * Save and restore children shapes. + * Used to avoid the LLLayoutStack resizing logic between mouse look mode switching. + */ + void savePanelsShape(); + void restorePanelsShape(); MASK mResizeState; @@ -177,6 +187,9 @@ private: typedef std::map state_object_width_map_t; state_object_width_map_t mObjectDefaultWidthMap; + typedef std::vector shape_list_t; + shape_list_t mSavedShapeList; + protected: LLBottomTray(const LLSD& key = LLSD()); diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index a402f59fa1..f346a4b8c2 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -47,11 +47,13 @@ #include "llfloaterreg.h" #include "llparticipantlist.h" #include "llspeakers.h" +#include "lltextutil.h" #include "lltransientfloatermgr.h" #include "llviewerwindow.h" #include "llvoicechannel.h" static void get_voice_participants_uuids(std::vector& speakers_uuids); +void reshape_floater(LLCallFloater* floater, S32 delta_height); class LLNonAvatarCaller : public LLAvatarListItem { @@ -76,6 +78,12 @@ public: return rv; } + void setName(const std::string& name) + { + const std::string& formatted_phone = LLTextUtil::formatPhoneNumber(name); + LLAvatarListItem::setName(formatted_phone); + } + void setSpeakerId(const LLUUID& id) { mSpeakingIndicator->setSpeakerId(id); } }; @@ -217,16 +225,6 @@ void LLCallFloater::onChange() } } -S32 LLCallFloater::notifyParent(const LLSD& info) -{ - if("size_changes" == info["action"]) - { - reshapeToFitContent(); - return 1; - } - return LLDockableFloater::notifyParent(info); -} - ////////////////////////////////////////////////////////////////////////// /// PRIVATE SECTION ////////////////////////////////////////////////////////////////////////// @@ -270,6 +268,11 @@ void LLCallFloater::updateSession() case IM_NOTHING_SPECIAL: case IM_SESSION_P2P_INVITE: mVoiceType = VC_PEER_TO_PEER; + + if (!im_session->mOtherParticipantIsAvatar) + { + mVoiceType = VC_PEER_TO_PEER_AVALINE; + } break; case IM_SESSION_CONFERENCE_START: case IM_SESSION_GROUP_START: @@ -302,8 +305,8 @@ void LLCallFloater::updateSession() //hide "Leave Call" button for nearby chat bool is_local_chat = mVoiceType == VC_LOCAL_CHAT; - childSetVisible("leave_call_btn", !is_local_chat); - + childSetVisible("leave_call_btn_panel", !is_local_chat); + refreshParticipantList(); updateAgentModeratorState(); @@ -321,16 +324,13 @@ void LLCallFloater::updateSession() void LLCallFloater::refreshParticipantList() { - bool non_avatar_caller = false; - if (VC_PEER_TO_PEER == mVoiceType) + bool non_avatar_caller = VC_PEER_TO_PEER_AVALINE == mVoiceType; + + if (non_avatar_caller) { LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSpeakerManager->getSessionID()); - non_avatar_caller = !session->mOtherParticipantIsAvatar; - if (non_avatar_caller) - { - mNonAvatarCaller->setSpeakerId(session->mOtherParticipantID); - mNonAvatarCaller->setName(session->mName); - } + mNonAvatarCaller->setSpeakerId(session->mOtherParticipantID); + mNonAvatarCaller->setName(session->mName); } mNonAvatarCaller->setVisible(non_avatar_caller); @@ -390,9 +390,17 @@ void LLCallFloater::updateTitle() title = getString("title_nearby"); break; case VC_PEER_TO_PEER: + case VC_PEER_TO_PEER_AVALINE: { + title = voice_channel->getSessionName(); + + if (VC_PEER_TO_PEER_AVALINE == mVoiceType) + { + title = LLTextUtil::formatPhoneNumber(title); + } + LLStringUtil::format_map_t args; - args["[NAME]"] = voice_channel->getSessionName(); + args["[NAME]"] = title; title = getString("title_peer_2_peer", args); } break; @@ -706,13 +714,28 @@ void LLCallFloater::removeVoiceRemoveTimer(const LLUUID& voice_speaker_id) bool LLCallFloater::validateSpeaker(const LLUUID& speaker_id) { - if (mVoiceType != VC_LOCAL_CHAT) - return true; + bool is_valid = true; + switch (mVoiceType) + { + case VC_LOCAL_CHAT: + { + // A nearby chat speaker is considered valid it it's known to LLVoiceClient (i.e. has enabled voice). + std::vector speakers; + get_voice_participants_uuids(speakers); + is_valid = std::find(speakers.begin(), speakers.end(), speaker_id) != speakers.end(); + } + break; + case VC_GROUP_CHAT: + // if participant had left this call before do not allow add her again. See EXT-4216. + // but if she Join she will be added into the list from the LLCallFloater::onChange() + is_valid = STATE_LEFT != getState(speaker_id); + break; + default: + // do nothing. required for Linux build + break; + } - // A nearby chat speaker is considered valid it it's known to LLVoiceClient (i.e. has enabled voice). - std::vector speakers; - get_voice_participants_uuids(speakers); - return std::find(speakers.begin(), speakers.end(), speaker_id) != speakers.end(); + return is_valid; } void LLCallFloater::connectToChannel(LLVoiceChannel* channel) @@ -765,91 +788,4 @@ void LLCallFloater::reset() mSpeakerManager = NULL; } -void reshape_floater(LLCallFloater* floater, S32 delta_height) -{ - // Try to update floater top side if it is docked(to bottom bar). - // Try to update floater bottom side or top side if it is un-docked. - // If world rect is too small, floater will not be reshaped at all. - - LLRect floater_rect = floater->getRect(); - LLRect world_rect = gViewerWindow->getWorldViewRectScaled(); - - // floater is docked to bottom bar - if(floater->isDocked()) - { - // can update floater top side - if(floater_rect.mTop + delta_height < world_rect.mTop) - { - floater_rect.set(floater_rect.mLeft, floater_rect.mTop + delta_height, - floater_rect.mRight, floater_rect.mBottom); - } - } - // floater is un-docked - else - { - // can update floater bottom side - if( floater_rect.mBottom - delta_height >= world_rect.mBottom ) - { - floater_rect.set(floater_rect.mLeft, floater_rect.mTop, - floater_rect.mRight, floater_rect.mBottom - delta_height); - } - // could not update floater bottom side, check if we can update floater top side - else if( floater_rect.mTop + delta_height < world_rect.mTop ) - { - floater_rect.set(floater_rect.mLeft, floater_rect.mTop + delta_height, - floater_rect.mRight, floater_rect.mBottom); - } - } - - floater->reshape(floater_rect.getWidth(), floater_rect.getHeight()); - floater->setRect(floater_rect); -} - -void LLCallFloater::reshapeToFitContent() -{ - const S32 ITEM_HEIGHT = getParticipantItemHeight(); - static const S32 MAX_VISIBLE_ITEMS = getMaxVisibleItems(); - - static S32 items_pad = mAvatarList->getItemsPad(); - S32 list_height = mAvatarList->getRect().getHeight(); - S32 items_height = mAvatarList->getItemsRect().getHeight(); - if(items_height <= 0) - { - // make "no one near" text visible - items_height = ITEM_HEIGHT + items_pad; - } - S32 max_list_height = MAX_VISIBLE_ITEMS * ITEM_HEIGHT + items_pad * (MAX_VISIBLE_ITEMS - 1); - max_list_height += 2* mAvatarList->getBorderWidth(); - - S32 delta = items_height - list_height; - // too many items, don't reshape floater anymore, let scroll bar appear. - if(items_height > max_list_height) - { - delta = max_list_height - list_height; - } - - reshape_floater(this, delta); -} - -S32 LLCallFloater::getParticipantItemHeight() -{ - std::vector items; - mAvatarList->getItems(items); - if(items.size() > 0) - { - return items[0]->getRect().getHeight(); - } - else - { - return getChild("non_avatar_caller")->getRect().getHeight(); - } -} - -S32 LLCallFloater::getMaxVisibleItems() -{ - S32 value = 5; // default value, in case convertToS32() fails. - LLStringUtil::convertToS32(getString("max_visible_items"), value); - return value; -} - //EOF diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index 8aba93fc43..096594aaa2 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -75,11 +75,6 @@ public: */ /*virtual*/ void onChange(); - /** - * Will reshape floater when participant list size changes - */ - /*virtual*/ S32 notifyParent(const LLSD& info); - static void sOnCurrentChannelChanged(const LLUUID& session_id); private: @@ -88,7 +83,8 @@ private: VC_LOCAL_CHAT, VC_GROUP_CHAT, VC_AD_HOC_CHAT, - VC_PEER_TO_PEER + VC_PEER_TO_PEER, + VC_PEER_TO_PEER_AVALINE }EVoiceControls; typedef enum e_speaker_state @@ -220,21 +216,6 @@ private: */ void reset(); - /** - * Reshapes floater to fit participant list height - */ - void reshapeToFitContent(); - - /** - * Returns height of participant list item - */ - S32 getParticipantItemHeight(); - - /** - * Returns predefined max visible participants. - */ - S32 getMaxVisibleItems(); - private: speaker_state_map_t mSpeakerStateMap; LLSpeakerMgr* mSpeakerManager; diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index 442dc660cd..b32a955038 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -48,7 +48,6 @@ #include "llcombobox.h" #include "llcommandhandler.h" // secondlife:///app/chat/ support #include "llviewercontrol.h" -#include "llfloaterchat.h" #include "llgesturemgr.h" #include "llkeyboard.h" #include "lllineeditor.h" @@ -548,7 +547,7 @@ void LLChatBar::onInputEditorFocusLost() // static void LLChatBar::onInputEditorGainFocus() { - LLFloaterChat::setHistoryCursorAndScrollToEnd(); + //LLFloaterChat::setHistoryCursorAndScrollToEnd(); } void LLChatBar::onClickSay( LLUICtrl* ctrl ) diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 21cadda6e3..a46cd84b60 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -499,7 +499,24 @@ void LLChatHistory::appendMessage(const LLChat& chat, const bool use_plain_text_ style_params.font.name(font_name); style_params.font.size(font_size); style_params.font.style(input_append_params.font.style); - + + std::string prefix = chat.mText.substr(0, 4); + + //IRC styled /me messages. + bool irc_me = prefix == "/me " || prefix == "/me'"; + + // Delimiter after a name in header copy/past and in plain text mode + std::string delimiter = (chat.mChatType != CHAT_TYPE_SHOUT && chat.mChatType != CHAT_TYPE_WHISPER) + ? ": " + : " "; + + // Don't add any delimiter after name in irc styled messages + if (irc_me || chat.mChatStyle == CHAT_STYLE_IRC) + { + delimiter = LLStringUtil::null; + style_params.font.style = "ITALIC"; + } + if (use_plain_text_chat_history) { mEditor->appendText("[" + chat.mTimeStr + "] ", mEditor->getText().size() != 0, style_params); @@ -512,11 +529,11 @@ void LLChatHistory::appendMessage(const LLChat& chat, const bool use_plain_text_ LLStyle::Params link_params(style_params); link_params.fillFrom(LLStyleMap::instance().lookupAgent(chat.mFromID)); // Convert the name to a hotlink and add to message. - mEditor->appendText(chat.mFromName + ": ", false, link_params); + mEditor->appendText(chat.mFromName + delimiter, false, link_params); } else { - mEditor->appendText(chat.mFromName + ": ", false, style_params); + mEditor->appendText(chat.mFromName + delimiter, false, style_params); } } } @@ -560,39 +577,25 @@ void LLChatHistory::appendMessage(const LLChat& chat, const bool use_plain_text_ view->reshape(target_rect.getWidth(), view->getRect().getHeight()); view->setOrigin(target_rect.mLeft, view->getRect().mBottom); - std::string header_text = "[" + chat.mTimeStr + "] "; + std::string widget_associated_text = "\n[" + chat.mTimeStr + "] "; if (utf8str_trim(chat.mFromName).size() != 0 && chat.mFromName != SYSTEM_FROM) - header_text += chat.mFromName + ": "; + widget_associated_text += chat.mFromName + delimiter; - mEditor->appendWidget(p, header_text, false); + mEditor->appendWidget(p, widget_associated_text, false); mLastFromName = chat.mFromName; mLastFromID = chat.mFromID; mLastMessageTime = new_message_time; } - //Handle IRC styled /me messages. - std::string prefix = chat.mText.substr(0, 4); - if (prefix == "/me " || prefix == "/me'") - { - style_params.font.style = "ITALIC"; - if (chat.mFromName.size() > 0) - mEditor->appendText(chat.mFromName + " ", TRUE, style_params); - // Ensure that message ends with NewLine, to avoid losing of new lines - // while copy/paste from text chat. See EXT-3263. - mEditor->appendText(chat.mText.substr(4) + NEW_LINE, FALSE, style_params); - } - else + std::string message = irc_me ? chat.mText.substr(3) : chat.mText; + mEditor->appendText(message, FALSE, style_params); + mEditor->blockUndo(); + + // automatically scroll to end when receiving chat from myself + if (chat.mFromID == gAgentID) { - std::string message(chat.mText); - if ( message.size() > 0 && !LLStringOps::isSpace(message[message.size() - 1]) ) - { - // Ensure that message ends with NewLine, to avoid losing of new lines - // while copy/paste from text chat. See EXT-3263. - message += NEW_LINE; - } - mEditor->appendText(message, FALSE, style_params); + mEditor->setCursorAndScrollToEnd(); } - mEditor->blockUndo(); } void LLChatHistory::draw() @@ -606,3 +609,11 @@ void LLChatHistory::draw() LLUICtrl::draw(); } +void LLChatHistory::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + bool is_scrolled_to_end = mEditor->scrolledToEnd(); + LLUICtrl::reshape( width, height, called_from_parent ); + // update scroll + if (is_scrolled_to_end) + mEditor->setCursorAndScrollToEnd(); +} diff --git a/indra/newview/llchathistory.h b/indra/newview/llchathistory.h index 260015e2dc..f2d403f639 100644 --- a/indra/newview/llchathistory.h +++ b/indra/newview/llchathistory.h @@ -119,6 +119,7 @@ class LLChatHistory : public LLUICtrl */ void appendMessage(const LLChat& chat, const bool use_plain_text_chat_history = false, const LLStyle::Params& input_append_params = LLStyle::Params()); /*virtual*/ void clear(); + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); private: std::string mLastFromName; diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index 92df281307..f7f7ee83af 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -168,30 +168,30 @@ void LLNearbyChatToastPanel::init(LLSD& notification) msg_text->setText(std::string("")); - std::string str_sender; - - if(gAgentID != mFromID) + if ( notification["chat_style"].asInteger() != CHAT_STYLE_IRC ) + { + std::string str_sender; + str_sender = fromName; - else - str_sender = LLTrans::getString("You"); - str_sender+=" "; + str_sender+=" "; - //append user name - { - LLStyle::Params style_params_name; + //append user name + { + LLStyle::Params style_params_name; - LLColor4 userNameColor = LLUIColorTable::instance().getColor("ChatToastAgentNameColor"); + LLColor4 userNameColor = LLUIColorTable::instance().getColor("ChatToastAgentNameColor"); - style_params_name.color(userNameColor); - - std::string font_name = LLFontGL::nameFromFont(messageFont); - std::string font_style_size = LLFontGL::sizeFromFont(messageFont); - style_params_name.font.name(font_name); - style_params_name.font.size(font_style_size); - - msg_text->appendText(str_sender, FALSE, style_params_name); - + style_params_name.color(userNameColor); + + std::string font_name = LLFontGL::nameFromFont(messageFont); + std::string font_style_size = LLFontGL::sizeFromFont(messageFont); + style_params_name.font.name(font_name); + style_params_name.font.size(font_style_size); + + msg_text->appendText(str_sender, FALSE, style_params_name); + + } } //append text @@ -252,11 +252,33 @@ void LLNearbyChatToastPanel::onMouseEnter (S32 x, S32 y, MASK mask) } BOOL LLNearbyChatToastPanel::handleMouseDown (S32 x, S32 y, MASK mask) +{ + return LLPanel::handleMouseDown(x,y,mask); +} + +BOOL LLNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask) { if(mSourceType != CHAT_SOURCE_AGENT) - return LLPanel::handleMouseDown(x,y,mask); + return LLPanel::handleMouseUp(x,y,mask); + + LLChatMsgBox* text_box = getChild("msg_text", false); + S32 local_x = x - text_box->getRect().mLeft; + S32 local_y = y - text_box->getRect().mBottom; + + //if text_box process mouse up (ussually this is click on url) - we didn't show nearby_chat. + if (text_box->pointInView(local_x, local_y) ) + { + if (text_box->handleMouseUp(local_x,local_y,mask) == TRUE) + return TRUE; + else + { + LLFloaterReg::showInstance("nearby_chat",LLSD()); + return FALSE; + } + } + LLFloaterReg::showInstance("nearby_chat",LLSD()); - return LLPanel::handleMouseDown(x,y,mask); + return LLPanel::handleMouseUp(x,y,mask); } void LLNearbyChatToastPanel::setHeaderVisibility(EShowItemHeader e) diff --git a/indra/newview/llchatitemscontainerctrl.h b/indra/newview/llchatitemscontainerctrl.h index 0a85c52401..f4b8655054 100644 --- a/indra/newview/llchatitemscontainerctrl.h +++ b/indra/newview/llchatitemscontainerctrl.h @@ -68,6 +68,7 @@ public: void onMouseLeave (S32 x, S32 y, MASK mask); void onMouseEnter (S32 x, S32 y, MASK mask); BOOL handleMouseDown (S32 x, S32 y, MASK mask); + BOOL handleMouseUp (S32 x, S32 y, MASK mask); virtual BOOL postBuild(); diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index dc2e22f899..8da207f887 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -47,6 +47,7 @@ #include "llnotificationsutil.h" #include "lloutputmonitorctrl.h" #include "llscriptfloater.h" +#include "llspeakers.h" #include "lltextbox.h" #include "llvoiceclient.h" #include "llgroupmgr.h" @@ -453,6 +454,7 @@ LLIMChiclet::LLIMChiclet(const LLIMChiclet::Params& p) , mNewMessagesIcon(NULL) , mSpeakerCtrl(NULL) , mCounterCtrl(NULL) +, mChicletButton(NULL) { enableCounterControl(p.enable_counter); } @@ -540,6 +542,11 @@ void LLIMChiclet::toggleSpeakerControl() void LLIMChiclet::setCounter(S32 counter) { + if (mCounterCtrl->getCounter() == counter) + { + return; + } + mCounterCtrl->setCounter(counter); setShowCounter(counter); setShowNewMessagesIcon(counter); @@ -571,6 +578,11 @@ void LLIMChiclet::onMouseDown() setCounter(0); } +void LLIMChiclet::setToggleState(bool toggle) +{ + mChicletButton->setToggleState(toggle); +} + BOOL LLIMChiclet::handleMouseDown(S32 x, S32 y, MASK mask) { onMouseDown(); @@ -629,6 +641,7 @@ LLIMChiclet::EType LLIMChiclet::getIMSessionType(const LLUUID& session_id) LLIMP2PChiclet::Params::Params() : avatar_icon("avatar_icon") +, chiclet_button("chiclet_button") , unread_notifications("unread_notifications") , speaker("speaker") , new_message_icon("new_message_icon") @@ -641,6 +654,10 @@ LLIMP2PChiclet::LLIMP2PChiclet(const Params& p) , mChicletIconCtrl(NULL) , mPopupMenu(NULL) { + LLButton::Params button_params = p.chiclet_button; + mChicletButton = LLUICtrlFactory::create(button_params); + addChild(mChicletButton); + LLIconCtrl::Params new_msg_params = p.new_message_icon; mNewMessagesIcon = LLUICtrlFactory::create(new_msg_params); addChild(mNewMessagesIcon); @@ -755,6 +772,7 @@ void LLIMP2PChiclet::onMenuItemClicked(const LLSD& user_data) LLAdHocChiclet::Params::Params() : avatar_icon("avatar_icon") +, chiclet_button("chiclet_button") , unread_notifications("unread_notifications") , speaker("speaker") , new_message_icon("new_message_icon") @@ -768,6 +786,10 @@ LLAdHocChiclet::LLAdHocChiclet(const Params& p) , mChicletIconCtrl(NULL) , mPopupMenu(NULL) { + LLButton::Params button_params = p.chiclet_button; + mChicletButton = LLUICtrlFactory::create(button_params); + addChild(mChicletButton); + LLIconCtrl::Params new_msg_params = p.new_message_icon; mNewMessagesIcon = LLUICtrlFactory::create(new_msg_params); addChild(mNewMessagesIcon); @@ -882,6 +904,7 @@ BOOL LLAdHocChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask) LLIMGroupChiclet::Params::Params() : group_icon("group_icon") +, chiclet_button("chiclet_button") , unread_notifications("unread_notifications") , speaker("speaker") , new_message_icon("new_message_icon") @@ -895,6 +918,10 @@ LLIMGroupChiclet::LLIMGroupChiclet(const Params& p) , mChicletIconCtrl(NULL) , mPopupMenu(NULL) { + LLButton::Params button_params = p.chiclet_button; + mChicletButton = LLUICtrlFactory::create(button_params); + addChild(mChicletButton); + LLIconCtrl::Params new_msg_params = p.new_message_icon; mNewMessagesIcon = LLUICtrlFactory::create(new_msg_params); addChild(mNewMessagesIcon); @@ -1267,10 +1294,12 @@ void LLChicletPanel::onChicletClick(LLUICtrl*ctrl,const LLSD¶m) void LLChicletPanel::removeChiclet(chiclet_list_t::iterator it) { - mScrollArea->removeChild(*it); + LLChiclet* chiclet = *it; + mScrollArea->removeChild(chiclet); mChicletList.erase(it); arrange(); + chiclet->die(); } void LLChicletPanel::removeChiclet(S32 index) @@ -1409,6 +1438,32 @@ S32 LLChicletPanel::notifyParent(const LLSD& info) return LLPanel::notifyParent(info); } +void LLChicletPanel::setChicletToggleState(const LLUUID& session_id, bool toggle) +{ + if(session_id.isNull()) + { + llwarns << "Null Session ID" << llendl; + } + + // toggle off all chiclets, except specified + S32 size = getChicletCount(); + for(int n = 0; n < size; ++n) + { + LLIMChiclet* chiclet = getChiclet(n); + if(chiclet && chiclet->getSessionId() != session_id) + { + chiclet->setToggleState(false); + } + } + + // toggle specified chiclet + LLIMChiclet* chiclet = findChiclet(session_id); + if(chiclet) + { + chiclet->setToggleState(toggle); + } +} + void LLChicletPanel::arrange() { if(mChicletList.empty()) @@ -1801,6 +1856,7 @@ LLChicletSpeakerCtrl::LLChicletSpeakerCtrl(const Params&p) LLScriptChiclet::Params::Params() : icon("icon") + , chiclet_button("chiclet_button") , new_message_icon("new_message_icon") { } @@ -1809,6 +1865,10 @@ LLScriptChiclet::LLScriptChiclet(const Params&p) : LLIMChiclet(p) , mChicletIconCtrl(NULL) { + LLButton::Params button_params = p.chiclet_button; + mChicletButton = LLUICtrlFactory::create(button_params); + addChild(mChicletButton); + LLIconCtrl::Params new_msg_params = p.new_message_icon; mNewMessagesIcon = LLUICtrlFactory::create(new_msg_params); addChild(mNewMessagesIcon); @@ -1857,6 +1917,7 @@ static const std::string INVENTORY_USER_OFFER ("UserGiveItem"); LLInvOfferChiclet::Params::Params() : icon("icon") + , chiclet_button("chiclet_button") , new_message_icon("new_message_icon") { } @@ -1865,6 +1926,10 @@ LLInvOfferChiclet::LLInvOfferChiclet(const Params&p) : LLIMChiclet(p) , mChicletIconCtrl(NULL) { + LLButton::Params button_params = p.chiclet_button; + mChicletButton = LLUICtrlFactory::create(button_params); + addChild(mChicletButton); + LLIconCtrl::Params new_msg_params = p.new_message_icon; mNewMessagesIcon = LLUICtrlFactory::create(new_msg_params); addChild(mNewMessagesIcon); diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 3665e4d093..bb4846aa57 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -325,7 +325,7 @@ public: }; - /*virtual*/ ~LLIMChiclet() {}; + virtual ~LLIMChiclet() {}; /** * Sets IM session name. This name will be displayed in chiclet tooltip. @@ -422,6 +422,8 @@ public: */ virtual void onMouseDown(); + virtual void setToggleState(bool toggle); + protected: LLIMChiclet(const LLIMChiclet::Params& p); @@ -438,7 +440,7 @@ protected: LLIconCtrl* mNewMessagesIcon; LLChicletNotificationCounterCtrl* mCounterCtrl; LLChicletSpeakerCtrl* mSpeakerCtrl; - + LLButton* mChicletButton; /** the id of another participant, either an avatar id or a group id*/ LLUUID mOtherParticipantId; @@ -473,6 +475,8 @@ class LLIMP2PChiclet : public LLIMChiclet public: struct Params : public LLInitParam::Block { + Optional chiclet_button; + Optional avatar_icon; Optional unread_notifications; @@ -538,6 +542,8 @@ class LLAdHocChiclet : public LLIMChiclet public: struct Params : public LLInitParam::Block { + Optional chiclet_button; + Optional avatar_icon; Optional unread_notifications; @@ -614,6 +620,8 @@ public: struct Params : public LLInitParam::Block { + Optional chiclet_button; + Optional icon; Optional new_message_icon; @@ -656,6 +664,8 @@ public: struct Params : public LLInitParam::Block { + Optional chiclet_button; + Optional icon; Optional new_message_icon; @@ -697,6 +707,8 @@ public: struct Params : public LLInitParam::Block { + Optional chiclet_button; + Optional group_icon; Optional unread_notifications; @@ -1040,6 +1052,11 @@ public: S32 notifyParent(const LLSD& info); + /** + * Toggle chiclet by session id ON and toggle OFF all other chiclets. + */ + void setChicletToggleState(const LLUUID& session_id, bool toggle); + protected: LLChicletPanel(const Params&p); friend class LLUICtrlFactory; diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index eb9a2fec2f..47f1b7c9f5 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -59,7 +59,6 @@ #include "llbutton.h" #include "lldir.h" -#include "llfloaterchat.h" #include "llnotificationsutil.h" #include "llviewerstats.h" #include "llvfile.h" @@ -447,14 +446,20 @@ void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id, if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ) { - LLChat chat(LLTrans::getString("CompileQueueScriptNotFound")); - LLFloaterChat::addChat(chat); + //TODO* CHAT: how to show this? + //LLSD args; + //args["MESSAGE"] = LLTrans::getString("CompileQueueScriptNotFound); + //LLNotificationsUtil::add("SystemMessage", args); + buffer = LLTrans::getString("CompileQueueProblemDownloading") + (": ") + data->mScriptName; } else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status) { - LLChat chat(LLTrans::getString("CompileQueueInsufficientPermDownload")); - LLFloaterChat::addChat(chat); + //TODO* CHAT: how to show this? + //LLSD args; + //args["MESSAGE"] = LLTrans::getString("CompileQueueScriptNotFound); + //LLNotificationsUtil::add("SystemMessage", args); + buffer = LLTrans::getString("CompileQueueInsufficientPermFor") + (": ") + data->mScriptName; } else diff --git a/indra/newview/lldateutil.cpp b/indra/newview/lldateutil.cpp index 10b7935caf..abb2fdeb9a 100644 --- a/indra/newview/lldateutil.cpp +++ b/indra/newview/lldateutil.cpp @@ -44,15 +44,18 @@ static S32 DAYS_PER_MONTH_LEAP[] = static S32 days_from_month(S32 year, S32 month) { + llassert_always(1 <= month); + llassert_always(month <= 12); + if (year % 4 == 0 && year % 100 != 0) { // leap year - return DAYS_PER_MONTH_LEAP[month]; + return DAYS_PER_MONTH_LEAP[month - 1]; } else { - return DAYS_PER_MONTH_NOLEAP[month]; + return DAYS_PER_MONTH_NOLEAP[month - 1]; } } diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 5f845c3721..03a8b108e2 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -1149,14 +1149,14 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLI if (!LLPipeline::sRenderDeferred) { bump->setExplicitFormat(GL_ALPHA8, GL_ALPHA); - bump->createGLTexture(bump->getDiscardLevel(), dst_image); + bump->createGLTexture(0, dst_image); } else { LLPointer nrm_image = new LLImageRaw(src->getWidth(), src->getHeight(), 4); generateNormalMapFromAlpha(src, nrm_image); bump->setExplicitFormat(GL_RGBA, GL_RGBA); - bump->createGLTexture(bump->getDiscardLevel(), nrm_image); + bump->createGLTexture(0, nrm_image); } diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp index c14ca2473b..c7cd77cb65 100644 --- a/indra/newview/lldrawpoolwlsky.cpp +++ b/indra/newview/lldrawpoolwlsky.cpp @@ -159,15 +159,10 @@ void LLDrawPoolWLSky::renderStars(void) const // *NOTE: have to have bound the cloud noise texture already since register // combiners blending below requires something to be bound // and we might as well only bind once. - //gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); gPipeline.disableLights(); - - if (!LLPipeline::sReflectionRender) - { - glPointSize(2.f); - } - + // *NOTE: we divide by two here and GL_ALPHA_SCALE by two below to avoid // clamping and allow the star_alpha param to brighten the stars. bool error; @@ -175,16 +170,20 @@ void LLDrawPoolWLSky::renderStars(void) const star_alpha.mV[3] = LLWLParamManager::instance()->mCurParams.getFloat("star_brightness", error) / 2.f; llassert_always(!error); + gGL.getTexUnit(0)->bind(gSky.mVOSkyp->getBloomTex()); + + gGL.pushMatrix(); + glRotatef(gFrameTimeSeconds*0.01f, 0.f, 0.f, 1.f); // gl_FragColor.rgb = gl_Color.rgb; // gl_FragColor.a = gl_Color.a * star_alpha.a; - gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_PREV_COLOR); - gGL.getTexUnit(0)->setTextureAlphaBlend(LLTexUnit::TBO_MULT_X2, LLTexUnit::TBS_PREV_ALPHA, LLTexUnit::TBS_CONST_ALPHA); + gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_MULT, LLTexUnit::TBS_TEX_COLOR, LLTexUnit::TBS_VERT_COLOR); + gGL.getTexUnit(0)->setTextureAlphaBlend(LLTexUnit::TBO_MULT_X2, LLTexUnit::TBS_CONST_ALPHA, LLTexUnit::TBS_TEX_ALPHA); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, star_alpha.mV); gSky.mVOWLSkyp->drawStars(); - glPointSize(1.f); - + gGL.popMatrix(); + // and disable the combiner states gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); } diff --git a/indra/newview/lldrawpoolwlsky.h b/indra/newview/lldrawpoolwlsky.h index 7ff760ac39..9059f6382f 100644 --- a/indra/newview/lldrawpoolwlsky.h +++ b/indra/newview/lldrawpoolwlsky.h @@ -43,7 +43,7 @@ public: static const U32 SKY_VERTEX_DATA_MASK = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0; static const U32 STAR_VERTEX_DATA_MASK = LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_COLOR; + LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_TEXCOORD0; LLDrawPoolWLSky(void); /*virtual*/ ~LLDrawPoolWLSky(); diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp index effa57b1ef..4fa97e789b 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -314,7 +314,7 @@ void LLFastTimerView::draw() S32 left, top, right, bottom; S32 x, y, barw, barh, dx, dy; S32 texth, textw; - LLPointer box_imagep = LLUI::getUIImage("rounded_square.tga"); + LLPointer box_imagep = LLUI::getUIImage("Rounded_Square"); // Draw the window background gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); @@ -323,7 +323,9 @@ void LLFastTimerView::draw() S32 xleft = margin; S32 ytop = margin; - mAverageCyclesPerTimer = llround(lerp((F32)mAverageCyclesPerTimer, (F32)(LLFastTimer::sTimerCycles / (U64)LLFastTimer::sTimerCalls), 0.1f)); + mAverageCyclesPerTimer = LLFastTimer::sTimerCalls == 0 + ? 0 + : llround(lerp((F32)mAverageCyclesPerTimer, (F32)(LLFastTimer::sTimerCycles / (U64)LLFastTimer::sTimerCalls), 0.1f)); LLFastTimer::sTimerCycles = 0; LLFastTimer::sTimerCalls = 0; diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 6ae6b4877a..fb94657278 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -483,6 +483,10 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, if (drop) { + if (mItems.empty()) + { + setLandingTab(NULL); + } handleNewFavoriteDragAndDrop(item, favorites_id, x, y); showDragMarker(FALSE); } @@ -508,14 +512,14 @@ void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y) if (dest) { - updateItemsOrder(mItems, mDragItemId, dest->getLandmarkId()); + LLInventoryModel::updateItemsOrder(mItems, mDragItemId, dest->getLandmarkId()); } else { mItems.push_back(gInventory.getItem(mDragItemId)); } - saveItemsOrder(mItems); + gInventory.saveItemsOrder(mItems); LLToggleableMenu* menu = (LLToggleableMenu*) mPopupMenuHandle.get(); @@ -644,7 +648,7 @@ LLXMLNodePtr LLFavoritesBarCtrl::getButtonXMLNode() bool success = LLUICtrlFactory::getLayeredXMLNode("favorites_bar_button.xml", buttonXMLNode); if (!success) { - llwarns << "Unable to read xml file with button for Favorites Bar: favorites_bar_button.xml" << llendl; + llwarns << "Failed to create Favorites Bar button from favorites_bar_button.xml" << llendl; buttonXMLNode = NULL; } return buttonXMLNode; @@ -1193,25 +1197,6 @@ BOOL LLFavoritesBarCtrl::needToSaveItemsOrder(const LLInventoryModel::item_array return result; } -void LLFavoritesBarCtrl::saveItemsOrder(LLInventoryModel::item_array_t& items) -{ - int sortField = 0; - - // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field - for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) - { - LLViewerInventoryItem* item = *i; - - item->setSortField(++sortField); - item->setComplete(TRUE); - item->updateServer(FALSE); - - gInventory.updateItem(item); - } - - gInventory.notifyObservers(); -} - LLInventoryModel::item_array_t::iterator LLFavoritesBarCtrl::findItemByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id) { LLInventoryModel::item_array_t::iterator result = items.end(); @@ -1228,15 +1213,6 @@ LLInventoryModel::item_array_t::iterator LLFavoritesBarCtrl::findItemByUUID(LLIn return result; } -void LLFavoritesBarCtrl::updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& srcItemId, const LLUUID& destItemId) -{ - LLViewerInventoryItem* srcItem = gInventory.getItem(srcItemId); - LLViewerInventoryItem* destItem = gInventory.getItem(destItemId); - - items.erase(findItemByUUID(items, srcItem->getUUID())); - items.insert(findItemByUUID(items, destItem->getUUID()), srcItem); -} - void LLFavoritesBarCtrl::insertBeforeItem(LLInventoryModel::item_array_t& items, const LLUUID& beforeItemId, LLViewerInventoryItem* insertedItem) { LLViewerInventoryItem* beforeItem = gInventory.getItem(beforeItemId); diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index 9ac734baff..40dd551eef 100644 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -126,16 +126,7 @@ private: // checks if the current order of the favorites items must be saved BOOL needToSaveItemsOrder(const LLInventoryModel::item_array_t& items); - // saves current order of the favorites items - void saveItemsOrder(LLInventoryModel::item_array_t& items); - - /* - * changes favorites items order by insertion of the item identified by srcItemId - * BEFORE the item identified by destItemId. both items must exist in items array. - */ - void updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& srcItemId, const LLUUID& destItemId); - - /* + /** * inserts an item identified by insertedItemId BEFORE an item identified by beforeItemId. * this function assumes that an item identified by insertedItemId doesn't exist in items array. */ diff --git a/indra/newview/llfirstuse.cpp b/indra/newview/llfirstuse.cpp index 7fd0e070be..b3fdf60b11 100644 --- a/indra/newview/llfirstuse.cpp +++ b/indra/newview/llfirstuse.cpp @@ -45,6 +45,7 @@ #include "llappviewer.h" #include "lltracker.h" +/* // static std::set LLFirstUse::sConfigVariables; @@ -75,7 +76,8 @@ void LLFirstUse::resetFirstUse() gWarningSettings.setBOOL(*iter, TRUE); } } - +*/ +/* // Called whenever the viewer detects that your balance went up void LLFirstUse::useBalanceIncrease(S32 delta) @@ -145,6 +147,8 @@ void LLFirstUse::useBuild() LLNotificationsUtil::add("FirstBuild"); } } + + */ /* // static void LLFirstUse::useLeftClickNoHit() @@ -157,6 +161,7 @@ void LLFirstUse::useLeftClickNoHit() } } */ +/* // static void LLFirstUse::useTeleport() { @@ -171,7 +176,7 @@ void LLFirstUse::useTeleport() } } } - +*/ // static void LLFirstUse::useOverrideKeys() { @@ -187,7 +192,7 @@ void LLFirstUse::useOverrideKeys() } } } - +/* // static void LLFirstUse::useAttach() { @@ -216,6 +221,7 @@ void LLFirstUse::useInventory() } } +*/ // static void LLFirstUse::useSandbox() @@ -230,7 +236,7 @@ void LLFirstUse::useSandbox() LLNotificationsUtil::add("FirstSandbox", args); } } - +/* // static void LLFirstUse::useFlexible() { @@ -277,3 +283,4 @@ void LLFirstUse::useMedia() //LLNotificationsUtil::add("FirstMedia"); } } +*/ diff --git a/indra/newview/llfirstuse.h b/indra/newview/llfirstuse.h index 7b4f9f516f..3c7551f6cb 100644 --- a/indra/newview/llfirstuse.h +++ b/indra/newview/llfirstuse.h @@ -79,6 +79,7 @@ object or from inventory. class LLFirstUse { public: +/* // Add a config variable to be reset on resetFirstUse() static void addConfigVariable(const std::string& var); @@ -97,11 +98,16 @@ public: static void useBuild(); // static void useLeftClickNoHit(); static void useTeleport(); +*/ static void useOverrideKeys(); +/* static void useAttach(); static void useAppearance(); static void useInventory(); + */ static void useSandbox(); + +/* static void useFlexible(); static void useDebugMenus(); static void useSculptedPrim(); @@ -109,6 +115,7 @@ public: protected: static std::set sConfigVariables; +*/ }; #endif diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index e80499688e..ef69f39ad2 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -167,20 +167,21 @@ BOOL LLFloaterAbout::postBuild() // Now build the various pieces support << getString("AboutHeader", args); - if (info.has("COMPILER")) - { - support << "\n\n" << getString("AboutCompiler", args); - } if (info.has("REGION")) { support << "\n\n" << getString("AboutPosition", args); } support << "\n\n" << getString("AboutSystem", args); + support << "\n"; if (info.has("GRAPHICS_DRIVER_VERSION")) { - support << "\n\n" << getString("AboutDriver", args); + support << "\n" << getString("AboutDriver", args); + } + support << "\n" << getString("AboutLibs", args); + if (info.has("COMPILER")) + { + support << "\n" << getString("AboutCompiler", args); } - support << "\n\n" << getString("AboutLibs", args); if (info.has("PACKETS_IN")) { support << '\n' << getString("AboutTraffic", args); @@ -193,11 +194,11 @@ BOOL LLFloaterAbout::postBuild() support_widget->blockUndo(); // Fix views - support_widget->setCursorPos(0); support_widget->setEnabled(FALSE); + support_widget->startOfDoc(); - credits_widget->setCursorPos(0); credits_widget->setEnabled(FALSE); + credits_widget->startOfDoc(); return TRUE; } @@ -268,7 +269,7 @@ LLSD LLFloaterAbout::getInfo() info["VIVOX_VERSION"] = gVoiceClient ? gVoiceClient->getAPIVersion() : LLTrans::getString("NotConnected"); // TODO: Implement media plugin version query - info["QT_WEBKIT_VERSION"] = "4.5.2 (version number hard-coded)"; + info["QT_WEBKIT_VERSION"] = "4.6 (version number hard-coded)"; if (gPacketsIn > 0) { diff --git a/indra/newview/llfloaterbulkpermission.cpp b/indra/newview/llfloaterbulkpermission.cpp index 538b44c056..5c3a54e34b 100644 --- a/indra/newview/llfloaterbulkpermission.cpp +++ b/indra/newview/llfloaterbulkpermission.cpp @@ -48,7 +48,6 @@ #include "llresmgr.h" #include "llbutton.h" #include "lldir.h" -#include "llfloaterchat.h" #include "llviewerstats.h" #include "lluictrlfactory.h" #include "llselectmgr.h" diff --git a/indra/newview/llfloatercamera.cpp b/indra/newview/llfloatercamera.cpp index 764aff68c9..9496e94780 100644 --- a/indra/newview/llfloatercamera.cpp +++ b/indra/newview/llfloatercamera.cpp @@ -144,6 +144,11 @@ void LLPanelCameraZoom::onSliderValueChanged() mSavedSliderVal = val; } +void activate_camera_tool() +{ + LLToolMgr::getInstance()->setTransientTool(LLToolCamera::getInstance()); +}; + // // Member functions // @@ -151,7 +156,7 @@ void LLPanelCameraZoom::onSliderValueChanged() /*static*/ bool LLFloaterCamera::inFreeCameraMode() { LLFloaterCamera* floater_camera = LLFloaterCamera::findInstance(); - if (floater_camera && floater_camera->mCurrMode == CAMERA_CTRL_MODE_FREE_CAMERA) + if (floater_camera && floater_camera->mCurrMode == CAMERA_CTRL_MODE_FREE_CAMERA && gAgent.getCameraMode() != CAMERA_MODE_MOUSELOOK) { return true; } @@ -177,27 +182,17 @@ void LLFloaterCamera::update() } -/*static*/ void LLFloaterCamera::updateIfNotInAvatarViewMode() -{ - LLFloaterCamera* floater_camera = LLFloaterCamera::findInstance(); - if (floater_camera && !floater_camera->inAvatarViewMode()) - { - floater_camera->update(); - } -} - - void LLFloaterCamera::toPrevMode() { switchMode(mPrevMode); } -/*static*/ void LLFloaterCamera::toPrevModeIfInAvatarViewMode() +/*static*/ void LLFloaterCamera::onLeavingMouseLook() { LLFloaterCamera* floater_camera = LLFloaterCamera::findInstance(); - if (floater_camera && floater_camera->inAvatarViewMode()) + if (floater_camera && floater_camera->inFreeCameraMode()) { - floater_camera->toPrevMode(); + activate_camera_tool(); } } @@ -325,7 +320,7 @@ void LLFloaterCamera::switchMode(ECameraControlMode mode) break; case CAMERA_CTRL_MODE_FREE_CAMERA: - LLToolMgr::getInstance()->setTransientTool(LLToolCamera::getInstance()); + activate_camera_tool(); break; case CAMERA_CTRL_MODE_AVATAR_VIEW: diff --git a/indra/newview/llfloatercamera.h b/indra/newview/llfloatercamera.h index 5d44b4944d..45d5e9a845 100644 --- a/indra/newview/llfloatercamera.h +++ b/indra/newview/llfloatercamera.h @@ -61,7 +61,7 @@ public: /* callback for camera presets changing */ static void onClickCameraPresets(const LLSD& param); - static void toPrevModeIfInAvatarViewMode(); + static void onLeavingMouseLook(); /** resets current camera mode to orbit mode */ static void resetCameraMode(); @@ -69,8 +69,6 @@ public: /* determines actual mode and updates ui */ void update(); - static void updateIfNotInAvatarViewMode(); - virtual void onOpen(const LLSD& key); virtual void onClose(bool app_quitting); diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index b9e0f928f1..cdb9b8edb8 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -37,8 +37,6 @@ #include "llviewerprecompiledheaders.h" -#include "llfloaterchat.h" - // project include #include "llagent.h" #include "llappviewer.h" @@ -190,7 +188,14 @@ void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file) { if (log_to_file && (gSavedPerAccountSettings.getBOOL("LogChat"))) { - LLLogChat::saveHistory("chat", chat.mFromName, chat.mFromID, chat.mText); + if (chat.mChatType != CHAT_TYPE_WHISPER && chat.mChatType != CHAT_TYPE_SHOUT) + { + LLLogChat::saveHistory("chat", chat.mFromName, chat.mFromID, chat.mText); + } + else + { + LLLogChat::saveHistory("chat", "", chat.mFromID, chat.mFromName + " " + chat.mText); + } } LLColor4 color = get_text_color(chat); @@ -306,27 +311,17 @@ void LLFloaterChat::onClickToggleShowMute(LLUICtrl* caller, void *data) } // Put a line of chat in all the right places -void LLFloaterChat::addChat(const LLChat& chat, BOOL from_instant_message, BOOL local_agent) +void LLFloaterChat::addChat(const LLChat& chat, BOOL local_agent) { triggerAlerts(chat.mText); // Add the sender to the list of people with which we've recently interacted. - if(chat.mSourceType == CHAT_SOURCE_AGENT && chat.mFromID.notNull()) - LLRecentPeople::instance().add(chat.mFromID); + // this is not the best place to add _all_ messages to recent list + // comment this for now, may remove later on code cleanup + //if(chat.mSourceType == CHAT_SOURCE_AGENT && chat.mFromID.notNull()) + // LLRecentPeople::instance().add(chat.mFromID); - bool add_chat = true; - bool log_chat = true; - if(from_instant_message) - { - if (!gSavedSettings.getBOOL("IMInChat")) - add_chat = false; - //log_chat = false; -} - - if (add_chat) - { - addChatHistory(chat, log_chat); - } + addChatHistory(chat, true); } // Moved from lltextparser.cpp to break llui/llaudio library dependency. diff --git a/indra/newview/llfloaterchat.h b/indra/newview/llfloaterchat.h index 84fc199bfa..4437a0a5c2 100644 --- a/indra/newview/llfloaterchat.h +++ b/indra/newview/llfloaterchat.h @@ -61,7 +61,7 @@ public: // *TODO:Skinning - move these to LLChat (or LLViewerChat?) // Add chat to console and history list. // Color based on source, type, distance. - static void addChat(const LLChat& chat, BOOL from_im = FALSE, BOOL local_agent = FALSE); + static void addChat(const LLChat& chat, BOOL local_agent = FALSE); // Add chat to history alone. static void addChatHistory(const LLChat& chat, bool log_to_file = true); diff --git a/indra/newview/llfloaterchatterbox.cpp b/indra/newview/llfloaterchatterbox.cpp index 9108cfb72b..84b423399e 100644 --- a/indra/newview/llfloaterchatterbox.cpp +++ b/indra/newview/llfloaterchatterbox.cpp @@ -38,7 +38,6 @@ #include "llfloaterreg.h" #include "llfloaterchatterbox.h" #include "lluictrlfactory.h" -#include "llfloaterchat.h" #include "llfloaterfriends.h" #include "llfloatergroups.h" #include "llviewercontrol.h" @@ -134,22 +133,6 @@ BOOL LLFloaterChatterBox::postBuild() addFloater(LLFloaterMyFriends::getInstance(), TRUE); } - if (gSavedSettings.getBOOL("ChatHistoryTornOff")) - { - LLFloaterChat* floater_chat = LLFloaterChat::getInstance(); - if(floater_chat) - { - // add then remove to set up relationship for re-attach - addFloater(floater_chat, FALSE); - removeFloater(floater_chat); - // reparent to floater view - gFloaterView->addChild(floater_chat); - } - } - else - { - addFloater(LLFloaterChat::getInstance(), FALSE); - } mTabContainer->lockTabs(); return TRUE; } @@ -230,8 +213,6 @@ void LLFloaterChatterBox::onOpen(const LLSD& key) //*TODO:Skinning show the session id associated with key if (key.asString() == "local") { - LLFloaterChat* chat = LLFloaterReg::findTypedInstance("chat"); - chat->openFloater(); } else if (key.isDefined()) { @@ -245,12 +226,6 @@ void LLFloaterChatterBox::onOpen(const LLSD& key) void LLFloaterChatterBox::onVisibilityChange ( const LLSD& new_visibility ) { - // HACK: potentially need to toggle console - LLFloaterChat* instance = LLFloaterChat::getInstance(); - if(instance) - { - instance->updateConsoleVisibility(); - } } void LLFloaterChatterBox::removeFloater(LLFloater* floaterp) @@ -349,8 +324,7 @@ LLFloater* LLFloaterChatterBox::getCurrentVoiceFloater() } if (LLVoiceChannelProximal::getInstance() == LLVoiceChannel::getCurrentVoiceChannel()) { - // show near me tab if in proximal channel - return LLFloaterChat::getInstance(); + return NULL; } else { diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp index 5072bc8c82..de65c6f876 100644 --- a/indra/newview/llfloatergesture.cpp +++ b/indra/newview/llfloatergesture.cpp @@ -110,7 +110,7 @@ LLFloaterGesture::LLFloaterGesture(const LLSD& key) mCommitCallbackRegistrar.add("Gesture.Action.ToogleActiveState", boost::bind(&LLFloaterGesture::onActivateBtnClick, this)); mCommitCallbackRegistrar.add("Gesture.Action.ShowPreview", boost::bind(&LLFloaterGesture::onClickEdit, this)); - mCommitCallbackRegistrar.add("Gesture.Action.CopyPast", boost::bind(&LLFloaterGesture::onCopyPastAction, this, _2)); + mCommitCallbackRegistrar.add("Gesture.Action.CopyPaste", boost::bind(&LLFloaterGesture::onCopyPasteAction, this, _2)); mCommitCallbackRegistrar.add("Gesture.Action.SaveToCOF", boost::bind(&LLFloaterGesture::addToCurrentOutFit, this)); mEnableCallbackRegistrar.add("Gesture.EnableAction", boost::bind(&LLFloaterGesture::isActionEnabled, this, _2)); @@ -245,6 +245,7 @@ void LLFloaterGesture::refreshAll() void LLFloaterGesture::buildGestureList() { + S32 scroll_pos = mGestureList->getScrollPos(); std::vector selected_items; getSelectedIds(selected_items); LL_DEBUGS("Gesture")<< "Rebuilding gesture list "<< LL_ENDL; @@ -274,13 +275,14 @@ void LLFloaterGesture::buildGestureList() } } } + // attempt to preserve scroll position through re-builds - // since we do re-build any time anything dirties + // since we do re-build whenever something gets dirty for(std::vector::iterator it = selected_items.begin(); it != selected_items.end(); it++) { mGestureList->selectByID(*it); } - mGestureList->scrollToShowSelected(); + mGestureList->setScrollPos(scroll_pos); } void LLFloaterGesture::addGesture(const LLUUID& item_id , LLMultiGesture* gesture,LLCtrlListInterface * list ) @@ -415,7 +417,7 @@ void LLFloaterGesture::onClickPlay() LL_DEBUGS("Gesture")<<" Trying to play gesture id: "<< item_id <refresh(); mPanelMedia->refresh(); mPanelAccess->refresh(); + mPanelCovenant->refresh(); } @@ -2795,12 +2796,6 @@ LLPanelLandCovenant::~LLPanelLandCovenant() { } -BOOL LLPanelLandCovenant::postBuild() -{ - refresh(); - return TRUE; -} - // virtual void LLPanelLandCovenant::refresh() { diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h index d7d02ba1a3..a4785e8f5b 100644 --- a/indra/newview/llfloaterland.h +++ b/indra/newview/llfloaterland.h @@ -391,7 +391,6 @@ class LLPanelLandCovenant public: LLPanelLandCovenant(LLSafeHandle& parcelp); virtual ~LLPanelLandCovenant(); - virtual BOOL postBuild(); void refresh(); static void updateCovenantText(const std::string& string); static void updateEstateName(const std::string& name); diff --git a/indra/newview/llfloatermediasettings.cpp b/indra/newview/llfloatermediasettings.cpp index 5cfd56193e..976af121ae 100644 --- a/indra/newview/llfloatermediasettings.cpp +++ b/indra/newview/llfloatermediasettings.cpp @@ -192,6 +192,9 @@ void LLFloaterMediaSettings::initValues( const LLSD& media_settings, bool editab sInstance->mPanelMediaSettingsGeneral->getValues( sInstance->mInitialValues ); sInstance->mPanelMediaSettingsSecurity->getValues( sInstance->mInitialValues ); sInstance->mPanelMediaSettingsPermissions->getValues( sInstance->mInitialValues ); + + sInstance->mApplyBtn->setEnabled(editable); + sInstance->mOKBtn->setEnabled(editable); } //////////////////////////////////////////////////////////////////////////////// @@ -266,8 +269,11 @@ const std::string LLFloaterMediaSettings::getHomeUrl() // virtual void LLFloaterMediaSettings::draw() { - // Set the enabled state of the "Apply" button if values changed - childSetEnabled( "Apply", haveValuesChanged() ); + if (NULL != mApplyBtn) + { + // Set the enabled state of the "Apply" button if values changed + mApplyBtn->setEnabled( haveValuesChanged() ); + } LLFloater::draw(); } diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 11dd48056c..fc036cb354 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -51,7 +51,7 @@ #include "lldirpicker.h" #include "llfeaturemanager.h" #include "llfocusmgr.h" -#include "llfirstuse.h" +//#include "llfirstuse.h" #include "llfloaterreg.h" #include "llfloaterabout.h" #include "llfloaterhardwaresettings.h" @@ -185,8 +185,8 @@ void handleNameTagOptionChanged(const LLSD& newvalue); viewer_media_t get_web_media(); bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response); -bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater); -bool callback_reset_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater); +//bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater); +//bool callback_reset_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater); void fractionFromDecimal(F32 decimal_val, S32& numerator, S32& denominator); @@ -236,7 +236,7 @@ void handleNameTagOptionChanged(const LLSD& newvalue) } } -bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater) +/*bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (0 == option && floater ) @@ -244,7 +244,7 @@ bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFlo if ( floater ) { floater->setAllIgnored(); - LLFirstUse::disableFirstUse(); + // LLFirstUse::disableFirstUse(); floater->buildPopupLists(); } } @@ -259,13 +259,13 @@ bool callback_reset_dialogs(const LLSD& notification, const LLSD& response, LLFl if ( floater ) { floater->resetAllIgnored(); - LLFirstUse::resetFirstUse(); + //LLFirstUse::resetFirstUse(); floater->buildPopupLists(); } } return false; } - +*/ void fractionFromDecimal(F32 decimal_val, S32& numerator, S32& denominator) { @@ -313,8 +313,8 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mCommitCallbackRegistrar.add("Pref.SelectSkin", boost::bind(&LLFloaterPreference::onSelectSkin, this)); mCommitCallbackRegistrar.add("Pref.VoiceSetKey", boost::bind(&LLFloaterPreference::onClickSetKey, this)); mCommitCallbackRegistrar.add("Pref.VoiceSetMiddleMouse", boost::bind(&LLFloaterPreference::onClickSetMiddleMouse, this)); - mCommitCallbackRegistrar.add("Pref.ClickSkipDialogs", boost::bind(&LLFloaterPreference::onClickSkipDialogs, this)); - mCommitCallbackRegistrar.add("Pref.ClickResetDialogs", boost::bind(&LLFloaterPreference::onClickResetDialogs, this)); +// mCommitCallbackRegistrar.add("Pref.ClickSkipDialogs", boost::bind(&LLFloaterPreference::onClickSkipDialogs, this)); +// mCommitCallbackRegistrar.add("Pref.ClickResetDialogs", boost::bind(&LLFloaterPreference::onClickResetDialogs, this)); mCommitCallbackRegistrar.add("Pref.ClickEnablePopup", boost::bind(&LLFloaterPreference::onClickEnablePopup, this)); mCommitCallbackRegistrar.add("Pref.ClickDisablePopup", boost::bind(&LLFloaterPreference::onClickDisablePopup, this)); mCommitCallbackRegistrar.add("Pref.LogPath", boost::bind(&LLFloaterPreference::onClickLogPath, this)); @@ -325,6 +325,8 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mCommitCallbackRegistrar.add("Pref.WindowedMod", boost::bind(&LLFloaterPreference::onCommitWindowedMode, this)); mCommitCallbackRegistrar.add("Pref.UpdateSliderText", boost::bind(&LLFloaterPreference::onUpdateSliderText,this, _1,_2)); mCommitCallbackRegistrar.add("Pref.AutoDetectAspect", boost::bind(&LLFloaterPreference::onCommitAutoDetectAspect, this)); + mCommitCallbackRegistrar.add("Pref.ParcelMediaAutoPlayEnable", boost::bind(&LLFloaterPreference::onCommitParcelMediaAutoPlayEnable, this)); + mCommitCallbackRegistrar.add("Pref.MediaEnabled", boost::bind(&LLFloaterPreference::onCommitMediaEnabled, this)); mCommitCallbackRegistrar.add("Pref.onSelectAspectRatio", boost::bind(&LLFloaterPreference::onKeystrokeAspectRatio, this)); mCommitCallbackRegistrar.add("Pref.QualityPerformance", boost::bind(&LLFloaterPreference::onChangeQuality, this, _2)); mCommitCallbackRegistrar.add("Pref.applyUIColor", boost::bind(&LLFloaterPreference::applyUIColor, this ,_1, _2)); @@ -601,8 +603,8 @@ void LLFloaterPreference::onBtnOK() apply(); closeFloater(false); - gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); LLUIColorTable::instance().saveUserSettings(); + gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); // save all settings, even if equals defaults gCrashSettings.saveToFile(crash_settings_filename, FALSE); @@ -986,6 +988,27 @@ void LLFloaterPreference::onCommitAutoDetectAspect() } } +void LLFloaterPreference::onCommitParcelMediaAutoPlayEnable() +{ + BOOL autoplay = getChild("autoplay_enabled")->get(); + + gSavedSettings.setBOOL(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING, autoplay); + + lldebugs << "autoplay now = " << int(autoplay) << llendl; +} + +void LLFloaterPreference::onCommitMediaEnabled() +{ + LLCheckBoxCtrl *media_enabled_ctrl = getChild("media_enabled"); + bool enabled = media_enabled_ctrl->get(); + gSavedSettings.setBOOL("AudioStreamingVideo", enabled); + gSavedSettings.setBOOL("AudioStreamingMusic", enabled); + gSavedSettings.setBOOL("AudioStreamingMedia", enabled); + media_enabled_ctrl->setTentative(false); + // Update enabled state of the "autoplay" checkbox + getChild("autoplay_enabled")->setEnabled(enabled); +} + void LLFloaterPreference::refresh() { LLPanel::refresh(); @@ -1050,7 +1073,7 @@ void LLFloaterPreference::onClickSetMiddleMouse() // update the control right away since we no longer wait for apply getChild("modifier_combo")->onCommit(); } - +/* void LLFloaterPreference::onClickSkipDialogs() { LLNotificationsUtil::add("SkipShowNextTimeDialogs", LLSD(), LLSD(), boost::bind(&callback_skip_dialogs, _1, _2, this)); @@ -1060,6 +1083,7 @@ void LLFloaterPreference::onClickResetDialogs() { LLNotificationsUtil::add("ResetShowNextTimeDialogs", LLSD(), LLSD(), boost::bind(&callback_reset_dialogs, _1, _2, this)); } + */ void LLFloaterPreference::onClickEnablePopup() { @@ -1399,6 +1423,20 @@ BOOL LLPanelPreference::postBuild() refresh(); } + //////////////////////PanelPrivacy /////////////////// + if(hasChild("media_enabled")) + { + bool video_enabled = gSavedSettings.getBOOL("AudioStreamingVideo"); + bool music_enabled = gSavedSettings.getBOOL("AudioStreamingMusic"); + bool media_enabled = gSavedSettings.getBOOL("AudioStreamingMedia"); + bool enabled = video_enabled || music_enabled || media_enabled; + + LLCheckBoxCtrl *media_enabled_ctrl = getChild("media_enabled"); + media_enabled_ctrl->set(enabled); + media_enabled_ctrl->setTentative(!(video_enabled == music_enabled == media_enabled)); + getChild("autoplay_enabled")->setEnabled(enabled); + } + apply(); return true; } diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 74a53d673c..6f382620ee 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -110,8 +110,8 @@ public: void onClickSetKey(); void setKey(KEY key); void onClickSetMiddleMouse(); - void onClickSkipDialogs(); - void onClickResetDialogs(); +// void onClickSkipDialogs(); +// void onClickResetDialogs(); void onClickEnablePopup(); void onClickDisablePopup(); void resetAllIgnored(); @@ -132,6 +132,8 @@ public: // void fractionFromDecimal(F32 decimal_val, S32& numerator, S32& denominator); void onCommitAutoDetectAspect(); + void onCommitParcelMediaAutoPlayEnable(); + void onCommitMediaEnabled(); void applyResolution(); void applyUIColor(LLUICtrl* ctrl, const LLSD& param); void getUIColor(LLUICtrl* ctrl, const LLSD& param); diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index c4b87c1b2d..03ff2cc370 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -406,6 +406,11 @@ LLPanelEstateCovenant* LLFloaterRegionInfo::getPanelCovenant() void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region) { + if (!region) + { + return; + } + // call refresh from region on all panels std::for_each( mInfoPanels.begin(), @@ -1598,7 +1603,7 @@ std::string all_estates_text() } else if (region && region->getOwner() == gAgent.getID()) { - return LLTrans::getString("AllEstatesYouOwn"); + return LLTrans::getString("RegionInfoAllEstatesYouOwn"); } else if (region && region->isEstateManager()) { diff --git a/indra/newview/llfloaterscriptlimits.cpp b/indra/newview/llfloaterscriptlimits.cpp index 3042fbc6ec..0964ad7f91 100644 --- a/indra/newview/llfloaterscriptlimits.cpp +++ b/indra/newview/llfloaterscriptlimits.cpp @@ -64,6 +64,8 @@ // summary which only shows available & correct information #define USE_SIMPLE_SUMMARY +const S32 SIZE_OF_ONE_KB = 1024; + LLFloaterScriptLimits::LLFloaterScriptLimits(const LLSD& seed) : LLFloater(seed) { @@ -130,7 +132,6 @@ void LLFloaterScriptLimits::refresh() } } - ///---------------------------------------------------------------------------- // Base class for panels ///---------------------------------------------------------------------------- @@ -331,6 +332,57 @@ void LLPanelScriptLimitsRegionMemory::setErrorStatus(U32 status, const std::stri llerrs << "Can't handle remote parcel request."<< " Http Status: "<< status << ". Reason : "<< reason<("scripts_list"); + std::vector::iterator id_itor; + for (id_itor = mObjectListItems.begin(); id_itor != mObjectListItems.end(); ++id_itor) + { + LLSD element = *id_itor; + if(element["owner_id"].asUUID() == id) + { + LLScrollListItem* item = list->getItem(element["id"].asUUID()); + + if(item) + { + item->getColumn(2)->setValue(LLSD(name)); + element["columns"][2]["value"] = name; + } + } + } + + // fill in the url's tab if needed, all urls must have memory so we can do it all here + LLFloaterScriptLimits* instance = LLFloaterReg::getTypedInstance("script_limits"); + if(instance) + { + LLTabContainer* tab = instance->getChild("scriptlimits_panels"); + LLPanelScriptLimitsRegionMemory* panel = (LLPanelScriptLimitsRegionMemory*)tab->getChild("script_limits_region_urls_panel"); + + LLScrollListCtrl *list = panel->getChild("scripts_list"); + std::vector::iterator id_itor; + for (id_itor = mObjectListItems.begin(); id_itor != mObjectListItems.end(); ++id_itor) + { + LLSD element = *id_itor; + if(element["owner_id"].asUUID() == id) + { + LLScrollListItem* item = list->getItem(element["id"].asUUID()); + + if(item) + { + item->getColumn(2)->setValue(LLSD(name)); + element["columns"][2]["value"] = name; + } + } + } + } +} + void LLPanelScriptLimitsRegionMemory::setRegionDetails(LLSD content) { LLScrollListCtrl *list = getChild("scripts_list"); @@ -345,22 +397,40 @@ void LLPanelScriptLimitsRegionMemory::setRegionDetails(LLSD content) S32 total_objects = 0; S32 total_size = 0; + std::vector names_requested; + for(S32 i = 0; i < number_parcels; i++) { std::string parcel_name = content["parcels"][i]["name"].asString(); - + LLUUID parcel_id = content["parcels"][i]["id"].asUUID(); S32 number_objects = content["parcels"][i]["objects"].size(); for(S32 j = 0; j < number_objects; j++) { - S32 size = content["parcels"][i]["objects"][j]["resources"]["memory"].asInteger() / 1024; + S32 size = content["parcels"][i]["objects"][j]["resources"]["memory"].asInteger() / SIZE_OF_ONE_KB; total_size += size; std::string name_buf = content["parcels"][i]["objects"][j]["name"].asString(); LLUUID task_id = content["parcels"][i]["objects"][j]["id"].asUUID(); + LLUUID owner_id = content["parcels"][i]["objects"][j]["owner_id"].asUUID(); + + std::string owner_buf; + + BOOL name_is_cached = gCacheName->getFullName(owner_id, owner_buf); + if(!name_is_cached) + { + if(std::find(names_requested.begin(), names_requested.end(), owner_id) == names_requested.end()) + { + names_requested.push_back(owner_id); + gCacheName->get(owner_id, TRUE, + boost::bind(&LLPanelScriptLimitsRegionMemory::onNameCache, + this, _1, _2, _3)); + } + } LLSD element; element["id"] = task_id; + element["owner_id"] = owner_id; element["columns"][0]["column"] = "size"; element["columns"][0]["value"] = llformat("%d", size); element["columns"][0]["font"] = "SANSSERIF"; @@ -368,18 +438,18 @@ void LLPanelScriptLimitsRegionMemory::setRegionDetails(LLSD content) element["columns"][1]["value"] = name_buf; element["columns"][1]["font"] = "SANSSERIF"; element["columns"][2]["column"] = "owner"; - element["columns"][2]["value"] = ""; + element["columns"][2]["value"] = owner_buf; element["columns"][2]["font"] = "SANSSERIF"; element["columns"][3]["column"] = "location"; element["columns"][3]["value"] = parcel_name; element["columns"][3]["font"] = "SANSSERIF"; - list->addElement(element); - mObjectListIDs.push_back(task_id); + list->addElement(element, ADD_SORTED); + mObjectListItems.push_back(element); total_objects++; } } - + mParcelMemoryUsed =total_size; mGotParcelMemoryUsed = TRUE; populateParcelMemoryText(); @@ -556,7 +626,7 @@ void LLPanelScriptLimitsRegionMemory::clearList() childSetValue("memory_used", LLSD(msg_empty_string)); childSetValue("parcels_listed", LLSD(msg_empty_string)); - mObjectListIDs.clear(); + mObjectListItems.clear(); } // static @@ -728,7 +798,7 @@ void LLPanelScriptLimitsRegionURLs::setRegionDetails(LLSD content) S32 total_objects = 0; S32 total_size = 0; - + for(S32 i = 0; i < number_parcels; i++) { std::string parcel_name = content["parcels"][i]["name"].asString(); @@ -744,6 +814,10 @@ void LLPanelScriptLimitsRegionURLs::setRegionDetails(LLSD content) std::string name_buf = content["parcels"][i]["objects"][j]["name"].asString(); LLUUID task_id = content["parcels"][i]["objects"][j]["id"].asUUID(); + LLUUID owner_id = content["parcels"][i]["objects"][j]["owner_id"].asUUID(); + + std::string owner_buf; + gCacheName->getFullName(owner_id, owner_buf); //dont care if this fails as the memory tab will request and fill the field LLSD element; @@ -755,14 +829,14 @@ void LLPanelScriptLimitsRegionURLs::setRegionDetails(LLSD content) element["columns"][1]["value"] = name_buf; element["columns"][1]["font"] = "SANSSERIF"; element["columns"][2]["column"] = "owner"; - element["columns"][2]["value"] = ""; + element["columns"][2]["value"] = owner_buf; element["columns"][2]["font"] = "SANSSERIF"; element["columns"][3]["column"] = "location"; element["columns"][3]["value"] = parcel_name; element["columns"][3]["font"] = "SANSSERIF"; list->addElement(element); - mObjectListIDs.push_back(task_id); + mObjectListItems.push_back(element); total_objects++; } } @@ -868,7 +942,7 @@ void LLPanelScriptLimitsRegionURLs::clearList() childSetValue("urls_used", LLSD(msg_empty_string)); childSetValue("parcels_listed", LLSD(msg_empty_string)); - mObjectListIDs.clear(); + mObjectListItems.clear(); } // static @@ -982,7 +1056,7 @@ void LLPanelScriptLimitsAttachment::setAttachmentDetails(LLSD content) S32 size = 0; if(content["attachments"][i]["objects"][j]["resources"].has("memory")) { - size = content["attachments"][i]["objects"][j]["resources"]["memory"].asInteger(); + size = content["attachments"][i]["objects"][j]["resources"]["memory"].asInteger() / SIZE_OF_ONE_KB; } S32 urls = 0; if(content["attachments"][i]["objects"][j]["resources"].has("urls")) @@ -1059,3 +1133,4 @@ void LLPanelScriptLimitsAttachment::onClickRefresh(void* userdata) return; } } + diff --git a/indra/newview/llfloaterscriptlimits.h b/indra/newview/llfloaterscriptlimits.h index 88239136e3..7e2b536eb6 100644 --- a/indra/newview/llfloaterscriptlimits.h +++ b/indra/newview/llfloaterscriptlimits.h @@ -54,12 +54,12 @@ public: // from LLPanel virtual void refresh(); - + private: - + LLFloaterScriptLimits(const LLSD& seed); ~LLFloaterScriptLimits(); - + protected: LLTabContainer* mTab; @@ -167,13 +167,17 @@ public: private: + void onNameCache( const LLUUID& id, + const std::string& first_name, + const std::string& last_name); + LLUUID mParcelId; BOOL mGotParcelMemoryUsed; BOOL mGotParcelMemoryMax; S32 mParcelMemoryMax; S32 mParcelMemoryUsed; - std::vector mObjectListIDs; + std::vector mObjectListItems; protected: @@ -218,7 +222,7 @@ private: S32 mParcelURLsMax; S32 mParcelURLsUsed; - std::vector mObjectListIDs; + std::vector mObjectListItems; protected: diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index c6d9fee630..a7401fdb6f 100644 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -32,6 +32,9 @@ */ #include "llviewerprecompiledheaders.h" + +#include "llcommandhandler.h" +#include "llfloaterreg.h" #include "llfloatersearch.h" #include "llmediactrl.h" #include "lllogininstance.h" @@ -41,6 +44,42 @@ #include "llviewercontrol.h" #include "llweb.h" +// support secondlife:///app/search/{CATEGORY}/{QUERY} SLapps +class LLSearchHandler : public LLCommandHandler +{ +public: + // requires trusted browser to trigger + LLSearchHandler() : LLCommandHandler("search", UNTRUSTED_THROTTLE) { } + bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) + { + const size_t parts = tokens.size(); + + // get the (optional) category for the search + std::string category; + if (parts > 0) + { + category = tokens[0].asString(); + } + + // get the (optional) search string + std::string search_text; + if (parts > 1) + { + search_text = tokens[1].asString(); + } + + // create the LLSD arguments for the search floater + LLSD args; + args["category"] = category; + args["id"] = LLURI::unescape(search_text); + + // open the search floater and perform the requested search + LLFloaterReg::showInstance("search", args); + return true; + } +}; +LLSearchHandler gSearchHandler; + LLFloaterSearch::LLFloaterSearch(const LLSD& key) : LLFloater(key), LLViewerMediaObserver(), diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp index 8979575ef7..a6ffa5ec09 100644 --- a/indra/newview/llfloatersettingsdebug.cpp +++ b/indra/newview/llfloatersettingsdebug.cpp @@ -34,7 +34,7 @@ #include "llfloatersettingsdebug.h" #include "llfloater.h" #include "lluictrlfactory.h" -#include "llfirstuse.h" +//#include "llfirstuse.h" #include "llcombobox.h" #include "llspinctrl.h" #include "llcolorswatch.h" diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index f53b62e490..afb58c9407 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -2078,8 +2078,10 @@ void LLFloaterSnapshot::draw() { if(previewp->getThumbnailImage()) { + LLRect thumbnail_rect = getChild("thumbnail_placeholder")->getRect(); + S32 offset_x = (getRect().getWidth() - previewp->getThumbnailWidth()) / 2 ; - S32 offset_y = getRect().getHeight() - 205 + (90 - previewp->getThumbnailHeight()) / 2 ; + S32 offset_y = thumbnail_rect.mBottom + (thumbnail_rect.getHeight() - previewp->getThumbnailHeight()) / 2 ; glMatrixMode(GL_MODELVIEW); gl_draw_scaled_image(offset_x, offset_y, diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index babef5b63d..241497aeaf 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -1134,7 +1134,8 @@ void LLFloaterTools::getMediaState() } // XXX DISABLE this for now, because when the fetch finally // does come in, the state of this floater doesn't properly - // update. This needs more thought. + // update. Re-selecting fixes the problem, but there is + // contention as to whether this is a sufficient solution. // if (object->isMediaDataBeingFetched()) // { // LL_INFOS("LLFloaterTools: media") @@ -1221,10 +1222,10 @@ void LLFloaterTools::getMediaState() mNeedMediaTitle = false; } - childSetEnabled("media_tex", bool_has_media & editable); - childSetEnabled( "edit_media", bool_has_media & editable ); - childSetEnabled( "delete_media", bool_has_media & editable ); - childSetEnabled( "add_media", ( ! bool_has_media ) & editable ); + childSetEnabled("media_tex", bool_has_media && editable); + childSetEnabled( "edit_media", bool_has_media && LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo && editable ); + childSetEnabled( "delete_media", bool_has_media && editable ); + childSetEnabled( "add_media", ( ! bool_has_media ) && editable ); // TODO: display a list of all media on the face - use 'identical' flag } else // not all face has media but at least one does. @@ -1252,7 +1253,7 @@ void LLFloaterTools::getMediaState() } childSetEnabled("media_tex", TRUE); - childSetEnabled( "edit_media", TRUE); + childSetEnabled( "edit_media", LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo); childSetEnabled( "delete_media", TRUE); childSetEnabled( "add_media", FALSE ); } @@ -1269,18 +1270,15 @@ void LLFloaterTools::getMediaState() // called when a user wants to add media to a prim or prim face void LLFloaterTools::onClickBtnAddMedia() { - // check for the edit tool and now many faces are selected - LLTool *tool = LLToolMgr::getInstance()->getCurrentTool(); - if((tool != LLToolFace::getInstance()) || LLSelectMgr::getInstance()->getSelection()->isMultipleTESelected()) + // check if multiple faces are selected + if(LLSelectMgr::getInstance()->getSelection()->isMultipleTESelected()) { - LLNotificationsUtil::add("MultipleFacesSelected",LLSD(), LLSD(), multipleFacesSelectedConfirm); - + LLNotificationsUtil::add("MultipleFacesSelected", LLSD(), LLSD(), multipleFacesSelectedConfirm); } else { onClickBtnEditMedia(); } - } // static @@ -1423,7 +1421,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getControls(); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_controls(default_media_data); identical = selected_objects->getSelectedTEValue( &func_controls, value_u8 ); @@ -1446,7 +1444,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getFirstClickInteract(); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_first_click(default_media_data); identical = selected_objects->getSelectedTEValue( &func_first_click, value_bool ); @@ -1469,7 +1467,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getHomeURL(); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_home_url(default_media_data); identical = selected_objects->getSelectedTEValue( &func_home_url, value_str ); @@ -1492,7 +1490,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getCurrentURL(); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_current_url(default_media_data); identical = selected_objects->getSelectedTEValue( &func_current_url, value_str ); @@ -1516,7 +1514,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getAutoZoom(); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_auto_zoom(default_media_data); identical = selected_objects->getSelectedTEValue( &func_auto_zoom, value_bool ); @@ -1539,7 +1537,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getAutoPlay(); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_auto_play(default_media_data); identical = selected_objects->getSelectedTEValue( &func_auto_play, value_bool ); @@ -1563,7 +1561,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getAutoScale();; }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_auto_scale(default_media_data); identical = selected_objects->getSelectedTEValue( &func_auto_scale, value_bool ); @@ -1586,7 +1584,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getAutoLoop(); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_auto_loop(default_media_data); identical = selected_objects->getSelectedTEValue( &func_auto_loop, value_bool ); @@ -1609,7 +1607,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getWidthPixels(); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_width_pixels(default_media_data); identical = selected_objects->getSelectedTEValue( &func_width_pixels, value_int ); @@ -1632,7 +1630,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getHeightPixels(); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_height_pixels(default_media_data); identical = selected_objects->getSelectedTEValue( &func_height_pixels, value_int ); @@ -1655,7 +1653,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getAltImageEnable(); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_enable_alt_image(default_media_data); identical = selected_objects->getSelectedTEValue( &func_enable_alt_image, value_bool ); @@ -1678,7 +1676,7 @@ void LLFloaterTools::updateMediaSettings() return 0 != ( mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_OWNER ); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_perms_owner_interact(default_media_data); identical = selected_objects->getSelectedTEValue( &func_perms_owner_interact, value_bool ); @@ -1701,7 +1699,7 @@ void LLFloaterTools::updateMediaSettings() return 0 != ( mMediaEntry.getPermsControl() & LLMediaEntry::PERM_OWNER ); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_perms_owner_control(default_media_data); identical = selected_objects ->getSelectedTEValue( &func_perms_owner_control, value_bool ); @@ -1724,7 +1722,7 @@ void LLFloaterTools::updateMediaSettings() return 0 != ( mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_GROUP ); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_perms_group_interact(default_media_data); identical = selected_objects->getSelectedTEValue( &func_perms_group_interact, value_bool ); @@ -1747,7 +1745,7 @@ void LLFloaterTools::updateMediaSettings() return 0 != ( mMediaEntry.getPermsControl() & LLMediaEntry::PERM_GROUP ); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_perms_group_control(default_media_data); identical = selected_objects->getSelectedTEValue( &func_perms_group_control, value_bool ); @@ -1770,7 +1768,7 @@ void LLFloaterTools::updateMediaSettings() return 0 != ( mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_ANYONE ); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_perms_anyone_interact(default_media_data); identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func_perms_anyone_interact, value_bool ); @@ -1793,7 +1791,7 @@ void LLFloaterTools::updateMediaSettings() return 0 != ( mMediaEntry.getPermsControl() & LLMediaEntry::PERM_ANYONE ); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_perms_anyone_control(default_media_data); identical = selected_objects->getSelectedTEValue( &func_perms_anyone_control, value_bool ); @@ -1816,7 +1814,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getWhiteListEnable(); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_whitelist_enable(default_media_data); identical = selected_objects->getSelectedTEValue( &func_whitelist_enable, value_bool ); @@ -1839,7 +1837,7 @@ void LLFloaterTools::updateMediaSettings() return mMediaEntry.getWhiteList(); }; - const LLMediaEntry & mMediaEntry; + const LLMediaEntry &mMediaEntry; } func_whitelist_urls(default_media_data); identical = selected_objects->getSelectedTEValue( &func_whitelist_urls, value_vector_str ); diff --git a/indra/newview/llfloatervoicedevicesettings.cpp b/indra/newview/llfloatervoicedevicesettings.cpp index 43024a4bd0..638c9f1b8c 100644 --- a/indra/newview/llfloatervoicedevicesettings.cpp +++ b/indra/newview/llfloatervoicedevicesettings.cpp @@ -110,32 +110,33 @@ void LLPanelVoiceDeviceSettings::draw() LLPanel::draw(); - F32 voice_power = gVoiceClient->tuningGetEnergy(); - S32 discrete_power = 0; - - if (!is_in_tuning_mode) - { - discrete_power = 0; - } - else - { - discrete_power = llmin(4, llfloor((voice_power / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 4.f)); - } - if (is_in_tuning_mode) { - for(S32 power_bar_idx = 0; power_bar_idx < 5; power_bar_idx++) + const S32 num_bars = 5; + F32 voice_power = gVoiceClient->tuningGetEnergy() / LLVoiceClient::OVERDRIVEN_POWER_LEVEL; + S32 discrete_power = llmin(num_bars, llfloor(voice_power * (F32)num_bars + 0.1f)); + + for(S32 power_bar_idx = 0; power_bar_idx < num_bars; power_bar_idx++) { std::string view_name = llformat("%s%d", "bar", power_bar_idx); LLView* bar_view = getChild(view_name); if (bar_view) { + gl_rect_2d(bar_view->getRect(), LLColor4::grey, TRUE); + + LLColor4 color; if (power_bar_idx < discrete_power) { - LLColor4 color = (power_bar_idx >= 3) ? LLUIColorTable::instance().getColor("OverdrivenColor") : LLUIColorTable::instance().getColor("SpeakingColor"); - gl_rect_2d(bar_view->getRect(), color, TRUE); + color = (power_bar_idx >= 3) ? LLUIColorTable::instance().getColor("OverdrivenColor") : LLUIColorTable::instance().getColor("SpeakingColor"); + } + else + { + color = LLUIColorTable::instance().getColor("PanelFocusBackgroundColor"); } - gl_rect_2d(bar_view->getRect(), LLColor4::grey, FALSE); + + LLRect color_rect = bar_view->getRect(); + color_rect.stretch(-1); + gl_rect_2d(color_rect, color, TRUE); } } } @@ -276,7 +277,10 @@ void LLPanelVoiceDeviceSettings::initialize() void LLPanelVoiceDeviceSettings::cleanup() { - gVoiceClient->tuningStop(); + if (gVoiceClient) + { + gVoiceClient->tuningStop(); + } LLVoiceChannel::resume(); } diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 98f9171237..f4d4ea3553 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -47,7 +47,7 @@ #include "llviewercontrol.h" #include "llcommandhandler.h" #include "lldraghandle.h" -#include "llfirstuse.h" +//#include "llfirstuse.h" #include "llfloaterreg.h" // getTypedInstance() #include "llfocusmgr.h" #include "llinventorymodel.h" @@ -100,8 +100,6 @@ enum EPanDirection // Values in pixels per region static const F32 ZOOM_MAX = 128.f; -static const F32 SIM_COORD_DEFAULT = 128.f; - //--------------------------------------------------------------------------- // Globals //--------------------------------------------------------------------------- @@ -118,9 +116,12 @@ public: { if (params.size() == 0) { - return false; + // support the secondlife:///app/worldmap SLapp + LLFloaterReg::showInstance("world_map", "center"); + return true; } + // support the secondlife:///app/worldmap/{LOCATION}/{COORDS} SLapp const std::string region_name = params[0].asString(); S32 x = (params.size() > 1) ? params[1].asInteger() : 128; S32 y = (params.size() > 2) ? params[2].asInteger() : 128; @@ -189,7 +190,8 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key) mInventory(NULL), mInventoryObserver(NULL), mFriendObserver(NULL), - mCompletingRegionName(""), + mCompletingRegionName(), + mCompletingRegionPos(), mWaitingForTracker(FALSE), mIsClosing(FALSE), mSetToUserPosition(TRUE), @@ -205,7 +207,6 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key) mCommitCallbackRegistrar.add("WMap.AvatarCombo", boost::bind(&LLFloaterWorldMap::onAvatarComboCommit, this)); mCommitCallbackRegistrar.add("WMap.Landmark", boost::bind(&LLFloaterWorldMap::onLandmarkComboCommit, this)); mCommitCallbackRegistrar.add("WMap.SearchResult", boost::bind(&LLFloaterWorldMap::onCommitSearchResult, this)); - mCommitCallbackRegistrar.add("WMap.CommitLocation", boost::bind(&LLFloaterWorldMap::onCommitLocation, this)); mCommitCallbackRegistrar.add("WMap.GoHome", boost::bind(&LLFloaterWorldMap::onGoHome, this)); mCommitCallbackRegistrar.add("WMap.Teleport", boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this)); mCommitCallbackRegistrar.add("WMap.ShowTarget", boost::bind(&LLFloaterWorldMap::onShowTargetBtn, this)); @@ -316,7 +317,7 @@ void LLFloaterWorldMap::onOpen(const LLSD& key) adjustZoomSliderBounds(); // Could be first show - LLFirstUse::useMap(); + //LLFirstUse::useMap(); // Start speculative download of landmarks const LLUUID landmark_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); @@ -664,10 +665,6 @@ void LLFloaterWorldMap::updateLocation() S32 agent_y = llround( (F32)fmod( agentPos.mdV[VY], (F64)REGION_WIDTH_METERS ) ); S32 agent_z = llround( (F32)agentPos.mdV[VZ] ); - childSetValue("spin x", LLSD(agent_x) ); - childSetValue("spin y", LLSD(agent_y) ); - childSetValue("spin z", LLSD(agent_z) ); - // Set the current SLURL mSLURL = LLSLURL::buildSLURL(agent_sim_name, agent_x, agent_y, agent_z); } @@ -699,9 +696,6 @@ void LLFloaterWorldMap::updateLocation() F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS ); F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS ); - childSetValue("spin x", LLSD(region_x) ); - childSetValue("spin y", LLSD(region_y) ); - childSetValue("spin z", LLSD((F32)pos_global.mdV[VZ]) ); // simNameFromPosGlobal can fail, so don't give the user an invalid SLURL if ( gotSimName ) @@ -733,9 +727,11 @@ void LLFloaterWorldMap::trackURL(const std::string& region_name, S32 x_coord, S3 { // fill in UI based on URL gFloaterWorldMap->childSetValue("location", region_name); - childSetValue("spin x", LLSD((F32)x_coord)); - childSetValue("spin y", LLSD((F32)y_coord)); - childSetValue("spin z", LLSD((F32)z_coord)); + + // Save local coords to highlight position after region global + // position is returned. + gFloaterWorldMap->mCompletingRegionPos.set( + (F32)x_coord, (F32)y_coord, (F32)z_coord); // pass sim name to combo box gFloaterWorldMap->mCompletingRegionName = region_name; @@ -899,18 +895,6 @@ void LLFloaterWorldMap::clearLocationSelection(BOOL clear_ui) { list->operateOnAll(LLCtrlListInterface::OP_DELETE); } - if (!childHasKeyboardFocus("spin x")) - { - childSetValue("spin x", SIM_COORD_DEFAULT); - } - if (!childHasKeyboardFocus("spin y")) - { - childSetValue("spin y", SIM_COORD_DEFAULT); - } - if (!childHasKeyboardFocus("spin z")) - { - childSetValue("spin z", 0); - } LLWorldMap::getInstance()->cancelTracking(); mCompletingRegionName = ""; } @@ -1466,21 +1450,6 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim) } } -void LLFloaterWorldMap::onCommitLocation() -{ - LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus(); - if ( LLTracker::TRACKING_LOCATION == tracking_status) - { - LLVector3d pos_global = LLTracker::getTrackedPositionGlobal(); - F64 local_x = childGetValue("spin x"); - F64 local_y = childGetValue("spin y"); - F64 local_z = childGetValue("spin z"); - pos_global.mdV[VX] += -fmod(pos_global.mdV[VX], 256.0) + local_x; - pos_global.mdV[VY] += -fmod(pos_global.mdV[VY], 256.0) + local_y; - pos_global.mdV[VZ] = local_z; - trackLocation(pos_global); - } -} void LLFloaterWorldMap::onCommitSearchResult() { @@ -1503,12 +1472,19 @@ void LLFloaterWorldMap::onCommitSearchResult() if (info->isName(sim_name)) { LLVector3d pos_global = info->getGlobalOrigin(); - F64 local_x = childGetValue("spin x"); - F64 local_y = childGetValue("spin y"); - F64 local_z = childGetValue("spin z"); - pos_global.mdV[VX] += local_x; - pos_global.mdV[VY] += local_y; - pos_global.mdV[VZ] = local_z; + + const F64 SIM_COORD_DEFAULT = 128.0; + LLVector3 pos_local(SIM_COORD_DEFAULT, SIM_COORD_DEFAULT, 0.0f); + + // Did this value come from a trackURL() request? + if (!mCompletingRegionPos.isExactlyZero()) + { + pos_local = mCompletingRegionPos; + mCompletingRegionPos.clear(); + } + pos_global.mdV[VX] += (F64)pos_local.mV[VX]; + pos_global.mdV[VY] += (F64)pos_local.mV[VY]; + pos_global.mdV[VZ] = (F64)pos_local.mV[VZ]; childSetValue("location", sim_name); trackLocation(pos_global); diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index 7feebb583d..00f5e788fb 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -148,7 +148,6 @@ protected: void updateSearchEnabled(); void onLocationFocusChanged( LLFocusableElement* ctrl ); void onLocationCommit(); - void onCommitLocation(); void onCommitSearchResult(); void cacheLandmarkPosition(); @@ -170,6 +169,10 @@ private: LLFriendObserver* mFriendObserver; std::string mCompletingRegionName; + // Local position from trackURL() request, used to select final + // position once region lookup complete. + LLVector3 mCompletingRegionPos; + std::string mLastRegionName; BOOL mWaitingForTracker; diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 112b23d2df..a63fb73032 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -78,7 +78,7 @@ ///---------------------------------------------------------------------------- const S32 RENAME_WIDTH_PAD = 4; -const S32 RENAME_HEIGHT_PAD = 2; +const S32 RENAME_HEIGHT_PAD = 1; const S32 AUTO_OPEN_STACK_DEPTH = 16; const S32 MIN_ITEM_WIDTH_VISIBLE = LLFolderViewItem::ICON_WIDTH + LLFolderViewItem::ICON_PAD @@ -206,7 +206,9 @@ LLFolderView::LLFolderView(const Params& p) mAutoOpenCandidate = NULL; mAutoOpenTimer.stop(); mKeyboardSelection = FALSE; - static LLUICachedControl indentation("FolderIndentation", 0); + const LLFolderViewItem::Params& item_params = + LLUICtrlFactory::getDefaultParams(); + S32 indentation = item_params.folder_indentation(); mIndentation = -indentation; // children start at indentation 0 gIdleCallbacks.addFunction(idle, this); @@ -395,7 +397,7 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen getRoot()->getFilter()->getShowFolderState(); S32 total_width = LEFT_PAD; - S32 running_height = mDebugFilters ? llceil(sSmallFont->getLineHeight()) : 0; + S32 running_height = mDebugFilters ? llceil(LLFontGL::getFontMonospace()->getLineHeight()) : 0; S32 target_height = running_height; S32 parent_item_height = getRect().getHeight(); @@ -569,6 +571,8 @@ LLFolderViewItem* LLFolderView::getCurSelectedItem( void ) BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus) { + mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS : SIGNAL_NO_KEYBOARD_FOCUS; + if( selection == this ) { return FALSE; @@ -596,8 +600,6 @@ BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, llassert(mSelectedItems.size() <= 1); - mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS : SIGNAL_NO_KEYBOARD_FOCUS; - return rv; } @@ -820,10 +822,11 @@ void LLFolderView::clearSelection() mSelectThisID.setNull(); } -BOOL LLFolderView::getSelectionList(std::set &selection) +BOOL LLFolderView::getSelectionList(std::set &selection) const { - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); + item_it != mSelectedItems.end(); + ++item_it) { selection.insert((*item_it)->getListener()->getUUID()); } @@ -866,8 +869,8 @@ void LLFolderView::draw() { std::string current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d", mFilter->getCurrentGeneration(), mFilter->getMinRequiredGeneration(), mFilter->getMustPassGeneration()); - sSmallFont->renderUTF8(current_filter_string, 0, 2, - getRect().getHeight() - sSmallFont->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), + LLFontGL::getFontMonospace()->renderUTF8(current_filter_string, 0, 2, + getRect().getHeight() - LLFontGL::getFontMonospace()->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); } @@ -1882,8 +1885,8 @@ void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constr S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); S32 label_height = llround(getLabelFontForStyle(mLabelStyle)->getLineHeight()); - // when navigating with keyboard, only move top of folders on screen, otherwise show whole folder - S32 max_height_to_show = mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); + // when navigating with keyboard, only move top of opened folder on screen, otherwise show whole folder + S32 max_height_to_show = item->isOpen() && mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); // get portion of item that we want to see... LLRect item_local_rect = LLRect(item->getIndentation(), @@ -2218,10 +2221,9 @@ void LLFolderView::updateRenamerPosition() { if(mRenameItem) { - LLFontGL* font = getLabelFontForStyle(mLabelStyle); - - S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD - 1 + mRenameItem->getIndentation(); - S32 y = llfloor(mRenameItem->getRect().getHeight() - font->getLineHeight()-2); + // See also LLFolderViewItem::draw() + S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mRenameItem->getIndentation(); + S32 y = mRenameItem->getRect().getHeight() - mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD; mRenameItem->localPointToScreen( x, y, &x, &y ); screenPointToLocal( x, y, &x, &y ); mRenamer->setOrigin( x, y ); @@ -2233,7 +2235,7 @@ void LLFolderView::updateRenamerPosition() } S32 width = llmax(llmin(mRenameItem->getRect().getWidth() - x, scroller_rect.getWidth() - x - getRect().mLeft), MINIMUM_RENAMER_WIDTH); - S32 height = llfloor(font->getLineHeight() + RENAME_HEIGHT_PAD); + S32 height = mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD; mRenamer->reshape( width, height, TRUE ); } } diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index 2598af4df4..89e1865e35 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -162,7 +162,7 @@ public: virtual S32 extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray& items); - virtual BOOL getSelectionList(std::set &selection); + virtual BOOL getSelectionList(std::set &selection) const; // make sure if ancestor is selected, descendents are not void sanitizeSelection(); diff --git a/indra/newview/llfoldervieweventlistener.h b/indra/newview/llfoldervieweventlistener.h index 473d0be912..d6c4459e6f 100644 --- a/indra/newview/llfoldervieweventlistener.h +++ b/indra/newview/llfoldervieweventlistener.h @@ -62,6 +62,7 @@ public: virtual PermissionMask getPermissionMask() const = 0; virtual LLFolderType::EType getPreferredType() const = 0; virtual LLPointer getIcon() const = 0; + virtual LLPointer getOpenIcon() const { return getIcon(); } virtual LLFontGL::StyleFlags getLabelStyle() const = 0; virtual std::string getLabelSuffix() const = 0; virtual void openItem( void ) = 0; diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index 9d54aafd67..4b48626b22 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -51,11 +51,10 @@ /// Class LLFolderViewItem ///---------------------------------------------------------------------------- +static LLDefaultChildRegistry::Register r("folder_view_item"); + // statics std::map LLFolderViewItem::sFonts; // map of styles to fonts -const LLFontGL* LLFolderViewItem::sSmallFont = NULL; -LLUIImagePtr LLFolderViewItem::sArrowImage; -LLUIImagePtr LLFolderViewItem::sBoxImage; // only integers can be initialized in header const F32 LLFolderViewItem::FOLDER_CLOSE_TIME_CONSTANT = 0.02f; @@ -84,33 +83,34 @@ LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style) //static void LLFolderViewItem::initClass() { - sSmallFont = LLFontGL::getFontMonospace(); - sArrowImage = LLUI::getUIImage("folder_arrow.tga"); - sBoxImage = LLUI::getUIImage("rounded_square.tga"); } //static void LLFolderViewItem::cleanupClass() { sFonts.clear(); - sArrowImage = NULL; - sBoxImage = NULL; } // NOTE: Optimize this, we call it a *lot* when opening a large inventory LLFolderViewItem::Params::Params() -: icon("icon"), - folder_arrow_image("folder_arrow_image", LLUI::getUIImage("folder_arrow.tga")), - selection_image("selection_image", LLUI::getUIImage("rounded_square.tga")) +: icon(), + icon_open(), + root(), + listener(), + folder_arrow_image("folder_arrow_image"), + folder_indentation("folder_indentation"), + selection_image("selection_image"), + item_height("item_height"), + item_top_pad("item_top_pad"), + creation_date() { mouse_opaque(true); follows.flags(FOLLOWS_LEFT|FOLLOWS_TOP|FOLLOWS_RIGHT); - // JAMESDEBUG tab_stop(false); } // Default constructor -LLFolderViewItem::LLFolderViewItem(LLFolderViewItem::Params p) +LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) : LLView(p), mLabelWidth(0), mLabelWidthDirty(false), @@ -121,6 +121,7 @@ LLFolderViewItem::LLFolderViewItem(LLFolderViewItem::Params p) mLabelStyle( LLFontGL::NORMAL ), mHasVisibleChildren(FALSE), mIndentation(0), + mItemHeight(p.item_height), mNumDescendantsSelected(0), mPassedFilter(FALSE), mLastFilterGeneration(-1), @@ -134,8 +135,6 @@ LLFolderViewItem::LLFolderViewItem(LLFolderViewItem::Params p) mIcon(p.icon), mIconOpen(p.icon_open), mListener(p.listener), - mArrowImage(p.folder_arrow_image), - mBoxImage(p.selection_image), mHidden(false), mShowLoadStatus(false) { @@ -392,10 +391,11 @@ BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* roo // makes sure that this view and it's children are the right size. S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation) { - static LLUICachedControl indentation("FolderIndentation", 0); + const Params& p = LLUICtrlFactory::getDefaultParams(); + S32 indentation = p.folder_indentation(); + // Only indent deeper items in hierarchy mIndentation = (getParentFolder() - && getParentFolder()->getParentFolder() - && getParentFolder()->getParentFolder()->getParentFolder()) + && getParentFolder()->getParentFolder() ) ? mParentFolder->getIndentation() + indentation : 0; if (mLabelWidthDirty) @@ -421,9 +421,10 @@ S32 LLFolderViewItem::getItemHeight() { if (mHidden) return 0; - S32 icon_height = mIcon->getHeight(); - S32 label_height = llround(getLabelFontForStyle(mLabelStyle)->getLineHeight()); - return llmax( icon_height, label_height ) + ICON_PAD; + //S32 icon_height = mIcon->getHeight(); + //S32 label_height = llround(getLabelFontForStyle(mLabelStyle)->getLineHeight()); + //return llmax( icon_height, label_height ) + ICON_PAD; + return mItemHeight; } void LLFolderViewItem::filter( LLInventoryFilter& filter) @@ -829,11 +830,16 @@ void LLFolderViewItem::draw() static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); + static LLUIColor sFocusOutlineColor = + LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); static LLUIColor sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); static LLUIColor sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemSuffixColor", DEFAULT_WHITE); static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); + const Params& default_params = LLUICtrlFactory::getDefaultParams(); + const S32 TOP_PAD = default_params.item_top_pad; + bool possibly_has_children = false; bool up_to_date = mListener && mListener->isUpToDate(); if((up_to_date && hasVisibleChildren() ) || // we fetched our children and some of them have passed the filter... @@ -843,13 +849,13 @@ void LLFolderViewItem::draw() } if(/*mControlLabel[0] != '\0' && */possibly_has_children) { - if (sArrowImage) - { - gl_draw_scaled_rotated_image(mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD, - ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, sArrowImage->getImage(), sFgColor); - } + LLUIImage* arrow_image = default_params.folder_arrow_image; + gl_draw_scaled_rotated_image( + mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD - TOP_PAD, + ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, arrow_image->getImage(), sFgColor); } + // See also LLFolderView::updateRenamerPosition() F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation); LLFontGL* font = getLabelFontForStyle(mLabelStyle); @@ -857,6 +863,10 @@ void LLFolderViewItem::draw() // If we have keyboard focus, draw selection filled BOOL show_context = getRoot()->getShowSelectionContext(); BOOL filled = show_context || (getRoot()->getParentPanel()->hasFocus()); + const S32 FOCUS_LEFT = 1; + S32 focus_top = getRect().getHeight(); + S32 focus_bottom = getRect().getHeight() - mItemHeight; + bool folder_open = (getRect().getHeight() > mItemHeight + 4); // always render "current" item, only render other selected items if // mShowSingleSelection is FALSE @@ -864,7 +874,6 @@ void LLFolderViewItem::draw() { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); LLColor4 bg_color = sHighlightBgColor; - //const S32 TRAILING_PAD = 5; // It just looks better with this. if (!mIsCurSelection) { // do time-based fade of extra objects @@ -882,35 +891,35 @@ void LLFolderViewItem::draw() } gl_rect_2d( - 0, - getRect().getHeight(), + FOCUS_LEFT, + focus_top, getRect().getWidth() - 2, - llfloor(getRect().getHeight() - font->getLineHeight() - ICON_PAD), + focus_bottom, bg_color, filled); if (mIsCurSelection) { gl_rect_2d( - 0, - getRect().getHeight(), + FOCUS_LEFT, + focus_top, getRect().getWidth() - 2, - llfloor(getRect().getHeight() - font->getLineHeight() - ICON_PAD), - sHighlightFgColor, FALSE); + focus_bottom, + sFocusOutlineColor, FALSE); } - if (getRect().getHeight() > llround(font->getLineHeight()) + ICON_PAD + 4) + if (folder_open) { gl_rect_2d( - 0, - llfloor(getRect().getHeight() - font->getLineHeight() - ICON_PAD) - 4, + FOCUS_LEFT, + focus_bottom + 1, // overlap with bottom edge of above rect getRect().getWidth() - 2, - 2, - sHighlightFgColor, FALSE); + 0, + sFocusOutlineColor, FALSE); if (show_context) { gl_rect_2d( - 0, - llfloor(getRect().getHeight() - font->getLineHeight() - ICON_PAD) - 4, + FOCUS_LEFT, + focus_bottom + 1, getRect().getWidth() - 2, - 2, + 0, sHighlightBgColor, TRUE); } } @@ -919,32 +928,32 @@ void LLFolderViewItem::draw() { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gl_rect_2d( - 0, - getRect().getHeight(), + FOCUS_LEFT, + focus_top, getRect().getWidth() - 2, - llfloor(getRect().getHeight() - font->getLineHeight() - ICON_PAD), + focus_bottom, sHighlightBgColor, FALSE); - - if (getRect().getHeight() > llround(font->getLineHeight()) + ICON_PAD + 2) + if (folder_open) { gl_rect_2d( - 0, - llfloor(getRect().getHeight() - font->getLineHeight() - ICON_PAD) - 2, + FOCUS_LEFT, + focus_bottom + 1, // overlap with bottom edge of above rect getRect().getWidth() - 2, - 2, + 0, sHighlightBgColor, FALSE); } mDragAndDropTarget = FALSE; } + S32 icon_x = mIndentation + ARROW_SIZE + TEXT_PAD; // First case is used for open folders if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80)) { - mIconOpen->draw(mIndentation + ARROW_SIZE + TEXT_PAD, getRect().getHeight() - mIcon->getHeight()); + mIconOpen->draw(icon_x, getRect().getHeight() - mIconOpen->getHeight() - TOP_PAD + 1); } else if(mIcon) { - mIcon->draw(mIndentation + ARROW_SIZE + TEXT_PAD, getRect().getHeight() - mIcon->getHeight()); + mIcon->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); } if (!mLabel.empty()) @@ -953,7 +962,7 @@ void LLFolderViewItem::draw() BOOL debug_filters = getRoot()->getDebugFilters(); LLColor4 color = ( (mIsSelected && filled) ? sHighlightFgColor : sFgColor ); F32 right_x; - F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD; + F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; if (debug_filters) { @@ -963,7 +972,8 @@ void LLFolderViewItem::draw() } LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? LLColor4(0.5f, 0.8f, 0.5f, 1.f) : LLColor4(0.8f, 0.5f, 0.5f, 1.f); - sSmallFont->renderUTF8(mStatusText, 0, text_left, y, filter_color, + LLFontGL::getFontMonospace()->renderUTF8( + mStatusText, 0, text_left, y, filter_color, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &right_x, FALSE ); text_left = right_x; @@ -1004,7 +1014,7 @@ void LLFolderViewItem::draw() S32_MAX, S32_MAX, &right_x, FALSE ); } - if (sBoxImage.notNull() && mStringMatchOffset != std::string::npos) + if (mStringMatchOffset != std::string::npos) { // don't draw backgrounds for zero-length strings S32 filter_string_length = getRoot()->getFilterSubString().size(); @@ -1013,14 +1023,15 @@ void LLFolderViewItem::draw() std::string combined_string = mLabel + mLabelSuffix; S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1; S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; - S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3); - S32 top = getRect().getHeight(); - + S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); + S32 top = getRect().getHeight() - TOP_PAD; + + LLUIImage* box_image = default_params.selection_image; LLRect box_rect(left, top, right, bottom); - sBoxImage->draw(box_rect, sFilterBGColor); + box_image->draw(box_rect, sFilterBGColor); F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mStringMatchOffset); - F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD; - font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, y, + F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; + font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, filter_string_length, S32_MAX, &right_x, FALSE ); } diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h index 6f8c738a59..be8e73a5a9 100644 --- a/indra/newview/llfolderviewitem.h +++ b/indra/newview/llfolderviewitem.h @@ -101,7 +101,10 @@ public: Optional listener; Optional folder_arrow_image; + Optional folder_indentation; // pixels Optional selection_image; + Optional item_height; // pixels + Optional item_top_pad; // pixels Optional creation_date; //UTC seconds @@ -110,7 +113,7 @@ public: // layout constants static const S32 LEFT_PAD = 5; - // LEFT_INDENTATION is set via settings.xml FolderIndentation + // LEFT_INDENTATION is set via folder_indentation above static const S32 ICON_PAD = 2; static const S32 ICON_WIDTH = 16; static const S32 TEXT_PAD = 1; @@ -127,11 +130,7 @@ protected: friend class LLUICtrlFactory; friend class LLFolderViewEventListener; - LLFolderViewItem(Params p = LLFolderViewItem::Params()); - - static const LLFontGL* sSmallFont; - static LLUIImagePtr sArrowImage; - static LLUIImagePtr sBoxImage; + LLFolderViewItem(const Params& p); std::string mLabel; std::string mSearchableLabel; @@ -150,6 +149,7 @@ protected: LLUIImagePtr mIconOpen; BOOL mHasVisibleChildren; S32 mIndentation; + S32 mItemHeight; S32 mNumDescendantsSelected; BOOL mPassedFilter; S32 mLastFilterGeneration; @@ -157,8 +157,6 @@ protected: F32 mControlLabelRotation; LLFolderView* mRoot; BOOL mDragAndDropTarget; - LLUIImagePtr mArrowImage; - LLUIImagePtr mBoxImage; BOOL mIsLoading; LLTimer mTimeSinceRequestStart; bool mHidden; @@ -237,7 +235,7 @@ public: virtual void recursiveDeselect(BOOL deselect_self); // gets multiple-element selection - virtual BOOL getSelectionList(std::set &selection){return TRUE;} + virtual BOOL getSelectionList(std::set &selection) const {return TRUE;} // Returns true is this object and all of its children can be removed (deleted by user) virtual BOOL isRemovable(); @@ -304,7 +302,7 @@ public: // Show children (unfortunate that this is called "open") virtual void setOpen(BOOL open = TRUE) {}; - virtual BOOL isOpen() { return FALSE; } + virtual BOOL isOpen() const { return FALSE; } virtual LLFolderView* getRoot(); BOOL isDescendantOf( const LLFolderViewFolder* potential_ancestor ); @@ -499,7 +497,7 @@ public: virtual void setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse = RECURSE_NO); // Get the current state of the folder. - virtual BOOL isOpen() { return mIsOpen; } + virtual BOOL isOpen() const { return mIsOpen; } // special case if an object is dropped on the child. BOOL handleDragAndDropFromChild(MASK mask, diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index df7aa9eabf..82293b4aa0 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -417,6 +417,16 @@ BOOL LLGestureManager::isGesturePlaying(const LLUUID& item_id) return gesture->mPlaying; } +BOOL LLGestureManager::isGesturePlaying(LLMultiGesture* gesture) +{ + if(!gesture) + { + return FALSE; + } + + return gesture->mPlaying; +} + void LLGestureManager::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id) { const LLUUID& base_item_id = get_linked_uuid(item_id); diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h index e80eea9ae9..c562587c6f 100644 --- a/indra/newview/llgesturemgr.h +++ b/indra/newview/llgesturemgr.h @@ -103,6 +103,8 @@ public: BOOL isGesturePlaying(const LLUUID& item_id); + BOOL isGesturePlaying(LLMultiGesture* gesture); + const item_map_t& getActiveGestures() const { return mActive; } // Force a gesture to be played, for example, if it is being // previewed. diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index 7dd8ea694e..d6e2bb0445 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -146,6 +146,12 @@ void LLGroupActions::startCall(const LLUUID& group_id) // static void LLGroupActions::join(const LLUUID& group_id) { + if (!gAgent.canJoinGroups()) + { + LLNotificationsUtil::add("JoinedTooManyGroups"); + return; + } + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_id); @@ -226,7 +232,9 @@ void LLGroupActions::activate(const LLUUID& group_id) static bool isGroupUIVisible() { - LLPanel* panel = LLSideTray::getInstance()->findChild("panel_group_info_sidetray"); + static LLPanel* panel = 0; + if(!panel) + panel = LLSideTray::getInstance()->findChild("panel_group_info_sidetray"); if(!panel) return false; return panel->isInVisibleChain(); diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 3ca459a403..e75d35bea4 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -210,7 +210,6 @@ void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LL item->setGroupID(id); item->setName(name, mNameFilter); item->setGroupIconID(icon_id); -// item->setContextMenu(mContextMenu); item->childSetVisible("info_btn", false); item->childSetVisible("profile_btn", false); @@ -268,8 +267,9 @@ bool LLGroupList::onContextMenuItemEnable(const LLSD& userdata) LLUUID selected_group_id = getSelectedUUID(); bool real_group_selected = selected_group_id.notNull(); // a "real" (not "none") group is selected + // each group including "none" can be activated if (userdata.asString() == "activate") - return real_group_selected && gAgent.getGroupID() != selected_group_id; + return gAgent.getGroupID() != selected_group_id; return real_group_selected; } @@ -283,7 +283,6 @@ LLGroupListItem::LLGroupListItem() mGroupIcon(NULL), mGroupNameBox(NULL), mInfoBtn(NULL), -//mContextMenu(NULL), //TODO: mGroupID(LLUUID::null) { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_group_list_item.xml"); diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp index 0b5da40be4..8ad94b957d 100644 --- a/indra/newview/llhudtext.cpp +++ b/indra/newview/llhudtext.cpp @@ -287,7 +287,7 @@ void LLHUDText::renderText(BOOL for_select) mOffsetY = lltrunc(mHeight * ((mVertAlignment == ALIGN_VERT_CENTER) ? 0.5f : 1.f)); // *TODO: cache this image - LLUIImagePtr imagep = LLUI::getUIImage("rounded_square.tga"); + LLUIImagePtr imagep = LLUI::getUIImage("Rounded_Square"); // *TODO: make this a per-text setting LLColor4 bg_color = LLUIColorTable::instance().getColor("BackgroundChatColor"); @@ -606,7 +606,7 @@ void LLHUDText::addLine(const LLWString &wstr, const LLColor4& color, const LLFo U32 line_length = 0; do { - S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), mUseBubble ? HUD_TEXT_MAX_WIDTH : HUD_TEXT_MAX_WIDTH_NO_BUBBLE, wline.length(), TRUE); + S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), mUseBubble ? HUD_TEXT_MAX_WIDTH : HUD_TEXT_MAX_WIDTH_NO_BUBBLE, wline.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); mTextSegments.push_back(LLHUDTextSegment(iter->substr(line_length, segment_length), style, color)); line_length += segment_length; } @@ -642,7 +642,7 @@ void LLHUDText::setLabel(const LLWString &wlabel) U32 line_length = 0; do { - S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), mUseBubble ? HUD_TEXT_MAX_WIDTH : HUD_TEXT_MAX_WIDTH_NO_BUBBLE, wstr.length(), TRUE); + S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), mUseBubble ? HUD_TEXT_MAX_WIDTH : HUD_TEXT_MAX_WIDTH_NO_BUBBLE, wstr.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); mLabelSegments.push_back(LLHUDTextSegment(iter->substr(line_length, segment_length), LLFontGL::NORMAL, mColor)); line_length += segment_length; } diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 259f629bdd..e06e0c94ec 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -42,7 +42,6 @@ #include "llbottomtray.h" #include "llchannelmanager.h" #include "llchiclet.h" -#include "llfloaterchat.h" #include "llfloaterreg.h" #include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container #include "lllayoutstack.h" @@ -59,6 +58,7 @@ #include "llinventorymodel.h" #include "llrootview.h" +#include "llspeakers.h" LLIMFloater::LLIMFloater(const LLUUID& session_id) @@ -115,11 +115,21 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) void LLIMFloater::onFocusLost() { LLIMModel::getInstance()->resetActiveSessionID(); + + LLBottomTray::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, false); } void LLIMFloater::onFocusReceived() { LLIMModel::getInstance()->setActiveSessionID(mSessionID); + + // return focus to the input field when active tab in the multitab container is clicked. + if (isChatMultiTab() && mInputEditor) + { + mInputEditor->setFocus(TRUE); + } + + LLBottomTray::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, true); } // virtual @@ -341,13 +351,15 @@ void* LLIMFloater::createPanelAdHocControl(void* userdata) void LLIMFloater::onSlide() { - LLPanel* im_control_panel = getChild("panel_im_control_panel"); - im_control_panel->setVisible(!im_control_panel->getVisible()); + mControlPanel->setVisible(!mControlPanel->getVisible()); + + gSavedSettings.setBOOL("IMShowControlPanel", mControlPanel->getVisible()); - gSavedSettings.setBOOL("IMShowControlPanel", im_control_panel->getVisible()); + getChild("slide_left_btn")->setVisible(mControlPanel->getVisible()); + getChild("slide_right_btn")->setVisible(!mControlPanel->getVisible()); - getChild("slide_left_btn")->setVisible(im_control_panel->getVisible()); - getChild("slide_right_btn")->setVisible(!im_control_panel->getVisible()); + LLLayoutStack* stack = getChild("im_panels"); + if (stack) stack->setAnimate(true); } //static @@ -355,35 +367,7 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) { if (!gIMMgr->hasSession(session_id)) return NULL; - // we should make sure all related chiclets are in place when the session is a voice call - // chiclets come firts, then comes IM window - if (gIMMgr->isVoiceCall(session_id)) - { - LLIMModel* im_model = LLIMModel::getInstance(); - LLBottomTray* b_tray = LLBottomTray::getInstance(); - - //*TODO hide that into Bottom tray - if (!b_tray->getChicletPanel()->findChiclet(session_id)) - { - LLIMChiclet* chiclet = b_tray->createIMChiclet(session_id); - if(chiclet) - { - chiclet->setIMSessionName(im_model->getName(session_id)); - chiclet->setOtherParticipantId(im_model->getOtherParticipantID(session_id)); - } - } - - LLIMWellWindow::getInstance()->addIMRow(session_id); - } - - bool not_existed = true; - - if(isChatMultiTab()) - { - LLIMFloater* target_floater = findInstance(session_id); - not_existed = NULL == target_floater; - } - else + if(!isChatMultiTab()) { //hide all LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); @@ -398,19 +382,33 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) } } - LLIMFloater* floater = LLFloaterReg::showTypedInstance("impanel", session_id); + bool exist = findInstance(session_id); + + LLIMFloater* floater = getInstance(session_id); + if (!floater) return NULL; if(isChatMultiTab()) { + LLIMFloaterContainer* floater_container = LLIMFloaterContainer::getInstance(); + // do not add existed floaters to avoid adding torn off instances - if (not_existed) + if (!exist) { // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END; + + if (floater_container) + { + floater_container->addFloater(floater, TRUE, i_pt); + } + } - LLIMFloaterContainer* floater_container = LLFloaterReg::showTypedInstance("im_container"); - floater_container->addFloater(floater, TRUE, i_pt); + if (floater_container) + { + //selecting the panel resets a chiclet's counter + floater_container->selectFloater(floater); + floater_container->setVisible(TRUE); } } else @@ -437,8 +435,8 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) } // window is positioned, now we can show it. - floater->setVisible(true); } + floater->setVisible(TRUE); return floater; } @@ -478,16 +476,6 @@ void LLIMFloater::setDocked(bool docked, bool pop_on_undock) } } -void LLIMFloater::setTornOff(bool torn_off) -{ - // When IM Floater isn't torn off, "close" button should be hidden. - // This call will just disables it, since there is a hack in LLFloater::updateButton, - // which prevents hiding of close button in that case. - setCanClose(torn_off); - - LLTransientDockableFloater::setTornOff(torn_off); -} - void LLIMFloater::setVisible(BOOL visible) { LLNotificationsUI::LLScreenChannel* channel = dynamic_cast @@ -508,6 +496,15 @@ void LLIMFloater::setVisible(BOOL visible) updateMessages(); mInputEditor->setFocus(TRUE); } + + if(!visible) + { + LLIMChiclet* chiclet = LLBottomTray::getInstance()->getChicletPanel()->findChiclet(mSessionID); + if(chiclet) + { + chiclet->setToggleState(false); + } + } } //static @@ -542,6 +539,11 @@ LLIMFloater* LLIMFloater::findInstance(const LLUUID& session_id) return LLFloaterReg::findTypedInstance("impanel", session_id); } +LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) +{ + return LLFloaterReg::getTypedInstance("impanel", session_id); +} + void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) { mSessionInitialized = true; @@ -589,7 +591,7 @@ void LLIMFloater::updateMessages() std::string time = msg["time"].asString(); LLUUID from_id = msg["from_id"].asUUID(); - std::string from = from_id != gAgentID ? msg["from"].asString() : LLTrans::getString("You"); + std::string from = msg["from"].asString(); std::string message = msg["message"].asString(); LLChat chat; @@ -618,6 +620,15 @@ void LLIMFloater::onInputEditorFocusReceived( LLFocusableElement* caller, void* //in disconnected state IM input editor should be disabled self->mInputEditor->setEnabled(!gDisconnected); } + + // when IM Floater is a part of the multitab container LLTabContainer set focus to the first + // child on tab button's mouse up. This leads input field lost focus. See EXT-3852. + if (isChatMultiTab()) + { + // So, clear control captured mouse to prevent LLTabContainer set focus on the panel's first child. + // do not pass self->mInputEditor, this leads to have "Edit Text" mouse pointer wherever it is. + gFocusMgr.setMouseCapture(NULL); + } } // static @@ -1011,3 +1022,20 @@ void LLIMFloater::sRemoveTypingIndicator(const LLSD& data) floater->removeTypingIndicator(); } + +void LLIMFloater::onIMChicletCreated( const LLUUID& session_id ) +{ + + if (isChatMultiTab()) + { + LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + if (!im_box) return; + + if (LLIMFloater::findInstance(session_id)) return; + + LLIMFloater* new_tab = LLIMFloater::getInstance(session_id); + + im_box->addFloater(new_tab, FALSE, LLTabContainer::END); + } + +} diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index bc7a43e852..d9db385d06 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -64,7 +64,6 @@ public: // LLFloater overrides /*virtual*/ void onClose(bool app_quitting); /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); - /*virtual*/ void setTornOff(bool torn_off); // Make IM conversion visible and update the message history static LLIMFloater* show(const LLUUID& session_id); @@ -75,6 +74,8 @@ public: static LLIMFloater* findInstance(const LLUUID& session_id); + static LLIMFloater* getInstance(const LLUUID& session_id); + void sessionInitReplyReceived(const LLUUID& im_session_id); // get new messages from LLIMModel @@ -113,6 +114,8 @@ public: //used as a callback on receiving new IM message static void sRemoveTypingIndicator(const LLSD& data); + static void onIMChicletCreated(const LLUUID& session_id); + private: // process focus events to set a currently active session /* virtual */ void onFocusLost(); diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index 2d7333f7e4..6cc985aef4 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -34,6 +34,7 @@ #include "llviewerprecompiledheaders.h" #include "llimfloatercontainer.h" +#include "llfloaterreg.h" // // LLIMFloaterContainer @@ -93,4 +94,14 @@ void LLIMFloaterContainer::addFloater(LLFloater* floaterp, } } +LLIMFloaterContainer* LLIMFloaterContainer::findInstance() +{ + return LLFloaterReg::findTypedInstance("im_container"); +} + +LLIMFloaterContainer* LLIMFloaterContainer::getInstance() +{ + return LLFloaterReg::getTypedInstance("im_container"); +} + // EOF diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h index ead7cf4730..d4a542dfc2 100644 --- a/indra/newview/llimfloatercontainer.h +++ b/indra/newview/llimfloatercontainer.h @@ -52,7 +52,11 @@ public: LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); static LLFloater* getCurrentVoiceFloater(); - + + static LLIMFloaterContainer* findInstance(); + + static LLIMFloaterContainer* getInstance(); + protected: LLFloater* mActiveVoiceFloater; diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 029ddbaf2c..4bdf5f42dc 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -40,6 +40,7 @@ #include "llfontgl.h" #include "llrect.h" #include "llerror.h" +#include "llmultifloater.h" #include "llstring.h" #include "message.h" #include "lltextbox.h" @@ -59,7 +60,6 @@ #include "llinventory.h" #include "llinventorymodel.h" #include "llfloaterinventory.h" -#include "llfloaterchat.h" #include "lliconctrl.h" #include "llkeyboard.h" #include "lllineeditor.h" diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 40227539d0..ff20a55358 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -42,46 +42,30 @@ #include "llhttpclient.h" #include "llsdutil_math.h" #include "llstring.h" +#include "lltrans.h" #include "lluictrlfactory.h" #include "llagent.h" +#include "llagentui.h" +#include "llappviewer.h" #include "llavatariconctrl.h" #include "llbottomtray.h" #include "llcallingcard.h" #include "llchat.h" -#include "llchiclet.h" -#include "llresmgr.h" -#include "llfloaterchat.h" #include "llfloaterchatterbox.h" -#include "llavataractions.h" -#include "llhttpnode.h" #include "llimfloater.h" -#include "llimpanel.h" #include "llgroupiconctrl.h" -#include "llresizebar.h" -#include "lltabcontainer.h" -#include "llviewercontrol.h" -#include "llfloater.h" #include "llmutelist.h" -#include "llresizehandle.h" -#include "llkeyboard.h" -#include "llui.h" -#include "llviewermenu.h" -#include "llcallingcard.h" -#include "lltoolbar.h" +#include "llrecentpeople.h" #include "llviewermessage.h" #include "llviewerwindow.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llnearbychat.h" -#include "llviewerregion.h" -#include "llvoicechannel.h" -#include "lltrans.h" -#include "llrecentpeople.h" -#include "llsyswellwindow.h" +#include "llspeakers.h" //for LLIMSpeakerMgr +#include "lltextutil.h" +#include "llviewercontrol.h" -#include "llfirstuse.h" -#include "llagentui.h" const static std::string IM_TIME("time"); const static std::string IM_TEXT("message"); @@ -92,6 +76,7 @@ const static std::string NO_SESSION("(IM Session Doesn't Exist)"); const static std::string ADHOC_NAME_SUFFIX(" Conference"); std::string LLCallDialogManager::sPreviousSessionlName = ""; +LLIMModel::LLIMSession::SType LLCallDialogManager::sPreviousSessionType = LLIMModel::LLIMSession::P2P_SESSION; std::string LLCallDialogManager::sCurrentSessionlName = ""; LLIMModel::LLIMSession* LLCallDialogManager::sSession = NULL; LLVoiceChannel::EState LLCallDialogManager::sOldState = LLVoiceChannel::STATE_READY; @@ -178,6 +163,7 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& if (IM_NOTHING_SPECIAL == type || IM_SESSION_P2P_INVITE == type) { mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id); + mOtherParticipantIsAvatar = LLVoiceClient::getInstance()->isParticipantAvatar(mSessionID); // check if it was AVALINE call if (!mOtherParticipantIsAvatar) @@ -208,7 +194,10 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& mSpeakers = new LLIMSpeakerMgr(mVoiceChannel); // All participants will be added to the list of people we've recently interacted with. - mSpeakers->addListener(&LLRecentPeople::instance(), "add"); + + // we need to add only _active_ speakers...so comment this. + // may delete this later on cleanup + //mSpeakers->addListener(&LLRecentPeople::instance(), "add"); //we need to wait for session initialization for outgoing ad-hoc and group chat session //correct session id for initiated ad-hoc chat will be received from the server @@ -224,7 +213,6 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& { mCallBackEnabled = LLVoiceClient::getInstance()->isSessionCallBackPossible(mSessionID); mTextIMPossible = LLVoiceClient::getInstance()->isSessionTextIMPossible(mSessionID); - mOtherParticipantIsAvatar = LLVoiceClient::getInstance()->isParticipantAvatar(mSessionID); } if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) @@ -675,6 +663,12 @@ bool LLIMModel::addMessage(const LLUUID& session_id, const std::string& from, co LLIMSession* session = addMessageSilently(session_id, from, from_id, utf8_text, log2file); if (!session) return false; + //good place to add some1 to recent list + //other places may be called from message history. + if( !from_id.isNull() && + ( session->isP2PSessionType() || session->isAdHocSessionType() ) ) + LLRecentPeople::instance().add(from_id); + // notify listeners LLSD arg; arg["session_id"] = session_id; @@ -781,7 +775,7 @@ LLIMSpeakerMgr* LLIMModel::getSpeakerManager( const LLUUID& session_id ) const LLIMSession* session = findIMSession(session_id); if (!session) { - llwarns << "session " << session_id << "does not exist " << llendl; + llwarns << "session " << session_id << " does not exist " << llendl; return NULL; } @@ -1360,8 +1354,15 @@ void LLCallDialogManager::onVoiceChannelChanged(const LLUUID &session_id) sCurrentSessionlName = ""; // Empty string results in "Nearby Voice Chat" after substitution return; } + + if (sSession) + { + // store previous session type to process Avaline calls in dialogs + sPreviousSessionType = sSession->mSessionType; + } + sSession = session; - sSession->mVoiceChannel->setStateChangedCallback(LLCallDialogManager::onVoiceChannelStateChanged); + sSession->mVoiceChannel->setStateChangedCallback(boost::bind(LLCallDialogManager::onVoiceChannelStateChanged, _1, _2, _3)); if(sCurrentSessionlName != session->mName) { sPreviousSessionlName = sCurrentSessionlName; @@ -1378,6 +1379,7 @@ void LLCallDialogManager::onVoiceChannelChanged(const LLUUID &session_id) mCallDialogPayload["session_name"] = sSession->mName; mCallDialogPayload["other_user_id"] = sSession->mOtherParticipantID; mCallDialogPayload["old_channel_name"] = sPreviousSessionlName; + mCallDialogPayload["old_session_type"] = sPreviousSessionType; mCallDialogPayload["state"] = LLVoiceChannel::STATE_CALL_STARTED; mCallDialogPayload["disconnected_channel_name"] = sSession->mName; mCallDialogPayload["session_type"] = sSession->mSessionType; @@ -1407,6 +1409,7 @@ void LLCallDialogManager::onVoiceChannelStateChanged(const LLVoiceChannel::EStat mCallDialogPayload["session_name"] = sSession->mName; mCallDialogPayload["other_user_id"] = sSession->mOtherParticipantID; mCallDialogPayload["old_channel_name"] = sPreviousSessionlName; + mCallDialogPayload["old_session_type"] = sPreviousSessionType; mCallDialogPayload["state"] = new_state; mCallDialogPayload["disconnected_channel_name"] = sSession->mName; mCallDialogPayload["session_type"] = sSession->mSessionType; @@ -1421,6 +1424,11 @@ void LLCallDialogManager::onVoiceChannelStateChanged(const LLVoiceChannel::EStat } break; + case LLVoiceChannel::STATE_HUNG_UP: + // this state is coming before session is changed, so, put it into payload map + mCallDialogPayload["old_session_type"] = sSession->mSessionType; + break; + case LLVoiceChannel::STATE_CONNECTED : ocd = LLFloaterReg::findTypedInstance("outgoing_call", LLOutgoingCallDialog::OCD_KEY); if (ocd) @@ -1561,7 +1569,15 @@ void LLOutgoingCallDialog::show(const LLSD& key) // tell the user which voice channel they are leaving if (!mPayload["old_channel_name"].asString().empty()) { - childSetTextArg("leaving", "[CURRENT_CHAT]", mPayload["old_channel_name"].asString()); + bool was_avaline_call = LLIMModel::LLIMSession::AVALINE_SESSION == mPayload["old_session_type"].asInteger(); + + std::string old_caller_name = mPayload["old_channel_name"].asString(); + if (was_avaline_call) + { + old_caller_name = LLTextUtil::formatPhoneNumber(old_caller_name); + } + + childSetTextArg("leaving", "[CURRENT_CHAT]", old_caller_name); } else { @@ -1570,15 +1586,28 @@ void LLOutgoingCallDialog::show(const LLSD& key) if (!mPayload["disconnected_channel_name"].asString().empty()) { - childSetTextArg("nearby", "[VOICE_CHANNEL_NAME]", mPayload["disconnected_channel_name"].asString()); + std::string channel_name = mPayload["disconnected_channel_name"].asString(); + if (LLIMModel::LLIMSession::AVALINE_SESSION == mPayload["session_type"].asInteger()) + { + channel_name = LLTextUtil::formatPhoneNumber(channel_name); + } + childSetTextArg("nearby", "[VOICE_CHANNEL_NAME]", channel_name); childSetTextArg("nearby_P2P", "[VOICE_CHANNEL_NAME]", mPayload["disconnected_channel_name"].asString()); } std::string callee_name = mPayload["session_name"].asString(); + + LLUUID session_id = mPayload["session_id"].asUUID(); + bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); + if (callee_name == "anonymous") { callee_name = getString("anonymous"); } + else if (!is_avatar) + { + callee_name = LLTextUtil::formatPhoneNumber(callee_name); + } setTitle(callee_name); @@ -1728,16 +1757,21 @@ BOOL LLIncomingCallDialog::postBuild() call_type = getString(mPayload["notify_box_type"]); } + + // check to see if this is an Avaline call + bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); + childSetVisible("Start IM", is_avatar); // no IM for avaline + if (caller_name == "anonymous") { caller_name = getString("anonymous"); } - - setTitle(caller_name + " " + call_type); + else if (!is_avatar) + { + caller_name = LLTextUtil::formatPhoneNumber(caller_name); + } - // check to see if this is an Avaline call - bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); - childSetVisible("Start IM", is_avatar); // no IM for avaline + setTitle(caller_name + " " + call_type); LLUICtrl* caller_name_widget = getChild("caller name"); caller_name_widget->setValue(caller_name + " " + call_type); @@ -1805,7 +1839,7 @@ void LLIncomingCallDialog::onStartIM(void* user_data) void LLIncomingCallDialog::processCallResponse(S32 response) { - if (!gIMMgr) + if (!gIMMgr || gDisconnected) return; LLUUID session_id = mPayload["session_id"].asUUID(); @@ -2185,7 +2219,6 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess LLChat chat(message); chat.mSourceType = CHAT_SOURCE_SYSTEM; - LLFloaterChat::addChatHistory(chat); LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat", LLSD()); if(nearby_chat) @@ -2353,7 +2386,9 @@ LLUUID LLIMMgr::addSession( //we don't need to show notes about online/offline, mute/unmute users' statuses for existing sessions if (!new_session) return session_id; - noteOfflineUsers(session_id, floater, ids); + //Per Plan's suggestion commented "explicit offline status warning" out to make Dessie happier (see EXT-3609) + //*TODO After February 2010 remove this commented out line if no one will be missing that warning + //noteOfflineUsers(session_id, floater, ids); // Only warn for regular IMs - not group IMs if( dialog == IM_NOTHING_SPECIAL ) @@ -3085,9 +3120,6 @@ public: ll_vector3_from_sd(message_params["position"]), true); - chat.mText = std::string("IM: ") + name + separator_string + saved + message; - LLFloaterChat::addChat(chat, TRUE, is_this_agent); - //K now we want to accept the invitation std::string url = gAgent.getRegion()->getCapability( "ChatSessionRequest"); diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 11860d0efb..a226d66b12 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -33,22 +33,19 @@ #ifndef LL_LLIMVIEW_H #define LL_LLIMVIEW_H -#include "lldarray.h" -#include "lldockablefloater.h" -#include "llspeakers.h" //for LLIMSpeakerMgr -#include "llimpanel.h" //for voice channels -#include "llmodaldialog.h" #include "lldockablefloater.h" #include "llinstantmessage.h" -#include "lluuid.h" -#include "llmultifloater.h" + #include "lllogchat.h" +#include "llvoicechannel.h" class LLFloaterChatterBox; class LLUUID; class LLFloaterIMPanel; class LLFriendObserver; class LLCallDialogManager; +class LLIMSpeakerMgr; + class LLIMModel : public LLSingleton { @@ -78,6 +75,11 @@ public: bool isP2P(); bool isOtherParticipantAvaline(); + bool isP2PSessionType() const { return mSessionType == P2P_SESSION;} + bool isAdHocSessionType() const { return mSessionType == ADHOC_SESSION;} + bool isGroupSessionType() const { return mSessionType == GROUP_SESSION;} + bool isAvalineSessionType() const { return mSessionType == AVALINE_SESSION;} + LLUUID mSessionID; std::string mName; EInstantMessage mType; @@ -475,6 +477,7 @@ public: protected: static std::string sPreviousSessionlName; + static LLIMModel::LLIMSession::SType sPreviousSessionType; static std::string sCurrentSessionlName; static LLIMModel::LLIMSession* sSession; static LLVoiceChannel::EState sOldState; diff --git a/indra/newview/llinspect.h b/indra/newview/llinspect.h index 731e99534b..a1cb9cd71c 100644 --- a/indra/newview/llinspect.h +++ b/indra/newview/llinspect.h @@ -55,7 +55,7 @@ public: /// Inspectors close themselves when they lose focus /*virtual*/ void onFocusLost(); -private: +protected: LLFrameTimer mCloseTimer; LLFrameTimer mOpenTimer; }; diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index dae980feb1..0374a1d25b 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -93,6 +93,10 @@ public: // Update view based on information from avatar properties processor void processAvatarData(LLAvatarData* data); + // override the inspector mouse leave so timer is only paused if + // gear menu is not open + /* virtual */ void onMouseLeave(S32 x, S32 y, MASK mask); + private: // Make network requests for all the data to display in this view. // Used on construction and if avatar id changes. @@ -112,6 +116,7 @@ private: void onClickAddFriend(); void onClickViewProfile(); void onClickIM(); + void onClickCall(); void onClickTeleport(); void onClickInviteToGroup(); void onClickPay(); @@ -204,6 +209,7 @@ LLInspectAvatar::LLInspectAvatar(const LLSD& sd) mCommitCallbackRegistrar.add("InspectAvatar.AddFriend", boost::bind(&LLInspectAvatar::onClickAddFriend, this)); mCommitCallbackRegistrar.add("InspectAvatar.IM", boost::bind(&LLInspectAvatar::onClickIM, this)); + mCommitCallbackRegistrar.add("InspectAvatar.Call", boost::bind(&LLInspectAvatar::onClickCall, this)); mCommitCallbackRegistrar.add("InspectAvatar.Teleport", boost::bind(&LLInspectAvatar::onClickTeleport, this)); mCommitCallbackRegistrar.add("InspectAvatar.InviteToGroup", boost::bind(&LLInspectAvatar::onClickInviteToGroup, this)); mCommitCallbackRegistrar.add("InspectAvatar.Pay", boost::bind(&LLInspectAvatar::onClickPay, this)); @@ -257,8 +263,6 @@ BOOL LLInspectAvatar::postBuild(void) } - - // Multiple calls to showInstance("inspect_avatar", foo) will provide different // LLSD for foo, which we will catch here. //virtual @@ -274,7 +278,7 @@ void LLInspectAvatar::onOpen(const LLSD& data) getChild("gear_self_btn")->setVisible(self); getChild("gear_btn")->setVisible(!self); - + // Position the inspector relative to the mouse cursor // Similar to how tooltips are positioned // See LLToolTipMgr::createToolTip @@ -382,11 +386,25 @@ void LLInspectAvatar::processAvatarData(LLAvatarData* data) mPropertiesRequest = NULL; } +// For the avatar inspector, we only want to unpause the fade timer +// if neither the gear menu or self gear menu are open +void LLInspectAvatar::onMouseLeave(S32 x, S32 y, MASK mask) +{ + LLMenuGL* gear_menu = getChild("gear_btn")->getMenu(); + LLMenuGL* gear_menu_self = getChild("gear_self_btn")->getMenu(); + if ( !(gear_menu && gear_menu->getVisible()) && + !(gear_menu_self && gear_menu_self->getVisible())) + { + mOpenTimer.unpause(); + } +} + void LLInspectAvatar::updateModeratorPanel() { bool enable_moderator_panel = false; - if (LLVoiceChannel::getCurrentVoiceChannel()) + if (LLVoiceChannel::getCurrentVoiceChannel() && + mAvatarID != gAgent.getID()) { LLUUID session_id = LLVoiceChannel::getCurrentVoiceChannel()->getSessionID(); @@ -400,6 +418,7 @@ void LLInspectAvatar::updateModeratorPanel() LLPointer selected_speakerp = speaker_mgr->findSpeaker(mAvatarID); if(speaker_mgr->isVoiceActive() && selected_speakerp && + selected_speakerp->isInVoiceChannel() && ((self_speakerp && self_speakerp->mIsModerator) || gAgent.isGodlike())) { getChild("enable_voice")->setVisible(selected_speakerp->mModeratorMutedVoice); @@ -497,42 +516,58 @@ void LLInspectAvatar::toggleSelectedVoice(bool enabled) void LLInspectAvatar::updateVolumeSlider() { - // By convention, we only display and toggle voice mutes, not all mutes - bool is_muted = LLMuteList::getInstance()-> - isMuted(mAvatarID, LLMute::flagVoiceChat); - bool voice_enabled = gVoiceClient->getVoiceEnabled(mAvatarID); - LLUICtrl* mute_btn = getChild("mute_btn"); - mute_btn->setEnabled( voice_enabled ); - mute_btn->setValue( is_muted ); + bool voice_enabled = gVoiceClient->getVoiceEnabled(mAvatarID); - LLUICtrl* volume_slider = getChild("volume_slider"); - volume_slider->setEnabled( voice_enabled && !is_muted ); - const F32 DEFAULT_VOLUME = 0.5f; - F32 volume; - if (is_muted) + // Do not display volume slider and mute button if it + // is ourself or we are not in a voice channel together + if (!voice_enabled || (mAvatarID == gAgent.getID())) { - // it's clearer to display their volume as zero - volume = 0.f; + getChild("mute_btn")->setVisible(false); + getChild("volume_slider")->setVisible(false); } - else if (!voice_enabled) - { - // use nominal value rather than 0 - volume = DEFAULT_VOLUME; - } - else + + else { - // actual volume - volume = gVoiceClient->getUserVolume(mAvatarID); + getChild("mute_btn")->setVisible(true); + getChild("volume_slider")->setVisible(true); - // *HACK: Voice client doesn't have any data until user actually - // says something. - if (volume == 0.f) + // By convention, we only display and toggle voice mutes, not all mutes + bool is_muted = LLMuteList::getInstance()-> + isMuted(mAvatarID, LLMute::flagVoiceChat); + + LLUICtrl* mute_btn = getChild("mute_btn"); + + bool is_linden = LLStringUtil::endsWith(mAvatarName, " Linden"); + + mute_btn->setEnabled( !is_linden); + mute_btn->setValue( is_muted ); + + LLUICtrl* volume_slider = getChild("volume_slider"); + volume_slider->setEnabled( !is_muted ); + + const F32 DEFAULT_VOLUME = 0.5f; + F32 volume; + if (is_muted) { - volume = DEFAULT_VOLUME; + // it's clearer to display their volume as zero + volume = 0.f; } + else + { + // actual volume + volume = gVoiceClient->getUserVolume(mAvatarID); + + // *HACK: Voice client doesn't have any data until user actually + // says something. + if (volume == 0.f) + { + volume = DEFAULT_VOLUME; + } + } + volume_slider->setValue( (F64)volume ); } - volume_slider->setValue( (F64)volume ); + } void LLInspectAvatar::onClickMuteVolume() @@ -611,6 +646,12 @@ void LLInspectAvatar::onClickIM() closeFloater(); } +void LLInspectAvatar::onClickCall() +{ + LLAvatarActions::startCall(mAvatarID); + closeFloater(); +} + void LLInspectAvatar::onClickTeleport() { LLAvatarActions::offerTeleport(mAvatarID); diff --git a/indra/newview/llinspectobject.cpp b/indra/newview/llinspectobject.cpp index cb35a287e9..dd313c528d 100644 --- a/indra/newview/llinspectobject.cpp +++ b/indra/newview/llinspectobject.cpp @@ -41,6 +41,7 @@ #include "llslurl.h" #include "llviewermenu.h" // handle_object_touch(), handle_buy() #include "llviewermedia.h" +#include "llviewermediafocus.h" #include "llviewerobjectlist.h" // to select the requested object // Linden libraries @@ -82,6 +83,10 @@ public: // Release the selection and do other cleanup /*virtual*/ void onClose(bool app_quitting); + // override the inspector mouse leave so timer is only paused if + // gear menu is not open + /* virtual */ void onMouseLeave(S32 x, S32 y, MASK mask); + private: // Refresh displayed data with information from selection manager void update(); @@ -181,7 +186,6 @@ BOOL LLInspectObject::postBuild(void) return TRUE; } - // Multiple calls to showInstance("inspect_avatar", foo) will provide different // LLSD for foo, which we will catch here. //virtual @@ -214,6 +218,10 @@ void LLInspectObject::onOpen(const LLSD& data) LLViewerObject* obj = gObjectList.findObject( mObjectID ); if (obj) { + // Media focus and this code fight over the select manager. + // Make sure any media is unfocused before changing the selection here. + LLViewerMediaFocus::getInstance()->clearFocus(); + LLSelectMgr::instance().deselectAll(); mObjectSelection = LLSelectMgr::instance().selectObjectAndFamily(obj); @@ -562,6 +570,16 @@ void LLInspectObject::updateSecureBrowsing() getChild("secure_browsing")->setVisible(is_secure_browsing); } +// For the object inspector, only unpause the fade timer +// if the gear menu is not open +void LLInspectObject::onMouseLeave(S32 x, S32 y, MASK mask) +{ + LLMenuGL* gear_menu = getChild("gear_btn")->getMenu(); + if ( !(gear_menu && gear_menu->getVisible())) + { + mOpenTimer.unpause(); + } +} void LLInspectObject::onClickBuy() { diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index d70221b22a..099f863dc9 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -2074,7 +2074,12 @@ void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model { if ("open" == action) { - openItem(); + LLFolderViewFolder *f = dynamic_cast(folder->getItemByID(mUUID)); + if (f) + { + f->setOpen(TRUE); + } + return; } else if ("paste" == action) @@ -2228,9 +2233,22 @@ LLUIImagePtr LLFolderBridge::getIcon() const LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type) { // we only have one folder image now + if (preferred_type == LLFolderType::FT_OUTFIT) + { + return LLUI::getUIImage("Inv_LookFolderClosed"); + } return LLUI::getUIImage("Inv_FolderClosed"); } +LLUIImagePtr LLFolderBridge::getOpenIcon() const +{ + if (getPreferredType() == LLFolderType::FT_OUTFIT) + { + return LLUI::getUIImage("Inv_LookFolderOpen"); + } + return LLUI::getUIImage("Inv_FolderOpen"); +} + BOOL LLFolderBridge::renameItem(const std::string& new_name) { if(!isItemRenameable()) @@ -2430,7 +2448,7 @@ void LLFolderBridge::folderOptionsMenu() const LLInventoryCategory* category = model->getCategory(mUUID); LLFolderType::EType type = category->getPreferredType(); - const bool is_default_folder = category && LLFolderType::lookupIsProtectedType(type); + const bool is_system_folder = category && LLFolderType::lookupIsProtectedType(type); // BAP change once we're no longer treating regular categories as ensembles. const bool is_ensemble = category && (type == LLFolderType::FT_NONE || LLFolderType::lookupIsEnsembleType(type)); @@ -2444,8 +2462,8 @@ void LLFolderBridge::folderOptionsMenu() mItems.push_back("Delete"); } - // Only enable calling-card related options for non-default folders. - if (!is_sidepanel && !is_default_folder) + // Only enable calling-card related options for non-system folders. + if (!is_sidepanel && !is_system_folder) { LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) @@ -2479,10 +2497,14 @@ void LLFolderBridge::folderOptionsMenu() mItems.push_back(std::string("Folder Wearables Separator")); } - // Only enable add/replace outfit for non-default folders. - if (!is_default_folder) + // Only enable add/replace outfit for non-system folders. + if (!is_system_folder) { - mItems.push_back(std::string("Add To Outfit")); + // Adding an outfit onto another (versus replacing) doesn't make sense. + if (type != LLFolderType::FT_OUTFIT) + { + mItems.push_back(std::string("Add To Outfit")); + } mItems.push_back(std::string("Replace Outfit")); } if (is_ensemble) @@ -2614,6 +2636,13 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { mItems.push_back(std::string("Rename")); mItems.push_back(std::string("Delete")); + + // EXT-4030: disallow deletion of currently worn outfit + const LLViewerInventoryItem *base_outfit_link = LLAppearanceManager::instance().getBaseOutfitLink(); + if (base_outfit_link && (cat == base_outfit_link->getLinkedCategory())) + { + mDisabledItems.push_back(std::string("Delete")); + } } } @@ -2902,80 +2931,6 @@ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response return false; } -/* -Next functions intended to reorder items in the inventory folder and save order on server -Is now used for Favorites folder. - -*TODO: refactoring is needed with Favorites Bar functionality. Probably should be moved in LLInventoryModel -*/ -void saveItemsOrder(LLInventoryModel::item_array_t& items) -{ - int sortField = 0; - - // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field - for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) - { - LLViewerInventoryItem* item = *i; - - item->setSortField(++sortField); - item->setComplete(TRUE); - item->updateServer(FALSE); - - gInventory.updateItem(item); - - // Tell the parent folder to refresh its sort order. - gInventory.addChangedMask(LLInventoryObserver::SORT, item->getParentUUID()); - } - - gInventory.notifyObservers(); -} - -LLInventoryModel::item_array_t::iterator findItemByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id) -{ - LLInventoryModel::item_array_t::iterator result = items.end(); - - for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) - { - if ((*i)->getUUID() == id) - { - result = i; - break; - } - } - - return result; -} - -// See also LLInventorySort where landmarks in the Favorites folder are sorted. -class LLViewerInventoryItemSort -{ -public: - bool operator()(const LLPointer& a, const LLPointer& b) - { - return a->getSortField() < b->getSortField(); - } -}; - -/** - * Sorts passed items by LLViewerInventoryItem sort field. - * - * @param[in, out] items - array of items, not sorted. - */ -void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& items) -{ - static LLViewerInventoryItemSort sort_functor; - std::sort(items.begin(), items.end(), sort_functor); -} - -void updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& srcItemId, const LLUUID& destItemId) -{ - LLViewerInventoryItem* srcItem = gInventory.getItem(srcItemId); - LLViewerInventoryItem* destItem = gInventory.getItem(destItemId); - - items.erase(findItemByUUID(items, srcItem->getUUID())); - items.insert(findItemByUUID(items, destItem->getUUID()), srcItem); -} - BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop) { @@ -3058,36 +3013,34 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // if dragging from/into favorites folder only reorder items if ((mUUID == inv_item->getParentUUID()) && folder_allows_reorder) { - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - LLIsType is_type(LLAssetType::AT_LANDMARK); - model->collectDescendentsIf(mUUID, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); - LLInventoryPanel* panel = dynamic_cast(mInventoryPanel.get()); LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; if (itemp) { LLUUID srcItemId = inv_item->getUUID(); LLUUID destItemId = itemp->getListener()->getUUID(); - - // ensure items are sorted properly before changing order. EXT-3498 - rearrange_item_order_by_sort_field(items); - - // update order - updateItemsOrder(items, srcItemId, destItemId); - - saveItemsOrder(items); + gInventory.rearrangeFavoriteLandmarks(srcItemId, destItemId); } } else if (favorites_id == mUUID) // if target is the favorites folder we use copy { + // use callback to rearrange favorite landmarks after adding + // to have new one placed before target (on which it was dropped). See EXT-4312. + LLPointer cb = new AddFavoriteLandmarkCallback(); + LLInventoryPanel* panel = dynamic_cast(mInventoryPanel.get()); + LLFolderViewItem* drag_over_item = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; + if (drag_over_item && drag_over_item->getListener()) + { + cb.get()->setTargetLandmarkId(drag_over_item->getListener()->getUUID()); + } + copy_inventory_item( gAgent.getID(), inv_item->getPermissions().getOwner(), inv_item->getUUID(), mUUID, std::string(), - LLPointer(NULL)); + cb); } else if (move_is_into_current_outfit || move_is_into_outfit) { diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index cc1fa45b26..fced0047e8 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -289,6 +289,7 @@ public: virtual LLFolderType::EType getPreferredType() const; virtual LLUIImagePtr getIcon() const; + virtual LLUIImagePtr getOpenIcon() const; static LLUIImagePtr getIcon(LLFolderType::EType preferred_type); virtual BOOL renameItem(const std::string& new_name); diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 8f4136c01f..2885ba13fa 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -50,8 +50,7 @@ // newview includes #include "llappearancemgr.h" #include "llappviewer.h" -#include "llfirstuse.h" -#include "llfloaterchat.h" +//#include "llfirstuse.h" #include "llfloatercustomize.h" #include "llfocusmgr.h" #include "llfolderview.h" diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 711114173c..e44adfb511 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1994,21 +1994,23 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const descendents_actual += update.mDescendentDelta; cat->setDescendentCount(descendents_actual); cat->setVersion(++version); - llinfos << "accounted: '" << cat->getName() << "' " - << version << " with " << descendents_actual - << " descendents." << llendl; + lldebugs << "accounted: '" << cat->getName() << "' " + << version << " with " << descendents_actual + << " descendents." << llendl; } } if(!accounted) { - lldebugs << "No accounting for: '" << cat->getName() << "' " + // Error condition, this means that the category did not register that + // it got new descendents (perhaps because it is still being loaded) + // which means its descendent count will be wrong. + llwarns << "Accounting failed for '" << cat->getName() << "' version:" << version << llendl; } } else { - llwarns << "No category found for update " << update.mCategoryID - << llendl; + llwarns << "No category found for update " << update.mCategoryID << llendl; } } @@ -3620,6 +3622,98 @@ BOOL LLInventoryModel::getIsFirstTimeInViewer2() return sFirstTimeInViewer2; } +static LLInventoryModel::item_array_t::iterator find_item_iter_by_uuid(LLInventoryModel::item_array_t& items, const LLUUID& id) +{ + LLInventoryModel::item_array_t::iterator result = items.end(); + + for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) + { + if ((*i)->getUUID() == id) + { + result = i; + break; + } + } + + return result; +} + +// static +void LLInventoryModel::updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& src_item_id, const LLUUID& dest_item_id) +{ + LLInventoryModel::item_array_t::iterator it_src = find_item_iter_by_uuid(items, src_item_id); + LLInventoryModel::item_array_t::iterator it_dest = find_item_iter_by_uuid(items, dest_item_id); + + if (it_src == items.end() || it_dest == items.end()) return; + + LLViewerInventoryItem* src_item = *it_src; + items.erase(it_src); + + // target iterator can not be valid because the container was changed, so update it. + it_dest = find_item_iter_by_uuid(items, dest_item_id); + items.insert(it_dest, src_item); +} + +void LLInventoryModel::saveItemsOrder(const LLInventoryModel::item_array_t& items) +{ + int sortField = 0; + + // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field + for (item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) + { + LLViewerInventoryItem* item = *i; + + item->setSortField(++sortField); + item->setComplete(TRUE); + item->updateServer(FALSE); + + updateItem(item); + + // Tell the parent folder to refresh its sort order. + addChangedMask(LLInventoryObserver::SORT, item->getParentUUID()); + } + + notifyObservers(); +} + +// See also LLInventorySort where landmarks in the Favorites folder are sorted. +class LLViewerInventoryItemSort +{ +public: + bool operator()(const LLPointer& a, const LLPointer& b) + { + return a->getSortField() < b->getSortField(); + } +}; + +/** + * Sorts passed items by LLViewerInventoryItem sort field. + * + * @param[in, out] items - array of items, not sorted. + */ +static void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& items) +{ + static LLViewerInventoryItemSort sort_functor; + std::sort(items.begin(), items.end(), sort_functor); +} + +void LLInventoryModel::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLIsType is_type(LLAssetType::AT_LANDMARK); + LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + gInventory.collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); + + // ensure items are sorted properly before changing order. EXT-3498 + rearrange_item_order_by_sort_field(items); + + // update order + updateItemsOrder(items, source_item_id, target_item_id); + + saveItemsOrder(items); +} + //---------------------------------------------------------------------------- // *NOTE: DEBUG functionality diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 39377b4ae2..2a2b48ce3c 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -384,6 +384,39 @@ public: void setLibraryOwnerID(const LLUUID& id); void setLibraryRootFolderID(const LLUUID& id); + + /** + * Changes items order by insertion of the item identified by src_item_id + * BEFORE the item identified by dest_item_id. Both items must exist in items array. + * + * Sorting is stored after method is finished. Only src_item_id is moved before dest_item_id. + * + * @param[in, out] items - vector with items to be updated. It should be sorted in a right way + * before calling this method. + * @param src_item_id - LLUUID of inventory item to be moved in new position + * @param dest_item_id - LLUUID of inventory item before which source item should be placed. + */ + static void updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& src_item_id, const LLUUID& dest_item_id); + + /** + * Saves current order of the passed items using inventory item sort field. + * + * It reset items' sort fields and saves them on server. + * Is used to save order for Favorites folder. + * + * @param[in] items vector of items in order to be saved. + */ + void saveItemsOrder(const LLInventoryModel::item_array_t& items); + + /** + * Rearranges Landmarks inside Favorites folder. + * Moves source landmark before target one. + * + * @param source_item_id - LLUUID of the source item to be moved into new position + * @param target_item_id - LLUUID of the target item before which source item should be placed. + */ + void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id); + protected: // Internal methods which add inventory and make sure that all of diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp index 2d9ea21b5f..2fb8aea4e9 100644 --- a/indra/newview/llinventoryobserver.cpp +++ b/indra/newview/llinventoryobserver.cpp @@ -311,10 +311,10 @@ bool LLInventoryFetchDescendentsObserver::isEverythingComplete() const bool LLInventoryFetchDescendentsObserver::isComplete(LLViewerInventoryCategory* cat) { - S32 version = cat->getVersion(); - S32 descendents = cat->getDescendentCount(); - if((LLViewerInventoryCategory::VERSION_UNKNOWN == version) - || (LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN == descendents)) + const S32 version = cat->getVersion(); + const S32 expected_num_descendents = cat->getDescendentCount(); + if ((version == LLViewerInventoryCategory::VERSION_UNKNOWN) || + (expected_num_descendents == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)) { return false; } @@ -325,15 +325,28 @@ bool LLInventoryFetchDescendentsObserver::isComplete(LLViewerInventoryCategory* gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items); if(!cats || !items) { - // bit of a hack - pretend we're done if they are gone or - // incomplete. should never know, but it would suck if this - // kept tight looping because of a corrupt memory state. + llwarns << "Category '" << cat->getName() << "' descendents corrupted, fetch failed." << llendl; + // NULL means the call failed -- cats/items map doesn't exist (note: this does NOT mean + // that the cat just doesn't have any items or subfolders). + // Unrecoverable, so just return done so that this observer can be cleared + // from memory. return true; } - S32 known = cats->count() + items->count(); - if(descendents == known) + const S32 current_num_known_descendents = cats->count() + items->count(); + + // Got the number of descendents that we were expecting, so we're done. + if (current_num_known_descendents == expected_num_descendents) + { + return true; + } + + // Error condition, but recoverable. This happens if something was added to the + // category before it was initialized, so accountForUpdate didn't update descendent + // count and thus the category thinks it has fewer descendents than it actually has. + if (current_num_known_descendents >= expected_num_descendents) { - // hey - we're done. + llwarns << "Category '" << cat->getName() << "' expected descendentcount:" << expected_num_descendents << " descendents but got descendentcount:" << current_num_known_descendents << llendl; + cat->setDescendentCount(current_num_known_descendents); return true; } return false; @@ -352,7 +365,7 @@ void LLInventoryFetchComboObserver::changed(U32 mask) continue; } if(item->isComplete()) - { + { mCompleteItems.push_back(*it); it = mIncompleteItems.erase(it); continue; diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 164e72e621..9141d50829 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -98,10 +98,6 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) : mCommitCallbackRegistrar.add("Inventory.AttachObject", boost::bind(&LLInventoryPanel::attachObject, this, _2)); mCommitCallbackRegistrar.add("Inventory.BeginIMSession", boost::bind(&LLInventoryPanel::beginIMSession, this)); - setBackgroundColor(LLUIColorTable::instance().getColor("InventoryBackgroundColor")); - setBackgroundVisible(TRUE); - setBackgroundOpaque(TRUE); - if (mStartFolderString != "") { mBuildDefaultHierarchy = false; @@ -433,11 +429,10 @@ void LLInventoryPanel::initializeViews() { mStartFolderID = (preferred_type != LLFolderType::FT_NONE ? gInventory.findCategoryUUIDForType(preferred_type) : LLUUID::null); } - llinfos << this << " Generating views for start folder " << mStartFolderString << llendl; rebuildViewsFor(mStartFolderID); mViewsInitialized = true; - defaultOpenInventory(); + openStartFolderOrMyInventory(); } void LLInventoryPanel::rebuildViewsFor(const LLUUID& id) @@ -491,13 +486,14 @@ void LLInventoryPanel::buildNewViews(const LLUUID& id) if (new_listener) { - LLFolderViewFolder::Params p; - p.name = new_listener->getDisplayName(); - p.icon = new_listener->getIcon(); - p.root = mFolders; - p.listener = new_listener; - p.tool_tip = p.name; - LLFolderViewFolder* folderp = LLUICtrlFactory::create(p); + LLFolderViewFolder::Params params; + params.name = new_listener->getDisplayName(); + params.icon = new_listener->getIcon(); + params.icon_open = new_listener->getOpenIcon(); + params.root = mFolders; + params.listener = new_listener; + params.tool_tip = params.name; + LLFolderViewFolder* folderp = LLUICtrlFactory::create(params); folderp->setItemSortOrder(mFolders->getSortOrder()); itemp = folderp; @@ -523,12 +519,13 @@ void LLInventoryPanel::buildNewViews(const LLUUID& id) if (new_listener) { LLFolderViewItem::Params params; - params.name(new_listener->getDisplayName()); - params.icon(new_listener->getIcon()); - params.creation_date(new_listener->getCreationDate()); - params.root(mFolders); - params.listener(new_listener); - params.rect(LLRect (0, 0, 0, 0)); + params.name = new_listener->getDisplayName(); + params.icon = new_listener->getIcon(); + params.icon_open = new_listener->getOpenIcon(); + params.creation_date = new_listener->getCreationDate(); + params.root = mFolders; + params.listener = new_listener; + params.rect = LLRect (0, 0, 0, 0); params.tool_tip = params.name; itemp = LLUICtrlFactory::create (params); } @@ -575,7 +572,7 @@ void LLInventoryPanel::buildNewViews(const LLUUID& id) } // bit of a hack to make sure the inventory is open. -void LLInventoryPanel::defaultOpenInventory() +void LLInventoryPanel::openStartFolderOrMyInventory() { if (mStartFolderString != "") { @@ -583,13 +580,17 @@ void LLInventoryPanel::defaultOpenInventory() } else { - // Get the first child (it should be "My Inventory") and - // open it up by name (just to make sure the first child is actually a folder). - LLView* first_child = mFolders->getFirstChild(); - if (first_child) + // Find My Inventory folder and open it up by name + for (LLView *child = mFolders->getFirstChild(); child; child = mFolders->findNextSibling(child)) { - const std::string& first_child_name = first_child->getName(); - mFolders->openFolder(first_child_name); + LLFolderViewFolder *fchild = dynamic_cast(child); + if (fchild && fchild->getListener() && + (fchild->getListener()->getUUID() == gInventory.getRootFolderID())) + { + const std::string& child_name = child->getName(); + mFolders->openFolder(child_name); + break; + } } } } diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 4f7f0a79f6..09533b52f1 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -167,7 +167,7 @@ public: static LLInventoryPanel *getActiveInventoryPanel(BOOL auto_open = TRUE); protected: - void defaultOpenInventory(); // open the first level of inventory + void openStartFolderOrMyInventory(); // open the first level of inventory LLInventoryModel* mInventory; LLInventoryObserver* mInventoryObserver; diff --git a/indra/newview/lllandmarklist.cpp b/indra/newview/lllandmarklist.cpp index bd9d22c327..ce84474c05 100644 --- a/indra/newview/lllandmarklist.cpp +++ b/indra/newview/lllandmarklist.cpp @@ -59,6 +59,13 @@ LLLandmark* LLLandmarkList::getAsset(const LLUUID& asset_uuid, loaded_callback_t LLLandmark* landmark = get_ptr_in_map(mList, asset_uuid); if(landmark) { + LLVector3d dummy; + if(cb && !landmark->getGlobalPos(dummy)) + { + // landmark is not completely loaded yet + loaded_callback_map_t::value_type vt(asset_uuid, cb); + mLoadedCallbackMap.insert(vt); + } return landmark; } else diff --git a/indra/newview/lllocationhistory.cpp b/indra/newview/lllocationhistory.cpp index d910dbf718..ae1b8f8540 100644 --- a/indra/newview/lllocationhistory.cpp +++ b/indra/newview/lllocationhistory.cpp @@ -123,6 +123,12 @@ void LLLocationHistory::save() const // build filename for each user std::string resolved_filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, mFilename); + if (resolved_filename.empty()) + { + llinfos << "can't get path to location history filename - probably not logged in yet." << llendl; + return; + } + // open a file for writing llofstream file (resolved_filename); if (!file.is_open()) diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index fc9654e9ad..4e5aaeb66a 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -35,7 +35,6 @@ #include "llagent.h" #include "llagentui.h" #include "lllogchat.h" -#include "llfloaterchat.h" #include "lltrans.h" #include "llviewercontrol.h" @@ -59,9 +58,6 @@ const static std::string NEW_LINE_SPACE_PREFIX("\n "); const static std::string TWO_SPACES(" "); const static std::string MULTI_LINE_PREFIX(" "); -//viewer 1.23 may have used "You" for Agent's entries -const static std::string YOU("You"); - /** * Chat log lines - timestamp and name are optional but message text is mandatory. * diff --git a/indra/newview/llmanip.cpp b/indra/newview/llmanip.cpp index f30821cacf..a96240e31c 100644 --- a/indra/newview/llmanip.cpp +++ b/indra/newview/llmanip.cpp @@ -436,7 +436,7 @@ void LLManip::renderXYZ(const LLVector3 &vec) glPushMatrix(); { - LLUIImagePtr imagep = LLUI::getUIImage("rounded_square.tga"); + LLUIImagePtr imagep = LLUI::getUIImage("Rounded_Square"); gViewerWindow->setup2DRender(); const LLVector2& display_scale = gViewerWindow->getDisplayScale(); glScalef(display_scale.mV[VX], display_scale.mV[VY], 1.f); diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 93f926b5d0..87ebce1d34 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -84,7 +84,6 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) : mHomePageUrl( "" ), mIgnoreUIScale( true ), mAlwaysRefresh( false ), - mExternalUrl( "" ), mMediaSource( 0 ), mTakeFocusOnClick( true ), mCurrentNavUrl( "" ), @@ -632,6 +631,7 @@ bool LLMediaCtrl::ensureMediaSourceExists() mMediaSource->setHomeURL(mHomePageUrl); mMediaSource->setVisible( getVisible() ); mMediaSource->addObserver( this ); + mMediaSource->setBackgroundColor( getBackgroundColor() ); if(mClearCache) { mMediaSource->clearCache(); @@ -671,34 +671,12 @@ LLPluginClassMedia* LLMediaCtrl::getMediaPlugin() // void LLMediaCtrl::draw() { - LLPluginClassMedia* media_plugin = NULL; - - if(mMediaSource && mMediaSource->hasMedia()) - { - media_plugin = mMediaSource->getMediaPlugin(); - } - else - { - return; - } - - if(!media_plugin || (!media_plugin->textureValid())) - { - // Don't try to draw without a valid texture - return; - } - - LLViewerMediaTexture* media_texture = LLViewerTextureManager::findMediaTexture(mMediaTextureID); - - if (!media_texture ) - return; - if ( gRestoreGL == 1 ) { LLRect r = getRect(); reshape( r.getWidth(), r.getHeight(), FALSE ); return; - }; + } // NOTE: optimization needed here - probably only need to do this once // unless tearoffs change the parent which they probably do. @@ -712,125 +690,161 @@ void LLMediaCtrl::draw() setFrequentUpdates( false ); }; + bool draw_media = false; + + LLPluginClassMedia* media_plugin = NULL; + LLViewerMediaTexture* media_texture = NULL; + + if(mMediaSource && mMediaSource->hasMedia()) + { + media_plugin = mMediaSource->getMediaPlugin(); + + if(media_plugin && (media_plugin->textureValid())) + { + media_texture = LLViewerTextureManager::findMediaTexture(mMediaTextureID); + if(media_texture) + { + draw_media = true; + } + } + } + if(mHidingInitialLoad) { // If we're hiding loading, don't draw at all. - return; + draw_media = false; } - // alpha off for this - LLGLSUIDefault gls_ui; - LLGLDisable gls_alphaTest( GL_ALPHA_TEST ); - - gGL.pushMatrix(); + bool background_visible = isBackgroundVisible(); + bool background_opaque = isBackgroundOpaque(); + + if(draw_media) { - if (mIgnoreUIScale) + // alpha off for this + LLGLSUIDefault gls_ui; + LLGLDisable gls_alphaTest( GL_ALPHA_TEST ); + + gGL.pushMatrix(); { - glLoadIdentity(); - // font system stores true screen origin, need to scale this by UI scale factor - // to get render origin for this view (with unit scale) - gGL.translatef(floorf(LLFontGL::sCurOrigin.mX * LLUI::sGLScaleFactor.mV[VX]), - floorf(LLFontGL::sCurOrigin.mY * LLUI::sGLScaleFactor.mV[VY]), - LLFontGL::sCurOrigin.mZ); - } + if (mIgnoreUIScale) + { + glLoadIdentity(); + // font system stores true screen origin, need to scale this by UI scale factor + // to get render origin for this view (with unit scale) + gGL.translatef(floorf(LLFontGL::sCurOrigin.mX * LLUI::sGLScaleFactor.mV[VX]), + floorf(LLFontGL::sCurOrigin.mY * LLUI::sGLScaleFactor.mV[VY]), + LLFontGL::sCurOrigin.mZ); + } - // scale texture to fit the space using texture coords - gGL.getTexUnit(0)->bind(media_texture); - gGL.color4fv( LLColor4::white.mV ); - F32 max_u = ( F32 )media_plugin->getWidth() / ( F32 )media_plugin->getTextureWidth(); - F32 max_v = ( F32 )media_plugin->getHeight() / ( F32 )media_plugin->getTextureHeight(); + // scale texture to fit the space using texture coords + gGL.getTexUnit(0)->bind(media_texture); + gGL.color4fv( LLColor4::white.mV ); + F32 max_u = ( F32 )media_plugin->getWidth() / ( F32 )media_plugin->getTextureWidth(); + F32 max_v = ( F32 )media_plugin->getHeight() / ( F32 )media_plugin->getTextureHeight(); - LLRect r = getRect(); - S32 width, height; - S32 x_offset = 0; - S32 y_offset = 0; - - if(mStretchToFill) - { - if(mMaintainAspectRatio) + LLRect r = getRect(); + S32 width, height; + S32 x_offset = 0; + S32 y_offset = 0; + + if(mStretchToFill) { - F32 media_aspect = (F32)(media_plugin->getWidth()) / (F32)(media_plugin->getHeight()); - F32 view_aspect = (F32)(r.getWidth()) / (F32)(r.getHeight()); - if(media_aspect > view_aspect) + if(mMaintainAspectRatio) { - // max width, adjusted height - width = r.getWidth(); - height = llmin(llmax(llround(width / media_aspect), 0), r.getHeight()); + F32 media_aspect = (F32)(media_plugin->getWidth()) / (F32)(media_plugin->getHeight()); + F32 view_aspect = (F32)(r.getWidth()) / (F32)(r.getHeight()); + if(media_aspect > view_aspect) + { + // max width, adjusted height + width = r.getWidth(); + height = llmin(llmax(llround(width / media_aspect), 0), r.getHeight()); + } + else + { + // max height, adjusted width + height = r.getHeight(); + width = llmin(llmax(llround(height * media_aspect), 0), r.getWidth()); + } } else { - // max height, adjusted width + width = r.getWidth(); height = r.getHeight(); - width = llmin(llmax(llround(height * media_aspect), 0), r.getWidth()); } } else { - width = r.getWidth(); - height = r.getHeight(); + width = llmin(media_plugin->getWidth(), r.getWidth()); + height = llmin(media_plugin->getHeight(), r.getHeight()); } - } - else - { - width = llmin(media_plugin->getWidth(), r.getWidth()); - height = llmin(media_plugin->getHeight(), r.getHeight()); - } - - x_offset = (r.getWidth() - width) / 2; - y_offset = (r.getHeight() - height) / 2; + + x_offset = (r.getWidth() - width) / 2; + y_offset = (r.getHeight() - height) / 2; - if(mIgnoreUIScale) - { - x_offset = llround((F32)x_offset * LLUI::sGLScaleFactor.mV[VX]); - y_offset = llround((F32)y_offset * LLUI::sGLScaleFactor.mV[VY]); - width = llround((F32)width * LLUI::sGLScaleFactor.mV[VX]); - height = llround((F32)height * LLUI::sGLScaleFactor.mV[VY]); - } + if(mIgnoreUIScale) + { + x_offset = llround((F32)x_offset * LLUI::sGLScaleFactor.mV[VX]); + y_offset = llround((F32)y_offset * LLUI::sGLScaleFactor.mV[VY]); + width = llround((F32)width * LLUI::sGLScaleFactor.mV[VX]); + height = llround((F32)height * LLUI::sGLScaleFactor.mV[VY]); + } - // draw the browser - gGL.setSceneBlendType(LLRender::BT_REPLACE); - gGL.begin( LLRender::QUADS ); - if (! media_plugin->getTextureCoordsOpenGL()) - { - // render using web browser reported width and height, instead of trying to invert GL scale - gGL.texCoord2f( max_u, 0.f ); - gGL.vertex2i( x_offset + width, y_offset + height ); + // draw the browser + gGL.setSceneBlendType(LLRender::BT_REPLACE); + gGL.begin( LLRender::QUADS ); + if (! media_plugin->getTextureCoordsOpenGL()) + { + // render using web browser reported width and height, instead of trying to invert GL scale + gGL.texCoord2f( max_u, 0.f ); + gGL.vertex2i( x_offset + width, y_offset + height ); - gGL.texCoord2f( 0.f, 0.f ); - gGL.vertex2i( x_offset, y_offset + height ); + gGL.texCoord2f( 0.f, 0.f ); + gGL.vertex2i( x_offset, y_offset + height ); - gGL.texCoord2f( 0.f, max_v ); - gGL.vertex2i( x_offset, y_offset ); + gGL.texCoord2f( 0.f, max_v ); + gGL.vertex2i( x_offset, y_offset ); - gGL.texCoord2f( max_u, max_v ); - gGL.vertex2i( x_offset + width, y_offset ); - } - else - { - // render using web browser reported width and height, instead of trying to invert GL scale - gGL.texCoord2f( max_u, max_v ); - gGL.vertex2i( x_offset + width, y_offset + height ); + gGL.texCoord2f( max_u, max_v ); + gGL.vertex2i( x_offset + width, y_offset ); + } + else + { + // render using web browser reported width and height, instead of trying to invert GL scale + gGL.texCoord2f( max_u, max_v ); + gGL.vertex2i( x_offset + width, y_offset + height ); - gGL.texCoord2f( 0.f, max_v ); - gGL.vertex2i( x_offset, y_offset + height ); + gGL.texCoord2f( 0.f, max_v ); + gGL.vertex2i( x_offset, y_offset + height ); - gGL.texCoord2f( 0.f, 0.f ); - gGL.vertex2i( x_offset, y_offset ); + gGL.texCoord2f( 0.f, 0.f ); + gGL.vertex2i( x_offset, y_offset ); - gGL.texCoord2f( max_u, 0.f ); - gGL.vertex2i( x_offset + width, y_offset ); + gGL.texCoord2f( max_u, 0.f ); + gGL.vertex2i( x_offset + width, y_offset ); + } + gGL.end(); + gGL.setSceneBlendType(LLRender::BT_ALPHA); } - gGL.end(); - gGL.setSceneBlendType(LLRender::BT_ALPHA); + gGL.popMatrix(); + } - gGL.popMatrix(); - + else + { + // Setting these will make LLPanel::draw draw the opaque background color. + setBackgroundVisible(true); + setBackgroundOpaque(true); + } + // highlight if keyboard focus here. (TODO: this needs some work) if ( mBorder && mBorder->getVisible() ) mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus( this ) ); LLPanel::draw(); + + // Restore the previous values + setBackgroundVisible(background_visible); + setBackgroundOpaque(background_opaque); } //////////////////////////////////////////////////////////////////////////////// @@ -862,9 +876,27 @@ bool LLMediaCtrl::onClickLinkExternalTarget(const LLSD& notification, const LLSD S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if ( 0 == option ) { - // open in external browser because we don't support - // creation of our own secondary browser windows - LLWeb::loadURLExternal( notification["payload"]["external_url"].asString() ); + LLSD payload = notification["payload"]; + std::string url = payload["url"].asString(); + S32 target_type = payload["target_type"].asInteger(); + + switch (target_type) + { + case LLPluginClassMedia::TARGET_EXTERNAL: + // load target in an external browser + LLWeb::loadURLExternal(url); + break; + + case LLPluginClassMedia::TARGET_BLANK: + // load target in the user's preferred browser + LLWeb::loadURL(url); + break; + + default: + // unsupported link target - shouldn't happen + LL_WARNS("LinkTarget") << "Unsupported link target type" << LL_ENDL; + break; + } } return false; } @@ -978,20 +1010,18 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) void LLMediaCtrl::onClickLinkHref( LLPluginClassMedia* self ) { // retrieve the event parameters - std::string target = self->getClickTarget(); std::string url = self->getClickURL(); + U32 target_type = self->getClickTargetType(); - // if there is a value for the target - if ( !target.empty() ) + // is there is a target specified for the link? + if (target_type == LLPluginClassMedia::TARGET_EXTERNAL || + target_type == LLPluginClassMedia::TARGET_BLANK) { - if ( target == "_external" ) - { - mExternalUrl = url; - LLSD payload; - payload["external_url"] = mExternalUrl; - LLNotificationsUtil::add( "WebLaunchExternalTarget", LLSD(), payload, onClickLinkExternalTarget); - return; - } + LLSD payload; + payload["url"] = url; + payload["target_type"] = LLSD::Integer(target_type); + LLNotificationsUtil::add( "WebLaunchExternalTarget", LLSD(), payload, onClickLinkExternalTarget); + return; } const std::string protocol1( "http://" ); diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h index 8f9e6e7179..b0aca3cfa4 100644 --- a/indra/newview/llmediactrl.h +++ b/indra/newview/llmediactrl.h @@ -182,7 +182,6 @@ public: bool mOpenLinksInInternalBrowser; bool mTrusted; std::string mHomePageUrl; - std::string mExternalUrl; std::string mCurrentNavUrl; bool mIgnoreUIScale; bool mAlwaysRefresh; diff --git a/indra/newview/llmenucommands.cpp b/indra/newview/llmenucommands.cpp index a2aef9ba63..8d950f072d 100644 --- a/indra/newview/llmenucommands.cpp +++ b/indra/newview/llmenucommands.cpp @@ -45,8 +45,7 @@ #include "llagent.h" #include "llcallingcard.h" #include "llviewercontrol.h" -#include "llfirstuse.h" -#include "llfloaterchat.h" +//#include "llfirstuse.h" #include "llfloaterworldmap.h" #include "lllineeditor.h" #include "llstatusbar.h" diff --git a/indra/newview/llmorphview.cpp b/indra/newview/llmorphview.cpp index f562e45770..b95e8bd3a2 100644 --- a/indra/newview/llmorphview.cpp +++ b/indra/newview/llmorphview.cpp @@ -40,7 +40,7 @@ #include "lldrawable.h" #include "lldrawpoolavatar.h" #include "llface.h" -#include "llfirstuse.h" +//#include "llfirstuse.h" #include "llfloatercustomize.h" #include "llfloatertools.h" #include "llresmgr.h" @@ -143,7 +143,7 @@ void LLMorphView::setVisible(BOOL visible) initialize(); // First run dialog - LLFirstUse::useAppearance(); + //LLFirstUse::useAppearance(); } else { diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index b520bc1c2d..7ee4c64f8f 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -64,7 +64,7 @@ #include "llviewerwindow.h" #include "llworld.h" //for particle system banning #include "llchat.h" -#include "llfloaterchat.h" +#include "llimpanel.h" #include "llimview.h" #include "llnotifications.h" #include "lluistring.h" @@ -258,7 +258,7 @@ LLMuteList::~LLMuteList() { // If we quit from the login screen we will not have an SL account // name. Don't try to save, otherwise we'll dump a file in - // C:\Program Files\SecondLife\ JC + // C:\Program Files\SecondLife\ or similar. JC std::string user_dir = gDirUtilp->getLindenUserDir(); if (!user_dir.empty()) { @@ -532,9 +532,6 @@ void notify_automute_callback(const LLUUID& agent_id, const std::string& first_n LLIMModel::getInstance()->addMessage(agent_id, SYSTEM_FROM, LLUUID::null, message); } - - LLChat auto_chat(message); - LLFloaterChat::addChat(auto_chat, FALSE, FALSE); } } diff --git a/indra/newview/llnamebox.cpp b/indra/newview/llnamebox.cpp index 2f4a266198..cd810b9793 100644 --- a/indra/newview/llnamebox.cpp +++ b/indra/newview/llnamebox.cpp @@ -52,6 +52,8 @@ LLNameBox::LLNameBox(const Params& p) : LLTextBox(p) { mNameID = LLUUID::null; + mLink = p.link; + mInitialValue = p.initial_value().asString(); LLNameBox::sInstances.insert(this); setText(LLStringUtil::null); } @@ -66,17 +68,23 @@ void LLNameBox::setNameID(const LLUUID& name_id, BOOL is_group) mNameID = name_id; std::string name; + BOOL got_name = FALSE; if (!is_group) { - gCacheName->getFullName(name_id, name); + got_name = gCacheName->getFullName(name_id, name); } else { - gCacheName->getGroupName(name_id, name); + got_name = gCacheName->getGroupName(name_id, name); } - setText(name); + // Got the name already? Set it. + // Otherwise it will be set later in refresh(). + if (got_name) + setName(name, is_group); + else + setText(mInitialValue); } void LLNameBox::refresh(const LLUUID& id, const std::string& firstname, @@ -93,7 +101,7 @@ void LLNameBox::refresh(const LLUUID& id, const std::string& firstname, { name = firstname; } - setText(name); + setName(name, is_group); } } @@ -109,3 +117,22 @@ void LLNameBox::refreshAll(const LLUUID& id, const std::string& firstname, box->refresh(id, firstname, lastname, is_group); } } + +void LLNameBox::setName(const std::string& name, BOOL is_group) +{ + if (mLink) + { + std::string url; + + if (is_group) + url = "[secondlife:///app/group/" + LLURI::escape(name) + "/about " + name + "]"; + else + url = "[secondlife:///app/agent/" + mNameID.asString() + "/about " + name + "]"; + + setText(url); + } + else + { + setText(name); + } +} diff --git a/indra/newview/llnamebox.h b/indra/newview/llnamebox.h index 3edb36883f..48b54faec8 100644 --- a/indra/newview/llnamebox.h +++ b/indra/newview/llnamebox.h @@ -47,9 +47,11 @@ public: struct Params : public LLInitParam::Block { Optional is_group; + Optional link; Params() : is_group("is_group", false) + , link("link", false) {} }; @@ -67,10 +69,14 @@ protected: friend class LLUICtrlFactory; private: + void setName(const std::string& name, BOOL is_group); + static std::set sInstances; private: LLUUID mNameID; + BOOL mLink; + std::string mInitialValue; }; diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index 7e6145f578..6375362ae2 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -74,12 +74,12 @@ void LLNameListCtrl::addNameItem(const LLUUID& agent_id, EAddPosition pos, { //llinfos << "LLNameListCtrl::addNameItem " << agent_id << llendl; - std::string fullname; - gCacheName->getFullName(agent_id, fullname); - - fullname.append(suffix); + NameItem item; + item.value = agent_id; + item.enabled = enabled; + item.target = INDIVIDUAL; - addStringUUIDItem(fullname, agent_id, pos, enabled); + addNameItemRow(item, pos); } // virtual, public @@ -130,11 +130,12 @@ BOOL LLNameListCtrl::handleDragAndDrop( return handled; } -void LLNameListCtrl::showAvatarInspector(const LLUUID& avatar_id) +void LLNameListCtrl::showInspector(const LLUUID& avatar_id, bool is_group) { - LLSD key; - key["avatar_id"] = avatar_id; - LLFloaterReg::showInstance("inspect_avatar", key); + if (is_group) + LLFloaterReg::showInstance("inspect_group", LLSD().with("group_id", avatar_id)); + else + LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", avatar_id)); } //virtual @@ -147,7 +148,7 @@ BOOL LLNameListCtrl::handleToolTip(S32 x, S32 y, MASK mask) && column_index == mNameColumnIndex) { // ...this is the column with the avatar name - LLUUID avatar_id = hit_item->getValue().asUUID(); + LLUUID avatar_id = getItemId(hit_item); if (avatar_id.notNull()) { // ...valid avatar id @@ -164,9 +165,12 @@ BOOL LLNameListCtrl::handleToolTip(S32 x, S32 y, MASK mask) LLCoordGL pos( sticky_rect.mRight - 16, sticky_rect.mTop ); LLPointer icon = LLUI::getUIImage("Info_Small"); + // Should we show a group or an avatar inspector? + bool is_group = hit_item->getValue()["is_group"].asBoolean(); + LLToolTip::Params params; params.background_visible( false ); - params.click_callback( boost::bind(&LLNameListCtrl::showAvatarInspector, this, avatar_id) ); + params.click_callback( boost::bind(&LLNameListCtrl::showInspector, this, avatar_id, is_group) ); params.delay_time(0.0f); // spawn instantly on hover params.image( icon ); params.message(""); @@ -220,9 +224,22 @@ LLScrollListItem* LLNameListCtrl::addElement(const LLSD& element, EAddPosition p } -LLScrollListItem* LLNameListCtrl::addNameItemRow(const LLNameListCtrl::NameItem& name_item, EAddPosition pos) +LLScrollListItem* LLNameListCtrl::addNameItemRow( + const LLNameListCtrl::NameItem& name_item, + EAddPosition pos, + std::string& suffix) { - LLScrollListItem* item = LLScrollListCtrl::addRow(name_item, pos); + LLUUID id = name_item.value().asUUID(); + LLScrollListItem* item = NULL; + + // Store item type so that we can invoke the proper inspector. + // *TODO Vadim: Is there a more proper way of storing additional item data? + { + LLNameListCtrl::NameItem name_item_(name_item); + name_item_.value = LLSD().with("uuid", id).with("is_group", name_item.target() == GROUP); + item = LLScrollListCtrl::addRow(name_item_, pos); + } + if (!item) return NULL; // use supplied name by default @@ -230,7 +247,7 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow(const LLNameListCtrl::NameItem& switch(name_item.target) { case GROUP: - gCacheName->getGroupName(name_item.value().asUUID(), fullname); + gCacheName->getGroupName(id, fullname); // fullname will be "nobody" if group not found break; case SPECIAL: @@ -239,7 +256,7 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow(const LLNameListCtrl::NameItem& case INDIVIDUAL: { std::string name; - if (gCacheName->getFullName(name_item.value().asUUID(), name)) + if (gCacheName->getFullName(id, name)) { fullname = name; } @@ -249,6 +266,12 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow(const LLNameListCtrl::NameItem& break; } + // Append optional suffix. + if (!suffix.empty()) + { + fullname.append(suffix); + } + LLScrollListCell* cell = item->getColumn(mNameColumnIndex); if (cell) { @@ -270,15 +293,24 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow(const LLNameListCtrl::NameItem& // public void LLNameListCtrl::removeNameItem(const LLUUID& agent_id) { - BOOL item_exists = selectByID( agent_id ); - if(item_exists) + // Find the item specified with agent_id. + S32 idx = -1; + for (item_list::iterator it = getItemList().begin(); it != getItemList().end(); it++) { - S32 index = getItemIndex(getFirstSelected()); - if(index >= 0) + LLScrollListItem* item = *it; + if (getItemId(item) == agent_id) { - deleteSingleItem(index); + idx = getItemIndex(item); + break; } } + + // Remove it. + if (idx >= 0) + { + selectNthItem(idx); // not sure whether this is needed, taken from previous implementation + deleteSingleItem(idx); + } } // public @@ -303,7 +335,7 @@ void LLNameListCtrl::refresh(const LLUUID& id, const std::string& first, for (iter = getItemList().begin(); iter != getItemList().end(); iter++) { LLScrollListItem* item = *iter; - if (item->getUUID() == id) + if (getItemId(item) == id) { LLScrollListCell* cell = (LLScrollListCell*)item->getColumn(0); cell = item->getColumn(mNameColumnIndex); @@ -343,3 +375,9 @@ void LLNameListCtrl::updateColumns() } } } + +// static +LLUUID LLNameListCtrl::getItemId(LLScrollListItem* item) +{ + return item->getValue()["uuid"].asUUID(); +} diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h index d0f0ec4d21..192a3a5afa 100644 --- a/indra/newview/llnamelistctrl.h +++ b/indra/newview/llnamelistctrl.h @@ -94,7 +94,7 @@ public: void addNameItem(NameItem& item, EAddPosition pos = ADD_BOTTOM); /*virtual*/ LLScrollListItem* addElement(const LLSD& element, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL); - LLScrollListItem* addNameItemRow(const NameItem& value, EAddPosition pos = ADD_BOTTOM); + LLScrollListItem* addNameItemRow(const NameItem& value, EAddPosition pos = ADD_BOTTOM, std::string& suffix = LLStringUtil::null); // Add a user to the list by name. It will be added, the name // requested from the cache, and updated as necessary. @@ -121,7 +121,8 @@ public: /*virtual*/ void updateColumns(); private: - void showAvatarInspector(const LLUUID& avatar_id); + void showInspector(const LLUUID& avatar_id, bool is_group); + static LLUUID getItemId(LLScrollListItem* item); private: S32 mNameColumnIndex; diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 2ad82d3e8e..a7c1e73328 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -63,7 +63,7 @@ static const S32 RESIZE_BAR_THICKNESS = 3; LLNearbyChat::LLNearbyChat(const LLSD& key) - : LLDockableFloater(NULL, false, key) + : LLDockableFloater(NULL, false, false, key) ,mChatHistory(NULL) { @@ -137,7 +137,7 @@ std::string appendTime() time_t utc_time; utc_time = time_corrected(); std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" - +LLTrans::getString("TimeMin")+"] "; + +LLTrans::getString("TimeMin")+"]"; LLSD substitution; @@ -178,28 +178,8 @@ void LLNearbyChat::addMessage(const LLChat& chat,bool archive) if (!chat.mMuted) { - tmp_chat.mFromName = chat.mFromID != gAgentID ? chat.mFromName : LLTrans::getString("You"); - - if (chat.mChatStyle == CHAT_STYLE_IRC) - { - LLColor4 txt_color = LLUIColorTable::instance().getColor("White"); - LLViewerChat::getChatColor(chat,txt_color); - LLFontGL* fontp = LLViewerChat::getChatFont(); - std::string font_name = LLFontGL::nameFromFont(fontp); - std::string font_size = LLFontGL::sizeFromFont(fontp); - LLStyle::Params append_style_params; - append_style_params.color(txt_color); - append_style_params.readonly_color(txt_color); - append_style_params.font.name(font_name); - append_style_params.font.size(font_size); - append_style_params.font.style = "ITALIC"; - - mChatHistory->appendMessage(chat, use_plain_text_chat_history, append_style_params); - } - else - { - mChatHistory->appendMessage(chat, use_plain_text_chat_history); - } + tmp_chat.mFromName = chat.mFromName; + mChatHistory->appendMessage(chat, use_plain_text_chat_history); } if(archive) diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp index 8dbaa5ac53..6cf8bcb417 100644 --- a/indra/newview/llnearbychatbar.cpp +++ b/indra/newview/llnearbychatbar.cpp @@ -48,13 +48,16 @@ #include "llcommandhandler.h" #include "llviewercontrol.h" #include "llnavigationbar.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "llrootview.h" S32 LLNearbyChatBar::sLastSpecialChatChannel = 0; // legacy callback glue void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel); -static LLDefaultChildRegistry::Register r("gesture_combo_box"); +static LLDefaultChildRegistry::Register r("gesture_combo_list"); struct LLChatTypeTrigger { std::string name; @@ -66,13 +69,42 @@ static LLChatTypeTrigger sChatTypeTriggers[] = { { "/shout" , CHAT_TYPE_SHOUT} }; -LLGestureComboBox::LLGestureComboBox(const LLGestureComboBox::Params& p) - : LLComboBox(p) - , mGestureLabelTimer() +LLGestureComboList::Params::Params() +: combo_button("combo_button"), + combo_list("combo_list") +{ +} + +LLGestureComboList::LLGestureComboList(const LLGestureComboList::Params& p) +: LLUICtrl(p) , mLabel(p.label) , mViewAllItemIndex(0) { - setCommitCallback(boost::bind(&LLGestureComboBox::onCommitGesture, this)); + LLButton::Params button_params = p.combo_button; + button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT); + + mButton = LLUICtrlFactory::create(button_params); + mButton->reshape(getRect().getWidth(),getRect().getHeight()); + mButton->setCommitCallback(boost::bind(&LLGestureComboList::onButtonCommit, this)); + + addChild(mButton); + + LLScrollListCtrl::Params params = p.combo_list; + params.name("GestureComboList"); + params.commit_callback.function(boost::bind(&LLGestureComboList::onItemSelected, this, _2)); + params.visible(false); + params.commit_on_keyboard_movement(false); + + mList = LLUICtrlFactory::create(params); + + // *HACK: adding list as a child to NonSideTrayView to make it fully visible without + // making it top control (because it would cause problems). + gViewerWindow->getNonSideTrayView()->addChild(mList); + mList->setVisible(FALSE); + + //****************************Gesture Part********************************/ + + setCommitCallback(boost::bind(&LLGestureComboList::onCommitGesture, this)); // now register us as observer since we have a place to put the results LLGestureManager::instance().addObserver(this); @@ -80,23 +112,139 @@ LLGestureComboBox::LLGestureComboBox(const LLGestureComboBox::Params& p) // refresh list from current active gestures refreshGestures(); - // This forces using of halign from xml, since LLComboBox - // sets it to LLFontGL::LEFT, if text entry is disabled - mButton->setHAlign(p.drop_down_button.font_halign); + setFocusLostCallback(boost::bind(&LLGestureComboList::hideList, this)); } -LLGestureComboBox::~LLGestureComboBox() +BOOL LLGestureComboList::handleKey(KEY key, MASK mask, BOOL called_from_parent) { - LLGestureManager::instance().removeObserver(this); + BOOL handled = FALSE; + + if (key == KEY_ESCAPE && mask == MASK_NONE ) + { + hideList(); + handled = TRUE; + } + else + { + handled = mList->handleKey(key, mask, called_from_parent); + } + + return handled; +} + +void LLGestureComboList::showList() +{ + LLRect rect = mList->getRect(); + LLRect screen; + mButton->localRectToScreen(getRect(), &screen); + + // Calculating amount of space between the navigation bar and gestures combo + LLNavigationBar* nb = LLNavigationBar::getInstance(); + + S32 x, nb_bottom; + nb->localPointToScreen(0, 0, &x, &nb_bottom); + + S32 max_height = nb_bottom - screen.mTop; + mList->calcColumnWidths(); + rect.setOriginAndSize(screen.mLeft, screen.mTop, llmax(mList->getMaxContentWidth(),mButton->getRect().getWidth()), max_height); + + mList->setRect(rect); + mList->fitContents( llmax(mList->getMaxContentWidth(),mButton->getRect().getWidth()), max_height); + + gFocusMgr.setKeyboardFocus(this); + + // Show the list and push the button down + mButton->setToggleState(TRUE); + mList->setVisible(TRUE); +} + +void LLGestureComboList::onButtonCommit() +{ + if (!mList->getVisible()) + { + // highlight the last selected item from the original selection before potentially selecting a new item + // as visual cue to original value of combo box + LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); + if (last_selected_item) + { + mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); + } + + if (mList->getItemCount() != 0) + { + showList(); + } + } + else + { + hideList(); + } +} + +void LLGestureComboList::hideList() +{ + if (mList->getVisible()) + { + mButton->setToggleState(FALSE); + mList->setVisible(FALSE); + mList->mouseOverHighlightNthItem(-1); + gFocusMgr.setKeyboardFocus(NULL); + } +} + +S32 LLGestureComboList::getCurrentIndex() const +{ + LLScrollListItem* item = mList->getFirstSelected(); + if( item ) + { + return mList->getItemIndex( item ); + } + return -1; +} + +void LLGestureComboList::onItemSelected(const LLSD& data) +{ + const std::string name = mList->getSelectedItemLabel(); + + S32 cur_id = getCurrentIndex(); + mLastSelectedIndex = cur_id; + if (cur_id != mList->getItemCount()-1 && cur_id != -1) + { + mButton->setLabel(name); + } + + // hiding the list reasserts the old value stored in the text editor/dropdown button + hideList(); + + // commit does the reverse, asserting the value in the list + onCommit(); +} + +void LLGestureComboList::sortByName(bool ascending) +{ + mList->sortOnce(0, ascending); +} + +LLSD LLGestureComboList::getValue() const +{ + LLScrollListItem* item = mList->getFirstSelected(); + if( item ) + { + return item->getValue(); + } + else + { + return LLSD(); + } } -void LLGestureComboBox::refreshGestures() +void LLGestureComboList::refreshGestures() { //store current selection so we can maintain it LLSD cur_gesture = getValue(); - selectFirstItem(); - // clear - clearRows(); + + mList->selectFirstItem(); + mList->clearRows(); mGestures.clear(); LLGestureManager::item_map_t::const_iterator it; @@ -107,7 +255,7 @@ void LLGestureComboBox::refreshGestures() LLMultiGesture* gesture = (*it).second; if (gesture) { - addSimpleElement(gesture->mName, ADD_BOTTOM, LLSD(idx)); + mList->addSimpleElement(gesture->mName, ADD_BOTTOM, LLSD(idx)); mGestures.push_back(gesture); idx++; } @@ -117,23 +265,42 @@ void LLGestureComboBox::refreshGestures() // store index followed by the last added Gesture and add View All item at bottom mViewAllItemIndex = idx; - addSimpleElement(LLTrans::getString("ViewAllGestures"), ADD_BOTTOM, LLSD(mViewAllItemIndex)); + + mList->addSimpleElement(LLTrans::getString("ViewAllGestures"), ADD_BOTTOM, LLSD(mViewAllItemIndex)); // Insert label after sorting, at top, with separator below it - addSeparator(ADD_TOP); - addSimpleElement(mLabel, ADD_TOP); + mList->addSeparator(ADD_TOP); + mList->addSimpleElement(mLabel, ADD_TOP); if (cur_gesture.isDefined()) { - selectByValue(cur_gesture); + mList->selectByValue(cur_gesture); + } else { - selectFirstItem(); + mList->selectFirstItem(); } + + LLCtrlListInterface* gestures = getListInterface(); + LLMultiGesture* gesture = NULL; + + if (gestures) + { + S32 index = gestures->getSelectedValue().asInteger(); + if(index > 0) + gesture = mGestures.at(index); + } + + if(gesture && LLGestureManager::instance().isGesturePlaying(gesture)) + { + return; + } + + mButton->setLabel(mLabel); } -void LLGestureComboBox::onCommitGesture() +void LLGestureComboList::onCommitGesture() { LLCtrlListInterface* gestures = getListInterface(); if (gestures) @@ -164,50 +331,11 @@ void LLGestureComboBox::onCommitGesture() } } } - - mGestureLabelTimer.start(); - // free focus back to chat bar - setFocus(FALSE); } -//virtual -void LLGestureComboBox::draw() +LLGestureComboList::~LLGestureComboList() { - // HACK: Leave the name of the gesture in place for a few seconds. - const F32 SHOW_GESTURE_NAME_TIME = 2.f; - if (mGestureLabelTimer.getStarted() && mGestureLabelTimer.getElapsedTimeF32() > SHOW_GESTURE_NAME_TIME) - { - LLCtrlListInterface* gestures = getListInterface(); - if (gestures) gestures->selectFirstItem(); - mGestureLabelTimer.stop(); - } - - LLComboBox::draw(); -} - -//virtual -void LLGestureComboBox::showList() -{ - LLComboBox::showList(); - - // Calculating amount of space between the navigation bar and gestures combo - LLNavigationBar* nb = LLNavigationBar::getInstance(); - S32 x, nb_bottom; - nb->localPointToScreen(0, 0, &x, &nb_bottom); - - S32 list_bottom; - mList->localPointToScreen(0, 0, &x, &list_bottom); - - S32 max_height = nb_bottom - list_bottom; - - LLRect rect = mList->getRect(); - // List overlapped navigation bar, downsize it - if (rect.getHeight() > max_height) - { - rect.setOriginAndSize(rect.mLeft, rect.mBottom, rect.getWidth(), max_height); - mList->setRect(rect); - mList->reshape(rect.getWidth(), rect.getHeight()); - } + LLGestureManager::instance().removeObserver(this); } LLNearbyChatBar::LLNearbyChatBar() diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h index 224118e088..d9a7403611 100644 --- a/indra/newview/llnearbychatbar.h +++ b/indra/newview/llnearbychatbar.h @@ -42,33 +42,52 @@ #include "llspeakers.h" -class LLGestureComboBox - : public LLComboBox - , public LLGestureManagerObserver +class LLGestureComboList + : public LLGestureManagerObserver + , public LLUICtrl { public: - struct Params : public LLInitParam::Block { }; + struct Params : public LLInitParam::Block + { + Optional combo_button; + Optional combo_list; + + Params(); + }; + protected: - LLGestureComboBox(const Params&); + friend class LLUICtrlFactory; + LLGestureComboList(const Params&); + std::vector mGestures; + std::string mLabel; + LLSD::Integer mViewAllItemIndex; + public: - ~LLGestureComboBox(); + ~LLGestureComboList(); + + LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)mList; }; + virtual void showList(); + virtual void hideList(); + virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); + + S32 getCurrentIndex() const; + void onItemSelected(const LLSD& data); + void sortByName(bool ascending = true); void refreshGestures(); void onCommitGesture(); - virtual void draw(); + void onButtonCommit(); + virtual LLSD getValue() const; // LLGestureManagerObserver trigger virtual void changed() { refreshGestures(); } -protected: - - virtual void showList(); +private: - LLFrameTimer mGestureLabelTimer; - std::vector mGestures; - std::string mLabel; - LLSD::Integer mViewAllItemIndex; + LLButton* mButton; + LLScrollListCtrl* mList; + S32 mLastSelectedIndex; }; class LLNearbyChatBar diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index 9e13a626b4..c50e049d4c 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -302,7 +302,6 @@ LLNearbyChatHandler::LLNearbyChatHandler(e_notification_type type, const LLSD& i channel->setCreatePanelCallback(callback); mChannel = LLChannelManager::getInstance()->addChannel(channel); - mChannel->setOverflowFormatString("You have %d unread nearby chat messages"); } LLNearbyChatHandler::~LLNearbyChatHandler() @@ -332,25 +331,25 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg) LLChat& tmp_chat = const_cast(chat_msg); - if (tmp_chat.mChatStyle == CHAT_STYLE_IRC) - { - if(!tmp_chat.mFromName.empty()) - tmp_chat.mText = tmp_chat.mFromName + tmp_chat.mText.substr(3); - else - tmp_chat.mText = tmp_chat.mText.substr(3); - } - + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat", LLSD()); { //sometimes its usefull to have no name at all... //if(tmp_chat.mFromName.empty() && tmp_chat.mFromID!= LLUUID::null) // tmp_chat.mFromName = tmp_chat.mFromID.asString(); } - - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat", LLSD()); nearby_chat->addMessage(chat_msg); if(nearby_chat->getVisible()) return;//no need in toast if chat is visible - + + // Handle irc styled messages for toast panel + if (tmp_chat.mChatStyle == CHAT_STYLE_IRC) + { + if(!tmp_chat.mFromName.empty()) + tmp_chat.mText = tmp_chat.mFromName + tmp_chat.mText.substr(3); + else + tmp_chat.mText = tmp_chat.mText.substr(3); + } + // arrange a channel on a screen if(!mChannel->getVisible()) { diff --git a/indra/newview/llnotificationofferhandler.cpp b/indra/newview/llnotificationofferhandler.cpp index dd66a6c507..fad0c6a91e 100644 --- a/indra/newview/llnotificationofferhandler.cpp +++ b/indra/newview/llnotificationofferhandler.cpp @@ -112,8 +112,8 @@ bool LLOfferHandler::processNotification(const LLSD& notify) LLHandlerUtil::spawnIMSession(name, from_id); } - if (notification->getPayload().has("SUPPRES_TOST") - && notification->getPayload()["SUPPRES_TOST"]) + if (notification->getPayload().has("SUPPRESS_TOAST") + && notification->getPayload()["SUPPRESS_TOAST"]) { LLNotificationsUtil::cancel(notification); } diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp index 63803469dd..f816dc589d 100644 --- a/indra/newview/lloutputmonitorctrl.cpp +++ b/indra/newview/lloutputmonitorctrl.cpp @@ -77,7 +77,9 @@ LLOutputMonitorCtrl::LLOutputMonitorCtrl(const LLOutputMonitorCtrl::Params& p) mImageLevel3(p.image_level_3), mAutoUpdate(p.auto_update), mSpeakerId(p.speaker_id), - mIsAgentControl(false) + mIsAgentControl(false), + mIsSwitchDirty(false), + mShouldSwitchOn(false) { //static LLUIColor output_monitor_muted_color = LLUIColorTable::instance().getColor("OutputMonitorMutedColor", LLColor4::orange); //static LLUIColor output_monitor_overdriven_color = LLUIColorTable::instance().getColor("OutputMonitorOverdrivenColor", LLColor4::red); @@ -108,6 +110,7 @@ LLOutputMonitorCtrl::LLOutputMonitorCtrl(const LLOutputMonitorCtrl::Params& p) LLOutputMonitorCtrl::~LLOutputMonitorCtrl() { LLMuteList::getInstance()->removeObserver(this); + LLSpeakingIndicatorManager::unregisterSpeakingIndicator(mSpeakerId, this); } void LLOutputMonitorCtrl::setPower(F32 val) @@ -117,6 +120,26 @@ void LLOutputMonitorCtrl::setPower(F32 val) void LLOutputMonitorCtrl::draw() { + // see also switchIndicator() + if (mIsSwitchDirty) + { + mIsSwitchDirty = false; + if (mShouldSwitchOn) + { + // just notify parent visibility may have changed + notifyParentVisibilityChanged(); + } + else + { + // make itself invisible and notify parent about this + setVisible(FALSE); + notifyParentVisibilityChanged(); + + // no needs to render for invisible element + return; + } + } + // Copied from llmediaremotectrl.cpp // *TODO: Give the LLOutputMonitorCtrl an agent-id to monitor, then // call directly into gVoiceClient to ask if that agent-id is muted, is @@ -229,6 +252,7 @@ void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id) if (speaker_id.isNull() || speaker_id == mSpeakerId) return; mSpeakerId = speaker_id; + LLSpeakingIndicatorManager::registerSpeakingIndicator(mSpeakerId, this); //mute management if (mAutoUpdate) @@ -251,3 +275,42 @@ void LLOutputMonitorCtrl::onChange() // check only blocking on voice. EXT-3542 setIsMuted(LLMuteList::getInstance()->isMuted(mSpeakerId, LLMute::flagVoiceChat)); } + +// virtual +void LLOutputMonitorCtrl::switchIndicator(bool switch_on) +{ + // ensure indicator is visible in case it is not in visible chain + // to be called when parent became visible next time to notify parent that visibility is changed. + setVisible(TRUE); + + // if parent is in visible chain apply switch_on state and notify it immediately + if (getParent() && getParent()->isInVisibleChain()) + { + LL_DEBUGS("SpeakingIndicator") << "Indicator is in visible chain, notifying parent: " << mSpeakerId << LL_ENDL; + setVisible((BOOL)switch_on); + notifyParentVisibilityChanged(); + } + + // otherwise remember necessary state and mark itself as dirty. + // State will be applied i next draw when parents chain became visible. + else + { + LL_DEBUGS("SpeakingIndicator") << "Indicator is not in visible chain, parent won't be notified: " << mSpeakerId << LL_ENDL; + mIsSwitchDirty = true; + mShouldSwitchOn = switch_on; + } +} + +////////////////////////////////////////////////////////////////////////// +// PRIVATE SECTION +////////////////////////////////////////////////////////////////////////// +void LLOutputMonitorCtrl::notifyParentVisibilityChanged() +{ + LL_DEBUGS("SpeakingIndicator") << "Notify parent that visibility was changed: " << mSpeakerId << " ,new_visibility: " << getVisible() << LL_ENDL; + + LLSD params = LLSD().with("visibility_changed", getVisible()); + + notifyParent(params); +} + +// EOF diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h index 85ea552a57..2bbfa251e9 100644 --- a/indra/newview/lloutputmonitorctrl.h +++ b/indra/newview/lloutputmonitorctrl.h @@ -36,6 +36,7 @@ #include "v4color.h" #include "llview.h" #include "llmutelist.h" +#include "llspeakingindicatormanager.h" class LLTextBox; class LLUICtrlFactory; @@ -45,7 +46,7 @@ class LLUICtrlFactory; // class LLOutputMonitorCtrl -: public LLView, LLMuteListObserver +: public LLView, public LLSpeakingIndicator, LLMuteListObserver { public: struct Params : public LLInitParam::Block @@ -90,7 +91,29 @@ public: //called by mute list virtual void onChange(); + /** + * Implementation of LLSpeakingIndicator interface. + * Behavior is implemented via changing visibility. + * + * If instance is in visible chain now (all parents are visible) it changes visibility + * and notify parent about this. + * + * Otherwise it marks an instance as dirty and stores necessary visibility. + * It will be applied in next draw and parent will be notified. + */ + virtual void switchIndicator(bool switch_on); + private: + + /** + * Notifies parent about changed visibility. + * + * Passes LLSD with "visibility_changed" => value. + * For now it is processed by LLAvatarListItem to update (reshape) its children. + * Implemented fo complete EXT-3976 + */ + void notifyParentVisibilityChanged(); + //static LLColor4 sColorMuted; //static LLColor4 sColorNormal; //static LLColor4 sColorOverdriven; @@ -117,6 +140,10 @@ private: /** uuid of a speaker being monitored */ LLUUID mSpeakerId; + + /** indicates if the instance is dirty and should notify parent */ + bool mIsSwitchDirty; + bool mShouldSwitchOn; }; #endif diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index f3d6dbbb46..85e95ca1d6 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -52,6 +52,7 @@ #include "llfloaterreg.h" #include "llnotificationsutil.h" #include "llvoiceclient.h" +#include "llnamebox.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLDropTarget @@ -594,8 +595,11 @@ void LLPanelAvatarProfile::processGroupProperties(const LLAvatarGroups* avatar_g if (it != mGroups.begin()) groups += ", "; - - std::string group_url="[secondlife:///app/group/" + it->second.asString() + "/about " + it->first + "]"; + std::string group_name = LLURI::escape(it->first); + std::string group_url= it->second.notNull() + ? "[secondlife:///app/group/" + it->second.asString() + "/about " + group_name + "]" + : getString("no_group_text"); + groups += group_url; } @@ -621,19 +625,15 @@ void LLPanelAvatarProfile::fillCommonData(const LLAvatarData* avatar_data) void LLPanelAvatarProfile::fillPartnerData(const LLAvatarData* avatar_data) { + LLNameBox* name_box = getChild("partner_text"); if (avatar_data->partner_id.notNull()) { - std::string first, last; - BOOL found = gCacheName->getName(avatar_data->partner_id, first, last); - if (found) - { - childSetTextArg("partner_text", "[FIRST]", first); - childSetTextArg("partner_text", "[LAST]", last); - } + name_box->setNameID(avatar_data->partner_id, FALSE); } else { - childSetTextArg("partner_text", "[FIRST]", getString("no_partner_text")); + name_box->setNameID(LLUUID::null, FALSE); + name_box->setText(getString("no_partner_text")); } } diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index e29320ffc2..3f5d80c123 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -72,6 +72,7 @@ #include "llviewerwindow.h" // for window width, height #include "llappviewer.h" // abortQuit() #include "lltrans.h" +#include "llstatusbar.h" const S32 MINIMUM_PRICE_FOR_LISTING = 50; // L$ const S32 MATURE_UNDEFINED = -1; @@ -1364,6 +1365,7 @@ static const S32 CB_ITEM_PG = 1; LLPanelClassifiedEdit::LLPanelClassifiedEdit() : LLPanelClassifiedInfo() , mIsNew(false) + , mCanClose(false) { } @@ -1559,7 +1561,7 @@ void LLPanelClassifiedEdit::resetControls() bool LLPanelClassifiedEdit::canClose() { - return isValidName(); + return mCanClose; } void LLPanelClassifiedEdit::sendUpdate() @@ -1676,12 +1678,23 @@ void LLPanelClassifiedEdit::onChange() void LLPanelClassifiedEdit::onSaveClick() { + mCanClose = false; + if(!isValidName()) { notifyInvalidName(); return; } + if(isNew()) + { + if(gStatusBar->getBalance() < getPriceForListing()) + { + LLNotificationsUtil::add("ClassifiedInsufficientFunds"); + return; + } + } + mCanClose = true; sendUpdate(); resetDirty(); } diff --git a/indra/newview/llpanelclassified.h b/indra/newview/llpanelclassified.h index 10fdf60bbe..e46806f576 100644 --- a/indra/newview/llpanelclassified.h +++ b/indra/newview/llpanelclassified.h @@ -340,6 +340,7 @@ protected: private: bool mIsNew; + bool mCanClose; }; #endif // LL_LLPANELCLASSIFIED_H diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index fff2575893..569d3001bf 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -151,6 +151,11 @@ BOOL LLPanelGroup::postBuild() button->setVisible(true); button->setEnabled(false); + button = getChild("btn_call"); + button->setClickedCallback(onBtnGroupCallClicked, this); + + button = getChild("btn_chat"); + button->setClickedCallback(onBtnGroupChatClicked, this); button = getChild("btn_join"); button->setVisible(false); @@ -215,6 +220,8 @@ void LLPanelGroup::reposButtons() reposButton("btn_create"); reposButton("btn_refresh"); reposButton("btn_cancel"); + reposButton("btn_chat"); + reposButton("btn_call"); } void LLPanelGroup::reshape(S32 width, S32 height, BOOL called_from_parent ) @@ -262,6 +269,18 @@ void LLPanelGroup::onBtnApply(void* user_data) self->apply(); } +void LLPanelGroup::onBtnGroupCallClicked(void* user_data) +{ + LLPanelGroup* self = static_cast(user_data); + self->callGroup(); +} + +void LLPanelGroup::onBtnGroupChatClicked(void* user_data) +{ + LLPanelGroup* self = static_cast(user_data); + self->chatGroup(); +} + void LLPanelGroup::onBtnJoin() { lldebugs << "joining group: " << mID << llendl; @@ -349,6 +368,8 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) LLButton* button_create = findChild("btn_create"); LLButton* button_join = findChild("btn_join"); LLButton* button_cancel = findChild("btn_cancel"); + LLButton* button_call = findChild("btn_call"); + LLButton* button_chat = findChild("btn_chat"); bool is_null_group_id = group_id == LLUUID::null; @@ -362,6 +383,11 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) if(button_cancel) button_cancel->setVisible(!is_null_group_id); + if(button_call) + button_call->setVisible(!is_null_group_id); + if(button_chat) + button_chat->setVisible(!is_null_group_id); + getChild("prepend_founded_by")->setVisible(!is_null_group_id); LLAccordionCtrl* tab_ctrl = findChild("group_accordion"); @@ -393,12 +419,17 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) if(tab_land->getDisplayChildren()) tab_land->changeOpenClose(tab_land->getDisplayChildren()); - tab_roles->canOpenClose(false); - tab_notices->canOpenClose(false); - tab_land->canOpenClose(false); + tab_roles->setVisible(false); + tab_notices->setVisible(false); + tab_land->setVisible(false); getChild("group_name")->setVisible(false); getChild("group_name_editor")->setVisible(true); + + if(button_call) + button_call->setVisible(false); + if(button_chat) + button_chat->setVisible(false); } else { @@ -413,19 +444,29 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) if(tab_land->getDisplayChildren()) tab_land->changeOpenClose(tab_land->getDisplayChildren()); } + + LLGroupData agent_gdatap; + bool is_member = gAgent.getGroupData(mID,agent_gdatap); - tab_roles->canOpenClose(true); - tab_notices->canOpenClose(true); - tab_land->canOpenClose(true); + tab_roles->setVisible(is_member); + tab_notices->setVisible(is_member); + tab_land->setVisible(is_member); getChild("group_name")->setVisible(true); getChild("group_name_editor")->setVisible(false); + + if(button_apply) + button_apply->setVisible(is_member); + if(button_call) + button_call->setVisible(is_member); + if(button_chat) + button_chat->setVisible(is_member); } reposButtons(); } -bool LLPanelGroup::apply(LLPanelGroupTab* tab) +bool LLPanelGroup::apply(LLPanelGroupTab* tab) { if(!tab) return false; @@ -468,12 +509,17 @@ void LLPanelGroup::draw() childEnable("btn_refresh"); } - bool enable = false; - std::string mesg; - for(std::vector::iterator it = mTabs.begin();it!=mTabs.end();++it) - enable = enable || (*it)->needsApply(mesg); + LLButton* button_apply = findChild("btn_apply"); + + if(button_apply && button_apply->getVisible()) + { + bool enable = false; + std::string mesg; + for(std::vector::iterator it = mTabs.begin();it!=mTabs.end();++it) + enable = enable || (*it)->needsApply(mesg); - childSetEnabled("btn_apply", enable); + childSetEnabled("btn_apply", enable); + } } void LLPanelGroup::refreshData() @@ -488,6 +534,15 @@ void LLPanelGroup::refreshData() mRefreshTimer.setTimerExpirySec(5); } +void LLPanelGroup::callGroup() +{ + LLGroupActions::startCall(getID()); +} + +void LLPanelGroup::chatGroup() +{ + LLGroupActions::startIM(getID()); +} void LLPanelGroup::showNotice(const std::string& subject, const std::string& message, diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h index f6aefdb676..7ea5e67b44 100644 --- a/indra/newview/llpanelgroup.h +++ b/indra/newview/llpanelgroup.h @@ -74,6 +74,8 @@ public: bool apply(); void refreshData(); + void callGroup(); + void chatGroup(); virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); @@ -103,6 +105,8 @@ protected: static void onBtnApply(void*); static void onBtnRefresh(void*); + static void onBtnGroupCallClicked(void*); + static void onBtnGroupChatClicked(void*); void reposButton(const std::string& name); void reposButtons(); diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index 31dfdde887..21b253223f 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -580,7 +580,6 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) } } - mComboActiveTitle->resetDirty(); } // If this was just a titles update, we are done. @@ -595,8 +594,6 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) { mCtrlShowInGroupList->set(gdatap->mShowInList); mCtrlShowInGroupList->setEnabled(mAllowEdit && can_change_ident); - mCtrlShowInGroupList->resetDirty(); - } if (mComboMature) { @@ -610,19 +607,16 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) } mComboMature->setEnabled(mAllowEdit && can_change_ident); mComboMature->setVisible( !gAgent.isTeen() ); - mComboMature->resetDirty(); } if (mCtrlOpenEnrollment) { mCtrlOpenEnrollment->set(gdatap->mOpenEnrollment); mCtrlOpenEnrollment->setEnabled(mAllowEdit && can_change_member_opts); - mCtrlOpenEnrollment->resetDirty(); } if (mCtrlEnrollmentFee) { mCtrlEnrollmentFee->set(gdatap->mMembershipFee > 0); mCtrlEnrollmentFee->setEnabled(mAllowEdit && can_change_member_opts); - mCtrlEnrollmentFee->resetDirty(); } if (mSpinEnrollmentFee) @@ -632,7 +626,6 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) mSpinEnrollmentFee->setEnabled( mAllowEdit && (fee > 0) && can_change_member_opts); - mSpinEnrollmentFee->resetDirty(); } if (mCtrlReceiveNotices) { @@ -641,7 +634,6 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) { mCtrlReceiveNotices->setEnabled(mAllowEdit); } - mCtrlReceiveNotices->resetDirty(); } @@ -665,7 +657,6 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) if (mEditCharter) { mEditCharter->setText(gdatap->mCharter); - mEditCharter->resetDirty(); } if (mListVisibleMembers) @@ -693,6 +684,8 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) mListVisibleMembers->addElement(row); } } + + resetDirty(); } void LLPanelGroupGeneral::updateMembers() diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp index 6210973dae..45fc3d4688 100644 --- a/indra/newview/llpanelgroupnotices.cpp +++ b/indra/newview/llpanelgroupnotices.cpp @@ -614,7 +614,7 @@ void LLPanelGroupNotices::showNotice(const std::string& subject, mViewInventoryIcon->setVisible(TRUE); std::stringstream ss; - ss << " " << inventory_name; + ss << " " << LLViewerInventoryItem::getDisplayName(inventory_name); mViewInventoryName->setText(ss.str()); mBtnOpenAttachment->setEnabled(TRUE); diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index 29b647415c..0e55ff3214 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -460,17 +460,8 @@ LLPanelGroupSubTab::~LLPanelGroupSubTab() { } -BOOL LLPanelGroupSubTab::postBuild() -{ - // Hook up the search widgets. - bool recurse = true; - mSearchEditor = getChild("filter_input", recurse); - - if (!mSearchEditor) - return FALSE; - - mSearchEditor->setCommitCallback(boost::bind(&LLPanelGroupSubTab::setSearchFilter, this, _2)); - +BOOL LLPanelGroupSubTab::postBuildSubTab(LLView* root) +{ // Get icons for later use. mActionIcons.clear(); @@ -488,6 +479,19 @@ BOOL LLPanelGroupSubTab::postBuild() { mActionIcons["partial"] = getString("power_partial_icon"); } + return TRUE; +} + +BOOL LLPanelGroupSubTab::postBuild() +{ + // Hook up the search widgets. + bool recurse = true; + mSearchEditor = getChild("filter_input", recurse); + + if (!mSearchEditor) + return FALSE; + + mSearchEditor->setCommitCallback(boost::bind(&LLPanelGroupSubTab::setSearchFilter, this, _2)); return LLPanelGroupTab::postBuild(); } @@ -567,7 +571,6 @@ bool LLPanelGroupSubTab::matchesActionSearchFilter(std::string action) void LLPanelGroupSubTab::buildActionsList(LLScrollListCtrl* ctrl, U64 allowed_by_some, U64 allowed_by_all, - icon_map_t& icons, LLUICtrl::commit_callback_t commit_callback, BOOL show_all, BOOL filter, @@ -588,7 +591,6 @@ void LLPanelGroupSubTab::buildActionsList(LLScrollListCtrl* ctrl, allowed_by_some, allowed_by_all, (*ras_it), - icons, commit_callback, show_all, filter, @@ -600,7 +602,6 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl, U64 allowed_by_some, U64 allowed_by_all, LLRoleActionSet* action_set, - icon_map_t& icons, LLUICtrl::commit_callback_t commit_callback, BOOL show_all, BOOL filter, @@ -614,26 +615,26 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl, LLSD row; row["columns"][0]["column"] = "icon"; - icon_map_t::iterator iter = icons.find("folder"); - if (iter != icons.end()) + row["columns"][0]["type"] = "icon"; + + icon_map_t::iterator iter = mActionIcons.find("folder"); + if (iter != mActionIcons.end()) { - row["columns"][0]["type"] = "icon"; row["columns"][0]["value"] = (*iter).second; } row["columns"][1]["column"] = "action"; + row["columns"][1]["type"] = "text"; row["columns"][1]["value"] = action_set->mActionSetData->mName; row["columns"][1]["font"]["name"] = "SANSSERIF_SMALL"; - row["columns"][1]["font"]["style"] = "BOLD"; + LLScrollListItem* title_row = ctrl->addElement(row, ADD_BOTTOM, action_set->mActionSetData); - LLScrollListText* name_textp = dynamic_cast(title_row->getColumn(1)); + LLScrollListText* name_textp = dynamic_cast(title_row->getColumn(2)); //?? I have no idea fix getColumn(1) return column spacer... if (name_textp) name_textp->setFontStyle(LLFontGL::BOLD); - - bool category_matches_filter = (filter) ? matchesActionSearchFilter(action_set->mActionSetData->mName) : true; std::vector::iterator ra_it = action_set->mActions.begin(); @@ -686,8 +687,8 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl, { if (show_full_strength) { - icon_map_t::iterator iter = icons.find("full"); - if (iter != icons.end()) + icon_map_t::iterator iter = mActionIcons.find("full"); + if (iter != mActionIcons.end()) { row["columns"][column_index]["column"] = "checkbox"; row["columns"][column_index]["type"] = "icon"; @@ -697,8 +698,8 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl, } else { - icon_map_t::iterator iter = icons.find("partial"); - if (iter != icons.end()) + icon_map_t::iterator iter = mActionIcons.find("partial"); + if (iter != mActionIcons.end()) { row["columns"][column_index]["column"] = "checkbox"; row["columns"][column_index]["type"] = "icon"; @@ -792,6 +793,8 @@ LLPanelGroupMembersSubTab::~LLPanelGroupMembersSubTab() BOOL LLPanelGroupMembersSubTab::postBuildSubTab(LLView* root) { + LLPanelGroupSubTab::postBuildSubTab(root); + // Upcast parent so we can ask it for sibling controls. LLPanelGroupRoles* parent = (LLPanelGroupRoles*) root; @@ -888,7 +891,6 @@ void LLPanelGroupMembersSubTab::handleMemberSelect() buildActionsList(mAllowedActionsList, allowed_by_some, allowed_by_all, - mActionIcons, NULL, FALSE, FALSE, @@ -1211,7 +1213,6 @@ void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id, buildActionsList(mAllowedActionsList, powers_some_have, powers_all_have, - mActionIcons, NULL, FALSE, FALSE, @@ -1684,6 +1685,8 @@ LLPanelGroupRolesSubTab::~LLPanelGroupRolesSubTab() BOOL LLPanelGroupRolesSubTab::postBuildSubTab(LLView* root) { + LLPanelGroupSubTab::postBuildSubTab(root); + // Upcast parent so we can ask it for sibling controls. LLPanelGroupRoles* parent = (LLPanelGroupRoles*) root; @@ -1994,7 +1997,6 @@ void LLPanelGroupRolesSubTab::handleRoleSelect() buildActionsList(mAllowedActionsList, rd.mRolePowers, 0LL, - mActionIcons, boost::bind(&LLPanelGroupRolesSubTab::handleActionCheck, this, _1, false), TRUE, FALSE, @@ -2381,6 +2383,8 @@ LLPanelGroupActionsSubTab::~LLPanelGroupActionsSubTab() BOOL LLPanelGroupActionsSubTab::postBuildSubTab(LLView* root) { + LLPanelGroupSubTab::postBuildSubTab(root); + // Upcast parent so we can ask it for sibling controls. LLPanelGroupRoles* parent = (LLPanelGroupRoles*) root; @@ -2448,7 +2452,6 @@ void LLPanelGroupActionsSubTab::update(LLGroupChange gc) buildActionsList(mActionList, GP_ALL_POWERS, GP_ALL_POWERS, - mActionIcons, NULL, FALSE, TRUE, diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h index bb3c9096cf..2f81900e60 100644 --- a/indra/newview/llpanelgrouproles.h +++ b/indra/newview/llpanelgrouproles.h @@ -108,7 +108,7 @@ public: virtual BOOL postBuild(); // This allows sub-tabs to collect child widgets from a higher level in the view hierarchy. - virtual BOOL postBuildSubTab(LLView* root) { return TRUE; } + virtual BOOL postBuildSubTab(LLView* root); virtual void setSearchFilter( const std::string& filter ); @@ -117,10 +117,15 @@ public: // Helper functions bool matchesActionSearchFilter(std::string action); + + + void setFooterEnabled(BOOL enable); + + virtual void setGroupID(const LLUUID& id); +protected: void buildActionsList(LLScrollListCtrl* ctrl, U64 allowed_by_some, U64 allowed_by_all, - icon_map_t& icons, LLUICtrl::commit_callback_t commit_callback, BOOL show_all, BOOL filter, @@ -129,15 +134,11 @@ public: U64 allowed_by_some, U64 allowed_by_all, LLRoleActionSet* action_set, - icon_map_t& icons, LLUICtrl::commit_callback_t commit_callback, BOOL show_all, BOOL filter, BOOL is_owner_role); - void setFooterEnabled(BOOL enable); - - virtual void setGroupID(const LLUUID& id); protected: LLPanel* mHeader; LLPanel* mFooter; diff --git a/indra/newview/llpanelhome.cpp b/indra/newview/llpanelhome.cpp index 92b6d2f619..713d2d79b4 100644 --- a/indra/newview/llpanelhome.cpp +++ b/indra/newview/llpanelhome.cpp @@ -37,7 +37,7 @@ #include "llmediactrl.h" #include "llviewerhome.h" -static LLRegisterPanelClassWrapper t_people("panel_sidetray_home"); +static LLRegisterPanelClassWrapper t_home("panel_sidetray_home"); LLPanelHome::LLPanelHome() : LLPanel(), diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index 279818d52f..b1cdb4d81f 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -46,6 +46,7 @@ #include "llimview.h" #include "llvoicechannel.h" #include "llsidetray.h" +#include "llspeakers.h" #include "lltrans.h" void LLPanelChatControlPanel::onCallButtonClicked() @@ -70,9 +71,9 @@ void LLPanelChatControlPanel::onVoiceChannelStateChanged(const LLVoiceChannel::E void LLPanelChatControlPanel::updateButtons(bool is_call_started) { - childSetVisible("end_call_btn", is_call_started); - childSetVisible("voice_ctrls_btn", is_call_started); - childSetVisible("call_btn", ! is_call_started); + childSetVisible("end_call_btn_panel", is_call_started); + childSetVisible("voice_ctrls_btn_panel", is_call_started); + childSetVisible("call_btn_panel", ! is_call_started); } LLPanelChatControlPanel::~LLPanelChatControlPanel() @@ -302,7 +303,7 @@ void LLPanelGroupControlPanel::setSessionId(const LLUUID& session_id) { LLPanelChatControlPanel::setSessionId(session_id); - mGroupID = LLIMModel::getInstance()->getOtherParticipantID(session_id); + mGroupID = session_id; // for group and Ad-hoc chat we need to include agent into list if(!mParticipantList) diff --git a/indra/newview/llpanellandaudio.cpp b/indra/newview/llpanellandaudio.cpp index 920fca66f2..6a4c909759 100644 --- a/indra/newview/llpanellandaudio.cpp +++ b/indra/newview/llpanellandaudio.cpp @@ -37,6 +37,7 @@ // viewer includes #include "llmimetypes.h" #include "llviewerparcelmgr.h" +#include "llviewerregion.h" #include "lluictrlfactory.h" // library includes @@ -83,8 +84,14 @@ BOOL LLPanelLandAudio::postBuild() mCheckSoundLocal = getChild("check sound local"); childSetCommitCallback("check sound local", onCommitAny, this); - mRadioVoiceChat = getChild("parcel_voice_channel"); - childSetCommitCallback("parcel_voice_channel", onCommitAny, this); + mCheckParcelEnableVoice = getChild("parcel_enable_voice_channel"); + childSetCommitCallback("parcel_enable_voice_channel", onCommitAny, this); + + // This one is always disabled so no need for a commit callback + mCheckEstateDisabledVoice = getChild("parcel_enable_voice_channel_is_estate_disabled"); + + mCheckParcelVoiceLocal = getChild("parcel_enable_voice_channel_local"); + childSetCommitCallback("parcel_enable_voice_channel_local", onCommitAny, this); mMusicURLEdit = getChild("music_url"); childSetCommitCallback("music_url", onCommitAny, this); @@ -118,19 +125,33 @@ void LLPanelLandAudio::refresh() mMusicUrlCheck->set( parcel->getObscureMusic() ); mMusicUrlCheck->setEnabled( can_change_media ); - if(parcel->getParcelFlagAllowVoice()) + bool allow_voice = parcel->getParcelFlagAllowVoice(); + + LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion(); + if (region && region->isVoiceEnabled()) { - if(parcel->getParcelFlagUseEstateVoiceChannel()) - mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatEstate); - else - mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatPrivate); + mCheckEstateDisabledVoice->setVisible(false); + + mCheckParcelEnableVoice->setVisible(true); + mCheckParcelEnableVoice->setEnabled( can_change_media ); + mCheckParcelEnableVoice->set(allow_voice); + + mCheckParcelVoiceLocal->setEnabled( can_change_media && allow_voice ); } else { - mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatDisable); + // Voice disabled at estate level, overrides parcel settings + // Replace the parcel voice checkbox with a disabled one + // labelled with an explanatory message + mCheckEstateDisabledVoice->setVisible(true); + + mCheckParcelEnableVoice->setVisible(false); + mCheckParcelEnableVoice->setEnabled(false); + mCheckParcelVoiceLocal->setEnabled(false); } - mRadioVoiceChat->setEnabled( can_change_media ); + mCheckParcelEnableVoice->set(allow_voice); + mCheckParcelVoiceLocal->set(!parcel->getParcelFlagUseEstateVoiceChannel()); mMusicURLEdit->setText(parcel->getMusicURL()); mMusicURLEdit->setEnabled( can_change_media ); @@ -149,30 +170,11 @@ void LLPanelLandAudio::onCommitAny(LLUICtrl*, void *userdata) // Extract data from UI BOOL sound_local = self->mCheckSoundLocal->get(); - int voice_setting = self->mRadioVoiceChat->getSelectedIndex(); std::string music_url = self->mMusicURLEdit->getText(); U8 obscure_music = self->mMusicUrlCheck->get(); - - BOOL voice_enabled; - BOOL voice_estate_chan; - - switch(voice_setting) - { - default: - case kRadioVoiceChatEstate: - voice_enabled = TRUE; - voice_estate_chan = TRUE; - break; - case kRadioVoiceChatPrivate: - voice_enabled = TRUE; - voice_estate_chan = FALSE; - break; - case kRadioVoiceChatDisable: - voice_enabled = FALSE; - voice_estate_chan = FALSE; - break; - } + BOOL voice_enabled = self->mCheckParcelEnableVoice->get(); + BOOL voice_estate_chan = !self->mCheckParcelVoiceLocal->get(); // Remove leading/trailing whitespace (common when copying/pasting) LLStringUtil::trim(music_url); diff --git a/indra/newview/llpanellandaudio.h b/indra/newview/llpanellandaudio.h index de5da95fa4..19766a40b6 100644 --- a/indra/newview/llpanellandaudio.h +++ b/indra/newview/llpanellandaudio.h @@ -52,7 +52,9 @@ private: private: LLCheckBoxCtrl* mCheckSoundLocal; - LLRadioGroup* mRadioVoiceChat; + LLCheckBoxCtrl* mCheckParcelEnableVoice; + LLCheckBoxCtrl* mCheckEstateDisabledVoice; + LLCheckBoxCtrl* mCheckParcelVoiceLocal; LLLineEditor* mMusicURLEdit; LLCheckBoxCtrl* mMusicUrlCheck; diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp index 597b8bdb2d..9654e17659 100644 --- a/indra/newview/llpanellandmarkinfo.cpp +++ b/indra/newview/llpanellandmarkinfo.cpp @@ -185,6 +185,13 @@ void LLPanelLandmarkInfo::processParcelInfo(const LLParcelData& parcel_data) region_z = llround(mPosRegion.mV[VZ]); } + LLSD info; + info["update_verbs"] = true; + info["global_x"] = parcel_data.global_x; + info["global_y"] = parcel_data.global_y; + info["global_z"] = parcel_data.global_z; + notifyParent(info); + if (mInfoType == CREATE_LANDMARK) { if (parcel_data.name.empty()) diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 30acf37f82..47feef496a 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -35,6 +35,7 @@ #include "llbutton.h" #include "llfloaterreg.h" +#include "llnotificationsutil.h" #include "llsdutil.h" #include "llsdutil_math.h" #include "llregionhandle.h" @@ -304,6 +305,29 @@ void LLLandmarksPanel::updateShowFolderState() ); } +void LLLandmarksPanel::setItemSelected(const LLUUID& obj_id, BOOL take_keyboard_focus) +{ + if (selectItemInAccordionTab(mFavoritesInventoryPanel, "tab_favorites", obj_id, take_keyboard_focus)) + { + return; + } + + if (selectItemInAccordionTab(mLandmarksInventoryPanel, "tab_landmarks", obj_id, take_keyboard_focus)) + { + return; + } + + if (selectItemInAccordionTab(mMyInventoryPanel, "tab_inventory", obj_id, take_keyboard_focus)) + { + return; + } + + if (selectItemInAccordionTab(mLibraryInventoryPanel, "tab_library", obj_id, take_keyboard_focus)) + { + return; + } +} + ////////////////////////////////////////////////////////////////////////// // PROTECTED METHODS ////////////////////////////////////////////////////////////////////////// @@ -349,6 +373,36 @@ LLFolderViewItem* LLLandmarksPanel::getCurSelectedItem() const return mCurrentSelectedList ? mCurrentSelectedList->getRootFolder()->getCurSelectedItem() : NULL; } +LLFolderViewItem* LLLandmarksPanel::selectItemInAccordionTab(LLPlacesInventoryPanel* inventory_list, + const std::string& tab_name, + const LLUUID& obj_id, + BOOL take_keyboard_focus) const +{ + if (!inventory_list) + return NULL; + + LLFolderView* folder_view = inventory_list->getRootFolder(); + + LLFolderViewItem* item = folder_view->getItemByID(obj_id); + if (!item) + return NULL; + + LLAccordionCtrlTab* tab = getChild(tab_name); + if (!tab->isExpanded()) + { + tab->changeOpenClose(false); + } + + folder_view->setSelection(item, FALSE, take_keyboard_focus); + + LLAccordionCtrl* accordion = getChild("landmarks_accordion"); + LLRect screen_rc; + localRectToScreen(item->getRect(), &screen_rc); + accordion->notifyParent(LLSD().with("scrollToShowRect", screen_rc.getValue())); + + return item; +} + void LLLandmarksPanel::updateSortOrder(LLInventoryPanel* panel, bool byDate) { if(!panel) return; @@ -632,8 +686,7 @@ void LLLandmarksPanel::onAddAction(const LLSD& userdata) const LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentPos(); if(landmark) { - LLSideTray::getInstance()->showPanel("panel_places", - LLSD().with("type", "landmark").with("id",landmark->getUUID())); + LLNotificationsUtil::add("LandmarkAlreadyExists"); } else { diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index 569739237d..96b790844c 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -73,6 +73,11 @@ public: */ void updateShowFolderState(); + /** + * Selects item with "obj_id" in one of accordion tabs. + */ + void setItemSelected(const LLUUID& obj_id, BOOL take_keyboard_focus); + protected: /** * @return true - if current selected panel is not null and selected item is a landmark @@ -81,6 +86,17 @@ protected: bool isReceivedFolderSelected() const; void doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb); LLFolderViewItem* getCurSelectedItem() const; + + /** + * Selects item with "obj_id" in "inventory_list" and scrolls accordion + * scrollbar to show the item. + * Returns pointer to the item if it is found in "inventory_list", otherwise NULL. + */ + LLFolderViewItem* selectItemInAccordionTab(LLPlacesInventoryPanel* inventory_list, + const std::string& tab_name, + const LLUUID& obj_id, + BOOL take_keyboard_focus) const; + void updateSortOrder(LLInventoryPanel* panel, bool byDate); //LLRemoteParcelInfoObserver interface diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index e74a39c85c..a5a61f0c7b 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -51,6 +51,8 @@ #include "llviewermenu.h" #include "llviewertexturelist.h" +const std::string FILTERS_FILENAME("filters.xml"); + static LLRegisterPanelClassWrapper t_inventory("panel_main_inventory"); void on_file_loaded_for_save(BOOL success, @@ -160,7 +162,7 @@ BOOL LLPanelMainInventory::postBuild() // Now load the stored settings from disk, if available. std::ostringstream filterSaveName; - filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "filters.xml"); + filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, FILTERS_FILENAME); llinfos << "LLPanelMainInventory::init: reading from " << filterSaveName << llendl; llifstream file(filterSaveName.str()); LLSD savedFilterState; @@ -230,7 +232,7 @@ LLPanelMainInventory::~LLPanelMainInventory( void ) } std::ostringstream filterSaveName; - filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "filters.xml"); + filterSaveName << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, FILTERS_FILENAME); llofstream filtersFile(filterSaveName.str()); if(!LLSDSerialize::toPrettyXML(filterRoot, filtersFile)) { diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 6a61e0f02f..d17c287cc7 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -73,7 +73,7 @@ #include "pipeline.h" #include "llviewercontrol.h" #include "lluictrlfactory.h" -#include "llfirstuse.h" +//#include "llfirstuse.h" #include "lldrawpool.h" @@ -682,7 +682,7 @@ void LLPanelObject::getState( ) if (objectp->getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT)) { selected_item = MI_SCULPT; - LLFirstUse::useSculptedPrim(); + //LLFirstUse::useSculptedPrim(); } diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 43366ef814..d4376550d6 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -445,7 +445,7 @@ bool remove_task_inventory_callback(const LLSD& notification, const LLSD& respon } // helper for remove -// ! REFACTOR ! two_uuids_list_t is also defined in llinevntorybridge.h, but differently. +// ! REFACTOR ! two_uuids_list_t is also defined in llinventorybridge.h, but differently. typedef std::pair > panel_two_uuids_list_t; typedef std::pair remove_data_t; BOOL LLTaskInvFVBridge::removeItem() diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 8e14074de1..fd5ce7a46d 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -35,6 +35,7 @@ #include "llagent.h" #include "llagentwearables.h" +#include "llappearancemgr.h" #include "llbutton.h" #include "llfloaterreg.h" @@ -44,6 +45,8 @@ #include "llinventoryfunctions.h" #include "llinventorypanel.h" #include "lllandmark.h" +#include "lllineeditor.h" +#include "llmodaldialog.h" #include "llsidepanelappearance.h" #include "llsidetray.h" #include "lltabcontainer.h" @@ -61,12 +64,75 @@ static LLRegisterPanelClassWrapper t_inventory("panel_outfits_inventory"); bool LLPanelOutfitsInventory::sShowDebugEditor = false; +class LLOutfitSaveAsDialog : public LLModalDialog +{ +private: + std::string mItemName; + std::string mTempItemName; + + boost::signals2::signal mSaveAsSignal; + +public: + LLOutfitSaveAsDialog( const LLSD& key ) + : LLModalDialog( key ), + mTempItemName(key.asString()) + { + } + + BOOL postBuild() + { + getChild("Save")->setCommitCallback(boost::bind(&LLOutfitSaveAsDialog::onSave, this )); + getChild("Cancel")->setCommitCallback(boost::bind(&LLOutfitSaveAsDialog::onCancel, this )); + + childSetTextArg("name ed", "[DESC]", mTempItemName); + return TRUE; + } + + void setSaveAsCommit( const boost::signals2::signal::slot_type& cb ) + { + mSaveAsSignal.connect(cb); + } + + virtual void onOpen(const LLSD& key) + { + LLLineEditor* edit = getChild("name ed"); + if (edit) + { + edit->setFocus(TRUE); + edit->selectAll(); + } + } + + void onSave() + { + mItemName = childGetValue("name ed").asString(); + LLStringUtil::trim(mItemName); + if( !mItemName.empty() ) + { + mSaveAsSignal(mItemName); + closeFloater(); // destroys this object + } + } + + void onCancel() + { + closeFloater(); // destroys this object + } +}; + LLPanelOutfitsInventory::LLPanelOutfitsInventory() : mActivePanel(NULL), mParent(NULL) { mSavedFolderState = new LLSaveFolderState(); mSavedFolderState->setApply(FALSE); + + static bool registered_dialog = false; + if (!registered_dialog) + { + LLFloaterReg::add("outfit_save_as", "floater_outfit_save_as.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + registered_dialog = true; + } } LLPanelOutfitsInventory::~LLPanelOutfitsInventory() @@ -84,6 +150,14 @@ BOOL LLPanelOutfitsInventory::postBuild() return TRUE; } +// virtual +void LLPanelOutfitsInventory::onOpen(const LLSD& key) +{ + // Make sure we know which tab is selected, update the filter, + // and update verbs. + onTabChange(); +} + void LLPanelOutfitsInventory::updateVerbs() { if (mParent) @@ -94,6 +168,7 @@ void LLPanelOutfitsInventory::updateVerbs() if (mListCommands) { mListCommands->childSetVisible("look_edit_btn",sShowDebugEditor); + updateListCommands(); } } @@ -168,15 +243,36 @@ void LLPanelOutfitsInventory::onEdit() { } -void LLPanelOutfitsInventory::onNew() +void LLPanelOutfitsInventory::onSave() +{ + std::string outfit_name; + + if (!LLAppearanceManager::getInstance()->getBaseOutfitName(outfit_name)) + { + outfit_name = LLViewerFolderType::lookupNewCategoryName(LLFolderType::FT_OUTFIT); + } + + LLOutfitSaveAsDialog* save_as_dialog = LLFloaterReg::showTypedInstance("outfit_save_as", LLSD(outfit_name), TRUE); + if (save_as_dialog) + { + save_as_dialog->setSaveAsCommit(boost::bind(&LLPanelOutfitsInventory::onSaveCommit, this, _1 )); + } +} + +void LLPanelOutfitsInventory::onSaveCommit(const std::string& outfit_name) { - const std::string& outfit_name = LLViewerFolderType::lookupNewCategoryName(LLFolderType::FT_OUTFIT); LLUUID outfit_folder = gAgentWearables.makeNewOutfitLinks(outfit_name); + LLSD key; + LLSideTray::getInstance()->showPanel("panel_outfits_inventory", key); + + if (mAppearanceTabs) + { + mAppearanceTabs->selectTabByName("outfitslist_tab"); + } } void LLPanelOutfitsInventory::onSelectionChange(const std::deque &items, BOOL user_action) { - updateListCommands(); updateVerbs(); if (getRootFolder()->needsAutoRename() && items.size()) { @@ -264,9 +360,12 @@ void LLPanelOutfitsInventory::updateListCommands() { bool trash_enabled = isActionEnabled("delete"); bool wear_enabled = isActionEnabled("wear"); + bool make_outfit_enabled = isActionEnabled("make_outfit"); mListCommands->childSetEnabled("trash_btn", trash_enabled); mListCommands->childSetEnabled("wear_btn", wear_enabled); + mListCommands->childSetVisible("wear_btn", wear_enabled); + mListCommands->childSetEnabled("make_outfit_btn", make_outfit_enabled); } void LLPanelOutfitsInventory::onGearButtonClick() @@ -276,7 +375,7 @@ void LLPanelOutfitsInventory::onGearButtonClick() void LLPanelOutfitsInventory::onAddButtonClick() { - onNew(); + onSave(); } void LLPanelOutfitsInventory::showActionMenu(LLMenuGL* menu, std::string spawning_view_name) @@ -303,6 +402,8 @@ void LLPanelOutfitsInventory::onClipboardAction(const LLSD& userdata) { std::string command_name = userdata.asString(); getActivePanel()->getRootFolder()->doToSelected(getActivePanel()->getModel(),command_name); + updateListCommands(); + updateVerbs(); } void LLPanelOutfitsInventory::onCustomAction(const LLSD& userdata) @@ -313,7 +414,7 @@ void LLPanelOutfitsInventory::onCustomAction(const LLSD& userdata) const std::string command_name = userdata.asString(); if (command_name == "new") { - onNew(); + onSave(); } if (command_name == "edit") { @@ -323,6 +424,7 @@ void LLPanelOutfitsInventory::onCustomAction(const LLSD& userdata) { onWearButtonClick(); } + // Note: This option has been removed from the gear menu. if (command_name == "add") { onAdd(); @@ -343,20 +445,22 @@ void LLPanelOutfitsInventory::onCustomAction(const LLSD& userdata) { onClipboardAction("delete"); } + updateListCommands(); + updateVerbs(); } BOOL LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata) { const std::string command_name = userdata.asString(); - if (command_name == "delete") + if (command_name == "delete" || command_name == "remove") { BOOL can_delete = FALSE; LLFolderView *folder = getActivePanel()->getRootFolder(); if (folder) { - can_delete = TRUE; std::set selection_set; folder->getSelectionList(selection_set); + can_delete = (selection_set.size() > 0); for (std::set::iterator iter = selection_set.begin(); iter != selection_set.end(); ++iter) @@ -375,9 +479,9 @@ BOOL LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata) LLFolderView *folder = getActivePanel()->getRootFolder(); if (folder) { - can_delete = TRUE; std::set selection_set; folder->getSelectionList(selection_set); + can_delete = (selection_set.size() > 0); for (std::set::iterator iter = selection_set.begin(); iter != selection_set.end(); ++iter) @@ -391,10 +495,27 @@ BOOL LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata) } return FALSE; } + if (command_name == "rename" || + command_name == "delete_outfit") + { + return (getCorrectListenerForAction() != NULL) && hasItemsSelected(); + } + + if (command_name == "wear") + { + const BOOL is_my_outfits = (mActivePanel->getName() == "outfitslist_tab"); + if (!is_my_outfits) + { + return FALSE; + } + } + if (command_name == "make_outfit") + { + return TRUE; + } + if (command_name == "edit" || - command_name == "wear" || - command_name == "add" || - command_name == "remove" + command_name == "add" ) { return (getCorrectListenerForAction() != NULL); @@ -402,6 +523,19 @@ BOOL LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata) return TRUE; } +bool LLPanelOutfitsInventory::hasItemsSelected() +{ + bool has_items_selected = false; + LLFolderView *folder = getActivePanel()->getRootFolder(); + if (folder) + { + std::set selection_set; + folder->getSelectionList(selection_set); + has_items_selected = (selection_set.size() > 0); + } + return has_items_selected; +} + bool LLPanelOutfitsInventory::handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept) { *accept = ACCEPT_NO; @@ -425,17 +559,15 @@ bool LLPanelOutfitsInventory::handleDragAndDropToTrash(BOOL drop, EDragAndDropTy void LLPanelOutfitsInventory::initTabPanels() { mTabPanels.resize(2); + + LLInventoryPanel *cof_panel = getChild("cof_tab"); + cof_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); + mTabPanels[0] = cof_panel; - LLInventoryPanel *myoutfits_panel = getChild("outfitslist_accordionpanel"); + LLInventoryPanel *myoutfits_panel = getChild("outfitslist_tab"); myoutfits_panel->setFilterTypes(1LL << LLFolderType::FT_OUTFIT, LLInventoryFilter::FILTERTYPE_CATEGORY); myoutfits_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - mTabPanels[0] = myoutfits_panel; - mActivePanel = myoutfits_panel; - - - LLInventoryPanel *cof_panel = getChild("cof_accordionpanel"); - cof_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - mTabPanels[1] = cof_panel; + mTabPanels[1] = myoutfits_panel; for (tabpanels_vec_t::iterator iter = mTabPanels.begin(); iter != mTabPanels.end(); @@ -447,6 +579,7 @@ void LLPanelOutfitsInventory::initTabPanels() mAppearanceTabs = getChild("appearance_tabs"); mAppearanceTabs->setCommitCallback(boost::bind(&LLPanelOutfitsInventory::onTabChange, this)); + mActivePanel = (LLInventoryPanel*)mAppearanceTabs->getCurrentPanel(); } void LLPanelOutfitsInventory::onTabSelectionChange(LLInventoryPanel* tab_panel, const std::deque &items, BOOL user_action) @@ -479,9 +612,7 @@ void LLPanelOutfitsInventory::onTabChange() return; } mActivePanel->setFilterSubString(mFilterSubString); - - bool is_my_outfits = (mActivePanel->getName() == "outfitslist_accordionpanel"); - mListCommands->childSetEnabled("make_outfit_btn", is_my_outfits); + updateVerbs(); } LLInventoryPanel* LLPanelOutfitsInventory::getActivePanel() diff --git a/indra/newview/llpaneloutfitsinventory.h b/indra/newview/llpaneloutfitsinventory.h index 1e084750a0..76110e2a3f 100644 --- a/indra/newview/llpaneloutfitsinventory.h +++ b/indra/newview/llpaneloutfitsinventory.h @@ -53,12 +53,15 @@ public: virtual ~LLPanelOutfitsInventory(); /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); void onSearchEdit(const std::string& string); void onAdd(); void onRemove(); void onEdit(); - void onNew(); + void onSave(); + + void onSaveCommit(const std::string& item_name); void onSelectionChange(const std::deque &items, BOOL user_action); void onSelectorButtonClicked(); @@ -114,6 +117,7 @@ protected: BOOL isActionEnabled(const LLSD& command_name); void onCustomAction(const LLSD& command_name); bool handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept); + bool hasItemsSelected(); private: LLPanel* mListCommands; LLMenuGL* mMenuGearDefault; diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index e14a5778ad..c14b282488 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -35,6 +35,7 @@ // libs #include "llfloaterreg.h" #include "llmenugl.h" +#include "llnotificationsutil.h" #include "llfiltereditor.h" #include "lltabcontainer.h" #include "lluictrlfactory.h" @@ -531,10 +532,10 @@ BOOL LLPanelPeople::postBuild() friends_panel->childSetAction("add_btn", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this)); friends_panel->childSetAction("del_btn", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this)); - mOnlineFriendList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mOnlineFriendList)); - mAllFriendList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mAllFriendList)); - mNearbyList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mNearbyList)); - mRecentList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mRecentList)); + mOnlineFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); + mAllFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); + mNearbyList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); + mRecentList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); mOnlineFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mOnlineFriendList)); mAllFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mAllFriendList)); @@ -750,7 +751,6 @@ void LLPanelPeople::updateButtons() LLPanel* groups_panel = mTabContainer->getCurrentPanel(); groups_panel->childSetEnabled("activate_btn", item_selected && !cur_group_active); // "none" or a non-active group selected - groups_panel->childSetEnabled("plus_btn", item_selected); groups_panel->childSetEnabled("minus_btn", item_selected && selected_id.notNull()); } else @@ -1005,12 +1005,15 @@ void LLPanelPeople::onTabSelected(const LLSD& param) mFilterEditor->setLabel(getString("people_filter_label")); } -void LLPanelPeople::onAvatarListDoubleClicked(LLAvatarList* list) +void LLPanelPeople::onAvatarListDoubleClicked(LLUICtrl* ctrl) { - LLUUID clicked_id = list->getSelectedUUID(); - - if (clicked_id.isNull()) + LLAvatarListItem* item = dynamic_cast(ctrl); + if(!item) + { return; + } + + LLUUID clicked_id = item->getAvatarId(); #if 0 // SJB: Useful for testing, but not currently functional or to spec LLAvatarActions::showProfile(clicked_id); @@ -1138,6 +1141,12 @@ void LLPanelPeople::onAvatarPicked( void LLPanelPeople::onGroupPlusButtonClicked() { + if (!gAgent.canJoinGroups()) + { + LLNotificationsUtil::add("JoinedTooManyGroups"); + return; + } + LLMenuGL* plus_menu = (LLMenuGL*)mGroupPlusMenuHandle.get(); if (!plus_menu) return; diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index da2c0e368c..7580fdbeef 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -109,7 +109,7 @@ private: void onNearbyViewSortButtonClicked(); void onFriendsViewSortButtonClicked(); void onGroupsViewSortButtonClicked(); - void onAvatarListDoubleClicked(LLAvatarList* list); + void onAvatarListDoubleClicked(LLUICtrl* ctrl); void onAvatarListCommitted(LLAvatarList* list); void onGroupPlusButtonClicked(); void onGroupMinusButtonClicked(); diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index 7a4dd3569d..5ac0587550 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -35,24 +35,30 @@ // profile. #include "llviewerprecompiledheaders.h" -#include "llpanel.h" + +#include "llpanelpick.h" + #include "message.h" -#include "llagent.h" -#include "llagentpicksinfo.h" + +#include "llparcel.h" + #include "llbutton.h" +#include "llfloaterreg.h" #include "lliconctrl.h" #include "lllineeditor.h" -#include "llparcel.h" -#include "llviewerparcelmgr.h" +#include "llpanel.h" +#include "llscrollcontainer.h" #include "lltexteditor.h" + +#include "llagent.h" +#include "llagentpicksinfo.h" +#include "llavatarpropertiesprocessor.h" +#include "llfloaterworldmap.h" #include "lltexturectrl.h" #include "lluiconstants.h" +#include "llviewerparcelmgr.h" #include "llviewerregion.h" #include "llworldmap.h" -#include "llfloaterworldmap.h" -#include "llfloaterreg.h" -#include "llavatarpropertiesprocessor.h" -#include "llpanelpick.h" #define XML_PANEL_EDIT_PICK "panel_edit_pick.xml" @@ -93,6 +99,10 @@ LLPanelPickInfo::LLPanelPickInfo() , mPickId(LLUUID::null) , mParcelId(LLUUID::null) , mRequestedId(LLUUID::null) + , mScrollingPanelMinHeight(0) + , mScrollingPanelWidth(0) + , mScrollingPanel(NULL) + , mScrollContainer(NULL) { } @@ -146,9 +156,35 @@ BOOL LLPanelPickInfo::postBuild() childSetAction("show_on_map_btn", boost::bind(&LLPanelPickInfo::onClickMap, this)); childSetAction("back_btn", boost::bind(&LLPanelPickInfo::onClickBack, this)); + mScrollingPanel = getChild("scroll_content_panel"); + mScrollContainer = getChild("profile_scroll"); + + mScrollingPanelMinHeight = mScrollContainer->getScrolledViewRect().getHeight(); + mScrollingPanelWidth = mScrollingPanel->getRect().getWidth(); + return TRUE; } +void LLPanelPickInfo::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLPanel::reshape(width, height, called_from_parent); + + if (!mScrollContainer || !mScrollingPanel) + return; + + static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + + S32 scroll_height = mScrollContainer->getRect().getHeight(); + if (mScrollingPanelMinHeight >= scroll_height) + { + mScrollingPanel->reshape(mScrollingPanelWidth, mScrollingPanelMinHeight); + } + else + { + mScrollingPanel->reshape(mScrollingPanelWidth + scrollbar_size, scroll_height); + } +} + void LLPanelPickInfo::processProperties(void* data, EAvatarProcessorType type) { if(APT_PICK_INFO != type) @@ -284,7 +320,6 @@ void LLPanelPickInfo::setPickName(const std::string& name) void LLPanelPickInfo::setPickDesc(const std::string& desc) { childSetValue(XML_DESC, desc); - updateContentPanelRect(); } void LLPanelPickInfo::setPickLocation(const std::string& location) @@ -292,31 +327,6 @@ void LLPanelPickInfo::setPickLocation(const std::string& location) childSetValue(XML_LOCATION, location); } -void LLPanelPickInfo::updateContentPanelRect() -{ - LLTextBox* desc = getChild(XML_DESC); - - S32 text_height = desc->getTextPixelHeight(); - LLRect text_rect = desc->getRect(); - - // let text-box height fit text height - text_rect.set(text_rect.mLeft, text_rect.mTop, text_rect.mRight, text_rect.mTop - text_height); - desc->setRect(text_rect); - desc->reshape(text_rect.getWidth(), text_rect.getHeight()); - // force reflow - desc->setText(desc->getText()); - - // bottom of description text-box will be bottom of content panel - desc->localRectToOtherView(desc->getLocalRect(), &text_rect, getChild("profile_scroll")); - - LLPanel* content_panel = getChild("scroll_content_panel"); - LLRect content_rect = content_panel->getRect(); - content_rect.set(content_rect.mLeft, content_rect.mTop, content_rect.mRight, text_rect.mBottom); - // Somehow setRect moves all elements down. - // Single reshape() updates rect and does not move anything. - content_panel->reshape(content_rect.getWidth(), content_rect.getHeight()); -} - void LLPanelPickInfo::onClickMap() { LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal()); @@ -438,7 +448,7 @@ BOOL LLPanelPickEdit::postBuild() { LLPanelPickInfo::postBuild(); - mSnapshotCtrl->setOnSelectCallback(boost::bind(&LLPanelPickEdit::onPickChanged, this, _1)); + mSnapshotCtrl->setCommitCallback(boost::bind(&LLPanelPickEdit::onSnapshotChanged, this)); LLLineEditor* line_edit = getChild("pick_name"); line_edit->setKeystrokeCallback(boost::bind(&LLPanelPickEdit::onPickChanged, this, _1), NULL); @@ -527,16 +537,14 @@ void LLPanelPickEdit::sendUpdate() } } +void LLPanelPickEdit::onSnapshotChanged() +{ + enableSaveButton(true); +} + void LLPanelPickEdit::onPickChanged(LLUICtrl* ctrl) { - if(isDirty()) - { - enableSaveButton(true); - } - else - { - enableSaveButton(false); - } + enableSaveButton(isDirty()); } void LLPanelPickEdit::resetData() @@ -603,10 +611,6 @@ void LLPanelPickEdit::initTexturePickerMouseEvents() mSnapshotCtrl->setMouseEnterCallback(boost::bind(&LLPanelPickEdit::onTexturePickerMouseEnter, this, _1)); mSnapshotCtrl->setMouseLeaveCallback(boost::bind(&LLPanelPickEdit::onTexturePickerMouseLeave, this, _1)); - // *WORKAROUND: Needed for EXT-1625: enabling save button each time when picker is opened, even if - // texture wasn't changed (see Steve's comment). - mSnapshotCtrl->setMouseDownCallback(boost::bind(&LLPanelPickEdit::enableSaveButton, this, true)); - text_icon->setVisible(FALSE); } diff --git a/indra/newview/llpanelpick.h b/indra/newview/llpanelpick.h index 12b5a116b4..4f27760a8d 100644 --- a/indra/newview/llpanelpick.h +++ b/indra/newview/llpanelpick.h @@ -43,6 +43,7 @@ class LLIconCtrl; class LLTextureCtrl; +class LLScrollContainer; class LLMessageSystem; class LLAvatarPropertiesObserver; @@ -69,6 +70,8 @@ public: /*virtual*/ BOOL postBuild(); + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); /** @@ -139,15 +142,6 @@ protected: virtual void setPosGlobal(const LLVector3d& pos) { mPosGlobal = pos; } virtual LLVector3d& getPosGlobal() { return mPosGlobal; } - /** - * Reshapes content panel to fit all elements. - * - * Assume that description text-box is the last element of panel. - * Reshape text-box to fit text height and then reshape content panel to fit - * text-box bottom. EXT-1326 - */ - void updateContentPanelRect(); - /** * Callback for "Map" button, opens Map */ @@ -162,7 +156,11 @@ protected: protected: - LLTextureCtrl* mSnapshotCtrl; + S32 mScrollingPanelMinHeight; + S32 mScrollingPanelWidth; + LLScrollContainer* mScrollContainer; + LLPanel* mScrollingPanel; + LLTextureCtrl* mSnapshotCtrl; LLUUID mAvatarId; LLVector3d mPosGlobal; @@ -223,6 +221,11 @@ protected: */ void sendUpdate(); + /** + * Called when snapshot image changes. + */ + void onSnapshotChanged(); + /** * Callback for Pick snapshot, name and description changed event. */ diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index 751705dd57..ada65c98a4 100644 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -411,6 +411,7 @@ BOOL LLPanelPicks::postBuild() LLUICtrl::CommitCallbackRegistry::ScopedRegistrar plus_registar; plus_registar.add("Picks.Plus.Action", boost::bind(&LLPanelPicks::onPlusMenuItemClicked, this, _2)); + mEnableCallbackRegistrar.add("Picks.Plus.Enable", boost::bind(&LLPanelPicks::isActionEnabled, this, _2)); mPlusMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_picks_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); return TRUE; @@ -430,6 +431,18 @@ void LLPanelPicks::onPlusMenuItemClicked(const LLSD& param) } } +bool LLPanelPicks::isActionEnabled(const LLSD& userdata) const +{ + std::string command_name = userdata.asString(); + + if (command_name == "new_pick" && LLAgentPicksInfo::getInstance()->isPickLimitReached()) + { + return false; + } + + return true; +} + void LLPanelPicks::onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab) { if(!mPicksAccTab->getDisplayChildren()) @@ -652,7 +665,6 @@ void LLPanelPicks::updateButtons() if (getAvatarId() == gAgentID) { - childSetEnabled(XML_BTN_NEW, !LLAgentPicksInfo::getInstance()->isPickLimitReached()); childSetEnabled(XML_BTN_DELETE, has_selected); } diff --git a/indra/newview/llpanelpicks.h b/indra/newview/llpanelpicks.h index 1b2e35ca46..3f757e482e 100644 --- a/indra/newview/llpanelpicks.h +++ b/indra/newview/llpanelpicks.h @@ -97,6 +97,7 @@ private: void onClickMap(); void onPlusMenuItemClicked(const LLSD& param); + bool isActionEnabled(const LLSD& userdata) const; void onListCommit(const LLFlatListView* f_list); void onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab); diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 685104a8b1..b037674c37 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -449,6 +449,22 @@ void LLPanelPlaces::setItem(LLInventoryItem* item) } } +S32 LLPanelPlaces::notifyParent(const LLSD& info) +{ + if(info.has("update_verbs")) + { + if(mPosGlobal.isExactlyZero()) + { + mPosGlobal.setVec(info["global_x"], info["global_y"], info["global_z"]); + } + + updateVerbs(); + + return 1; + } + return LLPanel::notifyParent(info); +} + void LLPanelPlaces::onLandmarkLoaded(LLLandmark* landmark) { if (!mLandmarkInfo) @@ -460,6 +476,8 @@ void LLPanelPlaces::onLandmarkLoaded(LLLandmark* landmark) mLandmarkInfo->displayParcelInfo(region_id, mPosGlobal); mSaveBtn->setEnabled(TRUE); + + updateVerbs(); } void LLPanelPlaces::onFilterEdit(const std::string& search_string, bool force_filter) @@ -824,6 +842,19 @@ void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible) mPlaceProfile->setVisible(FALSE); } + else + { + LLLandmarksPanel* landmarks_panel = + dynamic_cast(mTabContainer->getPanelByName("Landmarks")); + if (landmarks_panel && mItem.notNull()) + { + // If a landmark info is being closed we open the landmarks tab + // and set this landmark selected. + mTabContainer->selectTabPanel(landmarks_panel); + + landmarks_panel->setItemSelected(mItem->getUUID(), TRUE); + } + } } } diff --git a/indra/newview/llpanelplaces.h b/indra/newview/llpanelplaces.h index 5ee8704992..27b5911ebb 100644 --- a/indra/newview/llpanelplaces.h +++ b/indra/newview/llpanelplaces.h @@ -71,6 +71,12 @@ public: void setItem(LLInventoryItem* item); + LLInventoryItem* getItem() { return mItem; } + + std::string getPlaceInfoType() { return mPlaceInfoType; } + + /*virtual*/ S32 notifyParent(const LLSD& info); + private: void onLandmarkLoaded(LLLandmark* landmark); void onFilterEdit(const std::string& search_string, bool force_filter); diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 5cc9c1951b..2dc3a62637 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -127,7 +127,11 @@ LLPanelPrimMediaControls::LLPanelPrimMediaControls() : mScrollState = SCROLL_NONE; mPanelHandle.bind(this); + + mInactiveTimeout = gSavedSettings.getF32("MediaControlTimeout"); + mControlFadeTime = gSavedSettings.getF32("MediaControlFadeTime"); } + LLPanelPrimMediaControls::~LLPanelPrimMediaControls() { } @@ -156,8 +160,6 @@ BOOL LLPanelPrimMediaControls::postBuild() mSkipBackCtrl = getChild("skip_back"); mVolumeCtrl = getChild("media_volume"); mMuteBtn = getChild("media_mute_button"); - mVolumeUpCtrl = getChild("volume_up"); - mVolumeDownCtrl = getChild("volume_down"); mVolumeSliderCtrl = getChild("volume_slider"); mWhitelistIcon = getChild("media_whitelist_flag"); mSecureLockIcon = getChild("media_secure_lock_flag"); @@ -172,6 +174,7 @@ BOOL LLPanelPrimMediaControls::postBuild() LLStringUtil::convertToF32(getString("zoom_near_padding"), mZoomNearPadding); LLStringUtil::convertToF32(getString("zoom_medium_padding"), mZoomMediumPadding); LLStringUtil::convertToF32(getString("zoom_far_padding"), mZoomFarPadding); + LLStringUtil::convertToS32(getString("top_world_view_avoid_zone"), mTopWorldViewAvoidZone); // These are currently removed...but getChild creates a "dummy" widget. // This class handles them missing. @@ -207,11 +210,9 @@ BOOL LLPanelPrimMediaControls::postBuild() } mMediaAddress->setFocusReceivedCallback(boost::bind(&LLPanelPrimMediaControls::onInputURL, _1, this )); - mInactiveTimeout = gSavedSettings.getF32("MediaControlTimeout"); - mControlFadeTime = gSavedSettings.getF32("MediaControlFadeTime"); - + mCurrentZoom = ZOOM_NONE; - // clicks on HUD buttons do not remove keyboard focus from media + // clicks on buttons do not remove keyboard focus from media setIsChrome(TRUE); return TRUE; } @@ -336,8 +337,6 @@ void LLPanelPrimMediaControls::updateShape() mMediaAddressCtrl->setVisible(has_focus && !mini_controls); mMediaPlaySliderPanel->setVisible(has_focus && !mini_controls); mVolumeCtrl->setVisible(false); - mVolumeUpCtrl->setVisible(false); - mVolumeDownCtrl->setVisible(false); mWhitelistIcon->setVisible(!mini_controls && (media_data)?media_data->getWhiteListEnable():false); // Disable zoom if HUD @@ -370,11 +369,9 @@ void LLPanelPrimMediaControls::updateShape() mSkipBackCtrl->setEnabled(has_focus && !mini_controls); mVolumeCtrl->setVisible(has_focus); - mVolumeUpCtrl->setVisible(has_focus); - mVolumeDownCtrl->setVisible(has_focus); mVolumeCtrl->setEnabled(has_focus); - mVolumeSliderCtrl->setEnabled(has_focus && mVolumeSliderVisible > 0); - mVolumeSliderCtrl->setVisible(has_focus && mVolumeSliderVisible > 0); + mVolumeSliderCtrl->setEnabled(has_focus && shouldVolumeSliderBeVisible()); + mVolumeSliderCtrl->setVisible(has_focus && shouldVolumeSliderBeVisible()); mWhitelistIcon->setVisible(false); mSecureLockIcon->setVisible(false); @@ -414,21 +411,15 @@ void LLPanelPrimMediaControls::updateShape() // video vloume if(volume <= 0.0) { - mVolumeUpCtrl->setEnabled(TRUE); - mVolumeDownCtrl->setEnabled(FALSE); mMuteBtn->setToggleState(true); } else if (volume >= 1.0) { - mVolumeUpCtrl->setEnabled(FALSE); - mVolumeDownCtrl->setEnabled(TRUE); mMuteBtn->setToggleState(false); } else { mMuteBtn->setToggleState(false); - mVolumeUpCtrl->setEnabled(TRUE); - mVolumeDownCtrl->setEnabled(TRUE); } switch(result) @@ -473,12 +464,8 @@ void LLPanelPrimMediaControls::updateShape() mSkipBackCtrl->setEnabled(FALSE); mVolumeCtrl->setVisible(FALSE); - mVolumeUpCtrl->setVisible(FALSE); - mVolumeDownCtrl->setVisible(FALSE); mVolumeSliderCtrl->setVisible(FALSE); mVolumeCtrl->setEnabled(FALSE); - mVolumeUpCtrl->setEnabled(FALSE); - mVolumeDownCtrl->setEnabled(FALSE); mVolumeSliderCtrl->setEnabled(FALSE); if (mMediaPanelScroll) @@ -627,36 +614,45 @@ void LLPanelPrimMediaControls::updateShape() update_min_max(min, max, LLVector3(screen_vert.v)); } + // convert screenspace bbox to pixels (in screen coords) + LLRect window_rect = gViewerWindow->getWorldViewRectScaled(); LLCoordGL screen_min; - screen_min.mX = llround((F32)gViewerWindow->getWorldViewWidthScaled() * (min.mV[VX] + 1.f) * 0.5f); - screen_min.mY = llround((F32)gViewerWindow->getWorldViewHeightScaled() * (min.mV[VY] + 1.f) * 0.5f); + screen_min.mX = llround((F32)window_rect.getWidth() * (min.mV[VX] + 1.f) * 0.5f); + screen_min.mY = llround((F32)window_rect.getHeight() * (min.mV[VY] + 1.f) * 0.5f); LLCoordGL screen_max; - screen_max.mX = llround((F32)gViewerWindow->getWorldViewWidthScaled() * (max.mV[VX] + 1.f) * 0.5f); - screen_max.mY = llround((F32)gViewerWindow->getWorldViewHeightScaled() * (max.mV[VY] + 1.f) * 0.5f); + screen_max.mX = llround((F32)window_rect.getWidth() * (max.mV[VX] + 1.f) * 0.5f); + screen_max.mY = llround((F32)window_rect.getHeight() * (max.mV[VY] + 1.f) * 0.5f); - // grow panel so that screenspace bounding box fits inside "media_region" element of HUD - LLRect media_controls_rect; - S32 volume_slider_height = mVolumeSliderCtrl->getRect().getHeight() - /*fudge*/ 2; - getParent()->screenRectToLocal(LLRect(screen_min.mX, screen_max.mY, screen_max.mX, screen_min.mY), &media_controls_rect); - media_controls_rect.mLeft -= mMediaRegion->getRect().mLeft; - media_controls_rect.mBottom -= mMediaRegion->getRect().mBottom - volume_slider_height; - media_controls_rect.mTop += getRect().getHeight() - mMediaRegion->getRect().mTop; - media_controls_rect.mRight += getRect().getWidth() - mMediaRegion->getRect().mRight; + // grow panel so that screenspace bounding box fits inside "media_region" element of panel + LLRect media_panel_rect; + // Get the height of the controls (less the volume slider) + S32 controls_height = mMediaControlsStack->getRect().getHeight() - mVolumeSliderCtrl->getRect().getHeight(); + getParent()->screenRectToLocal(LLRect(screen_min.mX, screen_max.mY, screen_max.mX, screen_min.mY), &media_panel_rect); + media_panel_rect.mTop += controls_height; - // keep all parts of HUD on-screen - LLRect window_rect = getParent()->getLocalRect(); - media_controls_rect.intersectWith(window_rect); + // keep all parts of panel on-screen + // Area of the top of the world view to avoid putting the controls + window_rect.mTop -= mTopWorldViewAvoidZone; + // Don't include "spacing" bookends on left & right of the media controls + window_rect.mLeft -= mLeftBookend->getRect().getWidth(); + window_rect.mRight += mRightBookend->getRect().getWidth(); + // Don't include the volume slider + window_rect.mBottom -= mVolumeSliderCtrl->getRect().getHeight(); + media_panel_rect.intersectWith(window_rect); // clamp to minimum size, keeping rect inside window - S32 centerX = media_controls_rect.getCenterX(); - S32 centerY = media_controls_rect.getCenterY(); + S32 centerX = media_panel_rect.getCenterX(); + S32 centerY = media_panel_rect.getCenterY(); + // Shrink screen rect by min width and height, to ensure containment window_rect.stretch(-mMinWidth/2, -mMinHeight/2); window_rect.clampPointToRect(centerX, centerY); - media_controls_rect.setCenterAndSize(centerX, centerY, - llmax(mMinWidth, media_controls_rect.getWidth()), llmax(mMinHeight, media_controls_rect.getHeight())); + media_panel_rect.setCenterAndSize(centerX, centerY, + llmax(mMinWidth, media_panel_rect.getWidth()), + llmax(mMinHeight, media_panel_rect.getHeight())); - setShape(media_controls_rect, true); + // Finally set the size of the panel + setShape(media_panel_rect, true); // Test mouse position to see if the cursor is stationary LLCoordWindow cursor_pos_window; @@ -699,13 +695,13 @@ void LLPanelPrimMediaControls::updateShape() /*virtual*/ void LLPanelPrimMediaControls::draw() { - F32 alpha = 1.f; + F32 alpha = getDrawContext().mAlpha; if(mFadeTimer.getStarted()) { F32 time = mFadeTimer.getElapsedTimeF32(); - alpha = llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f); + alpha *= llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f); - if(mFadeTimer.getElapsedTimeF32() >= mControlFadeTime) + if(time >= mControlFadeTime) { if(mClearFaceOnFade) { @@ -726,27 +722,30 @@ void LLPanelPrimMediaControls::draw() // Build rect for icon area in coord system of this panel // Assumes layout_stack is a direct child of this panel mMediaControlsStack->updateLayout(); - LLRect icon_area = mMediaControlsStack->getRect(); - + + // adjust for layout stack spacing + S32 space = mMediaControlsStack->getPanelSpacing() + 1; + LLRect controls_bg_area = mMediaControlsStack->getRect(); + + controls_bg_area.mTop += space; + // adjust to ignore space from volume slider - icon_area.mTop -= mVolumeSliderCtrl->getRect().getHeight(); + controls_bg_area.mBottom += mVolumeSliderCtrl->getRect().getHeight(); // adjust to ignore space from left bookend padding - icon_area.mLeft += mLeftBookend->getRect().getWidth(); + controls_bg_area.mLeft += mLeftBookend->getRect().getWidth() - space; // ignore space from right bookend padding - icon_area.mRight -= mRightBookend->getRect().getWidth(); + controls_bg_area.mRight -= mRightBookend->getRect().getWidth() - space; // draw control background UI image - mBackgroundImage->draw( icon_area, UI_VERTEX_COLOR % alpha); + mBackgroundImage->draw( controls_bg_area, UI_VERTEX_COLOR % alpha); // draw volume slider background UI image if (mVolumeSliderCtrl->getVisible()) { - LLRect volume_slider_rect = mVolumeSliderCtrl->getRect(); - // For some reason the rect is not in the right place (??) - // This translates the bg to under the slider - volume_slider_rect.translate(mVolumeSliderCtrl->getParent()->getRect().mLeft, icon_area.getHeight()); + LLRect volume_slider_rect; + screenRectToLocal(mVolumeSliderCtrl->calcScreenRect(), &volume_slider_rect); mVolumeSliderBackgroundImage->draw(volume_slider_rect, UI_VERTEX_COLOR % alpha); } @@ -1259,6 +1258,11 @@ void LLPanelPrimMediaControls::onToggleMute() { media_impl->setVolume(0.0); } + else if (mVolumeSliderCtrl->getValueF32() == 0.0) + { + media_impl->setVolume(1.0); + mVolumeSliderCtrl->setValue(1.0); + } else { media_impl->setVolume(mVolumeSliderCtrl->getValueF32()); @@ -1271,8 +1275,12 @@ void LLPanelPrimMediaControls::showVolumeSlider() mVolumeSliderVisible++; } - void LLPanelPrimMediaControls::hideVolumeSlider() { mVolumeSliderVisible--; } + +bool LLPanelPrimMediaControls::shouldVolumeSliderBeVisible() +{ + return mVolumeSliderVisible > 0; +} diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h index d899ee4473..743cec70a1 100644 --- a/indra/newview/llpanelprimmediacontrols.h +++ b/indra/newview/llpanelprimmediacontrols.h @@ -111,6 +111,7 @@ private: void onToggleMute(); void showVolumeSlider(); void hideVolumeSlider(); + bool shouldVolumeSliderBeVisible(); static void onScrollUp(void* user_data); static void onScrollUpHeld(void* user_data); @@ -155,8 +156,6 @@ private: LLUICtrl *mMediaPlaySliderCtrl; LLUICtrl *mVolumeCtrl; LLButton *mMuteBtn; - LLUICtrl *mVolumeUpCtrl; - LLUICtrl *mVolumeDownCtrl; LLSliderCtrl *mVolumeSliderCtrl; LLIconCtrl *mWhitelistIcon; LLIconCtrl *mSecureLockIcon; @@ -171,6 +170,7 @@ private: F32 mZoomNearPadding; F32 mZoomMediumPadding; F32 mZoomFarPadding; + S32 mTopWorldViewAvoidZone; LLUICtrl *mMediaPanelScroll; LLButton *mScrollUpCtrl; diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 245f694ac6..1b8fb49641 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -550,7 +550,7 @@ void LLTeleportHistoryPanel::updateVerbs() LLTeleportHistoryFlatItem* itemp = dynamic_cast (mLastSelectedFlatlList->getSelectedItem()); - mTeleportBtn->setEnabled(NULL != itemp && itemp->getIndex() < (S32)mTeleportHistory->getItems().size() - 1); + mTeleportBtn->setEnabled(NULL != itemp); mShowOnMapBtn->setEnabled(NULL != itemp); } @@ -723,7 +723,10 @@ void LLTeleportHistoryPanel::onTeleportHistoryChange(S32 removed_index) if (-1 == removed_index) showTeleportHistory(); // recreate all items else + { replaceItem(removed_index); // replace removed item by most recent + updateVerbs(); + } } void LLTeleportHistoryPanel::replaceItem(S32 removed_index) @@ -1033,7 +1036,7 @@ void LLTeleportHistoryPanel::setAccordionCollapsedByUser(LLUICtrl* acc_tab, bool bool LLTeleportHistoryPanel::isAccordionCollapsedByUser(LLUICtrl* acc_tab) { LLSD param = acc_tab->getValue(); - if(!param.has("acc_collapsed")) + if(!param.has(COLLAPSED_BY_USER)) { return false; } @@ -1045,4 +1048,11 @@ void LLTeleportHistoryPanel::onAccordionExpand(LLUICtrl* ctrl, const LLSD& param bool expanded = param.asBoolean(); // Save accordion tab state to restore it in refresh() setAccordionCollapsedByUser(ctrl, !expanded); + + // Reset selection upon accordion being collapsed + // to disable "Teleport" and "Map" buttons for hidden item. + if (!expanded && mLastSelectedFlatlList) + { + mLastSelectedFlatlList->resetSelection(); + } } diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index 5a70842a73..fbe68b4d92 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -54,7 +54,7 @@ #include "llcolorswatch.h" #include "lltexturectrl.h" #include "llcombobox.h" -#include "llfirstuse.h" +//#include "llfirstuse.h" #include "llfocusmgr.h" #include "llmanipscale.h" #include "llpreviewscript.h" @@ -470,7 +470,7 @@ void LLPanelVolume::sendIsFlexible() if (is_flexible) { - LLFirstUse::useFlexible(); + //LLFirstUse::useFlexible(); if (objectp->getClickAction() == CLICK_ACTION_SIT) { diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index e2da4c4475..b049f914ad 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -70,7 +70,7 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* av mSpeakerMgr->addListener(mSpeakerModeratorListener, "update_moderator"); mAvatarList->setNoItemsCommentText(LLTrans::getString("LoadingData")); - mAvatarListDoubleClickConnection = mAvatarList->setDoubleClickCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList)); + mAvatarListDoubleClickConnection = mAvatarList->setItemDoubleClickCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, _1)); mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLParticipantList::onAvatarListRefreshed, this, _1, _2)); // Set onAvatarListDoubleClicked as default on_return action. mAvatarListReturnConnection = mAvatarList->setReturnCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList)); @@ -132,10 +132,15 @@ void LLParticipantList::setSpeakingIndicatorsVisible(BOOL visible) mAvatarList->setSpeakingIndicatorsVisible(visible); }; -void LLParticipantList::onAvatarListDoubleClicked(LLAvatarList* list) +void LLParticipantList::onAvatarListDoubleClicked(LLUICtrl* ctrl) { - // NOTE(EM): Should we check if there is multiple selection and start conference if it is so? - LLUUID clicked_id = list->getSelectedUUID(); + LLAvatarListItem* item = dynamic_cast(ctrl); + if(!item) + { + return; + } + + LLUUID clicked_id = item->getAvatarId(); if (clicked_id.isNull() || clicked_id == gAgent.getID()) return; @@ -166,7 +171,6 @@ void LLParticipantList::onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param) { name.erase(found, moderator_indicator_len); item->setName(name); - item->reshapeAvatarName(); } } } @@ -188,7 +192,6 @@ void LLParticipantList::onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param) name += " "; name += moderator_indicator; item->setName(name); - item->reshapeAvatarName(); } } } @@ -279,6 +282,9 @@ bool LLParticipantList::onModeratorUpdateEvent(LLPointer e mModeratorList.erase(id); } } + + // apply changes immediately + onAvatarListRefreshed(mAvatarList, LLSD()); } } return true; @@ -576,7 +582,7 @@ void LLParticipantList::LLParticipantListMenu::moderateVoiceOtherParticipants(co bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD& userdata) { std::string item = userdata.asString(); - if (item == "can_mute_text" || "can_block" == item) + if (item == "can_mute_text" || "can_block" == item || "can_share" == item || "can_im" == item) { return mUUIDs.front() != gAgentID; } @@ -592,8 +598,7 @@ bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD& if (speakerp.notNull()) { // not in voice participants can not be moderated - return speakerp->mStatus == LLSpeaker::STATUS_VOICE_ACTIVE - || speakerp->mStatus == LLSpeaker::STATUS_MUTED; + return speakerp->isInVoiceChannel(); } } return false; diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 70badbc40d..e1b1b5af00 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -232,7 +232,7 @@ class LLParticipantList }; private: - void onAvatarListDoubleClicked(LLAvatarList* list); + void onAvatarListDoubleClicked(LLUICtrl* ctrl); void onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param); /** diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index 2a40cbaba0..84bdaafacf 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -364,6 +364,12 @@ BOOL LLPreviewGesture::postBuild() LLTextBox* text; LLCheckBoxCtrl* check; + edit = getChild("name"); + edit->setKeystrokeCallback(onKeystrokeCommit, this); + + edit = getChild("desc"); + edit->setKeystrokeCallback(onKeystrokeCommit, this); + edit = getChild("trigger_editor"); edit->setKeystrokeCallback(onKeystrokeCommit, this); edit->setCommitCallback(onCommitSetDirty, this); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 95756ac5f3..cc70360528 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -55,7 +55,6 @@ #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "lldir.h" -//#include "llfloaterchat.h" #include "llviewerstats.h" #include "llviewercontrol.h" // gSavedSettings #include "llappviewer.h" // app_abort_quit() diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 646c9fb6a4..fccf71f3cb 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -79,7 +79,6 @@ #include "llslider.h" #include "lldir.h" #include "llcombobox.h" -//#include "llfloaterchat.h" #include "llviewerstats.h" #include "llviewertexteditor.h" #include "llviewerwindow.h" -- cgit v1.3 From 0f61c00218a07a7d487e33256382c4a10561e0c1 Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Mon, 22 Feb 2010 16:50:46 +0000 Subject: Enumerate available voice fonts. --- indra/newview/llvoiceclient.cpp | 124 ++++++++++++++++++++++++++++++++++++++++ indra/newview/llvoiceclient.h | 38 ++++++++++-- 2 files changed, 158 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 59606f17b2..92dfa6d30e 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -240,6 +240,12 @@ protected: std::string audioMediaString; std::string displayNameString; std::string deviceString; + std::string idString; + std::string descriptionString; + std::string expirationDateString; + bool hasExpired; + std::string fontTypeString; + std::string fontStatusString; int participantType; bool isLocallyMuted; bool isModeratorMuted; @@ -625,6 +631,34 @@ void LLVivoxProtocolParser::EndTag(const char *tag) autoAcceptMask = string; else if (!stricmp("AutoAddAsBuddy", tag)) autoAddAsBuddy = string; + else if (!stricmp("SessionFont", tag)) + { + gVoiceClient->addSessionFont(idString, nameString, descriptionString, expirationDateString, hasExpired, fontTypeString, fontStatusString); + } + else if (!stricmp("ID", tag)) + { + idString = string; + } + else if (!stricmp("Description", tag)) + { + descriptionString = string; + } + else if (!stricmp("ExpirationDate", tag)) + { + expirationDateString = string; + } + else if (!stricmp("Expired", tag)) + { + hasExpired = !stricmp(string.c_str(), "1"); + } + else if (!stricmp("Type", tag)) + { + fontTypeString = string; + } + else if (!stricmp("Status", tag)) + { + fontStatusString = string; + } else if (!stricmp("MessageHeader", tag)) messageHeader = string; else if (!stricmp("MessageBody", tag)) @@ -895,6 +929,10 @@ void LLVivoxProtocolParser::processResponse(std::string tag) { gVoiceClient->accountListAutoAcceptRulesResponse(statusCode, statusString); } + else if (!stricmp(actionCstr, "Account.GetSessionFonts.1")) + { + gVoiceClient->accountGetSessionFontsResponse(statusCode, statusString); + } else if (!stricmp(actionCstr, "Session.Set3DPosition.1")) { // We don't need to process these, but they're so spammy we don't want to log them. @@ -1243,6 +1281,9 @@ LLVoiceClient::LLVoiceClient() : mBuddyListMapPopulated(false), mBlockRulesListReceived(false), mAutoAcceptRulesListReceived(false), + + mSessionFontsReceived(false), + mCaptureDeviceDirty(false), mRenderDeviceDirty(false), mSpatialCoordsDirty(false), @@ -2156,6 +2197,9 @@ void LLVoiceClient::stateMachine() notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + // request the set of available voice fonts + accountGetSessionFontsSendMessage(); + // request the current set of block rules (we'll need them when updating the friends list) accountListBlockRulesSendMessage(); @@ -2641,6 +2685,24 @@ void LLVoiceClient::accountListAutoAcceptRulesSendMessage() } } +void LLVoiceClient::accountGetSessionFontsSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "requesting session font list" << LL_ENDL; + + stream + << "" + << "" << mAccountHandle << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + void LLVoiceClient::sessionGroupCreateSendMessage() { if(!mAccountHandle.empty()) @@ -2691,6 +2753,7 @@ void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAu stream << "" << (startAudio?"true":"false") << "" << "" << (startText?"true":"false") << "" +// << "0" << "" << mChannelName << "" << "\n\n\n"; writeString(stream.str()); @@ -2766,6 +2829,21 @@ void LLVoiceClient::sessionTextConnectSendMessage(sessionState *session) writeString(stream.str()); } +void LLVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session, const std::string &fontId) +{ + LL_DEBUGS("Voice") << "selecting voice font: " << fontId << " in session handle: " << session->mHandle << LL_ENDL; + + std::ostringstream stream; + + stream + << "" + << "" << session->mHandle << "" + << "" << fontId << "" + << "\n\n\n"; + + writeString(stream.str()); +} + void LLVoiceClient::sessionTerminate() { mSessionTerminateRequested = true; @@ -6958,6 +7036,46 @@ void LLVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const s } } +LLVoiceClient::voiceFontEntry::voiceFontEntry(const std::string &id) : + mID(id), + mHasExpired(false) +{ +} + +LLVoiceClient::voiceFontEntry *LLVoiceClient::addSessionFont(const std::string &id, + const std::string &name, + const std::string &description, + const std::string &expirationDate, + const bool hasExpired, + const std::string &fontType, + const std::string &fontStatus) +{ + voiceFontEntry *font = NULL; + + voiceFontMap::iterator iter = mSessionFontMap.find(&id); + if(iter != mSessionFontMap.end()) + { + // Found session font already in the map. + LL_DEBUGS("Voice") << "existing session font " << id << " : " << name << LL_ENDL; + font = iter->second; + } + + if(font == NULL) + { + LL_DEBUGS("Voice") << "adding session font " << id << " : " << name << (hasExpired?" (Expired)":"") << LL_ENDL; + font = new voiceFontEntry(id); + font->mName = name; + font->mDescription = description; + font->mExpirationDate = expirationDate; + font->mHasExpired = hasExpired; + font->mFontType = fontType; + font->mFontStatus = fontStatus; + + mSessionFontMap.insert(voiceFontMap::value_type(&(font->mID), font)); + } + return font; +} + void LLVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString) { // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done. @@ -6970,6 +7088,12 @@ void LLVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std mAutoAcceptRulesListReceived = true; } +void LLVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString) +{ + // Session font list entries were updated via addSessionFont() during parsing. Just flag that we're done. + mSessionFontsReceived = true; +} + void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) { mParticipantObservers.insert(observer); diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index a96cf18e27..05624bcefd 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -424,8 +424,32 @@ static void updatePosition(void); void deleteAllAutoAcceptRules(void); void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy); void accountListBlockRulesResponse(int statusCode, const std::string &statusString); - void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString); - + void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString); + + struct voiceFontEntry + { + voiceFontEntry(const std::string &id); + // *TODO: Decide which of these we don't need to store + std::string mID; + std::string mName; + std::string mDescription; + std::string mExpirationDate; + bool mHasExpired; + std::string mFontType; + std::string mFontStatus; + }; + + typedef std::map voiceFontMap; + + voiceFontEntry *addSessionFont(const std::string &id, + const std::string &name, + const std::string &description, + const std::string &expirationDate, + const bool hasExpired, + const std::string &fontType, + const std::string &fontStatus); + void accountGetSessionFontsResponse(int statusCode, const std::string &statusString); + ///////////////////////////// // session control messages void connectorCreate(); @@ -447,12 +471,15 @@ static void updatePosition(void); void accountListBlockRulesSendMessage(); void accountListAutoAcceptRulesSendMessage(); - + + void accountGetSessionFontsSendMessage(); + void sessionGroupCreateSendMessage(); void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false); void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false); void sessionMediaConnectSendMessage(sessionState *session); // just joins the audio session void sessionTextConnectSendMessage(sessionState *session); // just joins the text session + void sessionSetVoiceFontSendMessage(sessionState *session, const std::string &fontId); void sessionTerminateSendMessage(sessionState *session); void sessionGroupTerminateSendMessage(sessionState *session); void sessionMediaDisconnectSendMessage(sessionState *session); @@ -653,7 +680,10 @@ static void updatePosition(void); bool mBlockRulesListReceived; bool mAutoAcceptRulesListReceived; buddyListMap mBuddyListMap; - + + bool mSessionFontsReceived; + voiceFontMap mSessionFontMap; + deviceList mCaptureDevices; deviceList mRenderDevices; -- cgit v1.3 From 5518e15f89359ba1627633f59f7299b69fc2bd69 Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Mon, 22 Feb 2010 16:52:23 +0000 Subject: Added stateFontListReceived to the voice client state machine. This makes sure we have the voice font list before attempting to start a session. --- indra/newview/llvoiceclient.cpp | 11 +++++++++++ indra/newview/llvoiceclient.h | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 92dfa6d30e..de4e53f52c 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -1622,6 +1622,7 @@ std::string LLVoiceClient::state2string(LLVoiceClient::state inState) CASE(stateNeedsLogin); CASE(stateLoggingIn); CASE(stateLoggedIn); + CASE(stateFontListReceived); CASE(stateCreatingSessionGroup); CASE(stateNoChannel); CASE(stateJoiningSession); @@ -2231,6 +2232,11 @@ void LLVoiceClient::stateMachine() writeString(stream.str()); } } + + // accountGetSessionFontsResponse() will transition from here to stateFontListReceived. + + //MARK: stateFontListReceived + case stateFontListReceived: // font list received #if USE_SESSION_GROUPS // create the main session group @@ -7092,6 +7098,11 @@ void LLVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::st { // Session font list entries were updated via addSessionFont() during parsing. Just flag that we're done. mSessionFontsReceived = true; + + if(getState() == stateLoggedIn) + { + setState(stateFontListReceived); + } } void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 05624bcefd..2d8fff5508 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -429,7 +429,6 @@ static void updatePosition(void); struct voiceFontEntry { voiceFontEntry(const std::string &id); - // *TODO: Decide which of these we don't need to store std::string mID; std::string mName; std::string mDescription; @@ -584,6 +583,7 @@ static void updatePosition(void); stateNeedsLogin, // send login request stateLoggingIn, // waiting for account handle stateLoggedIn, // account handle received + stateFontListReceived, // List of available voice fonts received stateCreatingSessionGroup, // Creating the main session group stateNoChannel, // stateJoiningSession, // waiting for session handle -- cgit v1.3 From 9f9b240dcee53a50b56a0ccb8d255a86d0ce62c9 Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Tue, 6 Apr 2010 11:39:01 +0100 Subject: Added voice font selection to the voice floater. --- indra/newview/app_settings/logcontrol.xml | 1 + indra/newview/llcallfloater.cpp | 53 ++++++++++++++-- indra/newview/llcallfloater.h | 21 ++++--- indra/newview/llvoiceclient.cpp | 73 +++++++++++++++++----- indra/newview/llvoiceclient.h | 23 ++++--- .../default/xui/en/floater_voice_controls.xml | 38 ++++++++++- 6 files changed, 173 insertions(+), 36 deletions(-) (limited to 'indra') diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index d7bb64ce8a..d3fb958638 100644 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -40,6 +40,7 @@ tags + Voice diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index 76e058a1c3..52739184c0 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -52,6 +52,7 @@ #include "lltransientfloatermgr.h" #include "llviewerwindow.h" #include "llvoicechannel.h" +#include "llvoiceclient.h" // for Voice font list types #include "llviewerparcelmgr.h" static void get_voice_participants_uuids(std::vector& speakers_uuids); @@ -95,7 +96,7 @@ static void* create_non_avatar_caller(void*) return new LLNonAvatarCaller; } -LLVoiceChannel* LLCallFloater::sCurrentVoiceCanel = NULL; +LLVoiceChannel* LLCallFloater::sCurrentVoiceChannel = NULL; LLCallFloater::LLCallFloater(const LLSD& key) : LLTransientDockableFloater(NULL, false, key) @@ -105,6 +106,7 @@ LLCallFloater::LLCallFloater(const LLSD& key) , mNonAvatarCaller(NULL) , mVoiceType(VC_LOCAL_CHAT) , mAgentPanel(NULL) +, mVoiceFont(NULL) , mSpeakingIndicator(NULL) , mIsModeratorMutedVoice(false) , mInitParticipantsVoiceState(false) @@ -146,6 +148,9 @@ BOOL LLCallFloater::postBuild() childSetAction("leave_call_btn", boost::bind(&LLCallFloater::leaveCall, this)); + mVoiceFont = getChild("voice_font"); + childSetCommitCallback("voice_font", commitVoiceFont, this); // *FIX: childSetCommitCallback deprecated + mNonAvatarCaller = getChild("non_avatar_caller"); mNonAvatarCaller->setVisible(FALSE); @@ -157,7 +162,6 @@ BOOL LLCallFloater::postBuild() initAgentData(); - connectToChannel(LLVoiceChannel::getCurrentVoiceChannel()); setIsChrome(true); @@ -205,6 +209,9 @@ void LLCallFloater::draw() // virtual void LLCallFloater::onChange() { + // *FIX: Temporarily dumping this here till I decide where it should go + updateVoiceFont(); + if (NULL == mParticipants) return; updateParticipantsVoiceState(); @@ -231,6 +238,37 @@ void LLCallFloater::leaveCall() } } +/* static */ +void LLCallFloater::commitVoiceFont(LLUICtrl* ctrl, void* userdata) +{ + LLVoiceClient::getInstance()->setVoiceFont(ctrl->getValue()); +} + +void LLCallFloater::updateVoiceFont() +{ + if (mVoiceFont) + { + mVoiceFont->removeall(); + mVoiceFont->add(getString("no_voice_font"), 0); + + if (LLVoiceClient::getInstance()->getVoiceFontsAvailable()) + { + const LLVoiceClient::voice_font_list_t font_list = LLVoiceClient::getInstance()->getVoiceFontList(); + + for (LLVoiceClient::voice_font_list_t::const_iterator it = font_list.begin(); it != font_list.end(); ++it) + { + mVoiceFont->add(*(it->second), it->first, ADD_BOTTOM); + } + mVoiceFont->setEnabled(true); + mVoiceFont->setValue(LLVoiceClient::getInstance()->getVoiceFont()); + } + else + { + mVoiceFont->setEnabled(false); + } + } +} + void LLCallFloater::updateSession() { LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel(); @@ -369,7 +407,7 @@ void LLCallFloater::sOnCurrentChannelChanged(const LLUUID& /*session_id*/) // *NOTE: if signal was sent for voice channel with LLVoiceChannel::STATE_NO_CHANNEL_INFO // it sill be sent for the same channel again (when state is changed). // So, lets ignore this call. - if (channel == sCurrentVoiceCanel) return; + if (channel == sCurrentVoiceChannel) return; LLCallFloater* call_floater = LLFloaterReg::getTypedInstance("voice_controls"); @@ -714,9 +752,9 @@ void LLCallFloater::connectToChannel(LLVoiceChannel* channel) { mVoiceChannelStateChangeConnection.disconnect(); - sCurrentVoiceCanel = channel; + sCurrentVoiceChannel = channel; - mVoiceChannelStateChangeConnection = sCurrentVoiceCanel->setStateChangedCallback(boost::bind(&LLCallFloater::onVoiceChannelStateChanged, this, _1, _2)); + mVoiceChannelStateChangeConnection = sCurrentVoiceChannel->setStateChangedCallback(boost::bind(&LLCallFloater::onVoiceChannelStateChanged, this, _1, _2)); updateState(channel->getState()); } @@ -736,7 +774,7 @@ void LLCallFloater::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old void LLCallFloater::updateState(const LLVoiceChannel::EState& new_state) { - LL_DEBUGS("Voice") << "Updating state: " << new_state << ", session name: " << sCurrentVoiceCanel->getSessionName() << LL_ENDL; + LL_DEBUGS("Voice") << "Updating state: " << new_state << ", session name: " << sCurrentVoiceChannel->getSessionName() << LL_ENDL; if (LLVoiceChannel::STATE_CONNECTED == new_state) { updateSession(); @@ -745,6 +783,9 @@ void LLCallFloater::updateState(const LLVoiceChannel::EState& new_state) { reset(new_state); } + + // *FIX: Dumped here till I decide where to put it + updateVoiceFont(); } void LLCallFloater::reset(const LLVoiceChannel::EState& new_state) diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index 0a8ea7de39..9a13d853c9 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -40,6 +40,7 @@ class LLAvatarList; class LLAvatarListItem; +class LLComboBox; class LLNonAvatarCaller; class LLOutputMonitorCtrl; class LLParticipantList; @@ -47,15 +48,15 @@ class LLSpeakerMgr; class LLSpeakersDelayActionsStorage; /** - * The Voice Control Panel is an ambient window summoned by clicking the flyout chevron on the Speak button. - * It can be torn-off and freely positioned onscreen. + * The Voice Control Panel is an ambient window summoned by clicking the flyout chevron + * on the Speak button. It can be torn-off and freely positioned onscreen. * - * When the Resident is engaged in Nearby Voice Chat, the Voice Control Panel provides control over - * the Resident's own microphone input volume, the audible volume of each of the other participants, - * the Resident's own Voice Morphing settings (if she has subscribed to enable the feature), and Voice Recording. + * When the Resident is engaged in Voice Chat, the Voice Control Panel provides control + * over the audible volume of each of the other participants, the Resident's own Voice + * Morphing settings (if she has subscribed to enable the feature), and Voice Recording. * - * When the Resident is engaged in any chat except Nearby Chat, the Voice Control Panel also provides an - * 'Leave Call' button to allow the Resident to leave that voice channel. + * When the Resident is engaged in any chat except Nearby Chat, the Voice Control Panel + * also provides a 'Leave Call' button to allow the Resident to leave that voice channel. */ class LLCallFloater : public LLTransientDockableFloater, LLVoiceClientParticipantObserver { @@ -101,6 +102,9 @@ private: void leaveCall(); + static void commitVoiceFont(LLUICtrl*,void* userdata); + void updateVoiceFont(); + /** * Updates mSpeakerManager and list according to current Voice Channel * @@ -230,6 +234,7 @@ private: LLNonAvatarCaller* mNonAvatarCaller; EVoiceControls mVoiceType; LLPanel* mAgentPanel; + LLComboBox* mVoiceFont; LLOutputMonitorCtrl* mSpeakingIndicator; bool mIsModeratorMutedVoice; @@ -259,7 +264,7 @@ private: * * @see sOnCurrentChannelChanged() */ - static LLVoiceChannel* sCurrentVoiceCanel; + static LLVoiceChannel* sCurrentVoiceChannel; /* virtual */ LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index de4e53f52c..5502744882 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -240,7 +240,7 @@ protected: std::string audioMediaString; std::string displayNameString; std::string deviceString; - std::string idString; + S32 id; std::string descriptionString; std::string expirationDateString; bool hasExpired; @@ -503,7 +503,20 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) { gVoiceClient->deleteAllAutoAcceptRules(); } - + else if (!stricmp("SessionFonts", tag)) + { + LLVoiceClient::getInstance()->clearSessionFonts(); + } + else if (!stricmp("SessionFont", tag)) + { + id = 0; + nameString.clear(); + descriptionString.clear(); + expirationDateString.clear(); + hasExpired.clear(); + fontTypeString.clear(); + fontStatusString.clear(); + } } } responseDepth++; @@ -633,11 +646,11 @@ void LLVivoxProtocolParser::EndTag(const char *tag) autoAddAsBuddy = string; else if (!stricmp("SessionFont", tag)) { - gVoiceClient->addSessionFont(idString, nameString, descriptionString, expirationDateString, hasExpired, fontTypeString, fontStatusString); + LLVoiceClient::getInstance()->addSessionFont(id, nameString, descriptionString, expirationDateString, hasExpired, fontTypeString, fontStatusString); } else if (!stricmp("ID", tag)) { - idString = string; + id = strtol(string.c_str(), NULL, 10); } else if (!stricmp("Description", tag)) { @@ -860,6 +873,8 @@ void LLVivoxProtocolParser::processResponse(std::string tag) } else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent")) { + // *TODO: Receive the current SessionFontID? + /* c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 @@ -931,7 +946,7 @@ void LLVivoxProtocolParser::processResponse(std::string tag) } else if (!stricmp(actionCstr, "Account.GetSessionFonts.1")) { - gVoiceClient->accountGetSessionFontsResponse(statusCode, statusString); + LLVoiceClient::getInstance()->accountGetSessionFontsResponse(statusCode, statusString); } else if (!stricmp(actionCstr, "Session.Set3DPosition.1")) { @@ -1283,6 +1298,7 @@ LLVoiceClient::LLVoiceClient() : mAutoAcceptRulesListReceived(false), mSessionFontsReceived(false), + mFontID(0), mCaptureDeviceDirty(false), mRenderDeviceDirty(false), @@ -2759,7 +2775,7 @@ void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAu stream << "" << (startAudio?"true":"false") << "" << "" << (startText?"true":"false") << "" -// << "0" + << "" << mFontID << "" << "" << mChannelName << "" << "\n\n\n"; writeString(stream.str()); @@ -2835,16 +2851,17 @@ void LLVoiceClient::sessionTextConnectSendMessage(sessionState *session) writeString(stream.str()); } -void LLVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session, const std::string &fontId) +void LLVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session) { - LL_DEBUGS("Voice") << "selecting voice font: " << fontId << " in session handle: " << session->mHandle << LL_ENDL; + llassert(session); + LL_DEBUGS("Voice") << "requesting voice font: " << mFontID << " in session handle: " << session->mHandle << LL_ENDL; std::ostringstream stream; stream - << "" + << "" << "" << session->mHandle << "" - << "" << fontId << "" + << "" << mFontID << "" << "\n\n\n"; writeString(stream.str()); @@ -7042,13 +7059,13 @@ void LLVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const s } } -LLVoiceClient::voiceFontEntry::voiceFontEntry(const std::string &id) : +LLVoiceClient::voiceFontEntry::voiceFontEntry(S32 id) : mID(id), mHasExpired(false) { } -LLVoiceClient::voiceFontEntry *LLVoiceClient::addSessionFont(const std::string &id, +LLVoiceClient::voiceFontEntry *LLVoiceClient::addSessionFont(const voice_font_id_t &id, const std::string &name, const std::string &description, const std::string &expirationDate, @@ -7058,7 +7075,7 @@ LLVoiceClient::voiceFontEntry *LLVoiceClient::addSessionFont(const std::string & { voiceFontEntry *font = NULL; - voiceFontMap::iterator iter = mSessionFontMap.find(&id); + voice_font_map_t::iterator iter = mSessionFontMap.find(id); if(iter != mSessionFontMap.end()) { // Found session font already in the map. @@ -7077,11 +7094,39 @@ LLVoiceClient::voiceFontEntry *LLVoiceClient::addSessionFont(const std::string & font->mFontType = fontType; font->mFontStatus = fontStatus; - mSessionFontMap.insert(voiceFontMap::value_type(&(font->mID), font)); + mSessionFontList.insert(voice_font_list_t::value_type(font->mID, &(font->mName))); + mSessionFontMap.insert(voice_font_map_t::value_type(font->mID, font)); } return font; } +bool LLVoiceClient::setVoiceFont(voice_font_id_t id) +{ + if (!mAudioSession || !mAudioSession->mVoiceEnabled || !mSessionFontsReceived) + { + LL_DEBUGS("Voice") << "Session fonts not available" << LL_ENDL; + return false; + } + + if(id == 0 || mSessionFontMap.find(id) != mSessionFontMap.end()) + { + mFontID = id; + } + else + { + LL_DEBUGS("Voice") << "Invalid session font " << id << LL_ENDL; + return false; + } + + sessionSetVoiceFontSendMessage(mAudioSession); + return true; +} + +const LLVoiceClient::voice_font_id_t LLVoiceClient::getVoiceFont() const +{ + return mFontID; +} + void LLVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString) { // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done. diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 2d8fff5508..05e9dcc896 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -428,8 +428,8 @@ static void updatePosition(void); struct voiceFontEntry { - voiceFontEntry(const std::string &id); - std::string mID; + voiceFontEntry(S32 id); + S32 mID; std::string mName; std::string mDescription; std::string mExpirationDate; @@ -438,9 +438,16 @@ static void updatePosition(void); std::string mFontStatus; }; - typedef std::map voiceFontMap; + typedef S32 voice_font_id_t; + typedef std::map voice_font_list_t; + typedef std::map voice_font_map_t; - voiceFontEntry *addSessionFont(const std::string &id, + bool getVoiceFontsAvailable() const { return mSessionFontsReceived; }; + bool setVoiceFont(voice_font_id_t id); + const voice_font_id_t getVoiceFont() const; + const voice_font_list_t &getVoiceFontList() const { return mSessionFontList; }; + + voiceFontEntry *addSessionFont(const voice_font_id_t &id, const std::string &name, const std::string &description, const std::string &expirationDate, @@ -478,7 +485,7 @@ static void updatePosition(void); void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false); void sessionMediaConnectSendMessage(sessionState *session); // just joins the audio session void sessionTextConnectSendMessage(sessionState *session); // just joins the text session - void sessionSetVoiceFontSendMessage(sessionState *session, const std::string &fontId); + void sessionSetVoiceFontSendMessage(sessionState *session); void sessionTerminateSendMessage(sessionState *session); void sessionGroupTerminateSendMessage(sessionState *session); void sessionMediaDisconnectSendMessage(sessionState *session); @@ -510,7 +517,7 @@ static void updatePosition(void); deviceList *getCaptureDevices(); deviceList *getRenderDevices(); - + void setNonSpatialChannel( const std::string &uri, const std::string &credentials); @@ -682,7 +689,9 @@ static void updatePosition(void); buddyListMap mBuddyListMap; bool mSessionFontsReceived; - voiceFontMap mSessionFontMap; + S32 mFontID; + voice_font_list_t mSessionFontList; + voice_font_map_t mSessionFontMap; // *TODO: make private deviceList mCaptureDevices; deviceList mRenderDevices; diff --git a/indra/newview/skins/default/xui/en/floater_voice_controls.xml b/indra/newview/skins/default/xui/en/floater_voice_controls.xml index c4411db8c5..100a060557 100644 --- a/indra/newview/skins/default/xui/en/floater_voice_controls.xml +++ b/indra/newview/skins/default/xui/en/floater_voice_controls.xml @@ -33,6 +33,9 @@ name="no_one_near"> No one near has voice enabled + + No Voice Effect + - + + + + + + + Date: Tue, 6 Apr 2010 22:02:49 -0700 Subject: DEV-48542 - CID-444/CID-445: missing break in switch, llpanellogin.cpp Added missing breaks. CR: Josh --- indra/newview/llpanellogin.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'indra') diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index b620a005d2..548f97b9f1 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -785,11 +785,20 @@ void LLPanelLogin::getLocation(LLSLURL& slurl) switch(combo->getCurrentIndex()) { case 0: + { slurl = LLSLURL(LLSLURL::SIM_LOCATION_HOME); + break; + } case 1: + { slurl = LLSLURL(LLSLURL::SIM_LOCATION_LAST); + break; + } default: + { slurl = LLSLURL(combo->getValue().asString()); + break; + } } } -- cgit v1.3 From 658ccc3e85487f9f24ff3b5926e60d6cce7f42e0 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Wed, 7 Apr 2010 11:08:04 -0700 Subject: Re-insert backed out SLE checkin so we can fix it --- indra/llcommon/lluri.cpp | 3 +- indra/llcommon/lluuid.cpp | 162 +- indra/llmessage/llcurl.cpp | 29 +- indra/llmessage/llcurl.h | 12 +- indra/llmessage/llhttpclient.cpp | 16 +- indra/llmessage/llhttpclient.h | 11 +- indra/llmessage/llurlrequest.cpp | 51 +- indra/llmessage/llurlrequest.h | 68 +- indra/llui/llurlentry.cpp | 29 +- indra/llui/tests/llurlentry_test.cpp | 26 +- indra/llvfs/lldir.cpp | 39 +- indra/llvfs/lldir.h | 4 +- indra/newview/CMakeLists.txt | 55 +- indra/newview/Info-SecondLife.plist | 28 + indra/newview/app_settings/settings.xml | 115 +- .../installers/windows/installer_template.nsi | 6 + indra/newview/llagent.cpp | 4 +- indra/newview/llagent.h | 5 +- indra/newview/llagentlistener.cpp | 5 +- indra/newview/llagentui.cpp | 17 +- indra/newview/llagentui.h | 4 +- indra/newview/llappviewer.cpp | 124 +- indra/newview/llappviewer.h | 1 - indra/newview/llappviewerlinux.cpp | 2 +- indra/newview/llappviewermacosx.cpp | 1 - indra/newview/llavataractions.cpp | 2 +- indra/newview/llbottomtray.cpp | 4 +- indra/newview/llcallfloater.cpp | 33 +- indra/newview/llcallingcard.cpp | 1 + indra/newview/llchathistory.cpp | 15 +- indra/newview/llcurrencyuimanager.cpp | 2 +- indra/newview/llfloaterabout.cpp | 14 +- indra/newview/llfloaterbuyland.cpp | 4 +- indra/newview/llfloaterchat.cpp | 2 +- indra/newview/llfloaterchatterbox.cpp | 2 +- indra/newview/llfloaterevent.cpp | 2 +- indra/newview/llfloaterland.cpp | 11 +- indra/newview/llfloaterpreference.cpp | 4 +- indra/newview/llfloaterregioninfo.cpp | 3 +- indra/newview/llfloaterreporter.cpp | 12 +- indra/newview/llfloatersnapshot.cpp | 2 +- indra/newview/llfloatervoicedevicesettings.cpp | 41 +- indra/newview/llfloaterworldmap.cpp | 23 +- indra/newview/llfloaterworldmap.h | 3 +- indra/newview/llgrouplist.cpp | 2 +- indra/newview/llimfloater.cpp | 1 - indra/newview/llimpanel.cpp | 14 +- indra/newview/llimview.cpp | 27 +- indra/newview/llinspectavatar.cpp | 8 +- indra/newview/llinspectobject.cpp | 6 +- indra/newview/llinspectremoteobject.cpp | 4 +- indra/newview/llinventorymodel.cpp | 1 - indra/newview/lllandmarkactions.cpp | 4 +- indra/newview/lllocationinputctrl.cpp | 9 +- indra/newview/llloginhandler.cpp | 200 +- indra/newview/llloginhandler.h | 11 +- indra/newview/lllogininstance.cpp | 77 +- indra/newview/lllogininstance.h | 7 +- indra/newview/llnavigationbar.cpp | 34 +- indra/newview/lloutputmonitorctrl.cpp | 8 +- indra/newview/lloutputmonitorctrl.h | 2 +- indra/newview/lloverlaybar.cpp | 2 +- indra/newview/llpanelavatar.cpp | 8 +- indra/newview/llpanelgroup.cpp | 4 +- indra/newview/llpanelimcontrolpanel.cpp | 5 +- indra/newview/llpanellogin.cpp | 542 +- indra/newview/llpanellogin.h | 29 +- indra/newview/llpanelpeople.cpp | 4 +- indra/newview/llpanelplacestab.cpp | 5 +- indra/newview/llparticipantlist.cpp | 3 +- indra/newview/llsecapi.cpp | 161 + indra/newview/llsecapi.h | 493 ++ indra/newview/llsechandler_basic.cpp | 1586 +++++ indra/newview/llsechandler_basic.h | 285 + indra/newview/llselectmgr.cpp | 8 +- indra/newview/llslurl.cpp | 514 +- indra/newview/llslurl.h | 159 +- indra/newview/llspeakbutton.cpp | 10 +- indra/newview/llspeakers.cpp | 40 +- indra/newview/llspeakingindicatormanager.cpp | 4 +- indra/newview/llstartup.cpp | 722 +- indra/newview/llstartup.h | 17 +- indra/newview/llstylemap.cpp | 2 +- indra/newview/llurl.cpp | 6 + indra/newview/llurl.h | 1 + indra/newview/llurldispatcher.cpp | 201 +- indra/newview/llurldispatcher.h | 19 +- indra/newview/llurllineeditorctrl.cpp | 2 +- indra/newview/llvieweraudio.cpp | 10 +- indra/newview/llviewercontrol.cpp | 6 +- indra/newview/llviewerinventory.cpp | 13 +- indra/newview/llviewermenu.cpp | 16 +- indra/newview/llviewermessage.cpp | 23 +- indra/newview/llviewernetwork.cpp | 694 +- indra/newview/llviewernetwork.h | 182 +- indra/newview/llviewerobject.cpp | 12 +- indra/newview/llviewerstats.cpp | 6 +- indra/newview/llviewerwindow.cpp | 33 +- indra/newview/llvoavatar.cpp | 12 +- indra/newview/llvoicechannel.cpp | 64 +- indra/newview/llvoicechannel.h | 3 +- indra/newview/llvoiceclient.cpp | 7434 ++------------------ indra/newview/llvoiceclient.h | 1022 +-- indra/newview/llvoicevivox.cpp | 6967 ++++++++++++++++++ indra/newview/llvoicevivox.h | 914 +++ indra/newview/llweb.cpp | 2 +- indra/newview/llworld.cpp | 3 +- indra/newview/llxmlrpclistener.cpp | 18 +- indra/newview/llxmlrpctransaction.cpp | 106 +- indra/newview/llxmlrpctransaction.h | 3 + .../newview/skins/default/xui/en/floater_about.xml | 2 +- .../newview/skins/default/xui/en/notifications.xml | 63 + indra/newview/skins/default/xui/en/panel_login.xml | 88 +- indra/newview/skins/default/xui/en/strings.xml | 9 + indra/newview/tests/lllogininstance_test.cpp | 115 +- indra/newview/tests/llsecapi_test.cpp | 188 + indra/newview/tests/llsechandler_basic_test.cpp | 964 +++ indra/newview/tests/llslurl_test.cpp | 258 + indra/newview/tests/llviewernetwork_test.cpp | 486 ++ indra/newview/viewer_manifest.py | 17 + indra/viewer_components/login/lllogin.cpp | 43 +- 121 files changed, 16325 insertions(+), 9695 deletions(-) create mode 100644 indra/newview/llsecapi.cpp create mode 100644 indra/newview/llsecapi.h create mode 100644 indra/newview/llsechandler_basic.cpp create mode 100644 indra/newview/llsechandler_basic.h create mode 100644 indra/newview/llvoicevivox.cpp create mode 100644 indra/newview/llvoicevivox.h create mode 100644 indra/newview/tests/llsecapi_test.cpp create mode 100644 indra/newview/tests/llsechandler_basic_test.cpp create mode 100644 indra/newview/tests/llslurl_test.cpp create mode 100644 indra/newview/tests/llviewernetwork_test.cpp (limited to 'indra') diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index 9d4f3a98f0..0e8f3f0f73 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -231,7 +231,8 @@ static BOOL isDefault(const std::string& scheme, U16 port) void LLURI::parseAuthorityAndPathUsingOpaque() { if (mScheme == "http" || mScheme == "https" || - mScheme == "ftp" || mScheme == "secondlife" ) + mScheme == "ftp" || mScheme == "secondlife" || + mScheme == "x-grid-location-info") { if (mEscapedOpaque.substr(0,2) != "//") { diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp index bcbae06ec5..583c1e589b 100644 --- a/indra/llcommon/lluuid.cpp +++ b/indra/llcommon/lluuid.cpp @@ -33,9 +33,12 @@ // We can't use WIN32_LEAN_AND_MEAN here, needs lots of includes. #if LL_WINDOWS -# undef WIN32_LEAN_AND_MEAN -# include -# include +#undef WIN32_LEAN_AND_MEAN +#include +#include +// ugh, this is ugly. We need to straighten out our linking for this library +#pragma comment(lib, "IPHLPAPI.lib") +#include #endif #include "lldefs.h" @@ -452,67 +455,102 @@ static void get_random_bytes(void *buf, int nbytes) return; } -#if LL_WINDOWS -typedef struct _ASTAT_ -{ - ADAPTER_STATUS adapt; - NAME_BUFFER NameBuff [30]; -}ASTAT, * PASTAT; +#if LL_WINDOWS +// Code copied from http://msdn.microsoft.com/en-us/library/aa365939(VS.85).aspx +// This code grabs the first hardware address, rather than the first interface. +// Using a VPN can cause the first returned interface to be changed. + +const S32 MAC_ADDRESS_BYTES=6; + // static -S32 LLUUID::getNodeID(unsigned char * node_id) -{ - ASTAT Adapter; - NCB Ncb; - UCHAR uRetCode; - LANA_ENUM lenum; - int i; - int retval = 0; - - memset( &Ncb, 0, sizeof(Ncb) ); - Ncb.ncb_command = NCBENUM; - Ncb.ncb_buffer = (UCHAR *)&lenum; - Ncb.ncb_length = sizeof(lenum); - uRetCode = Netbios( &Ncb ); - // printf( "The NCBENUM return code is: 0x%x \n", uRetCode ); - - for(i=0; i < lenum.length ;i++) - { - memset( &Ncb, 0, sizeof(Ncb) ); - Ncb.ncb_command = NCBRESET; - Ncb.ncb_lana_num = lenum.lana[i]; - - uRetCode = Netbios( &Ncb ); - // printf( "The NCBRESET on LANA %d return code is: 0x%x \n", - // lenum.lana[i], uRetCode ); - - memset( &Ncb, 0, sizeof (Ncb) ); - Ncb.ncb_command = NCBASTAT; - Ncb.ncb_lana_num = lenum.lana[i]; - - strcpy( (char *)Ncb.ncb_callname, "* " ); /* Flawfinder: ignore */ - Ncb.ncb_buffer = (unsigned char *)&Adapter; - Ncb.ncb_length = sizeof(Adapter); - - uRetCode = Netbios( &Ncb ); -// printf( "The NCBASTAT on LANA %d return code is: 0x%x \n", -// lenum.lana[i], uRetCode ); - if ( uRetCode == 0 ) - { -// printf( "The Ethernet Number on LANA %d is: %02x%02x%02x%02x%02x%02x\n", -// lenum.lana[i], -// Adapter.adapt.adapter_address[0], -// Adapter.adapt.adapter_address[1], -// Adapter.adapt.adapter_address[2], -// Adapter.adapt.adapter_address[3], -// Adapter.adapt.adapter_address[4], -// Adapter.adapt.adapter_address[5] ); - memcpy(node_id,Adapter.adapt.adapter_address,6); /* Flawfinder: ignore */ - retval = 1; - - } - } - return retval; +S32 LLUUID::getNodeID(unsigned char *node_id) +{ + + // Declare and initialize variables. + DWORD dwSize = 0; + DWORD dwRetVal = 0; + int i; + +/* variables used for GetIfTable and GetIfEntry */ + MIB_IFTABLE *pIfTable; + MIB_IFROW *pIfRow; + + // Allocate memory for our pointers. + pIfTable = (MIB_IFTABLE *) malloc(sizeof (MIB_IFTABLE)); + if (pIfTable == NULL) + { + printf("Error allocating memory needed to call GetIfTable\n"); + return 0; + } + + // Before calling GetIfEntry, we call GetIfTable to make + // sure there are entries to get and retrieve the interface index. + + // Make an initial call to GetIfTable to get the + // necessary size into dwSize + if (GetIfTable(pIfTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { + free(pIfTable); + pIfTable = (MIB_IFTABLE *) malloc(dwSize); + if (pIfTable == NULL) + { + printf("Error allocating memory\n"); + return 0; + } + } + // Make a second call to GetIfTable to get the actual + // data we want. + if ((dwRetVal = GetIfTable(pIfTable, &dwSize, 0)) == NO_ERROR) + { + if (pIfTable->dwNumEntries > 0) + { + pIfRow = (MIB_IFROW *) malloc(sizeof (MIB_IFROW)); + if (pIfRow == NULL) + { + printf("Error allocating memory\n"); + if (pIfTable != NULL) + { + free(pIfTable); + pIfTable = NULL; + } + return 0; + } + + int limit = MAC_ADDRESS_BYTES; + memcpy(node_id, "\0\0\0\0\0\0", limit); // zero out array of bytes + for (i = 0; i < (int) pIfTable->dwNumEntries; i++) + { + pIfRow->dwIndex = pIfTable->table[i].dwIndex; + if ((dwRetVal = GetIfEntry(pIfRow)) == NO_ERROR) + { + switch (pIfRow->dwType) + { + case IF_TYPE_ETHERNET_CSMACD: + case IF_TYPE_IEEE80211: + limit = min((int) pIfRow->dwPhysAddrLen, limit); + if (pIfRow->dwPhysAddrLen == 0) + break; + memcpy(node_id, (UCHAR *)&pIfRow->bPhysAddr[0], limit); // just incase the PhysAddr is not the expected MAC_Address size + free(pIfTable); + return 1; //return first hardware device found. + break; + + case IF_TYPE_OTHER: + case IF_TYPE_PPP: + case IF_TYPE_SOFTWARE_LOOPBACK: + case IF_TYPE_ISO88025_TOKENRING: + case IF_TYPE_IEEE1394: + case IF_TYPE_ATM: + case IF_TYPE_TUNNEL: + default: + break; + } + } + } + } + } + free(pIfTable); + return 0; } #elif LL_DARWIN diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 024e17a777..91e11b8c0d 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -89,10 +89,6 @@ S32 gCurlMultiCount = 0; std::vector LLCurl::sSSLMutex; std::string LLCurl::sCAPath; std::string LLCurl::sCAFile; -// Verify SSL certificates by default (matches libcurl default). The ability -// to alter this flag is only to allow us to suppress verification if it's -// broken for some reason. -bool LLCurl::sSSLVerify = true; //static void LLCurl::setCAPath(const std::string& path) @@ -106,18 +102,6 @@ void LLCurl::setCAFile(const std::string& file) sCAFile = file; } -//static -void LLCurl::setSSLVerify(bool verify) -{ - sSSLVerify = verify; -} - -//static -bool LLCurl::getSSLVerify() -{ - return sSSLVerify; -} - //static std::string LLCurl::getVersionString() { @@ -481,8 +465,7 @@ void LLCurl::Easy::prepRequest(const std::string& url, setErrorBuffer(); setCA(); - setopt(CURLOPT_SSL_VERIFYPEER, LLCurl::getSSLVerify()); - setopt(CURLOPT_SSL_VERIFYHOST, LLCurl::getSSLVerify()? 2 : 0); + setopt(CURLOPT_SSL_VERIFYPEER, true); setopt(CURLOPT_TIMEOUT, CURL_REQUEST_TIMEOUT); setoptString(CURLOPT_URL, url); @@ -912,6 +895,15 @@ void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userd } } +void LLCurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata) +{ + if (mEasy) + { + mEasy->setopt(CURLOPT_SSL_CTX_FUNCTION, (void*)callback); + mEasy->setopt(CURLOPT_SSL_CTX_DATA, userdata); + } +} + void LLCurlEasyRequest::slist_append(const char* str) { if (mEasy) @@ -1061,3 +1053,4 @@ void LLCurl::cleanupClass() #endif curl_global_cleanup(); } + diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index caf02cccd9..b6a637ae5b 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -157,16 +157,6 @@ public: */ static const std::string& getCAPath() { return sCAPath; } - /** - * @ brief Set flag controlling whether to verify HTTPS certs. - */ - static void setSSLVerify(bool verify); - - /** - * @ brief Get flag controlling whether to verify HTTPS certs. - */ - static bool getSSLVerify(); - /** * @ brief Initialize LLCurl class */ @@ -192,7 +182,6 @@ public: private: static std::string sCAPath; static std::string sCAFile; - static bool sSSLVerify; }; namespace boost @@ -240,6 +229,7 @@ public: void setHeaderCallback(curl_header_callback callback, void* userdata); void setWriteCallback(curl_write_callback callback, void* userdata); void setReadCallback(curl_read_callback callback, void* userdata); + void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata); void slist_append(const char* str); void sendRequest(const std::string& url); void requestComplete(); diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index dd56e18caf..345b76d1a1 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -31,7 +31,7 @@ */ #include "linden_common.h" - +#include #include "llhttpclient.h" #include "llassetstorage.h" @@ -46,7 +46,10 @@ #include "message.h" #include + const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; +LLURLRequest::SSLCertVerifyCallback LLHTTPClient::mCertVerifyCallback = NULL; + //////////////////////////////////////////////////////////////////////////// // Responder class moved to LLCurl @@ -206,13 +209,19 @@ namespace LLPumpIO* theClientPump = NULL; } +void LLHTTPClient::setCertVerifyCallback(LLURLRequest::SSLCertVerifyCallback callback) +{ + LLHTTPClient::mCertVerifyCallback = callback; +} + static void request( const std::string& url, LLURLRequest::ERequestAction method, Injector* body_injector, LLCurl::ResponderPtr responder, const F32 timeout = HTTP_REQUEST_EXPIRY_SECS, - const LLSD& headers = LLSD()) + const LLSD& headers = LLSD() + ) { if (!LLHTTPClient::hasPump()) { @@ -222,7 +231,7 @@ static void request( LLPumpIO::chain_t chain; LLURLRequest* req = new LLURLRequest(method, url); - req->checkRootCertificate(LLCurl::getSSLVerify()); + req->setSSLVerifyCallback(LLHTTPClient::getCertVerifyCallback(), (void *)req); lldebugs << LLURLRequest::actionAsVerb(method) << " " << url << " " @@ -417,7 +426,6 @@ static LLSD blocking_request( std::string body_str; // other request method checks root cert first, we skip? - //req->checkRootCertificate(true); // * Set curl handle options curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1); // don't use SIGALRM for timeouts diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index 3d0646e5fe..8afbc9e0fc 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -40,7 +40,8 @@ #include #include - +#include +#include "llurlrequest.h" #include "llassettype.h" #include "llcurl.h" #include "lliopipe.h" @@ -61,6 +62,7 @@ public: typedef LLCurl::Responder Responder; typedef LLCurl::ResponderPtr ResponderPtr; + /** @name non-blocking API */ //@{ static void head( @@ -155,7 +157,12 @@ public: static void setPump(LLPumpIO& pump); ///< must be called before any of the above calls are made static bool hasPump(); - ///< for testing + + static void setCertVerifyCallback(LLURLRequest::SSLCertVerifyCallback callback); + static LLURLRequest::SSLCertVerifyCallback getCertVerifyCallback() { return mCertVerifyCallback; } + +protected: + static LLURLRequest::SSLCertVerifyCallback mCertVerifyCallback; }; #endif // LL_LLHTTPCLIENT_H diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 4e7ceff984..1e76d10828 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -36,7 +36,8 @@ #include "llurlrequest.h" #include - +#include +#include #include "llcurl.h" #include "llioutil.h" #include "llmemtype.h" @@ -56,6 +57,8 @@ const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes"); static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user); + + /** * class LLURLRequestDetail */ @@ -72,6 +75,7 @@ public: U32 mBodyLimit; S32 mByteAccumulator; bool mIsBodyLimitSet; + LLURLRequest::SSLCertVerifyCallback mSSLVerifyCallback; }; LLURLRequestDetail::LLURLRequestDetail() : @@ -80,7 +84,8 @@ LLURLRequestDetail::LLURLRequestDetail() : mLastRead(NULL), mBodyLimit(0), mByteAccumulator(0), - mIsBodyLimitSet(false) + mIsBodyLimitSet(false), + mSSLVerifyCallback(NULL) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); mCurlRequest = new LLCurlEasyRequest(); @@ -94,6 +99,36 @@ LLURLRequestDetail::~LLURLRequestDetail() mLastRead = NULL; } +void LLURLRequest::setSSLVerifyCallback(SSLCertVerifyCallback callback, void *param) +{ + mDetail->mSSLVerifyCallback = callback; + mDetail->mCurlRequest->setSSLCtxCallback(LLURLRequest::_sslCtxCallback, (void *)this); + mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, true); + mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, 2); +} + + +// _sslCtxFunction +// Callback function called when an SSL Context is created via CURL +// used to configure the context for custom cert validation + +CURLcode LLURLRequest::_sslCtxCallback(CURL * curl, void *sslctx, void *param) +{ + LLURLRequest *req = (LLURLRequest *)param; + if(req == NULL || req->mDetail->mSSLVerifyCallback == NULL) + { + SSL_CTX_set_cert_verify_callback((SSL_CTX *)sslctx, NULL, NULL); + return CURLE_OK; + } + SSL_CTX * ctx = (SSL_CTX *) sslctx; + // disable any default verification for server certs + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + // set the verification callback. + SSL_CTX_set_cert_verify_callback(ctx, req->mDetail->mSSLVerifyCallback, (void *)req); + // the calls are void + return CURLE_OK; + +} /** * class LLURLRequest @@ -148,6 +183,11 @@ void LLURLRequest::setURL(const std::string& url) mDetail->mURL = url; } +std::string LLURLRequest::getURL() const +{ + return mDetail->mURL; +} + void LLURLRequest::addHeader(const char* header) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); @@ -160,13 +200,6 @@ void LLURLRequest::setBodyLimit(U32 size) mDetail->mIsBodyLimitSet = true; } -void LLURLRequest::checkRootCertificate(bool check) -{ - mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE)); - mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, (check? 2 : 0)); - mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); -} - void LLURLRequest::setCallback(LLURLRequestComplete* callback) { LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index cb3c466440..69fd22e592 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -44,6 +44,8 @@ #include "lliopipe.h" #include "llchainio.h" #include "llerror.h" +#include +#include "llcurl.h" extern const std::string CONTEXT_REQUEST; @@ -72,6 +74,8 @@ class LLURLRequest : public LLIOPipe { LOG_CLASS(LLURLRequest); public: + + typedef int (* SSLCertVerifyCallback)(X509_STORE_CTX *ctx, void *param); /** * @brief This enumeration is for specifying the type of request. */ @@ -125,7 +129,7 @@ public: * */ void setURL(const std::string& url); - + std::string getURL() const; /** * @brief Add a header to the http post. * @@ -143,8 +147,9 @@ public: * Set whether request will check that remote server * certificates are signed by a known root CA when using HTTPS. */ - void checkRootCertificate(bool check); + void setSSLVerifyCallback(SSLCertVerifyCallback callback, void * param); + /** * @brief Return at most size bytes of body. * @@ -189,6 +194,7 @@ public: * @brief Give this pipe a chance to handle a generated error */ virtual EStatus handleError(EStatus status, LLPumpIO* pump); + protected: /** @@ -217,6 +223,8 @@ protected: S32 mRequestTransferedBytes; S32 mResponseTransferedBytes; + static CURLcode _sslCtxCallback(CURL * curl, void *sslctx, void *param); + private: /** * @brief Initialize the object. Called during construction. @@ -364,62 +372,6 @@ protected: }; -/** - * @class LLURLRequestClientFactory - * @brief Template class to build url request based client chains - * - * This class eases construction of a basic sd rpc client. Here is an - * example of it's use: - * - * class LLUsefulService : public LLService { ... }
- * LLService::registerCreator(
- * "useful",
- * LLService::creator_t(new LLURLRequestClientFactory))
- *
- * - * This class should work, but I never got around to using/testing it. - * - */ -#if 0 -template -class LLURLRequestClientFactory : public LLChainIOFactory -{ -public: - LLURLRequestClientFactory(LLURLRequest::ERequestAction action) {} - LLURLRequestClientFactory( - LLURLRequest::ERequestAction action, - const std::string& fixed_url) : - mAction(action), - mURL(fixed_url) - { - } - virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const - { - lldebugs << "LLURLRequestClientFactory::build" << llendl; - LLIOPipe::ptr_t service(new Client); - chain.push_back(service); - LLURLRequest* http(new LLURLRequest(mAction)); - LLIOPipe::ptr_t http_pipe(http); - // *FIX: how do we know the content type? - //http->addHeader("Content-Type: text/llsd"); - if(mURL.empty()) - { - chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http))); - } - else - { - http->setURL(mURL); - } - chain.push_back(http_pipe); - chain.push_back(service); - return true; - } - -protected: - LLURLRequest::ERequestAction mAction; - std::string mURL; -}; -#endif /** * External constants diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index e8e3459673..c38e38c900 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -41,6 +41,9 @@ #include "lltrans.h" #include "lluicolortable.h" +#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" + + LLUrlEntryBase::LLUrlEntryBase() : mColor(LLUIColorTable::instance().getColor("HTMLLinkColor")), mDisabledLink(false) @@ -303,10 +306,11 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const // // LLUrlEntryAgent Describes a Second Life agent Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about // LLUrlEntryAgent::LLUrlEntryAgent() { - mPattern = boost::regex("secondlife:///app/agent/[\\da-f-]+/\\w+", + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_agent.xml"; mIcon = "Generic_Person"; @@ -418,10 +422,11 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa // LLUrlEntryGroup Describes a Second Life group Url, e.g., // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect +// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect // LLUrlEntryGroup::LLUrlEntryGroup() { - mPattern = boost::regex("secondlife:///app/group/[\\da-f-]+/\\w+", + mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_group.xml"; mIcon = "Generic_Group"; @@ -482,7 +487,8 @@ LLUrlEntryInventory::LLUrlEntryInventory() //*TODO: add supporting of inventory item names with whitespaces //this pattern cann't parse for example //secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value - mPattern = boost::regex("secondlife:///app/inventory/[\\da-f-]+/\\w+\\S*", + //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value + mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_inventory.xml"; } @@ -496,10 +502,11 @@ std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLab /// /// LLUrlEntryParcel Describes a Second Life parcel Url, e.g., /// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about /// LLUrlEntryParcel::LLUrlEntryParcel() { - mPattern = boost::regex("secondlife:///app/parcel/[\\da-f-]+/about", + mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_parcel.xml"; mTooltip = LLTrans::getString("TooltipParcelUrl"); @@ -515,7 +522,7 @@ std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelC // LLUrlEntryPlace::LLUrlEntryPlace() { - mPattern = boost::regex("secondlife://\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", + mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_slurl.xml"; mTooltip = LLTrans::getString("TooltipSLURL"); @@ -560,10 +567,11 @@ std::string LLUrlEntryPlace::getLocation(const std::string &url) const // // LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., // secondlife:///app/teleport/Ahern/50/50/50/ +// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/ // LLUrlEntryTeleport::LLUrlEntryTeleport() { - mPattern = boost::regex("secondlife:///app/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*", + mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_teleport.xml"; mTooltip = LLTrans::getString("TooltipTeleportUrl"); @@ -581,7 +589,12 @@ std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabe LLURI uri(url); LLSD path_array = uri.pathArray(); S32 path_parts = path_array.size(); - const std::string label = LLTrans::getString("SLurlLabelTeleport"); + std::string host = uri.hostName(); + std::string label = LLTrans::getString("SLurlLabelTeleport"); + if (!host.empty()) + { + label += " " + host; + } if (path_parts == 6) { // handle teleport url with (X,Y,Z) coordinates @@ -680,7 +693,7 @@ std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const // LLUrlEntryWorldMap::LLUrlEntryWorldMap() { - mPattern = boost::regex("secondlife:///app/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*", + mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_map.xml"; mTooltip = LLTrans::getString("TooltipMapUrl"); diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index cbb303a059..4463b6cc6f 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -286,6 +286,13 @@ namespace tut "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar", "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar"); + testRegex("Standalone Agent Url ", url, + "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about", + "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + + testRegex("Standalone Agent Url Multicase with Text", url, + "M x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M", + "x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); } template<> template<> @@ -315,6 +322,15 @@ namespace tut testRegex("Group Url multicase", url, "XXX secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About XXX", "secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About"); + + testRegex("Standalone Group Url ", url, + "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about", + "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + + testRegex("Standalone Group Url Multicase ith Text", url, + "M x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M", + "x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + } template<> template<> @@ -361,7 +377,11 @@ namespace tut // DEV-35459: SLURLs and teleport Links not parsed properly testRegex("SLURL with quote", url, "XXX secondlife://A'ksha%20Oasis/41/166/701 XXX", - "secondlife://A%27ksha%20Oasis/41/166/701"); + "secondlife://A%27ksha%20Oasis/41/166/701"); + + testRegex("Standalone All Hands (50,50) [2] with text", url, + "XXX x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50 XXX", + "x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50"); } template<> template<> @@ -461,6 +481,10 @@ namespace tut testRegex("Teleport url with quote", url, "XXX secondlife:///app/teleport/A'ksha%20Oasis/41/166/701 XXX", "secondlife:///app/teleport/A%27ksha%20Oasis/41/166/701"); + + testRegex("Standalone All Hands", url, + "XXX x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50 XXX", + "x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50"); } template<> template<> diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index da4abde451..b4ee42ef3a 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -459,7 +459,6 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd } //llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl; - return expanded_filename; } @@ -565,27 +564,23 @@ std::string LLDir::getForbiddenFileChars() return "\\/:*?\"<>|"; } -void LLDir::setLindenUserDir(const std::string &first, const std::string &last) +void LLDir::setLindenUserDir(const std::string &username) { - // if both first and last aren't set, that's bad. - if (!first.empty() && !last.empty()) + // if the username isn't set, that's bad + if (!username.empty()) { // some platforms have case-sensitive filesystems, so be // utterly consistent with our firstname/lastname case. - std::string firstlower(first); - LLStringUtil::toLower(firstlower); - std::string lastlower(last); - LLStringUtil::toLower(lastlower); + std::string userlower(username); + LLStringUtil::toLower(userlower); + LLStringUtil::replaceChar(userlower, ' ', '_'); mLindenUserDir = getOSUserAppDir(); mLindenUserDir += mDirDelimiter; - mLindenUserDir += firstlower; - mLindenUserDir += "_"; - mLindenUserDir += lastlower; - llinfos << "Got name for LLDir::setLindenUserDir(first='" << first << "', last='" << last << "')" << llendl; + mLindenUserDir += userlower; } else { - llerrs << "Invalid name for LLDir::setLindenUserDir(first='" << first << "', last='" << last << "')" << llendl; + llerrs << "NULL name for LLDir::setLindenUserDir" << llendl; } dumpCurrentDirectories(); @@ -603,27 +598,25 @@ void LLDir::setChatLogsDir(const std::string &path) } } -void LLDir::setPerAccountChatLogsDir(const std::string &first, const std::string &last) +void LLDir::setPerAccountChatLogsDir(const std::string &username) { // if both first and last aren't set, assume we're grabbing the cached dir - if (!first.empty() && !last.empty()) + if (!username.empty()) { // some platforms have case-sensitive filesystems, so be // utterly consistent with our firstname/lastname case. - std::string firstlower(first); - LLStringUtil::toLower(firstlower); - std::string lastlower(last); - LLStringUtil::toLower(lastlower); + std::string userlower(username); + LLStringUtil::toLower(userlower); + LLStringUtil::replaceChar(userlower, ' ', '_'); mPerAccountChatLogsDir = getChatLogsDir(); mPerAccountChatLogsDir += mDirDelimiter; - mPerAccountChatLogsDir += firstlower; - mPerAccountChatLogsDir += "_"; - mPerAccountChatLogsDir += lastlower; + mPerAccountChatLogsDir += userlower; } else { - llwarns << "Invalid name for LLDir::setPerAccountChatLogsDir" << llendl; + llerrs << "NULL name for LLDir::setPerAccountChatLogsDir" << llendl; } + } void LLDir::setSkinFolder(const std::string &skin_folder) diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index 9067d75bac..05d5efc66f 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -137,8 +137,8 @@ class LLDir static std::string getForbiddenFileChars(); virtual void setChatLogsDir(const std::string &path); // Set the chat logs dir to this user's dir - virtual void setPerAccountChatLogsDir(const std::string &first, const std::string &last); // Set the per user chat log directory. - virtual void setLindenUserDir(const std::string &first, const std::string &last); // Set the linden user dir to this user's dir + virtual void setPerAccountChatLogsDir(const std::string &username); // Set the per user chat log directory. + virtual void setLindenUserDir(const std::string &username); // Set the linden user dir to this user's dir virtual void setSkinFolder(const std::string &skin_folder); virtual bool setCacheDir(const std::string &path); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 47fde08a9d..e0a31875c8 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -7,6 +7,7 @@ include(Boost) include(BuildVersion) include(DBusGlib) include(DirectX) +include(OpenSSL) include(DragDrop) include(ELFIO) include(FMOD) @@ -371,6 +372,8 @@ set(viewer_SOURCE_FILES llscrollingpanelparam.cpp llsearchcombobox.cpp llsearchhistory.cpp + llsecapi.cpp + llsechandler_basic.cpp llselectmgr.cpp llsidepanelappearance.cpp llsidepanelinventory.cpp @@ -445,7 +448,6 @@ set(viewer_SOURCE_FILES llurldispatcherlistener.cpp llurlhistory.cpp llurllineeditorctrl.cpp - llurlsimstring.cpp llurlwhitelist.cpp llvectorperfoptions.cpp llversioninfo.cpp @@ -512,7 +514,9 @@ set(viewer_SOURCE_FILES llvoground.cpp llvoicechannel.cpp llvoiceclient.cpp + llvoicedw.cpp llvoicevisualizer.cpp + llvoicevivox.cpp llvoinventorylistener.cpp llvopartgroup.cpp llvosky.cpp @@ -872,6 +876,8 @@ set(viewer_HEADER_FILES llscrollingpanelparam.h llsearchcombobox.h llsearchhistory.h + llsecapi.h + llsechandler_basic.h llselectmgr.h llsidepanelappearance.h llsidepanelinventory.h @@ -948,7 +954,6 @@ set(viewer_HEADER_FILES llurldispatcherlistener.h llurlhistory.h llurllineeditorctrl.h - llurlsimstring.h llurlwhitelist.h llvectorperfoptions.h llversioninfo.h @@ -1012,7 +1017,9 @@ set(viewer_HEADER_FILES llvoground.h llvoicechannel.h llvoiceclient.h + llvoicedw.h llvoicevisualizer.h + llvoicevivox.h llvoinventorylistener.h llvopartgroup.h llvosky.h @@ -1403,8 +1410,8 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libtcmalloc_minimal.dll ${SHARED_LIB_STAGING_DIR}/Debug/libtcmalloc_minimal-debug.dll ) - endif(USE_GOOGLE_PERFTOOLS) - + endif(USE_GOOGLE_PERFTOOLS) + set(COPY_INPUT_DEPENDECIES # The following commented dependencies are determined at variably at build time. Can't do this here. @@ -1621,6 +1628,8 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${WINDOWS_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${ELFIO_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} ${LLLOGIN_LIBRARIES} ${GOOGLE_PERFTOOLS_LIBRARIES} ) @@ -1797,6 +1806,43 @@ if (LL_TESTS) "${CMAKE_SOURCE_DIR}/llmessage/tests/test_llsdmessage_peer.py" ) + set(test_libs + ${LLMESSAGE_LIBRARIES} + ${WINDOWS_LIBRARIES} + ${LLVFS_LIBRARIES} + ${LLMATH_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ) + + LL_ADD_INTEGRATION_TEST(llsechandler_basic + llsechandler_basic.cpp + "${test_libs}" + ) + + LL_ADD_INTEGRATION_TEST(llsecapi + llsecapi.cpp + "${test_libs}" + ) + + set(llslurl_test_sources + llslurl.cpp + llviewernetwork.cpp + ) + + + LL_ADD_INTEGRATION_TEST(llslurl + "${llslurl_test_sources}" + "${test_libs}" + ) + + LL_ADD_INTEGRATION_TEST(llviewernetwork + llviewernetwork.cpp + "${test_libs}" + ) + #ADD_VIEWER_BUILD_TEST(llmemoryview viewer) #ADD_VIEWER_BUILD_TEST(llagentaccess viewer) #ADD_VIEWER_BUILD_TEST(llworldmap viewer) @@ -1804,6 +1850,7 @@ if (LL_TESTS) #ADD_VIEWER_BUILD_TEST(lltextureinfo viewer) #ADD_VIEWER_BUILD_TEST(lltextureinfodetails viewer) #ADD_VIEWER_BUILD_TEST(lltexturestatsuploader viewer) + endif (LL_TESTS) diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist index 4cb01a0f33..7436c5642e 100644 --- a/indra/newview/Info-SecondLife.plist +++ b/indra/newview/Info-SecondLife.plist @@ -18,6 +18,33 @@ APPL CFBundleSignature ???? + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + slurl + + CFBundleTypeIconFile + seconlife + CFBundleTypeMIMETypes + + application/x-grid-location-info + + CFBundleTypeName + Secondlife SLURL + CFBundleTypeOSTypes + + SLRL + + CFBundleTypeRole + Viewer + LSTypeIsPackage + + NSDocumentClass + SecondLifeSLURL + + CFBundleURLTypes @@ -26,6 +53,7 @@ CFBundleURLSchemes secondlife + x-grid-location-info LSIsAppleDefaultForScheme diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 30049b73ea..442cd5d31e 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1264,6 +1264,17 @@ Value 0 + CertStore + + Comment + Specifies the Certificate Store for certificate trust verification + Persist + 1 + Type + String + Value + default + ChatBarStealsFocus Comment @@ -1640,6 +1651,17 @@ Value 1 + CurrentGrid + + Comment + Currently Selected Grid + Persist + 1 + Type + String + Value + + CustomServer Comment @@ -2366,6 +2388,29 @@ Value 0 + DefaultFemaleAvatar + + Comment + Default Female Avatar + Persist + 1 + Type + String + Value + Female Shape & Outfit + + DefaultMaleAvatar + + Comment + Default Male Avatar + Persist + 1 + Type + String + Value + Male Shape & Outfit + + DefaultObjectTexture Comment @@ -3442,7 +3487,7 @@ Type Boolean Value - 0 + 1 ForceMandatoryUpdate @@ -7696,6 +7741,17 @@ Value 0 + SecondLifeEnterprise + + Comment + Enables Second Life Enterprise features + Persist + 1 + Type + Boolean + Value + 0 + SelectMovableOnly Comment @@ -8500,7 +8556,7 @@ Type Boolean Value - 0 + 1 ShowTangentBasis @@ -10393,6 +10449,17 @@ Value + VivoxDebugSIPURIHostName + + Comment + Hostname portion of vivox SIP URIs (empty string for the default). + Persist + 1 + Type + String + Value + + VivoxDebugVoiceAccountServerURI Comment @@ -10404,6 +10471,28 @@ Value + VivoxVoiceHost + + Comment + Client SLVoice host to connect to + Persist + 1 + Type + String + Value + 127.0.0.1 + + VivoxVoicePort + + Comment + Client SLVoice port to connect to + Persist + 1 + Type + U32 + Value + 44125 + VoiceCallsFriendsOnly Comment @@ -10536,6 +10625,17 @@ Value Default + VoiceLogFile + + Comment + Log file to use when launching the voice daemon + Persist + 1 + Type + String + Value + + VoiceOutputAudioDevice Comment @@ -10580,6 +10680,17 @@ Value 0 + VoiceServerType + + Comment + The type of voice server to connect to. + Persist + 0 + Type + String + Value + vivox + WLSkyDetail Comment diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index a7322749ca..b7b4c54001 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -797,6 +797,12 @@ WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\DefaultIcon" "" '"$INSTDIR\$INSTEXE"' ;; URL param must be last item passed to viewer, it ignores subsequent params ;; to avoid parameter injection attacks. WriteRegExpandStr HKEY_CLASSES_ROOT "${URLNAME}\shell\open\command" "" '"$INSTDIR\$INSTEXE" $INSTFLAGS -url "%1"' +WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info"(default)" "URL:Second Life" +WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "URL Protocol" "" +WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\DefaultIcon" "" '"$INSTDIR\$INSTEXE"' +;; URL param must be last item passed to viewer, it ignores subsequent params +;; to avoid parameter injection attacks. +WriteRegExpandStr HKEY_CLASSES_ROOT "x-grid-location-info\shell\open\command" "" '"$INSTDIR\$INSTEXE" $INSTFLAGS -url "%1"' ; write out uninstaller WriteUninstaller "$INSTDIR\uninst.exe" diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index f434782977..37d1bd15e1 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -3231,7 +3231,7 @@ bool LLAgent::teleportCore(bool is_local) // MBW -- Let the voice client know a teleport has begun so it can leave the existing channel. // This was breaking the case of teleporting within a single sim. Backing it out for now. -// gVoiceClient->leaveChannel(); +// LLVoiceClient::getInstance()->leaveChannel(); return true; } @@ -3375,7 +3375,7 @@ void LLAgent::setTeleportState(ETeleportState state) if (mTeleportState == TELEPORT_MOVING) { // We're outa here. Save "back" slurl. - mTeleportSourceSLURL = LLAgentUI::buildSLURL(); + LLAgentUI::buildSLURL(mTeleportSourceSLURL); } else if(mTeleportState == TELEPORT_ARRIVING) { diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index a460077b7e..32f9b00135 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -42,6 +42,7 @@ #include "llpointer.h" #include "lluicolor.h" #include "llvoavatardefines.h" +#include "llslurl.h" extern const BOOL ANIMATE; extern const U8 AGENT_STATE_TYPING; // Typing indication @@ -514,13 +515,13 @@ public: public: static void parseTeleportMessages(const std::string& xml_filename); - const std::string getTeleportSourceSLURL() const { return mTeleportSourceSLURL; } + const void getTeleportSourceSLURL(LLSLURL& slurl) const { slurl = mTeleportSourceSLURL; } public: // ! TODO ! Define ERROR and PROGRESS enums here instead of exposing the mappings. static std::map sTeleportErrorMessages; static std::map sTeleportProgressMessages; private: - std::string mTeleportSourceSLURL; // SLURL where last TP began + LLSLURL mTeleportSourceSLURL; // SLURL where last TP began //-------------------------------------------------------------------- // Teleport Actions diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index b3ed7c353e..7a8205acb5 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -53,7 +53,10 @@ void LLAgentListener::requestTeleport(LLSD const & event_data) const } else { - std::string url = LLSLURL::buildSLURL(event_data["regionname"], event_data["x"], event_data["y"], event_data["z"]); + std::string url = LLSLURL(event_data["regionname"], + LLVector3(event_data["x"].asReal(), + event_data["y"].asReal(), + event_data["z"].asReal())).getSLURLString(); LLURLDispatcher::dispatch(url, NULL, false); } } diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp index c4597ad6f8..15d9f36b74 100644 --- a/indra/newview/llagentui.cpp +++ b/indra/newview/llagentui.cpp @@ -76,16 +76,15 @@ void LLAgentUI::buildFullname(std::string& name) } //static -std::string LLAgentUI::buildSLURL(const bool escaped /*= true*/) +void LLAgentUI::buildSLURL(LLSLURL& slurl, const bool escaped /*= true*/) { - std::string slurl; - LLViewerRegion *regionp = gAgent.getRegion(); - if (regionp) - { - LLVector3d agentPos = gAgent.getPositionGlobal(); - slurl = LLSLURL::buildSLURLfromPosGlobal(regionp->getName(), agentPos, escaped); - } - return slurl; + LLSLURL return_slurl; + LLViewerRegion *regionp = gAgent.getRegion(); + if (regionp) + { + return_slurl = LLSLURL(regionp->getName(), gAgent.getPositionGlobal()); + } + slurl = return_slurl; } //static diff --git a/indra/newview/llagentui.h b/indra/newview/llagentui.h index 3478793e38..577b752fbe 100644 --- a/indra/newview/llagentui.h +++ b/indra/newview/llagentui.h @@ -33,6 +33,8 @@ #ifndef LLAGENTUI_H #define LLAGENTUI_H +class LLSLURL; + class LLAgentUI { public: @@ -48,7 +50,7 @@ public: static void buildName(std::string& name); static void buildFullname(std::string &name); - static std::string buildSLURL(const bool escaped = true); + static void buildSLURL(LLSLURL& slurl, const bool escaped = true); //build location string using the current position of gAgent. static BOOL buildLocationString(std::string& str, ELocationFormat fmt = LOCATION_FORMAT_LANDMARK); //build location string using a region position of the avatar. diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 43c8c679c6..2a355474b1 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -153,7 +153,7 @@ #include "llworld.h" #include "llhudeffecttrail.h" #include "llvectorperfoptions.h" -#include "llurlsimstring.h" +#include "llslurl.h" #include "llwatchdog.h" // Included so that constants/settings might be initialized @@ -193,6 +193,9 @@ #include "llparcel.h" #include "llavatariconctrl.h" +// Include for security api initialization +#include "llsecapi.h" + // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either // this app, or another 'component' of the viewer. App globals should be @@ -507,35 +510,6 @@ public: } }; -void LLAppViewer::initGridChoice() -{ - // Load up the initial grid choice from: - // - hard coded defaults... - // - command line settings... - // - if dev build, persisted settings... - - // Set the "grid choice", this is specified by command line. - std::string grid_choice = gSavedSettings.getString("CmdLineGridChoice"); - LLViewerLogin::getInstance()->setGridChoice(grid_choice); - - // Load last server choice by default - // ignored if the command line grid choice has been set - if(grid_choice.empty()) - { - S32 server = gSavedSettings.getS32("ServerChoice"); - server = llclamp(server, 0, (S32)GRID_INFO_COUNT - 1); - if(server == GRID_INFO_OTHER) - { - std::string custom_server = gSavedSettings.getString("CustomServer"); - LLViewerLogin::getInstance()->setGridChoice(custom_server); - } - else if(server != (S32)GRID_INFO_NONE) - { - LLViewerLogin::getInstance()->setGridChoice((EGridInfo)server); - } - } -} - //virtual bool LLAppViewer::initSLURLHandler() { @@ -647,7 +621,6 @@ bool LLAppViewer::init() LLCurl::initClass(); initThreads(); - writeSystemInfo(); // Build a string representing the current version number. @@ -777,10 +750,6 @@ bool LLAppViewer::init() return false; } - // Always fetch the Ethernet MAC address, needed both for login - // and password load. - LLUUID::getNodeID(gMACAddress); - // Prepare for out-of-memory situations, during which we will crash on // purpose and save a dump. #if LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP @@ -892,6 +861,7 @@ bool LLAppViewer::init() } } + // save the graphics card gDebugInfo["GraphicsCard"] = LLFeatureManager::getInstance()->getGPUString(); @@ -902,6 +872,17 @@ bool LLAppViewer::init() gSimFrames = (F32)gFrameCount; LLViewerJoystick::getInstance()->init(false); + + try { + initializeSecHandler(); + } + catch (LLProtectedDataException ex) + { + LLNotificationsUtil::add("CorruptedProtectedDataStore"); + } + LLHTTPClient::setCertVerifyCallback(secapiSSLCertVerifyCallback); + + gGLActive = FALSE; if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0) { @@ -936,13 +917,11 @@ bool LLAppViewer::mainLoop() gServicePump = new LLPumpIO(gAPRPoolp); LLHTTPClient::setPump(*gServicePump); LLCurl::setCAFile(gDirUtilp->getCAFile()); - LLCurl::setSSLVerify(! gSavedSettings.getBOOL("NoVerifySSLCert")); - + // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated. LLVoiceChannel::initClass(); - LLVoiceClient::init(gServicePump); - + LLVoiceClient::getInstance()->init(gServicePump); LLTimer frameTimer,idleTimer; LLTimer debugTime; LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); @@ -1273,7 +1252,7 @@ bool LLAppViewer::cleanup() // to ensure shutdown order LLMortician::setZealous(TRUE); - LLVoiceClient::terminate(); + LLVoiceClient::getInstance()->terminate(); disconnectViewer(); @@ -1472,13 +1451,6 @@ bool LLAppViewer::cleanup() llinfos << "Saving Data" << llendflush; - // Quitting with "Remember Password" turned off should always stomp your - // saved password, whether or not you successfully logged in. JC - if (!gSavedSettings.getBOOL("RememberPassword")) - { - LLStartUp::deletePasswordFromDisk(); - } - // Store the time of our current logoff gSavedPerAccountSettings.setU32("LastLogoff", time_corrected()); @@ -2047,7 +2019,6 @@ bool LLAppViewer::initConfiguration() } } - initGridChoice(); // If we have specified crash on startup, set the global so we'll trigger the crash at the right time if(clp.hasOption("crashonstartup")) @@ -2141,30 +2112,17 @@ bool LLAppViewer::initConfiguration() // injection and steal passwords. Phoenix. SL-55321 if(clp.hasOption("url")) { - std::string slurl = clp.getOption("url")[0]; - if (LLSLURL::isSLURLCommand(slurl)) - { - LLStartUp::sSLURLCommand = slurl; - } - else - { - LLURLSimString::setString(slurl); - } + LLStartUp::setStartSLURL(LLSLURL(clp.getOption("url")[0])); + if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION) + { + LLGridManager::getInstance()->setGridChoice(LLStartUp::getStartSLURL().getGrid()); + + } } else if(clp.hasOption("slurl")) { - std::string slurl = clp.getOption("slurl")[0]; - if(LLSLURL::isSLURL(slurl)) - { - if (LLSLURL::isSLURLCommand(slurl)) - { - LLStartUp::sSLURLCommand = slurl; - } - else - { - LLURLSimString::setString(slurl); - } - } + LLSLURL start_slurl(clp.getOption("slurl")[0]); + LLStartUp::setStartSLURL(start_slurl); } const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); @@ -2245,18 +2203,10 @@ bool LLAppViewer::initConfiguration() // don't call anotherInstanceRunning() when doing URL handoff, as // it relies on checking a marker file which will not work when running // out of different directories - std::string slurl; - if (!LLStartUp::sSLURLCommand.empty()) - { - slurl = LLStartUp::sSLURLCommand; - } - else if (LLURLSimString::parse()) - { - slurl = LLURLSimString::getURL(); - } - if (!slurl.empty()) + + if (LLStartUp::getStartSLURL().isValid()) { - if (sendURLToOtherInstance(slurl)) + if (sendURLToOtherInstance(LLStartUp::getStartSLURL().getSLURLString())) { // successfully handed off URL to existing instance, exit return false; @@ -2312,9 +2262,9 @@ bool LLAppViewer::initConfiguration() // need to do this here - need to have initialized global settings first std::string nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" ); - if ( nextLoginLocation.length() ) + if ( !nextLoginLocation.empty() ) { - LLURLSimString::setString( nextLoginLocation ); + LLStartUp::setStartSLURL(LLSLURL(nextLoginLocation)); }; gLastRunVersion = gSavedSettings.getString("LastRunVersion"); @@ -2535,7 +2485,7 @@ void LLAppViewer::writeSystemInfo() // The user is not logged on yet, but record the current grid choice login url // which may have been the intended grid. This can b - gDebugInfo["GridName"] = LLViewerLogin::getInstance()->getGridLabel(); + gDebugInfo["GridName"] = LLGridManager::getInstance()->getGridLabel(); // *FIX:Mani - move this ddown in llappviewerwin32 #ifdef LL_WINDOWS @@ -3886,7 +3836,7 @@ void LLAppViewer::sendLogoutRequest() gLogoutMaxTime = LOGOUT_REQUEST_TIME; mLogoutRequestSent = TRUE; - gVoiceClient->leaveChannel(); + LLVoiceClient::getInstance()->leaveChannel(); //Set internal status variables and marker files gLogoutInProgress = TRUE; @@ -4306,7 +4256,7 @@ void LLAppViewer::launchUpdater() #endif // *TODO change userserver to be grid on both viewer and sim, since // userserver no longer exists. - query_map["userserver"] = LLViewerLogin::getInstance()->getGridLabel(); + query_map["userserver"] = LLGridManager::getInstance()->getGridLabel(); query_map["channel"] = gSavedSettings.getString("VersionChannelName"); // *TODO constantize this guy // *NOTE: This URL is also used in win_setup/lldownloader.cpp @@ -4319,10 +4269,10 @@ void LLAppViewer::launchUpdater() LLAppViewer::sUpdaterInfo = new LLAppViewer::LLUpdaterInfo() ; // if a sim name was passed in via command line parameter (typically through a SLURL) - if ( LLURLSimString::sInstance.mSimString.length() ) + if ( LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION ) { // record the location to start at next time - gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); + gSavedSettings.setString( "NextLoginLocation", LLStartUp::getStartSLURL().getSLURLString()); }; #if LL_WINDOWS diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index a915b7fa50..27e8bec1d5 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -191,7 +191,6 @@ private: bool initThreads(); // Initialize viewer threads, return false on failure. bool initConfiguration(); // Initialize settings from the command line/config file. - void initGridChoice(); bool initCache(); // Initialize local client cache. diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index d34bcb4a68..78b0f7ba83 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -604,7 +604,7 @@ void LLAppViewerLinux::handleCrashReporting(bool reportFreeze) {cmd.c_str(), ask_dialog, "-user", - (char*)LLViewerLogin::getInstance()->getGridLabel().c_str(), + (char*)LLGridManager::getInstance()->getGridLabel().c_str(), "-name", LLAppViewer::instance()->getSecondLifeTitle().c_str(), NULL}; diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index 80d9b14345..58d28883c6 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -44,7 +44,6 @@ #include "llviewernetwork.h" #include "llviewercontrol.h" #include "llmd5.h" -#include "llurlsimstring.h" #include "llfloaterworldmap.h" #include "llurldispatcher.h" #include diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index c85c72837c..48a85dc73f 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -282,7 +282,7 @@ bool LLAvatarActions::isCalling(const LLUUID &id) //static bool LLAvatarActions::canCall() { - return LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking(); + return LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); } // static diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index 41bee540fc..9824f59358 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -319,7 +319,7 @@ void LLBottomTray::onChange(EStatusType status, const std::string &channelURI, b // skipped to avoid button blinking if (status != STATUS_JOINING && status!= STATUS_LEFT_CHANNEL) { - mSpeakBtn->setFlyoutBtnEnabled(LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking()); + mSpeakBtn->setFlyoutBtnEnabled(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking()); } } @@ -489,7 +489,7 @@ BOOL LLBottomTray::postBuild() mSpeakBtn->setShowToolTip( getString("VoiceControlBtnToolTip") ); // Registering Chat Bar to receive Voice client status change notifications. - gVoiceClient->addObserver(this); + LLVoiceClient::getInstance()->addObserver(this); mObjectDefaultWidthMap[RS_BUTTON_GESTURES] = mGesturePanel->getRect().getWidth(); mObjectDefaultWidthMap[RS_BUTTON_MOVEMENT] = mMovementPanel->getRect().getWidth(); diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index 4ea3c61ab2..215f1b95aa 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -133,9 +133,9 @@ LLCallFloater::~LLCallFloater() // Don't use LLVoiceClient::getInstance() here // singleton MAY have already been destroyed. - if(gVoiceClient) + if(LLVoiceClient::getInstance()) { - gVoiceClient->removeObserver(this); + LLVoiceClient::getInstance()->removeObserver(this); } LLTransientFloaterMgr::getInstance()->removeControlView(this); } @@ -191,7 +191,7 @@ void LLCallFloater::draw() // Seems this is a problem somewhere in Voice Client (LLVoiceClient::participantAddedEvent) // onChange(); - bool is_moderator_muted = gVoiceClient->getIsModeratorMuted(gAgentID); + bool is_moderator_muted = LLVoiceClient::getInstance()->getIsModeratorMuted(gAgentID); if (mIsModeratorMutedVoice != is_moderator_muted) { @@ -209,7 +209,6 @@ void LLCallFloater::draw() void LLCallFloater::onChange() { if (NULL == mParticipants) return; - updateParticipantsVoiceState(); // Add newly joined participants. @@ -239,11 +238,11 @@ void LLCallFloater::updateSession() LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel(); if (voice_channel) { - lldebugs << "Current voice channel: " << voice_channel->getSessionID() << llendl; + LL_DEBUGS("Voice") << "Current voice channel: " << voice_channel->getSessionID() << LL_ENDL; if (mSpeakerManager && voice_channel->getSessionID() == mSpeakerManager->getSessionID()) { - lldebugs << "Speaker manager is already set for session: " << voice_channel->getSessionID() << llendl; + LL_DEBUGS("Voice") << "Speaker manager is already set for session: " << voice_channel->getSessionID() << LL_ENDL; return; } else @@ -253,7 +252,6 @@ void LLCallFloater::updateSession() } const LLUUID& session_id = voice_channel ? voice_channel->getSessionID() : LLUUID::null; - lldebugs << "Set speaker manager for session: " << session_id << llendl; LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id); if (im_session) @@ -293,7 +291,7 @@ void LLCallFloater::updateSession() { // by default let show nearby chat participants mSpeakerManager = LLLocalSpeakerMgr::getInstance(); - lldebugs << "Set DEFAULT speaker manager" << llendl; + LL_DEBUGS("Voice") << "Set DEFAULT speaker manager" << LL_ENDL; mVoiceType = VC_LOCAL_CHAT; } @@ -472,16 +470,15 @@ void LLCallFloater::updateAgentModeratorState() static void get_voice_participants_uuids(uuid_vec_t& speakers_uuids) { // Get a list of participants from VoiceClient - LLVoiceClient::participantMap *voice_map = gVoiceClient->getParticipantList(); - if (voice_map) + std::set participants; + LLVoiceClient::getInstance()->getParticipantList(participants); + + for (std::set::const_iterator iter = participants.begin(); + iter != participants.end(); ++iter) { - for (LLVoiceClient::participantMap::const_iterator iter = voice_map->begin(); - iter != voice_map->end(); ++iter) - { - LLUUID id = (*iter).second->mAvatarID; - speakers_uuids.push_back(id); - } + speakers_uuids.push_back(*iter); } + } void LLCallFloater::initParticipantsVoiceState() @@ -557,7 +554,7 @@ void LLCallFloater::updateParticipantsVoiceState() uuid_vec_t::iterator speakers_iter = std::find(speakers_uuids.begin(), speakers_uuids.end(), participant_id); - lldebugs << "processing speaker: " << item->getAvatarName() << ", " << item->getAvatarId() << llendl; + LL_DEBUGS("Voice") << "processing speaker: " << item->getAvatarName() << ", " << item->getAvatarId() << LL_ENDL; // If an avatarID assigned to a panel is found in a speakers list // obtained from VoiceClient we assign the JOINED status to the owner @@ -727,7 +724,7 @@ void LLCallFloater::connectToChannel(LLVoiceChannel* channel) void LLCallFloater::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) { // check is voice operational and if it doesn't work hide VCP (EXT-4397) - if(LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking()) + if(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking()) { updateState(new_state); } diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp index 79a2631c31..1a6c11fa73 100644 --- a/indra/newview/llcallingcard.cpp +++ b/indra/newview/llcallingcard.cpp @@ -700,6 +700,7 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online) args["FIRST"] = first; args["LAST"] = last; } + } } else diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 71e7ae7061..1e404d611c 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -640,20 +640,19 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL if ( chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull()) { // for object IMs, create a secondlife:///app/objectim SLapp - std::string url = LLSLURL::buildCommand("objectim", chat.mFromID, ""); + std::string url = LLSLURL("objectim", chat.mFromID, "").getSLURLString(); url += "?name=" + chat.mFromName; url += "&owner=" + args["owner_id"].asString(); std::string slurl = args["slurl"].asString(); if (slurl.empty()) { - LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosAgent(chat.mPosAgent); - if (region) - { - S32 x, y, z; - LLSLURL::globalPosToXYZ(LLVector3d(chat.mPosAgent), x, y, z); - slurl = region->getName() + llformat("/%d/%d/%d", x, y, z); - } + LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosAgent(chat.mPosAgent); + if(region) + { + LLSLURL region_slurl(region->getName(), chat.mPosAgent); + slurl = region_slurl.getLocationString(); + } } url += "&slurl=" + slurl; diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index be6c15eab4..fd3df359bd 100644 --- a/indra/newview/llcurrencyuimanager.cpp +++ b/indra/newview/llcurrencyuimanager.cpp @@ -284,7 +284,7 @@ void LLCurrencyUIManager::Impl::startTransaction(TransactionType type, static std::string transactionURI; if (transactionURI.empty()) { - transactionURI = LLViewerLogin::getInstance()->getHelperURI() + "currency.php"; + transactionURI = LLGridManager::getInstance()->getHelperURI() + "currency.php"; } delete mTransaction; diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index ef69f39ad2..56bc4a7933 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -266,8 +266,18 @@ LLSD LLFloaterAbout::getInfo() info["J2C_VERSION"] = LLImageJ2C::getEngineInfo(); bool want_fullname = true; info["AUDIO_DRIVER_VERSION"] = gAudiop ? LLSD(gAudiop->getDriverName(want_fullname)) : LLSD(); - info["VIVOX_VERSION"] = gVoiceClient ? gVoiceClient->getAPIVersion() : LLTrans::getString("NotConnected"); - + if(LLVoiceClient::getInstance()->voiceEnabled()) + { + LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); + std::ostringstream version_string; + version_string << version.serverType << " " << version.serverVersion << std::endl; + info["VOICE_VERSION"] = version_string.str(); + } + else + { + info["VOICE_VERSION"] = LLTrans::getString("NotConnected"); + } + // TODO: Implement media plugin version query info["QT_WEBKIT_VERSION"] = "4.6 (version number hard-coded)"; diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp index d37bc01885..464b3a7214 100644 --- a/indra/newview/llfloaterbuyland.cpp +++ b/indra/newview/llfloaterbuyland.cpp @@ -830,7 +830,7 @@ void LLFloaterBuyLandUI::updateNames() else { mParcelSellerName = - LLSLURL::buildCommand("agent", parcelp->getOwnerID(), "inspect"); + LLSLURL("agent", parcelp->getOwnerID(), "inspect").getSLURLString(); } } @@ -859,7 +859,7 @@ void LLFloaterBuyLandUI::startTransaction(TransactionType type, const LLXMLRPCVa static std::string transaction_uri; if (transaction_uri.empty()) { - transaction_uri = LLViewerLogin::getInstance()->getHelperURI() + "landtool.php"; + transaction_uri = LLGridManager::getInstance()->getHelperURI() + "landtool.php"; } const char* method; diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index cdb9b8edb8..882d12f68e 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -166,7 +166,7 @@ void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4& if (chat.mSourceType == CHAT_SOURCE_AGENT && chat.mFromID != LLUUID::null) { - chat.mURL = LLSLURL::buildCommand("agent", chat.mFromID, "inspect"); + chat.mURL = LLSLURL("agent", chat.mFromID, "inspect").getSLURLString(); } // If the chat line has an associated url, link it up to the name. diff --git a/indra/newview/llfloaterchatterbox.cpp b/indra/newview/llfloaterchatterbox.cpp index 774caaec90..a15cef7ea4 100644 --- a/indra/newview/llfloaterchatterbox.cpp +++ b/indra/newview/llfloaterchatterbox.cpp @@ -318,7 +318,7 @@ LLFloaterChatterBox* LLFloaterChatterBox::getInstance() //static LLFloater* LLFloaterChatterBox::getCurrentVoiceFloater() { - if (!LLVoiceClient::voiceEnabled()) + if (!LLVoiceClient::getInstance()->voiceEnabled()) { return NULL; } diff --git a/indra/newview/llfloaterevent.cpp b/indra/newview/llfloaterevent.cpp index 97ebab3425..84a5c3dc77 100644 --- a/indra/newview/llfloaterevent.cpp +++ b/indra/newview/llfloaterevent.cpp @@ -193,7 +193,7 @@ void LLFloaterEvent::processEventInfoReply(LLMessageSystem *msg, void **) floater->mTBCategory->setText(floater->mEventInfo.mCategoryStr); floater->mTBDate->setText(floater->mEventInfo.mTimeStr); floater->mTBDesc->setText(floater->mEventInfo.mDesc); - floater->mTBRunBy->setText(LLSLURL::buildCommand("agent", floater->mEventInfo.mRunByID, "inspect")); + floater->mTBRunBy->setText(LLSLURL("agent", floater->mEventInfo.mRunByID, "inspect").getSLURLString()); floater->mTBDuration->setText(llformat("%d:%.2d", floater->mEventInfo.mDuration / 60, floater->mEventInfo.mDuration % 60)); diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 02c83dcd09..25d3f971b5 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -42,7 +42,6 @@ #include "llnotificationsutil.h" #include "llparcel.h" #include "message.h" -#include "lluserauth.h" #include "llagent.h" #include "llbutton.h" @@ -804,7 +803,7 @@ void LLPanelLandGeneral::refreshNames() else { // Figure out the owner's name - owner = LLSLURL::buildCommand("agent", parcel->getOwnerID(), "inspect"); + owner = LLSLURL("agent", parcel->getOwnerID(), "inspect").getSLURLString(); } if(LLParcel::OS_LEASE_PENDING == parcel->getOwnershipStatus()) @@ -816,7 +815,7 @@ void LLPanelLandGeneral::refreshNames() std::string group; if (!parcel->getGroupID().isNull()) { - group = LLSLURL::buildCommand("group", parcel->getGroupID(), "inspect"); + group = LLSLURL("group", parcel->getGroupID(), "inspect").getSLURLString(); } mTextGroup->setText(group); @@ -825,9 +824,9 @@ void LLPanelLandGeneral::refreshNames() const LLUUID& auth_buyer_id = parcel->getAuthorizedBuyerID(); if(auth_buyer_id.notNull()) { - std::string name; - name = LLSLURL::buildCommand("agent", auth_buyer_id, "inspect"); - mSaleInfoForSale2->setTextArg("[BUYER]", name); + std::string name; + name = LLSLURL("agent", auth_buyer_id, "inspect").getSLURLString(); + mSaleInfoForSale2->setTextArg("[BUYER]", name); } else { diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 764a0dc954..3a73037ae8 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -595,7 +595,7 @@ void LLFloaterPreference::onBtnOK() llinfos << "Can't close preferences!" << llendl; } - LLPanelLogin::refreshLocation( false ); + LLPanelLogin::updateLocationCombo( false ); } // static @@ -612,7 +612,7 @@ void LLFloaterPreference::onBtnApply( ) apply(); saveSettings(); - LLPanelLogin::refreshLocation( false ); + LLPanelLogin::updateLocationCombo( false ); } // static diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 3758cbe74f..8c219cb3fd 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -2922,8 +2922,7 @@ bool LLDispatchEstateUpdateInfo::operator()( LLUUID owner_id(strings[1]); regionp->setOwner(owner_id); // Update estate owner name in UI - std::string owner_name = - LLSLURL::buildCommand("agent", owner_id, "inspect"); + std::string owner_name = LLSLURL("agent", owner_id, "inspect").getSLURLString(); panel->setOwnerName(owner_name); U32 estate_id = strtoul(strings[2].c_str(), NULL, 10); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index b42b34835d..f7c8855bf6 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -126,7 +126,9 @@ void LLFloaterReporter::processRegionInfo(LLMessageSystem* msg) // virtual BOOL LLFloaterReporter::postBuild() { - childSetText("abuse_location_edit", LLAgentUI::buildSLURL()); + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + childSetText("abuse_location_edit", slurl.getSLURLString()); enableControls(TRUE); @@ -280,7 +282,6 @@ void LLFloaterReporter::getObjectInfo(const LLUUID& object_id) { object_owner.append("Unknown"); } - setFromAvatar(mObjectID, object_owner); } else @@ -325,7 +326,8 @@ void LLFloaterReporter::setFromAvatar(const LLUUID& avatar_id, const std::string mAbuserID = mObjectID = avatar_id; mOwnerName = avatar_name; - std::string avatar_link = LLSLURL::buildCommand("agent", mObjectID, "inspect"); + std::string avatar_link = + LLSLURL("agent", mObjectID, "inspect").getSLURLString(); childSetText("owner_name", avatar_link); childSetText("object_name", avatar_name); childSetText("abuser_name_edit", avatar_name); @@ -504,7 +506,7 @@ void LLFloaterReporter::setPickedObjectProperties(const std::string& object_name { childSetText("object_name", object_name); std::string owner_link = - LLSLURL::buildCommand("agent", owner_id, "inspect"); + LLSLURL("agent", owner_id, "inspect").getSLURLString(); childSetText("owner_name", owner_link); childSetText("abuser_name_edit", owner_name); mAbuserID = owner_id; @@ -566,7 +568,7 @@ LLSD LLFloaterReporter::gatherReport() mCopyrightWarningSeen = FALSE; std::ostringstream summary; - if (!LLViewerLogin::getInstance()->isInProductionGrid()) + if (!LLGridManager::getInstance()->isInProductionGrid()) { summary << "Preview "; } diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index adac9861d4..aae379afe2 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -1123,7 +1123,7 @@ void LLSnapshotLivePreview::saveWeb(std::string url) void LLSnapshotLivePreview::regionNameCallback(std::string url, LLSD body, const std::string& name, S32 x, S32 y, S32 z) { - body["slurl"] = LLSLURL::buildSLURL(name, x, y, z); + body["slurl"] = LLSLURL(name, LLVector3d(x, y, z)).getSLURLString(); LLHTTPClient::post(url, body, new LLSendWebResponder()); diff --git a/indra/newview/llfloatervoicedevicesettings.cpp b/indra/newview/llfloatervoicedevicesettings.cpp index 638c9f1b8c..63365e3461 100644 --- a/indra/newview/llfloatervoicedevicesettings.cpp +++ b/indra/newview/llfloatervoicedevicesettings.cpp @@ -64,9 +64,6 @@ LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings() // grab "live" mic volume level mMicVolume = gSavedSettings.getF32("AudioLevelMic"); - // ask for new device enumeration - // now do this in onOpen() instead... - //gVoiceClient->refreshDeviceLists(); } LLPanelVoiceDeviceSettings::~LLPanelVoiceDeviceSettings() @@ -105,7 +102,7 @@ void LLPanelVoiceDeviceSettings::draw() refresh(); // let user know that volume indicator is not yet available - bool is_in_tuning_mode = gVoiceClient->inTuningMode(); + bool is_in_tuning_mode = LLVoiceClient::getInstance()->inTuningMode(); childSetVisible("wait_text", !is_in_tuning_mode); LLPanel::draw(); @@ -113,7 +110,7 @@ void LLPanelVoiceDeviceSettings::draw() if (is_in_tuning_mode) { const S32 num_bars = 5; - F32 voice_power = gVoiceClient->tuningGetEnergy() / LLVoiceClient::OVERDRIVEN_POWER_LEVEL; + F32 voice_power = LLVoiceClient::getInstance()->tuningGetEnergy() / LLVoiceClient::OVERDRIVEN_POWER_LEVEL; S32 discrete_power = llmin(num_bars, llfloor(voice_power * (F32)num_bars + 0.1f)); for(S32 power_bar_idx = 0; power_bar_idx < num_bars; power_bar_idx++) @@ -194,13 +191,13 @@ void LLPanelVoiceDeviceSettings::refresh() LLSlider* volume_slider = getChild("mic_volume_slider"); // set mic volume tuning slider based on last mic volume setting F32 current_volume = (F32)volume_slider->getValue().asReal(); - gVoiceClient->tuningSetMicVolume(current_volume); + LLVoiceClient::getInstance()->tuningSetMicVolume(current_volume); // Fill in popup menus mCtrlInputDevices = getChild("voice_input_device"); mCtrlOutputDevices = getChild("voice_output_device"); - if(!gVoiceClient->deviceSettingsAvailable()) + if(!LLVoiceClient::getInstance()->deviceSettingsAvailable()) { // The combo boxes are disabled, since we can't get the device settings from the daemon just now. // Put the currently set default (ONLY) in the box, and select it. @@ -219,17 +216,16 @@ void LLPanelVoiceDeviceSettings::refresh() } else if (!mDevicesUpdated) { - LLVoiceClient::deviceList *devices; - - LLVoiceClient::deviceList::iterator iter; + LLVoiceDeviceList::const_iterator iter; if(mCtrlInputDevices) { mCtrlInputDevices->removeall(); mCtrlInputDevices->add( getString("default_text"), ADD_BOTTOM ); - devices = gVoiceClient->getCaptureDevices(); - for(iter=devices->begin(); iter != devices->end(); iter++) + for(iter=LLVoiceClient::getInstance()->getCaptureDevices().begin(); + iter != LLVoiceClient::getInstance()->getCaptureDevices().end(); + iter++) { mCtrlInputDevices->add( *iter, ADD_BOTTOM ); } @@ -245,8 +241,8 @@ void LLPanelVoiceDeviceSettings::refresh() mCtrlOutputDevices->removeall(); mCtrlOutputDevices->add( getString("default_text"), ADD_BOTTOM ); - devices = gVoiceClient->getRenderDevices(); - for(iter=devices->begin(); iter != devices->end(); iter++) + for(iter= LLVoiceClient::getInstance()->getRenderDevices().begin(); + iter != LLVoiceClient::getInstance()->getRenderDevices().end(); iter++) { mCtrlOutputDevices->add( *iter, ADD_BOTTOM ); } @@ -268,37 +264,34 @@ void LLPanelVoiceDeviceSettings::initialize() mDevicesUpdated = FALSE; // ask for new device enumeration - gVoiceClient->refreshDeviceLists(); + LLVoiceClient::getInstance()->refreshDeviceLists(); // put voice client in "tuning" mode - gVoiceClient->tuningStart(); + LLVoiceClient::getInstance()->tuningStart(); LLVoiceChannel::suspend(); } void LLPanelVoiceDeviceSettings::cleanup() { - if (gVoiceClient) - { - gVoiceClient->tuningStop(); - } + LLVoiceClient::getInstance()->tuningStop(); LLVoiceChannel::resume(); } // static void LLPanelVoiceDeviceSettings::onCommitInputDevice(LLUICtrl* ctrl, void* user_data) { - if(gVoiceClient) + if(LLVoiceClient::getInstance()) { - gVoiceClient->setCaptureDevice(ctrl->getValue().asString()); + LLVoiceClient::getInstance()->setCaptureDevice(ctrl->getValue().asString()); } } // static void LLPanelVoiceDeviceSettings::onCommitOutputDevice(LLUICtrl* ctrl, void* user_data) { - if(gVoiceClient) + if(LLVoiceClient::getInstance()) { - gVoiceClient->setRenderDevice(ctrl->getValue().asString()); + LLVoiceClient::getInstance()->setRenderDevice(ctrl->getValue().asString()); } } diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index f17c9765b9..896c410e32 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -461,7 +461,7 @@ void LLFloaterWorldMap::draw() childSetEnabled("Teleport", (BOOL)tracking_status); // childSetEnabled("Clear", (BOOL)tracking_status); childSetEnabled("Show Destination", (BOOL)tracking_status || LLWorldMap::getInstance()->isTracking()); - childSetEnabled("copy_slurl", (mSLURL.size() > 0) ); + childSetEnabled("copy_slurl", (mSLURL.isValid()) ); setMouseOpaque(TRUE); getDragHandle()->setMouseOpaque(TRUE); @@ -660,14 +660,8 @@ void LLFloaterWorldMap::updateLocation() childSetValue("location", agent_sim_name); // Figure out where user is - LLVector3d agentPos = gAgent.getPositionGlobal(); - - S32 agent_x = llround( (F32)fmod( agentPos.mdV[VX], (F64)REGION_WIDTH_METERS ) ); - S32 agent_y = llround( (F32)fmod( agentPos.mdV[VY], (F64)REGION_WIDTH_METERS ) ); - S32 agent_z = llround( (F32)agentPos.mdV[VZ] ); - // Set the current SLURL - mSLURL = LLSLURL::buildSLURL(agent_sim_name, agent_x, agent_y, agent_z); + mSLURL = LLSLURL(agent_sim_name, gAgent.getPositionGlobal()); } } @@ -694,18 +688,15 @@ void LLFloaterWorldMap::updateLocation() } childSetValue("location", sim_name); - - F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS ); - F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS ); // simNameFromPosGlobal can fail, so don't give the user an invalid SLURL if ( gotSimName ) { - mSLURL = LLSLURL::buildSLURL(sim_name, llround(region_x), llround(region_y), llround((F32)pos_global.mdV[VZ])); + mSLURL = LLSLURL(sim_name, pos_global); } else { // Empty SLURL will disable the "Copy SLURL to clipboard" button - mSLURL = ""; + mSLURL = LLSLURL(); } } } @@ -1174,7 +1165,7 @@ void LLFloaterWorldMap::onClearBtn() mTrackedStatus = LLTracker::TRACKING_NOTHING; LLTracker::stopTracking((void *)(intptr_t)TRUE); LLWorldMap::getInstance()->cancelTracking(); - mSLURL = ""; // Clear the SLURL since it's invalid + mSLURL = LLSLURL(); // Clear the SLURL since it's invalid mSetToUserPosition = TRUE; // Revert back to the current user position } @@ -1197,10 +1188,10 @@ void LLFloaterWorldMap::onClickTeleportBtn() void LLFloaterWorldMap::onCopySLURL() { - getWindow()->copyTextToClipboard(utf8str_to_wstring(mSLURL)); + getWindow()->copyTextToClipboard(utf8str_to_wstring(mSLURL.getSLURLString())); LLSD args; - args["SLURL"] = mSLURL; + args["SLURL"] = mSLURL.getSLURLString(); LLNotificationsUtil::add("CopySLURL", args); } diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index 00f5e788fb..52809ff830 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -43,6 +43,7 @@ #include "llhudtext.h" #include "llmapimagetype.h" #include "lltracker.h" +#include "llslurl.h" class LLEventInfo; class LLFriendObserver; @@ -183,7 +184,7 @@ private: LLTracker::ETrackingStatus mTrackedStatus; std::string mTrackedSimName; std::string mTrackedAvatarName; - std::string mSLURL; + LLSLURL mSLURL; }; extern LLFloaterWorldMap* gFloaterWorldMap; diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 8a056f836f..03a47b5983 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -287,7 +287,7 @@ bool LLGroupList::onContextMenuItemEnable(const LLSD& userdata) return gAgent.getGroupID() != selected_group_id; if (userdata.asString() == "call") - return real_group_selected && LLVoiceClient::voiceEnabled()&&gVoiceClient->voiceWorking(); + return real_group_selected && LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); return real_group_selected; } diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 3ec8d11fb0..9704c7537a 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -58,7 +58,6 @@ #include "lltransientfloatermgr.h" #include "llinventorymodel.h" #include "llrootview.h" - #include "llspeakers.h" diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 4bdf5f42dc..0e3b78df7f 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -300,7 +300,7 @@ void LLFloaterIMPanel::onVolumeChange(LLUICtrl* source, void* user_data) LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data; if (floaterp) { - gVoiceClient->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal()); + LLVoiceClient::getInstance()->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal()); } } @@ -312,7 +312,7 @@ void LLFloaterIMPanel::draw() BOOL enable_connect = (region && region->getCapability("ChatSessionRequest") != "") && mSessionInitialized - && LLVoiceClient::voiceEnabled() + && LLVoiceClient::getInstance()->voiceEnabled() && mCallBackEnabled; // hide/show start call and end call buttons @@ -320,8 +320,8 @@ void LLFloaterIMPanel::draw() if (!voice_channel) return; - childSetVisible("end_call_btn", LLVoiceClient::voiceEnabled() && voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); - childSetVisible("start_call_btn", LLVoiceClient::voiceEnabled() && voice_channel->getState() < LLVoiceChannel::STATE_CALL_STARTED); + childSetVisible("end_call_btn", LLVoiceClient::getInstance()->voiceEnabled() && voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); + childSetVisible("start_call_btn", LLVoiceClient::getInstance()->voiceEnabled() && voice_channel->getState() < LLVoiceChannel::STATE_CALL_STARTED); childSetEnabled("start_call_btn", enable_connect); childSetEnabled("send_btn", !childGetValue("chat_editor").asString().empty()); @@ -384,11 +384,11 @@ void LLFloaterIMPanel::draw() else { // refresh volume and mute checkbox - childSetVisible("speaker_volume", LLVoiceClient::voiceEnabled() && voice_channel->isActive()); - childSetValue("speaker_volume", gVoiceClient->getUserVolume(mOtherParticipantUUID)); + childSetVisible("speaker_volume", LLVoiceClient::getInstance()->voiceEnabled() && voice_channel->isActive()); + childSetValue("speaker_volume", LLVoiceClient::getInstance()->getUserVolume(mOtherParticipantUUID)); childSetValue("mute_btn", LLMuteList::getInstance()->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat)); - childSetVisible("mute_btn", LLVoiceClient::voiceEnabled() && voice_channel->isActive()); + childSetVisible("mute_btn", LLVoiceClient::getInstance()->voiceEnabled() && voice_channel->isActive()); } LLFloater::draw(); } diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index e0f155a6a9..5201f92dbc 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -344,13 +344,13 @@ LLIMModel::LLIMSession::~LLIMSession() mSpeakers = NULL; // End the text IM session if necessary - if(gVoiceClient && mOtherParticipantID.notNull()) + if(LLVoiceClient::getInstance() && mOtherParticipantID.notNull()) { switch(mType) { case IM_NOTHING_SPECIAL: case IM_SESSION_P2P_INVITE: - gVoiceClient->endUserIMSession(mOtherParticipantID); + LLVoiceClient::getInstance()->endUserIMSession(mOtherParticipantID); break; default: @@ -925,7 +925,7 @@ void LLIMModel::sendMessage(const std::string& utf8_text, if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id))) { // User is online through the OOW connector, but not with a regular viewer. Try to send the message via SLVoice. - sent = gVoiceClient->sendTextMessage(other_participant_id, utf8_text); + sent = LLVoiceClient::getInstance()->sendTextMessage(other_participant_id, utf8_text); } if(!sent) @@ -1717,7 +1717,7 @@ void LLOutgoingCallDialog::show(const LLSD& key) // skipping "You will now be reconnected to nearby" in notification when call is ended by disabling voice, // so no reconnection to nearby chat happens (EXT-4397) - bool voice_works = LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking(); + bool voice_works = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); std::string reconnect_nearby = voice_works ? LLTrans::getString("reconnect_nearby") : std::string(); childSetTextArg("nearby", "[RECONNECT_NEARBY]", reconnect_nearby); @@ -1843,7 +1843,11 @@ LLCallDialog(payload) void LLIncomingCallDialog::onLifetimeExpired() { // check whether a call is valid or not - if (LLVoiceClient::getInstance()->findSession(mPayload["caller_id"].asUUID())) + LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mPayload["session_id"].asUUID()); + if(channelp && + (channelp->getState() != LLVoiceChannel::STATE_NO_CHANNEL_INFO) && + (channelp->getState() != LLVoiceChannel::STATE_ERROR) && + (channelp->getState() != LLVoiceChannel::STATE_HUNG_UP)) { // restart notification's timer if call is still valid mLifetimeTimer.start(); @@ -2077,10 +2081,10 @@ void LLIncomingCallDialog::processCallResponse(S32 response) { if (type == IM_SESSION_P2P_INVITE) { - if(gVoiceClient) + if(LLVoiceClient::getInstance()) { std::string s = mPayload["session_handle"].asString(); - gVoiceClient->declineInvite(s); + LLVoiceClient::getInstance()->declineInvite(s); } } else @@ -2168,11 +2172,8 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) { if (type == IM_SESSION_P2P_INVITE) { - if(gVoiceClient) - { - std::string s = payload["session_handle"].asString(); - gVoiceClient->declineInvite(s); - } + std::string s = payload["session_handle"].asString(); + LLVoiceClient::getInstance()->declineInvite(s); } else { @@ -3078,7 +3079,7 @@ public: return; } - if(!LLVoiceClient::voiceEnabled()) + if(!LLVoiceClient::getInstance()->voiceEnabled()) { // Don't display voice invites unless the user has voice enabled. return; diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 94ea236757..1299324105 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -536,8 +536,7 @@ void LLInspectAvatar::toggleSelectedVoice(bool enabled) void LLInspectAvatar::updateVolumeSlider() { - - bool voice_enabled = gVoiceClient->getVoiceEnabled(mAvatarID); + bool voice_enabled = LLVoiceClient::getInstance()->getVoiceEnabled(mAvatarID); // Do not display volume slider and mute button if it // is ourself or we are not in a voice channel together @@ -567,6 +566,7 @@ void LLInspectAvatar::updateVolumeSlider() volume_slider->setEnabled( !is_muted ); F32 volume; + if (is_muted) { // it's clearer to display their volume as zero @@ -575,7 +575,7 @@ void LLInspectAvatar::updateVolumeSlider() else { // actual volume - volume = gVoiceClient->getUserVolume(mAvatarID); + volume = LLVoiceClient::getInstance()->getUserVolume(mAvatarID); } volume_slider->setValue( (F64)volume ); } @@ -604,7 +604,7 @@ void LLInspectAvatar::onClickMuteVolume() void LLInspectAvatar::onVolumeChange(const LLSD& data) { F32 volume = (F32)data.asReal(); - gVoiceClient->setUserVolume(mAvatarID, volume); + LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume); } void LLInspectAvatar::nameUpdatedCallback( diff --git a/indra/newview/llinspectobject.cpp b/indra/newview/llinspectobject.cpp index 91cbbbf430..a2b5ffbac4 100644 --- a/indra/newview/llinspectobject.cpp +++ b/indra/newview/llinspectobject.cpp @@ -480,7 +480,7 @@ void LLInspectObject::updateCreator(LLSelectNode* nodep) // Objects cannot be created by a group, so use agent URL format LLUUID creator_id = nodep->mPermissions->getCreator(); std::string creator_url = - LLSLURL::buildCommand("agent", creator_id, "about"); + LLSLURL("agent", creator_id, "about").getSLURLString(); args["[CREATOR]"] = creator_url; // created by one user but owned by another @@ -490,12 +490,12 @@ void LLInspectObject::updateCreator(LLSelectNode* nodep) if (group_owned) { owner_id = nodep->mPermissions->getGroup(); - owner_url = LLSLURL::buildCommand("group", owner_id, "about"); + owner_url = LLSLURL("group", owner_id, "about").getSLURLString(); } else { owner_id = nodep->mPermissions->getOwner(); - owner_url = LLSLURL::buildCommand("agent", owner_id, "about"); + owner_url = LLSLURL("agent", owner_id, "about").getSLURLString(); } args["[OWNER]"] = owner_url; diff --git a/indra/newview/llinspectremoteobject.cpp b/indra/newview/llinspectremoteobject.cpp index 66e4a1bf66..97ff771658 100644 --- a/indra/newview/llinspectremoteobject.cpp +++ b/indra/newview/llinspectremoteobject.cpp @@ -176,11 +176,11 @@ void LLInspectRemoteObject::update() { if (mGroupOwned) { - owner = LLSLURL::buildCommand("group", mOwnerID, "about"); + owner = LLSLURL("group", mOwnerID, "about").getSLURLString(); } else { - owner = LLSLURL::buildCommand("agent", mOwnerID, "about"); + owner = LLSLURL("agent", mOwnerID, "about").getSLURLString(); } } else diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index d1cc0ae936..b6202c6a8c 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1329,7 +1329,6 @@ bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) return cat->fetchDescendents(); } - void LLInventoryModel::cache( const LLUUID& parent_folder_id, const LLUUID& agent_id) diff --git a/indra/newview/lllandmarkactions.cpp b/indra/newview/lllandmarkactions.cpp index 7336efb62a..539ca97a93 100644 --- a/indra/newview/lllandmarkactions.cpp +++ b/indra/newview/lllandmarkactions.cpp @@ -299,7 +299,7 @@ void LLLandmarkActions::getSLURLfromPosGlobal(const LLVector3d& global_pos, slur bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name); if (gotSimName) { - std::string slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped); + std::string slurl = LLSLURL(sim_name, global_pos).getSLURLString(); cb(slurl); return; @@ -351,7 +351,7 @@ void LLLandmarkActions::onRegionResponseSLURL(slurl_callback_t cb, bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name); if (gotSimName) { - slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped); + slurl = LLSLURL(sim_name, global_pos).getSLURLString(); } else { diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index ad2e594b49..3b4a4a1344 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -668,9 +668,8 @@ void LLLocationInputCtrl::onLocationPrearrange(const LLSD& data) value["item_type"] = TELEPORT_HISTORY; value["global_pos"] = result->mGlobalPos.getValue(); std::string region_name = result->mTitle.substr(0, result->mTitle.find(',')); - //TODO*: add slurl to teleportitem or parse region name from title - value["tooltip"] = LLSLURL::buildSLURLfromPosGlobal(region_name, - result->mGlobalPos, false); + //TODO*: add Surl to teleportitem or parse region name from title + value["tooltip"] = LLSLURL(region_name, result->mGlobalPos).getSLURLString(); add(result->getTitle(), value); } result = std::find_if(result + 1, th_items.end(), boost::bind( @@ -1011,7 +1010,9 @@ void LLLocationInputCtrl::changeLocationPresentation() if(!mTextEntry->hasSelection() && text == mHumanReadableLocation) { //needs unescaped one - mTextEntry->setText(LLAgentUI::buildSLURL(false)); + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl, false); + mTextEntry->setText(slurl.getSLURLString()); mTextEntry->selectAll(); mMaturityIcon->setVisible(FALSE); diff --git a/indra/newview/llloginhandler.cpp b/indra/newview/llloginhandler.cpp index e3817eecc4..4e0a7594ba 100644 --- a/indra/newview/llloginhandler.cpp +++ b/indra/newview/llloginhandler.cpp @@ -35,13 +35,14 @@ #include "llloginhandler.h" // viewer includes +#include "llsecapi.h" #include "lllogininstance.h" // to check if logged in yet #include "llpanellogin.h" // save_password_to_disk() #include "llstartup.h" // getStartupState() -#include "llurlsimstring.h" +#include "llslurl.h" #include "llviewercontrol.h" // gSavedSettings #include "llviewernetwork.h" // EGridInfo -#include "llviewerwindow.h" // getWindow() +#include "llviewerwindow.h" // getWindow() // library includes #include "llmd5.h" @@ -60,109 +61,33 @@ bool LLLoginHandler::parseDirectLogin(std::string url) LLURI uri(url); parse(uri.queryMap()); - if (/*mWebLoginKey.isNull() ||*/ - mFirstName.empty() || - mLastName.empty()) - { - return false; - } - else - { - return true; - } + // NOTE: Need to add direct login as per identity evolution + return true; } - void LLLoginHandler::parse(const LLSD& queryMap) { - //mWebLoginKey = queryMap["web_login_key"].asUUID(); - mFirstName = queryMap["first_name"].asString(); - mLastName = queryMap["last_name"].asString(); - EGridInfo grid_choice = GRID_INFO_NONE; - if (queryMap["grid"].asString() == "aditi") - { - grid_choice = GRID_INFO_ADITI; - } - else if (queryMap["grid"].asString() == "agni") - { - grid_choice = GRID_INFO_AGNI; - } - else if (queryMap["grid"].asString() == "siva") - { - grid_choice = GRID_INFO_SIVA; - } - else if (queryMap["grid"].asString() == "damballah") - { - grid_choice = GRID_INFO_DAMBALLAH; - } - else if (queryMap["grid"].asString() == "durga") - { - grid_choice = GRID_INFO_DURGA; - } - else if (queryMap["grid"].asString() == "shakti") - { - grid_choice = GRID_INFO_SHAKTI; - } - else if (queryMap["grid"].asString() == "soma") - { - grid_choice = GRID_INFO_SOMA; - } - else if (queryMap["grid"].asString() == "ganga") - { - grid_choice = GRID_INFO_GANGA; - } - else if (queryMap["grid"].asString() == "vaak") - { - grid_choice = GRID_INFO_VAAK; - } - else if (queryMap["grid"].asString() == "uma") - { - grid_choice = GRID_INFO_UMA; - } - else if (queryMap["grid"].asString() == "mohini") - { - grid_choice = GRID_INFO_MOHINI; - } - else if (queryMap["grid"].asString() == "yami") - { - grid_choice = GRID_INFO_YAMI; - } - else if (queryMap["grid"].asString() == "nandi") + if (queryMap.has("grid")) { - grid_choice = GRID_INFO_NANDI; + LLGridManager::getInstance()->setGridChoice(queryMap["grid"].asString()); } - else if (queryMap["grid"].asString() == "mitra") - { - grid_choice = GRID_INFO_MITRA; - } - else if (queryMap["grid"].asString() == "radha") - { - grid_choice = GRID_INFO_RADHA; - } - else if (queryMap["grid"].asString() == "ravi") - { - grid_choice = GRID_INFO_RAVI; - } - else if (queryMap["grid"].asString() == "aruna") - { - grid_choice = GRID_INFO_ARUNA; - } - - if(grid_choice != GRID_INFO_NONE) - { - LLViewerLogin::getInstance()->setGridChoice(grid_choice); - } - + + std::string startLocation = queryMap["location"].asString(); - + if (startLocation == "specify") { - LLURLSimString::setString(queryMap["region"].asString()); + LLStartUp::setStartSLURL(LLSLURL(LLGridManager::getInstance()->getGridLoginID(), + queryMap["region"].asString())); } - else if (!startLocation.empty()) // "last" or "home" or ??? (let LLURLSimString figure it out) + else if (startLocation == "home") { - LLURLSimString::setString(startLocation); + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); + } + else if (startLocation == "last") + { + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); } } @@ -220,40 +145,65 @@ bool LLLoginHandler::handle(const LLSD& tokens, return true; } - std::string password = query_map["password"].asString(); - - if (!password.empty()) + if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) //on splash page { - gSavedSettings.setBOOL("RememberPassword", TRUE); - - if (password.substr(0,3) != "$1$") - { - LLMD5 pass((unsigned char*)password.c_str()); - char md5pass[33]; /* Flawfinder: ignore */ - pass.hex_digest(md5pass); - std::string hashed_password = ll_safe_string(md5pass, 32); - LLStartUp::savePasswordToDisk(hashed_password); - } + // as the login page may change from grid to grid, as well as + // things like username/password/etc, we simply refresh the + // login page to make sure everything is set up correctly + LLPanelLogin::loadLoginPage(); + LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); } - + return true; +} - if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) //on splash page - { - if (!mFirstName.empty() || !mLastName.empty()) - { - // Fill in the name, and maybe the password - LLPanelLogin::setFields(mFirstName, mLastName, password); - } - //if (mWebLoginKey.isNull()) - //{ - // LLPanelLogin::loadLoginPage(); - //} - //else - //{ - // LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); - //} - LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); - } - return true; + +// Initialize the credentials +// If the passed in URL contains login info, parse +// that into a credential and web login key. Otherwise +// check the command line. If the command line +// does not contain any login creds, load the last saved +// ones from the protected credential store. +// This always returns with a credential structure set in the +// login handler +LLPointer LLLoginHandler::initializeLoginInfo() +{ + LLPointer result = NULL; + // so try to load it from the UserLoginInfo + result = loadSavedUserLoginInfo(); + if (result.isNull()) + { + result = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); + } + + return result; +} + + +LLPointer LLLoginHandler::loadSavedUserLoginInfo() +{ + // load the saved user login info into a LLCredential. + // perhaps this should be moved. + LLSD cmd_line_login = gSavedSettings.getLLSD("UserLoginInfo"); + if (cmd_line_login.size() == 3) + { + + LLMD5 pass((unsigned char*)cmd_line_login[2].asString().c_str()); + char md5pass[33]; /* Flawfinder: ignore */ + pass.hex_digest(md5pass); + LLSD identifier = LLSD::emptyMap(); + identifier["type"] = "agent"; + identifier["first_name"] = cmd_line_login[0]; + identifier["last_name"] = cmd_line_login[1]; + + LLSD authenticator = LLSD::emptyMap(); + authenticator["type"] = "hash"; + authenticator["algorithm"] = "md5"; + authenticator["secret"] = md5pass; + // yuck, we'll fix this with mani's changes. + gSavedSettings.setBOOL("AutoLogin", TRUE); + return gSecAPIHandler->createCredential(LLGridManager::getInstance()->getGrid(), + identifier, authenticator); + } + return NULL; } diff --git a/indra/newview/llloginhandler.h b/indra/newview/llloginhandler.h index ac4648761b..c15b998c91 100644 --- a/indra/newview/llloginhandler.h +++ b/indra/newview/llloginhandler.h @@ -34,6 +34,7 @@ #define LLLOGINHANDLER_H #include "llcommandhandler.h" +#include "llsecapi.h" class LLLoginHandler : public LLCommandHandler { @@ -46,19 +47,15 @@ class LLLoginHandler : public LLCommandHandler // secondlife:///app/login?first=Bob&last=Dobbs bool parseDirectLogin(std::string url); - std::string getFirstName() const { return mFirstName; } - std::string getLastName() const { return mLastName; } - // Web-based login unsupported //LLUUID getWebLoginKey() const { return mWebLoginKey; } + LLPointer loadSavedUserLoginInfo(); + LLPointer initializeLoginInfo(); + private: void parse(const LLSD& queryMap); -private: - std::string mFirstName; - std::string mLastName; - //LLUUID mWebLoginKey; }; extern LLLoginHandler gLoginHandler; diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 24c72c65ce..0459c85050 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -48,13 +48,16 @@ // newview #include "llviewernetwork.h" #include "llviewercontrol.h" -#include "llurlsimstring.h" +#include "llslurl.h" +#include "llstartup.h" #include "llfloaterreg.h" #include "llnotifications.h" #include "llwindow.h" #if LL_LINUX || LL_SOLARIS #include "lltrans.h" #endif +#include "llsecapi.h" +#include "llstartup.h" static const char * const TOS_REPLY_PUMP = "lllogininstance_tos_callback"; static const char * const TOS_LISTENER_NAME = "lllogininstance_tos"; @@ -83,14 +86,14 @@ LLLoginInstance::~LLLoginInstance() { } -void LLLoginInstance::connect(const LLSD& credentials) +void LLLoginInstance::connect(LLPointer credentials) { std::vector uris; - LLViewerLogin::getInstance()->getLoginURIs(uris); + LLGridManager::getInstance()->getLoginURIs(uris); connect(uris.front(), credentials); } -void LLLoginInstance::connect(const std::string& uri, const LLSD& credentials) +void LLLoginInstance::connect(const std::string& uri, LLPointer credentials) { mAttemptComplete = false; // Reset attempt complete at this point! constructAuthParams(credentials); @@ -102,7 +105,7 @@ void LLLoginInstance::reconnect() // Sort of like connect, only using the pre-existing // request params. std::vector uris; - LLViewerLogin::getInstance()->getLoginURIs(uris); + LLGridManager::getInstance()->getLoginURIs(uris); mLoginModule->connect(uris.front(), mRequestData); } @@ -118,7 +121,7 @@ LLSD LLLoginInstance::getResponse() return mResponseData; } -void LLLoginInstance::constructAuthParams(const LLSD& credentials) +void LLLoginInstance::constructAuthParams(LLPointer user_credential) { // Set up auth request options. //#define LL_MINIMIAL_REQUESTED_OPTIONS @@ -145,8 +148,10 @@ void LLLoginInstance::constructAuthParams(const LLSD& credentials) requested_options.append("adult_compliant"); //requested_options.append("inventory-targets"); requested_options.append("buddy-list"); + requested_options.append("newuser-config"); requested_options.append("ui-config"); #endif + requested_options.append("voice-config"); requested_options.append("tutorial_setting"); requested_options.append("login-flags"); requested_options.append("global-textures"); @@ -155,20 +160,18 @@ void LLLoginInstance::constructAuthParams(const LLSD& credentials) gSavedSettings.setBOOL("UseDebugMenus", TRUE); requested_options.append("god-connect"); } + + // (re)initialize the request params with creds. + LLSD request_params = user_credential->getLoginParams(); char hashed_mac_string[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */ LLMD5 hashed_mac; - hashed_mac.update( gMACAddress, MAC_ADDRESS_BYTES ); + unsigned char MACAddress[MAC_ADDRESS_BYTES]; + LLUUID::getNodeID(MACAddress); + hashed_mac.update( MACAddress, MAC_ADDRESS_BYTES ); hashed_mac.finalize(); hashed_mac.hex_digest(hashed_mac_string); - - // prepend "$1$" to the password to indicate its the md5'd version. - std::string dpasswd("$1$"); - dpasswd.append(credentials["passwd"].asString()); - - // (re)initialize the request params with creds. - LLSD request_params(credentials); - request_params["passwd"] = dpasswd; + request_params["start"] = construct_start_string(); request_params["skipoptional"] = mSkipOptionalUpdate; request_params["agree_to_tos"] = false; // Always false here. Set true in @@ -247,6 +250,15 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event) LLSD data(LLSD::emptyMap()); data["message"] = message_response; data["reply_pump"] = TOS_REPLY_PUMP; + if(response.has("error_code")) + { + data["error_code"] = response["error_code"]; + } + if(response.has("certificate")) + { + data["certificate"] = response["certificate"]; + } + LLFloaterReg::showInstance("message_critical", data); LLEventPumps::instance().obtain(TOS_REPLY_PUMP) .listen(TOS_LISTENER_NAME, @@ -454,20 +466,31 @@ bool LLLoginInstance::updateDialogCallback(const LLSD& notification, const LLSD& std::string construct_start_string() { std::string start; - if (LLURLSimString::parse()) + LLSLURL start_slurl = LLStartUp::getStartSLURL(); + switch(start_slurl.getType()) { - // a startup URL was specified - std::string unescaped_start = + case LLSLURL::LOCATION: + { + // a startup URL was specified + LLVector3 position = start_slurl.getPosition(); + std::string unescaped_start = STRINGIZE( "uri:" - << LLURLSimString::sInstance.mSimName << "&" - << LLURLSimString::sInstance.mX << "&" - << LLURLSimString::sInstance.mY << "&" - << LLURLSimString::sInstance.mZ); - start = xml_escape_string(unescaped_start); - } - else - { - start = gSavedSettings.getString("LoginLocation"); + << start_slurl.getRegion() << "&" + << position[VX] << "&" + << position[VY] << "&" + << position[VZ]); + start = xml_escape_string(unescaped_start); + break; + } + case LLSLURL::HOME_LOCATION: + { + start = "home"; + break; + } + default: + { + start = "last"; + } } return start; } diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h index c8704eddb4..44271bb75e 100644 --- a/indra/newview/lllogininstance.h +++ b/indra/newview/lllogininstance.h @@ -36,6 +36,7 @@ #include "lleventdispatcher.h" #include #include +#include "llsecapi.h" class LLLogin; class LLEventStream; class LLNotificationsInterface; @@ -48,8 +49,8 @@ public: LLLoginInstance(); ~LLLoginInstance(); - void connect(const LLSD& credential); // Connect to the current grid choice. - void connect(const std::string& uri, const LLSD& credential); // Connect to the given uri. + void connect(LLPointer credentials); // Connect to the current grid choice. + void connect(const std::string& uri, LLPointer credentials); // Connect to the given uri. void reconnect(); // reconnect using the current credentials. void disconnect(); @@ -81,7 +82,7 @@ public: void setUpdaterLauncher(const UpdaterLauncherCallback& ulc) { mUpdaterLauncher = ulc; } private: - void constructAuthParams(const LLSD& credentials); + void constructAuthParams(LLPointer user_credentials); void updateApp(bool mandatory, const std::string& message); bool updateDialogCallback(const LLSD& notification, const LLSD& response); diff --git a/indra/newview/llnavigationbar.cpp b/indra/newview/llnavigationbar.cpp index e11df06d86..c3d0f1bfc2 100644 --- a/indra/newview/llnavigationbar.cpp +++ b/indra/newview/llnavigationbar.cpp @@ -52,7 +52,6 @@ #include "llsearchcombobox.h" #include "llsidetray.h" #include "llslurl.h" -#include "llurlsimstring.h" #include "llurlregistry.h" #include "llurldispatcher.h" #include "llviewerinventory.h" @@ -508,29 +507,34 @@ void LLNavigationBar::onLocationSelection() std::string region_name; LLVector3 local_coords(128, 128, 0); - S32 x = 0, y = 0, z = 0; // Is the typed location a SLURL? - if (LLSLURL::isSLURL(typed_location)) + LLSLURL slurl = LLSLURL(typed_location); + if (slurl.getType() == LLSLURL::LOCATION) { - // Yes. Extract region name and local coordinates from it. - if (LLURLSimString::parse(LLSLURL::stripProtocol(typed_location), ®ion_name, &x, &y, &z)) - local_coords.set(x, y, z); - else - return; + region_name = slurl.getRegion(); + local_coords = slurl.getPosition(); } - // we have to do this check after previous, because LLUrlRegistry contains handlers for slurl too - //but we need to know whether typed_location is a simple http url. - else if (LLUrlRegistry::instance().isUrl(typed_location)) + else if(!slurl.isValid()) { + // we have to do this check after previous, because LLUrlRegistry contains handlers for slurl too + // but we need to know whether typed_location is a simple http url. + if (LLUrlRegistry::instance().isUrl(typed_location)) + { // display http:// URLs in the media browser, or // anything else is sent to the search floater LLWeb::loadURL(typed_location); return; + } + else + { + // assume that an user has typed the {region name} or possible {region_name, parcel} + region_name = typed_location.substr(0,typed_location.find(',')); + } } else { - // assume that an user has typed the {region name} or possible {region_name, parcel} - region_name = typed_location.substr(0,typed_location.find(',')); + // was an app slurl, home, whatever. Bail + return; } // Resolve the region name to its global coordinates. @@ -562,7 +566,7 @@ void LLNavigationBar::onTeleportFinished(const LLVector3d& global_agent_pos) */ LLAgentUI::buildLocationString(location, LLAgentUI::LOCATION_FORMAT_NO_MATURITY, gAgent.getPosAgentFromGlobal(global_agent_pos)); - std::string tooltip (LLSLURL::buildSLURLfromPosGlobal(gAgent.getRegion()->getName(), global_agent_pos, false)); + std::string tooltip (LLSLURL(gAgent.getRegion()->getName(), global_agent_pos).getSLURLString()); LLLocationHistoryItem item (location, global_agent_pos, tooltip,TYPED_REGION_SLURL);// we can add into history only TYPED location @@ -651,7 +655,7 @@ void LLNavigationBar::onRegionNameResponse( LLVector3d region_pos = from_region_handle(region_handle); LLVector3d global_pos = region_pos + (LLVector3d) local_coords; - llinfos << "Teleporting to: " << LLSLURL::buildSLURLfromPosGlobal(region_name, global_pos, false) << llendl; + llinfos << "Teleporting to: " << LLSLURL(region_name, global_pos).getSLURLString() << llendl; gAgent.teleportViaLocation(global_pos); } diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp index d6d48a4ead..197a0ef728 100644 --- a/indra/newview/lloutputmonitorctrl.cpp +++ b/indra/newview/lloutputmonitorctrl.cpp @@ -142,7 +142,7 @@ void LLOutputMonitorCtrl::draw() // Copied from llmediaremotectrl.cpp // *TODO: Give the LLOutputMonitorCtrl an agent-id to monitor, then - // call directly into gVoiceClient to ask if that agent-id is muted, is + // call directly into LLVoiceClient::getInstance() to ask if that agent-id is muted, is // speaking, and what power. This avoids duplicating data, which can get // out of sync. const F32 LEVEL_0 = LLVoiceClient::OVERDRIVEN_POWER_LEVEL / 3.f; @@ -151,14 +151,14 @@ void LLOutputMonitorCtrl::draw() if (getVisible() && mAutoUpdate && !mIsMuted && mSpeakerId.notNull()) { - setPower(gVoiceClient->getCurrentPower(mSpeakerId)); + setPower(LLVoiceClient::getInstance()->getCurrentPower(mSpeakerId)); if(mIsAgentControl) { - setIsTalking(gVoiceClient->getUserPTTState()); + setIsTalking(LLVoiceClient::getInstance()->getUserPTTState()); } else { - setIsTalking(gVoiceClient->getIsSpeaking(mSpeakerId)); + setIsTalking(LLVoiceClient::getInstance()->getIsSpeaking(mSpeakerId)); } } diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h index b7454a5066..3a83da67e2 100644 --- a/indra/newview/lloutputmonitorctrl.h +++ b/indra/newview/lloutputmonitorctrl.h @@ -143,7 +143,7 @@ private: LLPointer mImageLevel2; LLPointer mImageLevel3; - /** whether to deal with gVoiceClient directly */ + /** whether to deal with LLVoiceClient::getInstance() directly */ bool mAutoUpdate; /** uuid of a speaker being monitored */ diff --git a/indra/newview/lloverlaybar.cpp b/indra/newview/lloverlaybar.cpp index 67e048885f..3f1b23ba14 100644 --- a/indra/newview/lloverlaybar.cpp +++ b/indra/newview/lloverlaybar.cpp @@ -258,7 +258,7 @@ void LLOverlayBar::refresh() { // update "remotes" childSetVisible("media_remote_container", TRUE); - childSetVisible("voice_remote_container", LLVoiceClient::voiceEnabled()); + childSetVisible("voice_remote_container", LLVoiceClient::getInstance()->voiceEnabled()); childSetVisible("state_buttons", TRUE); } diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index a0ba2f739b..b554af66f0 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -163,7 +163,7 @@ BOOL LLPanelAvatarNotes::postBuild() resetControls(); resetData(); - gVoiceClient->addObserver((LLVoiceClientStatusObserver*)this); + LLVoiceClient::getInstance()->addObserver((LLVoiceClientStatusObserver*)this); return TRUE; } @@ -374,7 +374,7 @@ void LLPanelAvatarNotes::onChange(EStatusType status, const std::string &channel return; } - childSetEnabled("call", LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking()); + childSetEnabled("call", LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking()); } void LLPanelAvatarNotes::setAvatarId(const LLUUID& id) @@ -518,7 +518,7 @@ BOOL LLPanelAvatarProfile::postBuild() pic = getChild("real_world_pic"); pic->setFallbackImageName("default_profile_picture.j2c"); - gVoiceClient->addObserver((LLVoiceClientStatusObserver*)this); + LLVoiceClient::getInstance()->addObserver((LLVoiceClientStatusObserver*)this); resetControls(); resetData(); @@ -809,7 +809,7 @@ void LLPanelAvatarProfile::onChange(EStatusType status, const std::string &chann return; } - childSetEnabled("call", LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking()); + childSetEnabled("call", LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking()); } void LLPanelAvatarProfile::setAvatarId(const LLUUID& id) diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index c00b6a4147..716166a945 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -201,7 +201,7 @@ BOOL LLPanelGroup::postBuild() mJoinText = panel_general->getChild("join_cost_text"); } - gVoiceClient->addObserver(this); + LLVoiceClient::getInstance()->addObserver(this); return TRUE; } @@ -322,7 +322,7 @@ void LLPanelGroup::onChange(EStatusType status, const std::string &channelURI, b return; } - childSetEnabled("btn_call", LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking()); + childSetEnabled("btn_call", LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking()); } void LLPanelGroup::notifyObservers() diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index c34f0633b9..709bb83fe4 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -81,7 +81,8 @@ void LLPanelChatControlPanel::onVoiceChannelStateChanged(const LLVoiceChannel::E void LLPanelChatControlPanel::updateCallButton() { - bool voice_enabled = LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking(); + // hide/show call button + bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionId); @@ -124,7 +125,7 @@ BOOL LLPanelChatControlPanel::postBuild() childSetAction("end_call_btn", boost::bind(&LLPanelChatControlPanel::onEndCallButtonClicked, this)); childSetAction("voice_ctrls_btn", boost::bind(&LLPanelChatControlPanel::onOpenVoiceControlsClicked, this)); - gVoiceClient->addObserver(this); + LLVoiceClient::getInstance()->addObserver(this); return TRUE; } diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index ee4dcc44fe..42e4b397db 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -51,11 +51,12 @@ #include "llfocusmgr.h" #include "lllineeditor.h" #include "llnotificationsutil.h" +#include "llsecapi.h" #include "llstartup.h" #include "lltextbox.h" #include "llui.h" #include "lluiconstants.h" -#include "llurlsimstring.h" +#include "llslurl.h" #include "llversioninfo.h" #include "llviewerhelp.h" #include "llviewertexturelist.h" @@ -77,6 +78,7 @@ #pragma warning(disable: 4355) // 'this' used in initializer list #endif // LL_WINDOWS +#include "llsdserialize.h" #define USE_VIEWER_AUTH 0 const S32 BLACK_BORDER_HEIGHT = 160; @@ -104,7 +106,6 @@ public: LLLoginRefreshHandler gLoginRefreshHandler; - // helper class that trys to download a URL from a web site and calls a method // on parent class indicating if the web server is working or not class LLIamHereLogin : public LLHTTPClient::Responder @@ -153,10 +154,6 @@ namespace { boost::intrusive_ptr< LLIamHereLogin > gResponsePtr = 0; }; -void set_start_location(LLUICtrl* ctrl, void* data) -{ - LLURLSimString::setString(ctrl->getValue().asString()); -} //--------------------------------------------------------------------------- // Public methods @@ -187,6 +184,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, delete LLPanelLogin::sInstance; } + mPasswordModified = FALSE; LLPanelLogin::sInstance = this; // add to front so we are the bottom-most child @@ -213,10 +211,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, } #if !USE_VIEWER_AUTH - childSetPrevalidate("first_name_edit", LLTextValidate::validateASCIIPrintableNoSpace); - childSetPrevalidate("last_name_edit", LLTextValidate::validateASCIIPrintableNoSpace); - - childSetCommitCallback("password_edit", mungePassword, this); + childSetPrevalidate("username_edit", LLTextValidate::validateASCIIPrintableNoPipe); getChild("password_edit")->setKeystrokeCallback(onPassKey, this); // change z sort of clickable text to be behind buttons @@ -228,27 +223,19 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLComboBox* combo = getChild("start_location_combo"); - std::string sim_string = LLURLSimString::sInstance.mSimString; - if(sim_string.empty()) - { - LLURLSimString::setString(gSavedSettings.getString("LoginLocation")); - sim_string = LLURLSimString::sInstance.mSimString; - } - - if (!sim_string.empty()) + if(LLStartUp::getStartSLURL().getType() != LLSLURL::LOCATION) { - // Replace "" with this region name - combo->remove(2); - combo->add( sim_string ); - combo->setTextEntry(sim_string); - combo->setCurrentByIndex( 2 ); + LLSLURL slurl(gSavedSettings.getString("LoginLocation")); + LLStartUp::setStartSLURL(slurl); } - - combo->setCommitCallback( &set_start_location, NULL ); + updateLocationCombo(false); + + combo->setCommitCallback(onSelectLocation, NULL); LLComboBox* server_choice_combo = sInstance->getChild("server_combo"); server_choice_combo->setCommitCallback(onSelectServer, NULL); server_choice_combo->setFocusLostCallback(boost::bind(onServerComboLostFocus, _1)); + updateServerCombo(); childSetAction("connect_btn", onClickConnect, this); @@ -304,17 +291,10 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, // kick off a request to grab the url manually gResponsePtr = LLIamHereLogin::build( this ); - std::string login_page = gSavedSettings.getString("LoginPage"); - if (login_page.empty()) - { - login_page = getString( "real_url" ); - } - LLHTTPClient::head( login_page, gResponsePtr ); -#if !USE_VIEWER_AUTH - // Initialize visibility (and don't force visibility - use prefs) - refreshLocation( false ); -#endif + LLHTTPClient::head( LLGridManager::getInstance()->getLoginPage(), gResponsePtr ); + + updateLocationCombo(false); } @@ -378,21 +358,6 @@ void LLPanelLogin::setSiteIsAlive( bool alive ) } } -void LLPanelLogin::mungePassword(LLUICtrl* caller, void* user_data) -{ - LLPanelLogin* self = (LLPanelLogin*)user_data; - LLLineEditor* editor = (LLLineEditor*)caller; - std::string password = editor->getText(); - - // Re-md5 if we've changed at all - if (password != self->mIncomingPassword) - { - LLMD5 pass((unsigned char *)password.c_str()); - char munged_password[MD5HEX_STR_SIZE]; - pass.hex_digest(munged_password); - self->mMungedPassword = munged_password; - } -} LLPanelLogin::~LLPanelLogin() { @@ -499,14 +464,14 @@ void LLPanelLogin::giveFocus() if( sInstance ) { // Grab focus and move cursor to first blank input field - std::string first = sInstance->childGetText("first_name_edit"); + std::string username = sInstance->childGetText("username_edit"); std::string pass = sInstance->childGetText("password_edit"); - BOOL have_first = !first.empty(); + BOOL have_username = !username.empty(); BOOL have_pass = !pass.empty(); LLLineEditor* edit = NULL; - if (have_first && !have_pass) + if (have_username && !have_pass) { // User saved his name but not his password. Move // focus to password field. @@ -515,7 +480,7 @@ void LLPanelLogin::giveFocus() else { // User doesn't have a name, so start there. - edit = sInstance->getChild("first_name_edit"); + edit = sInstance->getChild("username_edit"); } if (edit) @@ -537,8 +502,8 @@ void LLPanelLogin::showLoginWidgets() // *TODO: Append all the usual login parameters, like first_login=Y etc. std::string splash_screen_url = sInstance->getString("real_url"); web_browser->navigateTo( splash_screen_url, "text/html" ); - LLUICtrl* first_name_edit = sInstance->getChild("first_name_edit"); - first_name_edit->setFocus(TRUE); + LLUICtrl* username_edit = sInstance->getChild("username_edit"); + username_edit->setFocus(TRUE); } // static @@ -560,77 +525,120 @@ void LLPanelLogin::show(const LLRect &rect, } // static -void LLPanelLogin::setFields(const std::string& firstname, - const std::string& lastname, - const std::string& password) +void LLPanelLogin::setFields(LLPointer credential, + BOOL remember) { if (!sInstance) { llwarns << "Attempted fillFields with no login view shown" << llendl; return; } + LL_INFOS("Credentials") << "Setting login fields to " << *credential << LL_ENDL; - sInstance->childSetText("first_name_edit", firstname); - sInstance->childSetText("last_name_edit", lastname); - - // Max "actual" password length is 16 characters. - // Hex digests are always 32 characters. - if (password.length() == 32) + LLSD identifier = credential->getIdentifier(); + if((std::string)identifier["type"] == "agent") + { + sInstance->childSetText("username_edit", (std::string)identifier["first_name"] + " " + + (std::string)identifier["last_name"]); + } + else if((std::string)identifier["type"] == "account") + { + sInstance->childSetText("username_edit", (std::string)identifier["account_name"]); + } + else + { + sInstance->childSetText("username_edit", std::string()); + } + // if the password exists in the credential, set the password field with + // a filler to get some stars + LLSD authenticator = credential->getAuthenticator(); + LL_INFOS("Credentials") << "Setting authenticator field " << authenticator["type"].asString() << LL_ENDL; + if(authenticator.isMap() && + authenticator.has("secret") && + (authenticator["secret"].asString().size() > 0)) { + // This is a MD5 hex digest of a password. // We don't actually use the password input field, // fill it with MAX_PASSWORD characters so we get a // nice row of asterixes. const std::string filler("123456789!123456"); - sInstance->childSetText("password_edit", filler); - sInstance->mIncomingPassword = filler; - sInstance->mMungedPassword = password; + sInstance->childSetText("password_edit", std::string("123456789!123456")); } else { - // this is a normal text password - sInstance->childSetText("password_edit", password); - sInstance->mIncomingPassword = password; - LLMD5 pass((unsigned char *)password.c_str()); - char munged_password[MD5HEX_STR_SIZE]; - pass.hex_digest(munged_password); - sInstance->mMungedPassword = munged_password; + sInstance->childSetText("password_edit", std::string()); } + sInstance->childSetValue("remember_check", remember); } // static -void LLPanelLogin::addServer(const std::string& server, S32 domain_name) +void LLPanelLogin::getFields(LLPointer& credential, + BOOL remember) { if (!sInstance) { - llwarns << "Attempted addServer with no login view shown" << llendl; + llwarns << "Attempted getFields with no login view shown" << llendl; return; } + + // load the credential so we can pass back the stored password or hash if the user did + // not modify the password field. + + credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); - LLComboBox* combo = sInstance->getChild("server_combo"); - combo->add(server, LLSD(domain_name) ); - combo->setCurrentByIndex(0); -} - -// static -void LLPanelLogin::getFields(std::string *firstname, - std::string *lastname, - std::string *password) -{ - if (!sInstance) + LLSD identifier = LLSD::emptyMap(); + LLSD authenticator = LLSD::emptyMap(); + + if(credential.notNull()) { - llwarns << "Attempted getFields with no login view shown" << llendl; - return; + authenticator = credential->getAuthenticator(); } - *firstname = sInstance->childGetText("first_name_edit"); - LLStringUtil::trim(*firstname); - - *lastname = sInstance->childGetText("last_name_edit"); - LLStringUtil::trim(*lastname); + std::string username = sInstance->childGetText("username_edit"); + LLStringUtil::trim(username); + std::string password = sInstance->childGetText("password_edit"); - *password = sInstance->mMungedPassword; + LL_INFOS2("Credentials", "Authentication") << "retrieving username:" << username << LL_ENDL; + // determine if the username is a first/last form or not. + size_t separator_index = username.find_first_of(' '); + if (separator_index == username.npos) + { + LL_INFOS2("Credentials", "Authentication") << "account: " << username << LL_ENDL; + // single username, so this is a 'clear' identifier + identifier["type"] = "account"; + identifier["account_name"] = username; + + if (LLPanelLogin::sInstance->mPasswordModified) + { + authenticator = LLSD::emptyMap(); + // password is plaintext + authenticator["type"] = "clear"; + authenticator["secret"] = password; + } + } + else if (separator_index == username.find_last_of(' ')) + { + LL_INFOS2("Credentials", "Authentication") << "agent: " << username << LL_ENDL; + // traditional firstname / lastname + identifier["type"] = "agent"; + identifier["first_name"] = username.substr(0, separator_index); + identifier["last_name"] = username.substr(separator_index+1, username.npos); + + if (LLPanelLogin::sInstance->mPasswordModified) + { + authenticator = LLSD::emptyMap(); + authenticator["type"] = "hash"; + authenticator["algorithm"] = "md5"; + LLMD5 pass((const U8 *)password.c_str()); + char md5pass[33]; /* Flawfinder: ignore */ + pass.hex_digest(md5pass); + authenticator["secret"] = md5pass; + } + } + credential = gSecAPIHandler->createCredential(LLGridManager::getInstance()->getGrid(), identifier, authenticator); + remember = sInstance->childGetValue("remember_check"); } // static @@ -650,64 +658,147 @@ BOOL LLPanelLogin::isGridComboDirty() } // static -void LLPanelLogin::getLocation(std::string &location) +BOOL LLPanelLogin::areCredentialFieldsDirty() { if (!sInstance) { - llwarns << "Attempted getLocation with no login view shown" << llendl; - return; + llwarns << "Attempted getServer with no login view shown" << llendl; } - - LLComboBox* combo = sInstance->getChild("start_location_combo"); - location = combo->getValue().asString(); + else + { + std::string username = sInstance->childGetText("username_edit"); + LLStringUtil::trim(username); + std::string password = sInstance->childGetText("password_edit"); + LLLineEditor* ctrl = sInstance->getChild("username_edit"); + if(ctrl && ctrl->isDirty()) + { + return true; + } + ctrl = sInstance->getChild("password_edit"); + if(ctrl && ctrl->isDirty()) + { + return true; + } + } + return false; } + // static -void LLPanelLogin::refreshLocation( bool force_visible ) +void LLPanelLogin::updateLocationCombo( bool force_visible ) { - if (!sInstance) return; - -#if USE_VIEWER_AUTH - loadLoginPage(); -#else + if (!sInstance) + { + return; + } + + llinfos << "updatelocationcombo " << LLStartUp::getStartSLURL().asString() << llendl; + LLComboBox* combo = sInstance->getChild("start_location_combo"); + + switch(LLStartUp::getStartSLURL().getType()) + { + case LLSLURL::LOCATION: + { + + combo->setCurrentByIndex( 2 ); + combo->setTextEntry(LLStartUp::getStartSLURL().getLocationString()); + break; + } + case LLSLURL::HOME_LOCATION: + combo->setCurrentByIndex(1); + break; + default: + combo->setCurrentByIndex(0); + break; + } + BOOL show_start = TRUE; - + if ( ! force_visible ) - { - // Don't show on first run after install - // Otherwise ShowStartLocation defaults to true. show_start = gSavedSettings.getBOOL("ShowStartLocation"); - } - - // Update the value of the location combo. - updateLocationUI(); sInstance->childSetVisible("start_location_combo", show_start); sInstance->childSetVisible("start_location_text", show_start); - - BOOL show_server = gSavedSettings.getBOOL("ForceShowGrid"); - sInstance->childSetVisible("server_combo", show_server); - -#endif + + sInstance->childSetVisible("server_combo", TRUE); } // static -void LLPanelLogin::updateLocationUI() +void LLPanelLogin::onSelectLocation(LLUICtrl*, void*) { if (!sInstance) return; - std::string sim_string = LLURLSimString::sInstance.mSimString; - if (!sim_string.empty()) + LLComboBox* combo = sInstance->getChild("start_location_combo"); + S32 index = combo->getCurrentIndex(); + + switch (index) { - // Replace "" with this region name - LLComboBox* combo = sInstance->getChild("start_location_combo"); - combo->remove(2); - combo->add( sim_string ); - combo->setTextEntry(sim_string); - combo->setCurrentByIndex( 2 ); + case 2: + { + LLSLURL slurl = LLSLURL(combo->getSelectedValue()); + if((slurl.getType() == LLSLURL::LOCATION) && + (slurl.getGrid() != LLStartUp::getStartSLURL().getGrid())) + { + + + // we've changed the grid, so update the grid selection + try + { + LLStartUp::setStartSLURL(slurl); + } + catch (LLInvalidGridName ex) + { + LLSD args; + args["GRID"] = slurl.getGrid(); + LLNotificationsUtil::add("InvalidGrid", args); + return; + } + loadLoginPage(); + } + break; + } + case 1: + { + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); + break; + } + default: + { + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); + break; + } } } + +// static +void LLPanelLogin::getLocation(LLSLURL& slurl) +{ + LLSLURL result; + if (!sInstance) + { + llwarns << "Attempted getLocation with no login view shown" << llendl; + } + + LLComboBox* combo = sInstance->getChild("start_location_combo"); + + switch(combo->getCurrentIndex()) + { + case 0: + slurl = LLSLURL(LLSLURL::SIM_LOCATION_HOME); + case 1: + slurl = LLSLURL(LLSLURL::SIM_LOCATION_LAST); + default: + slurl = LLSLURL(combo->getValue().asString()); + } +} + +void LLPanelLogin::setLocation(const LLSLURL& slurl) +{ + LLStartUp::setStartSLURL(slurl); + updateServer(); +} + // static void LLPanelLogin::closePanel() { @@ -741,15 +832,13 @@ void LLPanelLogin::loadLoginPage() std::ostringstream oStr; - std::string login_page = gSavedSettings.getString("LoginPage"); - if (login_page.empty()) - { - login_page = sInstance->getString( "real_url" ); - } + std::string login_page = LLGridManager::getInstance()->getLoginPage(); + oStr << login_page; // Use the right delimeter depending on how LLURI parses the URL LLURI login_page_uri = LLURI(login_page); + std::string first_query_delimiter = "&"; if (login_page_uri.queryMap().size() == 0) { @@ -781,11 +870,10 @@ void LLPanelLogin::loadLoginPage() curl_free(curl_version); // Grid - char* curl_grid = curl_escape(LLViewerLogin::getInstance()->getGridLabel().c_str(), 0); + char* curl_grid = curl_escape(LLGridManager::getInstance()->getGridLoginID().c_str(), 0); oStr << "&grid=" << curl_grid; curl_free(curl_grid); - - gViewerWindow->setMenuBackgroundColor(false, !LLViewerLogin::getInstance()->isInProductionGrid()); + gViewerWindow->setMenuBackgroundColor(false, !LLGridManager::getInstance()->isInProductionGrid()); gLoginMenuBarView->setBackgroundColor(gMenuBarView->getBackgroundColor()); @@ -810,30 +898,20 @@ void LLPanelLogin::loadLoginPage() location = gSavedSettings.getString("LoginLocation"); } - std::string firstname, lastname; + std::string username; if(gSavedSettings.getLLSD("UserLoginInfo").size() == 3) { LLSD cmd_line_login = gSavedSettings.getLLSD("UserLoginInfo"); - firstname = cmd_line_login[0].asString(); - lastname = cmd_line_login[1].asString(); + username = cmd_line_login[0].asString() + " " + cmd_line_login[1]; password = cmd_line_login[2].asString(); } - if (firstname.empty()) - { - firstname = gSavedSettings.getString("FirstName"); - } - - if (lastname.empty()) - { - lastname = gSavedSettings.getString("LastName"); - } char* curl_region = curl_escape(region.c_str(), 0); - oStr <<"firstname=" << firstname << - "&lastname=" << lastname << "&location=" << location << "®ion=" << curl_region; + oStr <<"username=" << username << + "&location=" << location << "®ion=" << curl_region; curl_free(curl_region); @@ -866,7 +944,7 @@ void LLPanelLogin::loadLoginPage() #endif LLMediaCtrl* web_browser = sInstance->getChild("login_html"); - + // navigate to the "real" page if (gSavedSettings.getBOOL("RegInClient")) { @@ -915,34 +993,33 @@ void LLPanelLogin::onClickConnect(void *) // JC - Make sure the fields all get committed. sInstance->setFocus(FALSE); - std::string first = sInstance->childGetText("first_name_edit"); - std::string last = sInstance->childGetText("last_name_edit"); - LLComboBox* combo = sInstance->getChild("start_location_combo"); - std::string combo_text = combo->getSimple(); - - bool has_first_and_last = !(first.empty() || last.empty()); - bool has_location = false; - - if(combo_text=="" || combo_text =="") + LLComboBox* combo = sInstance->getChild("server_combo"); + LLSD combo_val = combo->getSelectedValue(); + if (combo_val.isUndefined()) { - // *NOTE: Mani - Location field is not always committed by this point! - // This may be duplicate work, but better than not doing the work! - LLURLSimString::sInstance.setString(""); + combo_val = combo->getValue(); } - else + if(combo_val.isUndefined()) { - // *NOTE: Mani - Location field is not always committed by this point! - LLURLSimString::sInstance.setString(combo_text); - has_location = true; + LLNotificationsUtil::add("StartRegionEmpty"); + return; + } + try + { + LLGridManager::getInstance()->setGridChoice(combo_val.asString()); } - - if(!has_first_and_last) + catch (LLInvalidGridName ex) { - LLNotificationsUtil::add("MustHaveAccountToLogIn"); + LLSD args; + args["GRID"] = combo_val.asString(); + LLNotificationsUtil::add("InvalidGrid", args); + return; } - else if(!has_location) + + std::string username = sInstance->childGetText("username_edit"); + if(username.empty()) { - LLNotificationsUtil::add("StartRegionEmpty"); + LLNotificationsUtil::add("MustHaveAccountToLogIn"); } else { @@ -1005,6 +1082,8 @@ void LLPanelLogin::onClickHelp(void*) // static void LLPanelLogin::onPassKey(LLLineEditor* caller, void* user_data) { + LLPanelLogin *This = (LLPanelLogin *) user_data; + This->mPasswordModified = TRUE; if (gKeyboard->getKeyDown(KEY_CAPSLOCK) && sCapslockDidNotification == FALSE) { LLNotificationsUtil::add("CapsKeyOn"); @@ -1012,54 +1091,90 @@ void LLPanelLogin::onPassKey(LLLineEditor* caller, void* user_data) } } -// static -void LLPanelLogin::onSelectServer(LLUICtrl*, void*) -{ - // *NOTE: The paramters for this method are ignored. - // LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe, void*) - // calls this method. - - // The user twiddled with the grid choice ui. - // apply the selection to the grid setting. - std::string grid_label; - S32 grid_index; - LLComboBox* combo = sInstance->getChild("server_combo"); - LLSD combo_val = combo->getValue(); - - if (LLSD::TypeInteger == combo_val.type()) +void LLPanelLogin::updateServer() +{ + try { - grid_index = combo->getValue().asInteger(); - if ((S32)GRID_INFO_OTHER == grid_index) + updateServerCombo(); + // if they've selected another grid, we should load the credentials + // for that grid and set them to the UI. + if(sInstance && !sInstance->areCredentialFieldsDirty()) { - // This happens if the user specifies a custom grid - // via command line. - grid_label = combo->getSimple(); + LLPointer credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); + bool remember = sInstance->childGetValue("remember_check"); + sInstance->setFields(credential, remember); } + // grid changed so show new splash screen (possibly) + loadLoginPage(); + updateLocationCombo(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION); } - else + catch (LLInvalidGridName ex) { - // no valid selection, return other - grid_index = (S32)GRID_INFO_OTHER; - grid_label = combo_val.asString(); + // do nothing } +} - // This new seelction will override preset uris - // from the command line. - LLViewerLogin* vl = LLViewerLogin::getInstance(); - vl->resetURIs(); - if(grid_index != GRID_INFO_OTHER) +void LLPanelLogin::updateServerCombo() +{ + if (!sInstance) { - vl->setGridChoice((EGridInfo)grid_index); + return; } - else + // We add all of the possible values, sorted, and then add a bar and the current value at the top + LLComboBox* server_choice_combo = sInstance->getChild("server_combo"); + server_choice_combo->removeall(); +#ifdef LL_RELEASE_FOR_DOWNLOAD + std::map known_grids = LLGridManager::getInstance()->getKnownGrids(TRUE); +#else + std::map known_grids = LLGridManager::getInstance()->getKnownGrids(FALSE); +#endif + for (std::map::iterator grid_choice = known_grids.begin(); + grid_choice != known_grids.end(); + grid_choice++) { - vl->setGridChoice(grid_label); + if (!grid_choice->first.empty()) + { + server_choice_combo->add(grid_choice->second, grid_choice->first, ADD_SORTED); + } } + + server_choice_combo->addSeparator(ADD_TOP); + + server_choice_combo->add(LLGridManager::getInstance()->getGridLabel(), + LLGridManager::getInstance()->getGrid(), ADD_TOP); + + server_choice_combo->selectFirstItem(); +} - // grid changed so show new splash screen (possibly) - loadLoginPage(); +// static +void LLPanelLogin::onSelectServer(LLUICtrl*, void*) +{ + // *NOTE: The paramters for this method are ignored. + // LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe, void*) + // calls this method. + LL_INFOS("AppInit") << "onSelectServer" << LL_ENDL; + // The user twiddled with the grid choice ui. + // apply the selection to the grid setting. + LLPointer credential; + + LLComboBox* combo = sInstance->getChild("server_combo"); + LLSD combo_val = combo->getSelectedValue(); + if (combo_val.isUndefined()) + { + combo_val = combo->getValue(); + } + + combo = sInstance->getChild("start_location_combo"); + combo->setCurrentByIndex(1); + LLStartUp::setStartSLURL(LLSLURL(gSavedSettings.getString("LoginLocation"))); + LLGridManager::getInstance()->setGridChoice(combo_val.asString()); + // This new selection will override preset uris + // from the command line. + updateServer(); + updateLocationCombo(false); + updateLoginPanelLinks(); } void LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe) @@ -1072,3 +1187,14 @@ void LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe) onSelectServer(combo, NULL); } } + +void LLPanelLogin::updateLoginPanelLinks() +{ + LLSD grid_data = LLGridManager::getInstance()->getGridInfo(); + bool system_grid = grid_data.has(GRID_IS_SYSTEM_GRID_VALUE); + + // need to call through sInstance, as it's called from onSelectServer, which + // is static. + sInstance->childSetVisible("create_new_account_text", system_grid); + sInstance->childSetVisible("forgot_password_text", system_grid); +} diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 1fdc3a9361..9301c263da 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -41,6 +41,8 @@ class LLLineEditor; class LLUIImage; class LLPanelLoginListener; +class LLSLURL; +class LLCredential; class LLPanelLogin: public LLPanel, @@ -65,20 +67,16 @@ public: void (*callback)(S32 option, void* user_data), void* callback_data); - // Remember password checkbox is set via gSavedSettings "RememberPassword" - static void setFields(const std::string& firstname, const std::string& lastname, - const std::string& password); + static void setFields(LLPointer credential, BOOL remember); - static void addServer(const std::string& server, S32 domain_name); - static void refreshLocation( bool force_visible ); - static void updateLocationUI(); - - static void getFields(std::string *firstname, std::string *lastname, - std::string *password); + static void getFields(LLPointer& credential, BOOL remember); static BOOL isGridComboDirty(); - static void getLocation(std::string &location); - + static BOOL areCredentialFieldsDirty(); + static void getLocation(LLSLURL& slurl); + static void setLocation(const LLSLURL& slurl); + + static void updateLocationCombo(bool force_visible); // simply update the combo box static void closePanel(); void setSiteIsAlive( bool alive ); @@ -86,10 +84,10 @@ public: static void loadLoginPage(); static void giveFocus(); static void setAlwaysRefresh(bool refresh); - static void mungePassword(LLUICtrl* caller, void* user_data); // inherited from LLViewerMediaObserver /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); + static void updateServer(); // update the combo box, change the login page to the new server, clear the combo private: friend class LLPanelLoginListener; @@ -103,6 +101,10 @@ private: static void onPassKey(LLLineEditor* caller, void* user_data); static void onSelectServer(LLUICtrl*, void*); static void onServerComboLostFocus(LLFocusableElement*); + static void updateServerCombo(); + static void onSelectLocation(LLUICtrl*, void*); + + static void updateLoginPanelLinks(); private: LLPointer mLogoImage; @@ -111,8 +113,7 @@ private: void (*mCallback)(S32 option, void *userdata); void* mCallbackData; - std::string mIncomingPassword; - std::string mMungedPassword; + BOOL mPasswordModified; static LLPanelLogin* sInstance; static BOOL sCapslockDidNotification; diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 288edeb031..7f5e63adee 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -619,7 +619,7 @@ BOOL LLPanelPeople::postBuild() if(recent_view_sort) mRecentViewSortMenuHandle = recent_view_sort->getHandle(); - gVoiceClient->addObserver(this); + LLVoiceClient::getInstance()->addObserver(this); // call this method in case some list is empty and buttons can be in inconsistent state updateButtons(); @@ -809,7 +809,7 @@ void LLPanelPeople::updateButtons() } } - bool enable_calls = gVoiceClient->voiceWorking() && gVoiceClient->voiceEnabled(); + bool enable_calls = LLVoiceClient::getInstance()->isVoiceWorking() && LLVoiceClient::getInstance()->voiceEnabled(); buttonSetEnabled("teleport_btn", friends_tab_active && item_selected && isFriendOnline(selected_uuids.front())); buttonSetEnabled("view_profile_btn", item_selected); diff --git a/indra/newview/llpanelplacestab.cpp b/indra/newview/llpanelplacestab.cpp index 9806b8c64d..6b12796e59 100644 --- a/indra/newview/llpanelplacestab.cpp +++ b/indra/newview/llpanelplacestab.cpp @@ -70,10 +70,7 @@ void LLPanelPlacesTab::onRegionResponse(const LLVector3d& landmark_global_pos, std::string sl_url; if ( gotSimName ) { - F32 region_x = (F32)fmod( landmark_global_pos.mdV[VX], (F64)REGION_WIDTH_METERS ); - F32 region_y = (F32)fmod( landmark_global_pos.mdV[VY], (F64)REGION_WIDTH_METERS ); - - sl_url = LLSLURL::buildSLURL(sim_name, llround(region_x), llround(region_y), llround((F32)landmark_global_pos.mdV[VZ])); + sl_url = LLSLURL(sim_name, landmark_global_pos).getSLURLString(); } else { diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 0a20ff6226..268738d88c 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -346,7 +346,6 @@ void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) { if (mExcludeAgent && gAgent.getID() == avatar_id) return; if (mAvatarList->contains(avatar_id)) return; - mAvatarList->getIDs().push_back(avatar_id); mAvatarList->setDirty(); adjustParticipant(avatar_id); @@ -632,7 +631,7 @@ bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD& else if (item == "can_call") { bool not_agent = mUUIDs.front() != gAgentID; - bool can_call = not_agent && LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking(); + bool can_call = not_agent && LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); return can_call; } diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp new file mode 100644 index 0000000000..ba343f5387 --- /dev/null +++ b/indra/newview/llsecapi.cpp @@ -0,0 +1,161 @@ +/** + * @file llsecapi.cpp + * @brief Security API for services such as certificate handling + * secure local storage, etc. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" +#include "llsecapi.h" +#include "llsechandler_basic.h" +#include +#include +#include "llhttpclient.h" + + + +std::map > gHandlerMap; +LLPointer gSecAPIHandler; + +void initializeSecHandler() +{ + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_digests(); + gHandlerMap[BASIC_SECHANDLER] = new LLSecAPIBasicHandler(); + + + // Currently, we only have the Basic handler, so we can point the main sechandler + // pointer to the basic handler. Later, we'll create a wrapper handler that + // selects the appropriate sechandler as needed, for instance choosing the + // mac keyring handler, with fallback to the basic sechandler + gSecAPIHandler = gHandlerMap[BASIC_SECHANDLER]; + + // initialize all SecAPIHandlers + LLProtectedDataException ex = LLProtectedDataException(""); + std::map >::const_iterator itr; + for(itr = gHandlerMap.begin(); itr != gHandlerMap.end(); ++itr) + { + LLPointer handler = (*itr).second; + try + { + handler->init(); + } + catch (LLProtectedDataException e) + { + ex = e; + } + } + if (ex.getMessage().length() > 0 ) // an exception was thrown. + { + throw ex; + } + +} +// start using a given security api handler. If the string is empty +// the default is used +LLPointer getSecHandler(const std::string& handler_type) +{ + if (gHandlerMap.find(handler_type) != gHandlerMap.end()) + { + return gHandlerMap[handler_type]; + } + else + { + return LLPointer(NULL); + } +} +// register a handler +void registerSecHandler(const std::string& handler_type, + LLPointer& handler) +{ + gHandlerMap[handler_type] = handler; +} + +std::ostream& operator <<(std::ostream& s, const LLCredential& cred) +{ + return s << (std::string)cred; +} + + +// secapiSSLCertVerifyCallback +// basic callback called when a cert verification is requested. +// calls SECAPI to validate the context +// not initialized in the above initialization function, due to unit tests +// see llappviewer + +int secapiSSLCertVerifyCallback(X509_STORE_CTX *ctx, void *param) +{ + LLURLRequest *req = (LLURLRequest *)param; + LLPointer store = gSecAPIHandler->getCertificateStore(""); + LLPointer chain = gSecAPIHandler->getCertificateChain(ctx); + LLSD validation_params = LLSD::emptyMap(); + LLURI uri(req->getURL()); + validation_params[CERT_HOSTNAME] = uri.hostName(); + try + { + chain->validate(VALIDATION_POLICY_SSL, store, validation_params); + } + catch (LLCertValidationTrustException& cert_exception) + { + LL_WARNS("AppInit") << "Cert not trusted: " << cert_exception.getMessage() << LL_ENDL; + return 0; + } + catch (LLCertException& cert_exception) + { + LL_WARNS("AppInit") << "cert error " << cert_exception.getMessage() << LL_ENDL; + return 0; + } + catch (...) + { + LL_WARNS("AppInit") << "cert error " << LL_ENDL; + return 0; + } + return 1; +} + +LLSD LLCredential::getLoginParams() +{ + LLSD result = LLSD::emptyMap(); + if (mIdentifier["type"].asString() == "agent") + { + // legacy credential + result["passwd"] = "$1$" + mAuthenticator["secret"].asString(); + result["first"] = mIdentifier["first_name"]; + result["last"] = mIdentifier["last_name"]; + + } + else if (mIdentifier["type"].asString() == "account") + { + result["username"] = mIdentifier["account_name"]; + result["passwd"] = mAuthenticator["secret"]; + + } + return result; +} diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h new file mode 100644 index 0000000000..5211dc2699 --- /dev/null +++ b/indra/newview/llsecapi.h @@ -0,0 +1,493 @@ +/** + * @file llsecapi.h + * @brief Security API for services such as certificate handling + * secure local storage, etc. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LLSECAPI_H +#define LLSECAPI_H +#include +#include +#include + +#ifdef LL_WINDOWS +#pragma warning(disable:4250) +#endif // LL_WINDOWS + +// All error handling is via exceptions. + + +#define CERT_SUBJECT_NAME "subject_name" +#define CERT_ISSUER_NAME "issuer_name" +#define CERT_NAME_CN "commonName" + +#define CERT_SUBJECT_NAME_STRING "subject_name_string" +#define CERT_ISSUER_NAME_STRING "issuer_name_string" + +#define CERT_SERIAL_NUMBER "serial_number" + +#define CERT_VALID_FROM "valid_from" +#define CERT_VALID_TO "valid_to" +#define CERT_SHA1_DIGEST "sha1_digest" +#define CERT_MD5_DIGEST "md5_digest" +#define CERT_HOSTNAME "hostname" +#define CERT_BASIC_CONSTRAINTS "basicConstraints" +#define CERT_BASIC_CONSTRAINTS_CA "CA" +#define CERT_BASIC_CONSTRAINTS_PATHLEN "pathLen" + +#define CERT_KEY_USAGE "keyUsage" +#define CERT_KU_DIGITAL_SIGNATURE "digitalSignature" +#define CERT_KU_NON_REPUDIATION "nonRepudiation" +#define CERT_KU_KEY_ENCIPHERMENT "keyEncipherment" +#define CERT_KU_DATA_ENCIPHERMENT "dataEncipherment" +#define CERT_KU_KEY_AGREEMENT "keyAgreement" +#define CERT_KU_CERT_SIGN "certSigning" +#define CERT_KU_CRL_SIGN "crlSigning" +#define CERT_KU_ENCIPHER_ONLY "encipherOnly" +#define CERT_KU_DECIPHER_ONLY "decipherOnly" + +#define BASIC_SECHANDLER "BASIC_SECHANDLER" +#define CERT_VALIDATION_DATE "validation_date" + +#define CERT_EXTENDED_KEY_USAGE "extendedKeyUsage" +#define CERT_EKU_SERVER_AUTH SN_server_auth + +#define CERT_SUBJECT_KEY_IDENTFIER "subjectKeyIdentifier" +#define CERT_AUTHORITY_KEY_IDENTIFIER "authorityKeyIdentifier" +#define CERT_AUTHORITY_KEY_IDENTIFIER_ID "authorityKeyIdentifierId" +#define CERT_AUTHORITY_KEY_IDENTIFIER_NAME "authorityKeyIdentifierName" +#define CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL "authorityKeyIdentifierSerial" + +// validate the current time lies within +// the validation period of the cert +#define VALIDATION_POLICY_TIME 1 + +// validate that the CA, or some cert in the chain +// lies within the certificate store +#define VALIDATION_POLICY_TRUSTED 2 + +// validate that the subject name of +// the cert contains the passed in hostname +// or validates against the hostname +#define VALIDATION_POLICY_HOSTNAME 4 + + +// validate that the cert contains the SSL EKU +#define VALIDATION_POLICY_SSL_KU 8 + +// validate that the cert contains the SSL EKU +#define VALIDATION_POLICY_CA_KU 16 + +#define VALIDATION_POLICY_CA_BASIC_CONSTRAINTS 32 + +// validate that the cert is correct for SSL +#define VALIDATION_POLICY_SSL (VALIDATION_POLICY_TIME | \ + VALIDATION_POLICY_HOSTNAME | \ + VALIDATION_POLICY_TRUSTED | \ + VALIDATION_POLICY_SSL_KU | \ + VALIDATION_POLICY_CA_BASIC_CONSTRAINTS | \ + VALIDATION_POLICY_CA_KU) + + + + + + +class LLProtectedDataException +{ +public: + LLProtectedDataException(const char *msg) + { + LL_WARNS("SECAPI") << "Protected Data Error: " << (std::string)msg << LL_ENDL; + mMsg = (std::string)msg; + } + std::string getMessage() { return mMsg; } +protected: + std::string mMsg; +}; + +// class LLCertificate +// parent class providing an interface for certifiate. +// LLCertificates are considered unmodifiable +// Certificates are pulled out of stores, or created via +// factory calls +class LLCertificate : public LLRefCount +{ + LOG_CLASS(LLCertificate); +public: + LLCertificate() {} + + virtual ~LLCertificate() {} + + // return a PEM encoded certificate. The encoding + // includes the -----BEGIN CERTIFICATE----- and end certificate elements + virtual std::string getPem() const=0; + + // return a DER encoded certificate + virtual std::vector getBinary() const=0; + + // return an LLSD object containing information about the certificate + // such as its name, signature, expiry time, serial number + virtual LLSD getLLSD() const=0; + + // return an openSSL X509 struct for the certificate + virtual X509* getOpenSSLX509() const=0; + +}; + +// class LLCertificateVector +// base class for a list of certificates. + + +class LLCertificateVector : public LLRefCount +{ + +public: + + LLCertificateVector() {}; + virtual ~LLCertificateVector() {}; + + // base iterator implementation class, providing + // the functionality needed for the iterator class. + class iterator_impl : public LLRefCount + { + public: + iterator_impl() {}; + virtual ~iterator_impl() {}; + virtual void seek(bool incr)=0; + virtual LLPointer clone() const=0; + virtual bool equals(const LLPointer& _iter) const=0; + virtual LLPointer get()=0; + }; + + // iterator class + class iterator + { + public: + iterator(LLPointer impl) : mImpl(impl) {} + iterator() : mImpl(NULL) {} + iterator(const iterator& _iter) {mImpl = _iter.mImpl->clone(); } + ~iterator() {} + iterator& operator++() { if(mImpl.notNull()) mImpl->seek(true); return *this;} + iterator& operator--() { if(mImpl.notNull()) mImpl->seek(false); return *this;} + + iterator operator++(int) { iterator result = *this; if(mImpl.notNull()) mImpl->seek(true); return result;} + iterator operator--(int) { iterator result = *this; if(mImpl.notNull()) mImpl->seek(false); return result;} + LLPointer operator*() { return mImpl->get(); } + + LLPointer mImpl; + protected: + friend bool operator==(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs); + bool equals(const iterator& _iter) const { return mImpl->equals(_iter.mImpl); } + }; + + // numeric indexer + virtual LLPointer operator[](int)=0; + + // Iteration + virtual iterator begin()=0; + + virtual iterator end()=0; + + // find a cert given params + virtual iterator find(const LLSD& params) =0; + + // return the number of certs in the store + virtual int size() const = 0; + + // append the cert to the store. if a copy of the cert already exists in the store, it is removed first + virtual void add(LLPointer cert)=0; + + // insert the cert to the store. if a copy of the cert already exists in the store, it is removed first + virtual void insert(iterator location, LLPointer cert)=0; + + // remove a certificate from the store + virtual LLPointer erase(iterator cert)=0; +}; + + +// class LLCertificateStore +// represents a store of certificates, typically a store of root CA +// certificates. The store can be persisted, and can be used to validate +// a cert chain +// +class LLCertificateStore : virtual public LLCertificateVector +{ + +public: + + LLCertificateStore() {} + virtual ~LLCertificateStore() {} + + // persist the store + virtual void save()=0; + + // return the store id + virtual std::string storeId() const=0; +}; + +// class LLCertificateChain +// Class representing a chain of certificates in order, with the +// first element being the child cert. +class LLCertificateChain : virtual public LLCertificateVector +{ + +public: + LLCertificateChain() {} + + virtual ~LLCertificateChain() {} + + // validate a certificate chain given the params. + // Will throw exceptions on error + + virtual void validate(int validation_policy, + LLPointer ca_store, + const LLSD& validation_params) =0; +}; + + + + +inline +bool operator==(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs) +{ + return _lhs.equals(_rhs); +} +inline +bool operator!=(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs) +{ + return !(_lhs == _rhs); +} + + +// +// LLCredential - interface for credentials providing the following functionality: +// * persistance of credential information based on grid (for saving username/password) +// * serialization to an OGP identifier/authenticator pair +// +class LLCredential : public LLRefCount +{ +public: + + LLCredential() {} + + LLCredential(const std::string& grid) + { + mGrid = grid; + mIdentifier = LLSD::emptyMap(); + mAuthenticator = LLSD::emptyMap(); + } + + virtual ~LLCredential() {} + + virtual void setCredentialData(const LLSD& identifier, const LLSD& authenticator) + { + mIdentifier = identifier; + mAuthenticator = authenticator; + } + virtual LLSD getIdentifier() { return mIdentifier; } + virtual LLSD getAuthenticator() { return mAuthenticator; } + virtual LLSD getLoginParams(); + virtual std::string getGrid() { return mGrid; } + + + virtual void clearAuthenticator() { mAuthenticator = LLSD(); } + virtual std::string userID() const { return std::string("unknown");} + virtual std::string asString() const { return std::string("unknown");} + operator std::string() const { return asString(); } +protected: + LLSD mIdentifier; + LLSD mAuthenticator; + std::string mGrid; +}; + +std::ostream& operator <<(std::ostream& s, const LLCredential& cred); + + +// All error handling is via exceptions. + +class LLCertException +{ +public: + LLCertException(LLPointer cert, const char* msg) + { + + mCert = cert; + + LL_WARNS("SECAPI") << "Certificate Error: " << (std::string)msg << LL_ENDL; + mMsg = (std::string)msg; + } + LLPointer getCert() { return mCert; } + std::string getMessage() { return mMsg; } +protected: + LLPointer mCert; + std::string mMsg; +}; + +class LLInvalidCertificate : public LLCertException +{ +public: + LLInvalidCertificate(LLPointer cert) : LLCertException(cert, "CertInvalid") + { + } +protected: +}; + +class LLCertValidationTrustException : public LLCertException +{ +public: + LLCertValidationTrustException(LLPointer cert) : LLCertException(cert, "CertUntrusted") + { + } +protected: +}; + +class LLCertValidationHostnameException : public LLCertException +{ +public: + LLCertValidationHostnameException(std::string hostname, + LLPointer cert) : LLCertException(cert, "CertInvalidHostname") + { + mHostname = hostname; + } + + std::string getHostname() { return mHostname; } +protected: + std::string mHostname; +}; + +class LLCertValidationExpirationException : public LLCertException +{ +public: + LLCertValidationExpirationException(LLPointer cert, + LLDate current_time) : LLCertException(cert, "CertExpired") + { + mTime = current_time; + } + LLDate GetTime() { return mTime; } +protected: + LLDate mTime; +}; + +class LLCertKeyUsageValidationException : public LLCertException +{ +public: + LLCertKeyUsageValidationException(LLPointer cert) : LLCertException(cert, "CertKeyUsage") + { + } +protected: +}; + +class LLCertBasicConstraintsValidationException : public LLCertException +{ +public: + LLCertBasicConstraintsValidationException(LLPointer cert) : LLCertException(cert, "CertBasicConstraints") + { + } +protected: +}; + +class LLCertValidationInvalidSignatureException : public LLCertException +{ +public: + LLCertValidationInvalidSignatureException(LLPointer cert) : LLCertException(cert, "CertInvalidSignature") + { + } +protected: +}; + +// LLSecAPIHandler Class +// Interface handler class for the various security storage handlers. +class LLSecAPIHandler : public LLRefCount +{ +public: + + + LLSecAPIHandler() {} + virtual ~LLSecAPIHandler() {} + + // initialize the SecAPIHandler + virtual void init() {}; + + // instantiate a certificate from a pem string + virtual LLPointer getCertificate(const std::string& pem_cert)=0; + + + + // instiate a certificate from an openssl X509 structure + virtual LLPointer getCertificate(X509* openssl_cert)=0; + + // instantiate a chain from an X509_STORE_CTX + virtual LLPointer getCertificateChain(const X509_STORE_CTX* chain)=0; + + // instantiate a cert store given it's id. if a persisted version + // exists, it'll be loaded. If not, one will be created (but not + // persisted) + virtual LLPointer getCertificateStore(const std::string& store_id)=0; + + // persist data in a protected store + virtual void setProtectedData(const std::string& data_type, + const std::string& data_id, + const LLSD& data)=0; + + // retrieve protected data + virtual LLSD getProtectedData(const std::string& data_type, + const std::string& data_id)=0; + + // delete a protected data item from the store + virtual void deleteProtectedData(const std::string& data_type, + const std::string& data_id)=0; + + virtual LLPointer createCredential(const std::string& grid, + const LLSD& identifier, + const LLSD& authenticator)=0; + + virtual LLPointer loadCredential(const std::string& grid)=0; + + virtual void saveCredential(LLPointer cred, bool save_authenticator)=0; + + virtual void deleteCredential(LLPointer cred)=0; + +}; + +void initializeSecHandler(); + +// retrieve a security api depending on the api type +LLPointer getSecHandler(const std::string& handler_type); + +void registerSecHandler(const std::string& handler_type, + LLPointer& handler); + +extern LLPointer gSecAPIHandler; + + +int secapiSSLCertVerifyCallback(X509_STORE_CTX *ctx, void *param); + + +#endif // LL_SECAPI_H diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp new file mode 100644 index 0000000000..51e250ffc6 --- /dev/null +++ b/indra/newview/llsechandler_basic.cpp @@ -0,0 +1,1586 @@ +/** + * @file llsechandler_basic.cpp + * @brief Security API for services such as certificate handling + * secure local storage, etc. + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2000, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * +LLS * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" +#include "llsecapi.h" +#include "llsechandler_basic.h" +#include "llsdserialize.h" +#include "llviewernetwork.h" +#include "llxorcipher.h" +#include "llfile.h" +#include "lldir.h" +#include "llviewercontrol.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +// 128 bits of salt data... +#define STORE_SALT_SIZE 16 +#define BUFFER_READ_SIZE 256 +std::string cert_string_from_asn1_string(ASN1_STRING* value); +std::string cert_string_from_octet_string(ASN1_OCTET_STRING* value); + +LLSD _basic_constraints_ext(X509* cert); +LLSD _key_usage_ext(X509* cert); +LLSD _ext_key_usage_ext(X509* cert); +LLSD _subject_key_identifier_ext(X509 *cert); +LLSD _authority_key_identifier_ext(X509* cert); + +LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert) +{ + + // BIO_new_mem_buf returns a read only bio, but takes a void* which isn't const + // so we need to cast it. + BIO * pem_bio = BIO_new_mem_buf((void*)pem_cert.c_str(), pem_cert.length()); + if(pem_bio == NULL) + { + LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; + throw LLInvalidCertificate(this); + } + mCert = NULL; + PEM_read_bio_X509(pem_bio, &mCert, 0, NULL); + BIO_free(pem_bio); + if (!mCert) + { + throw LLInvalidCertificate(this); + } + _initLLSD(); +} + + +LLBasicCertificate::LLBasicCertificate(X509* pCert) +{ + if (!pCert || !pCert->cert_info) + { + throw LLInvalidCertificate(this); + } + mCert = X509_dup(pCert); + _initLLSD(); +} + +LLBasicCertificate::~LLBasicCertificate() +{ + if(mCert) + { + X509_free(mCert); + } +} + +// +// retrieve the pem using the openssl functionality +std::string LLBasicCertificate::getPem() const +{ + char * pem_bio_chars = NULL; + // a BIO is the equivalent of a 'std::stream', and + // can be a file, mem stream, whatever. Grab a memory based + // BIO for the result + BIO *pem_bio = BIO_new(BIO_s_mem()); + if (!pem_bio) + { + LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; + return std::string(); + } + PEM_write_bio_X509(pem_bio, mCert); + int length = BIO_get_mem_data(pem_bio, &pem_bio_chars); + std::string result = std::string(pem_bio_chars, length); + BIO_free(pem_bio); + return result; +} + +// get the DER encoding for the cert +// DER is a binary encoding format for certs... +std::vector LLBasicCertificate::getBinary() const +{ + U8 * der_bio_data = NULL; + // get a memory bio + BIO *der_bio = BIO_new(BIO_s_mem()); + if (!der_bio) + { + LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; + return std::vector(); + } + i2d_X509_bio(der_bio, mCert); + int length = BIO_get_mem_data(der_bio, &der_bio_data); + std::vector result(length); + // vectors are guranteed to be a contiguous chunk of memory. + memcpy(&result[0], der_bio_data, length); + BIO_free(der_bio); + return result; +} + + +LLSD LLBasicCertificate::getLLSD() const +{ + return mLLSDInfo; +} + +// Initialize the LLSD info for the certificate +LLSD& LLBasicCertificate::_initLLSD() +{ + + // call the various helpers to build the LLSD + mLLSDInfo[CERT_SUBJECT_NAME] = cert_name_from_X509_NAME(X509_get_subject_name(mCert)); + mLLSDInfo[CERT_ISSUER_NAME] = cert_name_from_X509_NAME(X509_get_issuer_name(mCert)); + mLLSDInfo[CERT_SUBJECT_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_subject_name(mCert)); + mLLSDInfo[CERT_ISSUER_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_issuer_name(mCert)); + ASN1_INTEGER *sn = X509_get_serialNumber(mCert); + if (sn != NULL) + { + mLLSDInfo[CERT_SERIAL_NUMBER] = cert_string_from_asn1_integer(sn); + } + + mLLSDInfo[CERT_VALID_TO] = cert_date_from_asn1_time(X509_get_notAfter(mCert)); + mLLSDInfo[CERT_VALID_FROM] = cert_date_from_asn1_time(X509_get_notBefore(mCert)); + mLLSDInfo[CERT_SHA1_DIGEST] = cert_get_digest("sha1", mCert); + mLLSDInfo[CERT_MD5_DIGEST] = cert_get_digest("md5", mCert); + // add the known extensions + mLLSDInfo[CERT_BASIC_CONSTRAINTS] = _basic_constraints_ext(mCert); + mLLSDInfo[CERT_KEY_USAGE] = _key_usage_ext(mCert); + mLLSDInfo[CERT_EXTENDED_KEY_USAGE] = _ext_key_usage_ext(mCert); + mLLSDInfo[CERT_SUBJECT_KEY_IDENTFIER] = _subject_key_identifier_ext(mCert); + mLLSDInfo[CERT_AUTHORITY_KEY_IDENTIFIER] = _authority_key_identifier_ext(mCert); + return mLLSDInfo; +} + +// Retrieve the basic constraints info +LLSD _basic_constraints_ext(X509* cert) +{ + LLSD result; + BASIC_CONSTRAINTS *bs = (BASIC_CONSTRAINTS *)X509_get_ext_d2i(cert, NID_basic_constraints, NULL, NULL); + if(bs) + { + result = LLSD::emptyMap(); + // Determines whether the cert can be used as a CA + result[CERT_BASIC_CONSTRAINTS_CA] = (bool)bs->ca; + + if(bs->pathlen) + { + // the pathlen determines how deep a certificate chain can be from + // this CA + if((bs->pathlen->type == V_ASN1_NEG_INTEGER) + || !bs->ca) + { + result[CERT_BASIC_CONSTRAINTS_PATHLEN] = 0; + } + else + { + result[CERT_BASIC_CONSTRAINTS_PATHLEN] = (int)ASN1_INTEGER_get(bs->pathlen); + } + } + + } + return result; +} + +// retrieve the key usage, which specifies how the cert can be used. +// +LLSD _key_usage_ext(X509* cert) +{ + LLSD result; + ASN1_STRING *usage_str = (ASN1_STRING *)X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL); + if(usage_str) + { + result = LLSD::emptyArray(); + long usage = 0; + if(usage_str->length > 0) + { + usage = usage_str->data[0]; + if(usage_str->length > 1) + { + usage |= usage_str->data[1] << 8; + } + } + ASN1_STRING_free(usage_str); + if(usage) + { + if(usage & KU_DIGITAL_SIGNATURE) result.append(LLSD((std::string)CERT_KU_DIGITAL_SIGNATURE)); + if(usage & KU_NON_REPUDIATION) result.append(LLSD((std::string)CERT_KU_NON_REPUDIATION)); + if(usage & KU_KEY_ENCIPHERMENT) result.append(LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT)); + if(usage & KU_DATA_ENCIPHERMENT) result.append(LLSD((std::string)CERT_KU_DATA_ENCIPHERMENT)); + if(usage & KU_KEY_AGREEMENT) result.append(LLSD((std::string)CERT_KU_KEY_AGREEMENT)); + if(usage & KU_KEY_CERT_SIGN) result.append(LLSD((std::string)CERT_KU_CERT_SIGN)); + if(usage & KU_CRL_SIGN) result.append(LLSD((std::string)CERT_KU_CRL_SIGN)); + if(usage & KU_ENCIPHER_ONLY) result.append(LLSD((std::string)CERT_KU_ENCIPHER_ONLY)); + if(usage & KU_DECIPHER_ONLY) result.append(LLSD((std::string)CERT_KU_DECIPHER_ONLY)); + } + } + return result; +} + +// retrieve the extended key usage for the cert +LLSD _ext_key_usage_ext(X509* cert) +{ + LLSD result; + EXTENDED_KEY_USAGE *eku = (EXTENDED_KEY_USAGE *)X509_get_ext_d2i(cert, NID_ext_key_usage, NULL, NULL); + if(eku) + { + result = LLSD::emptyArray(); + while(sk_ASN1_OBJECT_num(eku)) + { + ASN1_OBJECT *usage = sk_ASN1_OBJECT_pop(eku); + if(usage) + { + int nid = OBJ_obj2nid(usage); + if (nid) + { + std::string sn = OBJ_nid2sn(nid); + result.append(sn); + } + ASN1_OBJECT_free(usage); + } + } + } + return result; +} + +// retrieve the subject key identifier of the cert +LLSD _subject_key_identifier_ext(X509 *cert) +{ + LLSD result; + ASN1_OCTET_STRING *skeyid = (ASN1_OCTET_STRING *)X509_get_ext_d2i(cert, NID_subject_key_identifier, NULL, NULL); + if(skeyid) + { + result = cert_string_from_octet_string(skeyid); + } + return result; +} + +// retrieve the authority key identifier of the cert +LLSD _authority_key_identifier_ext(X509* cert) +{ + LLSD result; + AUTHORITY_KEYID *akeyid = (AUTHORITY_KEYID *)X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL); + if(akeyid) + { + result = LLSD::emptyMap(); + if(akeyid->keyid) + { + result[CERT_AUTHORITY_KEY_IDENTIFIER_ID] = cert_string_from_octet_string(akeyid->keyid); + } + if(akeyid->serial) + { + result[CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL] = cert_string_from_asn1_integer(akeyid->serial); + } + } + + // we ignore the issuer name in the authority key identifier, we check the issue name via + // the the issuer name entry in the cert. + + + return result; +} + +// retrieve an openssl x509 object, +// which must be freed by X509_free +X509* LLBasicCertificate::getOpenSSLX509() const +{ + return X509_dup(mCert); +} + +// generate a single string containing the subject or issuer +// name of the cert. +std::string cert_string_name_from_X509_NAME(X509_NAME* name) +{ + char * name_bio_chars = NULL; + // get a memory bio + BIO *name_bio = BIO_new(BIO_s_mem()); + // stream the name into the bio. The name will be in the 'short name' format + X509_NAME_print_ex(name_bio, name, 0, XN_FLAG_RFC2253); + int length = BIO_get_mem_data(name_bio, &name_bio_chars); + std::string result = std::string(name_bio_chars, length); + BIO_free(name_bio); + return result; +} + +// generate an LLSD from a certificate name (issuer or subject name). +// the name will be strings indexed by the 'long form' +LLSD cert_name_from_X509_NAME(X509_NAME* name) +{ + LLSD result = LLSD::emptyMap(); + int name_entries = X509_NAME_entry_count(name); + for (int entry_index=0; entry_index < name_entries; entry_index++) + { + char buffer[32]; + X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, entry_index); + + std::string name_value = std::string((const char*)M_ASN1_STRING_data(X509_NAME_ENTRY_get_data(entry)), + M_ASN1_STRING_length(X509_NAME_ENTRY_get_data(entry))); + + ASN1_OBJECT* name_obj = X509_NAME_ENTRY_get_object(entry); + OBJ_obj2txt(buffer, sizeof(buffer), name_obj, 0); + std::string obj_buffer_str = std::string(buffer); + result[obj_buffer_str] = name_value; + } + + return result; +} + +// Generate a string from an ASN1 integer. ASN1 Integers are +// bignums, so they can be 'infinitely' long, therefore we +// cannot simply use a conversion to U64 or something. +// We retrieve as a readable string for UI + +std::string cert_string_from_asn1_integer(ASN1_INTEGER* value) +{ + std::string result; + BIGNUM *bn = ASN1_INTEGER_to_BN(value, NULL); + if(bn) + { + char * ascii_bn = BN_bn2hex(bn); + + if(ascii_bn) + { + result = ascii_bn; + OPENSSL_free(ascii_bn); + } + BN_free(bn); + } + return result; +} + +// Generate a string from an OCTET string. +// we retrieve as a + +std::string cert_string_from_octet_string(ASN1_OCTET_STRING* value) +{ + + std::stringstream result; + result << std::hex << std::setprecision(2); + for (int i=0; i < value->length; i++) + { + if (i != 0) + { + result << ":"; + } + result << std::setfill('0') << std::setw(2) << (int)value->data[i]; + } + return result.str(); +} + +// Generate a string from an ASN1 integer. ASN1 Integers are +// bignums, so they can be 'infinitely' long, therefore we +// cannot simply use a conversion to U64 or something. +// We retrieve as a readable string for UI + +std::string cert_string_from_asn1_string(ASN1_STRING* value) +{ + char * string_bio_chars = NULL; + std::string result; + // get a memory bio + BIO *string_bio = BIO_new(BIO_s_mem()); + if(!string_bio) + { + // stream the name into the bio. The name will be in the 'short name' format + ASN1_STRING_print_ex(string_bio, value, ASN1_STRFLGS_RFC2253); + int length = BIO_get_mem_data(string_bio, &string_bio_chars); + result = std::string(string_bio_chars, length); + BIO_free(string_bio); + } + else + { + LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; + } + + return result; +} + +// retrieve a date structure from an ASN1 time, for +// validity checking. +LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time) +{ + + struct tm timestruct = {0}; + int i = asn1_time->length; + + if (i < 10) + { + return LLDate(); + } + // convert the date from the ASN1 time (which is a string in ZULU time), to + // a timeval. + timestruct.tm_year = (asn1_time->data[0]-'0') * 10 + (asn1_time->data[1]-'0'); + + /* Deal with Year 2000 */ + if (timestruct.tm_year < 70) + timestruct.tm_year += 100; + + timestruct.tm_mon = (asn1_time->data[2]-'0') * 10 + (asn1_time->data[3]-'0') - 1; + timestruct.tm_mday = (asn1_time->data[4]-'0') * 10 + (asn1_time->data[5]-'0'); + timestruct.tm_hour = (asn1_time->data[6]-'0') * 10 + (asn1_time->data[7]-'0'); + timestruct.tm_min = (asn1_time->data[8]-'0') * 10 + (asn1_time->data[9]-'0'); + timestruct.tm_sec = (asn1_time->data[10]-'0') * 10 + (asn1_time->data[11]-'0'); + +#if LL_WINDOWS + return LLDate((F64)_mkgmtime(×truct)); +#else // LL_WINDOWS + return LLDate((F64)timegm(×truct)); +#endif // LL_WINDOWS +} + + +// Generate a string containing a digest. The digest time is 'ssh1' or +// 'md5', and the resulting string is of the form "aa:12:5c:' and so on +std::string cert_get_digest(const std::string& digest_type, X509 *cert) +{ + unsigned char digest_data[BUFFER_READ_SIZE]; + unsigned int len = sizeof(digest_data); + std::stringstream result; + const EVP_MD* digest = NULL; + // we could use EVP_get_digestbyname, but that requires initializer code which + // would require us to complicate things by plumbing it into the system. + if (digest_type == "md5") + { + digest = EVP_md5(); + } + else if (digest_type == "sha1") + { + digest = EVP_sha1(); + } + else + { + return std::string(); + } + + X509_digest(cert, digest, digest_data, &len); + result << std::hex << std::setprecision(2); + for (unsigned int i=0; i < len; i++) + { + if (i != 0) + { + result << ":"; + } + result << std::setfill('0') << std::setw(2) << (int)digest_data[i]; + } + return result.str(); +} + + +// class LLBasicCertificateVector +// This class represents a list of certificates, implemented by a vector of certificate pointers. +// it contains implementations of the virtual functions for iterators, search, add, remove, etc. +// + +// Find a certificate in the list. +// It will find a cert that has minimally the params listed, with the values being the same +LLBasicCertificateVector::iterator LLBasicCertificateVector::find(const LLSD& params) +{ + BOOL found = FALSE; + // loop through the entire vector comparing the values in the certs + // against those passed in via the params. + // params should be a map. Only the items specified in the map will be + // checked, but they must match exactly, even if they're maps or arrays. + + for(iterator cert = begin(); + cert != end(); + cert++) + { + + found= TRUE; + LLSD cert_info = (*cert)->getLLSD(); + for (LLSD::map_const_iterator param = params.beginMap(); + param != params.endMap(); + param++) + { + + if (!cert_info.has((std::string)param->first) || + (!valueCompareLLSD(cert_info[(std::string)param->first], param->second))) + { + found = FALSE; + break; + } + } + if (found) + { + return (cert); + } + } + return end(); +} + +// Insert a certificate into the store. If the certificate already +// exists in the store, nothing is done. +void LLBasicCertificateVector::insert(iterator _iter, + LLPointer cert) +{ + LLSD cert_info = cert->getLLSD(); + if (cert_info.isMap() && cert_info.has(CERT_SHA1_DIGEST)) + { + LLSD existing_cert_info = LLSD::emptyMap(); + existing_cert_info[CERT_MD5_DIGEST] = cert_info[CERT_MD5_DIGEST]; + if(find(existing_cert_info) == end()) + { + BasicIteratorImpl *basic_iter = dynamic_cast(_iter.mImpl.get()); + mCerts.insert(basic_iter->mIter, cert); + } + } +} + +// remove a certificate from the store +LLPointer LLBasicCertificateVector::erase(iterator _iter) +{ + + if (_iter != end()) + { + BasicIteratorImpl *basic_iter = dynamic_cast(_iter.mImpl.get()); + LLPointer result = (*_iter); + mCerts.erase(basic_iter->mIter); + return result; + } + return NULL; +} + + +// +// LLBasicCertificateStore +// This class represents a store of CA certificates. The basic implementation +// uses a pem file such as the legacy CA.pem stored in the existing +// SL implementation. +LLBasicCertificateStore::LLBasicCertificateStore(const std::string& filename) +{ + mFilename = filename; + load_from_file(filename); +} + +void LLBasicCertificateStore::load_from_file(const std::string& filename) +{ + // scan the PEM file extracting each certificate + BIO* file_bio = BIO_new(BIO_s_file()); + if(file_bio) + { + if (BIO_read_filename(file_bio, filename.c_str()) > 0) + { + X509 *cert_x509 = NULL; + while((PEM_read_bio_X509(file_bio, &cert_x509, 0, NULL)) && + (cert_x509 != NULL)) + { + try + { + add(new LLBasicCertificate(cert_x509)); + } + catch (...) + { + LL_WARNS("SECAPI") << "Failure creating certificate from the certificate store file." << LL_ENDL; + } + X509_free(cert_x509); + cert_x509 = NULL; + } + BIO_free(file_bio); + } + } + else + { + LL_WARNS("SECAPI") << "Could not allocate a file BIO" << LL_ENDL; + } +} + + +LLBasicCertificateStore::~LLBasicCertificateStore() +{ +} + + +// persist the store +void LLBasicCertificateStore::save() +{ + llofstream file_store(mFilename, llofstream::binary); + if(!file_store.fail()) + { + for(iterator cert = begin(); + cert != end(); + cert++) + { + std::string pem = (*cert)->getPem(); + if(!pem.empty()) + { + file_store << (*cert)->getPem() << std::endl; + } + } + file_store.close(); + } + else + { + LL_WARNS("SECAPI") << "Could not open certificate store " << mFilename << "for save" << LL_ENDL; + } +} + +// return the store id +std::string LLBasicCertificateStore::storeId() const +{ + // this is the basic handler which uses the CA.pem store, + // so we ignore this. + return std::string(""); +} + + +// +// LLBasicCertificateChain +// This class represents a chain of certs, each cert being signed by the next cert +// in the chain. Certs must be properly signed by the parent +LLBasicCertificateChain::LLBasicCertificateChain(const X509_STORE_CTX* store) +{ + + // we're passed in a context, which contains a cert, and a blob of untrusted + // certificates which compose the chain. + if((store == NULL) || (store->cert == NULL)) + { + LL_WARNS("SECAPI") << "An invalid store context was passed in when trying to create a certificate chain" << LL_ENDL; + return; + } + // grab the child cert + LLPointer current = new LLBasicCertificate(store->cert); + + add(current); + if(store->untrusted != NULL) + { + // if there are other certs in the chain, we build up a vector + // of untrusted certs so we can search for the parents of each + // consecutive cert. + LLBasicCertificateVector untrusted_certs; + for(int i = 0; i < sk_X509_num(store->untrusted); i++) + { + LLPointer cert = new LLBasicCertificate(sk_X509_value(store->untrusted, i)); + untrusted_certs.add(cert); + + } + while(untrusted_certs.size() > 0) + { + LLSD find_data = LLSD::emptyMap(); + LLSD cert_data = current->getLLSD(); + // we simply build the chain via subject/issuer name as the + // client should not have passed in multiple CA's with the same + // subject name. If they did, it'll come out in the wash during + // validation. + find_data[CERT_SUBJECT_NAME_STRING] = cert_data[CERT_ISSUER_NAME_STRING]; + LLBasicCertificateVector::iterator issuer = untrusted_certs.find(find_data); + if (issuer != untrusted_certs.end()) + { + current = untrusted_certs.erase(issuer); + add(current); + } + else + { + break; + } + } + } +} + + +// subdomain wildcard specifiers can be divided into 3 parts +// the part before the first *, the part after the first * but before +// the second *, and the part after the second *. +// It then iterates over the second for each place in the string +// that it matches. ie if the subdomain was testfoofoobar, and +// the wildcard was test*foo*bar, it would match test, then +// recursively match foofoobar and foobar + +bool _cert_subdomain_wildcard_match(const std::string& subdomain, + const std::string& wildcard) +{ + // split wildcard into the portion before the *, and the portion after + + int wildcard_pos = wildcard.find_first_of('*'); + // check the case where there is no wildcard. + if(wildcard_pos == wildcard.npos) + { + return (subdomain == wildcard); + } + + // we need to match the first part of the subdomain string up to the wildcard + // position + if(subdomain.substr(0, wildcard_pos) != wildcard.substr(0, wildcard_pos)) + { + // the first portions of the strings didn't match + return FALSE; + } + + // as the portion of the wildcard string before the * matched, we need to check the + // portion afterwards. Grab that portion. + std::string new_wildcard_string = wildcard.substr( wildcard_pos+1, wildcard.npos); + if(new_wildcard_string.empty()) + { + // we had nothing after the *, so it's an automatic match + return TRUE; + } + + // grab the portion of the remaining wildcard string before the next '*'. We need to find this + // within the remaining subdomain string. and then recursively check. + std::string new_wildcard_match_string = new_wildcard_string.substr(0, new_wildcard_string.find_first_of('*')); + + // grab the portion of the subdomain after the part that matched the initial wildcard portion + std::string new_subdomain = subdomain.substr(wildcard_pos, subdomain.npos); + + // iterate through the current subdomain, finding instances of the match string. + int sub_pos = new_subdomain.find_first_of(new_wildcard_match_string); + while(sub_pos != std::string::npos) + { + new_subdomain = new_subdomain.substr(sub_pos, std::string::npos); + if(_cert_subdomain_wildcard_match(new_subdomain, new_wildcard_string)) + { + return TRUE; + } + sub_pos = new_subdomain.find_first_of(new_wildcard_match_string, 1); + + + } + // didn't find any instances of the match string that worked in the subdomain, so fail. + return FALSE; +} + + +// RFC2459 does not address wildcards as part of it's name matching +// specification, and there is no RFC specifying wildcard matching, +// RFC2818 does a few statements about wildcard matching, but is very +// general. Generally, wildcard matching is per implementation, although +// it's pretty similar. +// in our case, we use the '*' wildcard character only, within each +// subdomain. The hostname and the CN specification should have the +// same number of subdomains. +// We then iterate that algorithm over each subdomain. +bool _cert_hostname_wildcard_match(const std::string& hostname, const std::string& common_name) +{ + std::string new_hostname = hostname; + std::string new_cn = common_name; + int subdomain_pos = new_hostname.find_first_of('.'); + int subcn_pos = new_cn.find_first_of('.'); + + while((subcn_pos != std::string::npos) && (subdomain_pos != std::string::npos)) + { + // snip out the first subdomain and cn element + + if(!_cert_subdomain_wildcard_match(new_hostname.substr(0, subdomain_pos), + new_cn.substr(0, subcn_pos))) + { + return FALSE; + } + new_hostname = new_hostname.substr(subdomain_pos+1, std::string::npos); + new_cn = new_cn.substr(subcn_pos+1, std::string::npos); + subdomain_pos = new_hostname.find_first_of('.'); + subcn_pos = new_cn.find_first_of('.'); + } + return _cert_subdomain_wildcard_match(new_hostname, new_cn); + +} + +// validate that the LLSD array in llsd_set contains the llsd_value +bool _LLSDArrayIncludesValue(const LLSD& llsd_set, LLSD llsd_value) +{ + for(LLSD::array_const_iterator set_value = llsd_set.beginArray(); + set_value != llsd_set.endArray(); + set_value++) + { + if(valueCompareLLSD((*set_value), llsd_value)) + { + return TRUE; + } + } + return FALSE; +} + +void _validateCert(int validation_policy, + const LLPointer cert, + const LLSD& validation_params, + int depth) +{ + + LLSD current_cert_info = cert->getLLSD(); + // check basic properties exist in the cert + if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info.has(CERT_SUBJECT_NAME_STRING)) + { + throw LLCertException(cert, "Cert doesn't have a Subject Name"); + } + + if(!current_cert_info.has(CERT_ISSUER_NAME_STRING)) + { + throw LLCertException(cert, "Cert doesn't have an Issuer Name"); + } + + // check basic properties exist in the cert + if(!current_cert_info.has(CERT_VALID_FROM) || !current_cert_info.has(CERT_VALID_TO)) + { + throw LLCertException(cert, "Cert doesn't have an expiration period"); + } + if (!current_cert_info.has(CERT_SHA1_DIGEST)) + { + throw LLCertException(cert, "No SHA1 digest"); + } + + if (validation_policy & VALIDATION_POLICY_TIME) + { + + LLDate validation_date(time(NULL)); + if(validation_params.has(CERT_VALIDATION_DATE)) + { + validation_date = validation_params[CERT_VALIDATION_DATE]; + } + + if((validation_date < current_cert_info[CERT_VALID_FROM].asDate()) || + (validation_date > current_cert_info[CERT_VALID_TO].asDate())) + { + throw LLCertValidationExpirationException(cert, validation_date); + } + } + if (validation_policy & VALIDATION_POLICY_SSL_KU) + { + if (current_cert_info.has(CERT_KEY_USAGE) && current_cert_info[CERT_KEY_USAGE].isArray() && + (!(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], + LLSD((std::string)CERT_KU_DIGITAL_SIGNATURE))) || + !(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], + LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT))))) + { + throw LLCertKeyUsageValidationException(cert); + } + // only validate EKU if the cert has it + if(current_cert_info.has(CERT_EXTENDED_KEY_USAGE) && current_cert_info[CERT_EXTENDED_KEY_USAGE].isArray() && + (!_LLSDArrayIncludesValue(current_cert_info[CERT_EXTENDED_KEY_USAGE], + LLSD((std::string)CERT_EKU_SERVER_AUTH)))) + { + throw LLCertKeyUsageValidationException(cert); + } + } + if (validation_policy & VALIDATION_POLICY_CA_KU) + { + if (current_cert_info.has(CERT_KEY_USAGE) && current_cert_info[CERT_KEY_USAGE].isArray() && + (!_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], + (std::string)CERT_KU_CERT_SIGN))) + { + throw LLCertKeyUsageValidationException(cert); + } + } + + // validate basic constraints + if ((validation_policy & VALIDATION_POLICY_CA_BASIC_CONSTRAINTS) && + current_cert_info.has(CERT_BASIC_CONSTRAINTS) && + current_cert_info[CERT_BASIC_CONSTRAINTS].isMap()) + { + if(!current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_CA) || + !current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_CA]) + { + throw LLCertBasicConstraintsValidationException(cert); + } + if (current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_PATHLEN) && + ((current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger() != 0) && + (depth > current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger()))) + { + throw LLCertBasicConstraintsValidationException(cert); + } + } +} + +bool _verify_signature(LLPointer parent, + LLPointer child) +{ + bool verify_result = FALSE; + LLSD cert1 = parent->getLLSD(); + LLSD cert2 = child->getLLSD(); + X509 *signing_cert = parent->getOpenSSLX509(); + X509 *child_cert = child->getOpenSSLX509(); + if((signing_cert != NULL) && (child_cert != NULL)) + { + EVP_PKEY *pkey = X509_get_pubkey(signing_cert); + + + if(pkey) + { + int verify_code = X509_verify(child_cert, pkey); + verify_result = ( verify_code > 0); + EVP_PKEY_free(pkey); + } + else + { + LL_WARNS("SECAPI") << "Could not validate the cert chain signature, as the public key of the signing cert could not be retrieved" << LL_ENDL; + } + + } + else + { + LL_WARNS("SECAPI") << "Signature verification failed as there are no certs in the chain" << LL_ENDL; + } + if(child_cert) + { + X509_free(child_cert); + } + if(signing_cert) + { + X509_free(signing_cert); + } + return verify_result; +} + +// validate the certificate chain against a store. +// There are many aspects of cert validatioin policy involved in +// trust validation. The policies in this validation algorithm include +// * Hostname matching for SSL certs +// * Expiration time matching +// * Signature validation +// * Chain trust (is the cert chain trusted against the store) +// * Basic constraints +// * key usage and extended key usage +// TODO: We should add 'authority key identifier' for chaining. +// This algorithm doesn't simply validate the chain by itself +// and verify the last cert is in the certificate store, or points +// to a cert in the store. It validates whether any cert in the chain +// is trusted in the store, even if it's not the last one. +void LLBasicCertificateChain::validate(int validation_policy, + LLPointer ca_store, + const LLSD& validation_params) +{ + + if(size() < 1) + { + throw LLCertException(NULL, "No certs in chain"); + } + iterator current_cert = begin(); + LLSD current_cert_info = (*current_cert)->getLLSD(); + LLSD validation_date; + if (validation_params.has(CERT_VALIDATION_DATE)) + { + validation_date = validation_params[CERT_VALIDATION_DATE]; + } + + if (validation_policy & VALIDATION_POLICY_HOSTNAME) + { + if(!validation_params.has(CERT_HOSTNAME)) + { + throw LLCertException((*current_cert), "No hostname passed in for validation"); + } + if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info[CERT_SUBJECT_NAME].has(CERT_NAME_CN)) + { + throw LLInvalidCertificate((*current_cert)); + } + + LL_INFOS("SECAPI") << "Validating the hostname " << validation_params[CERT_HOSTNAME].asString() << + "against the cert CN " << current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString() << LL_ENDL; + if(!_cert_hostname_wildcard_match(validation_params[CERT_HOSTNAME].asString(), + current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString())) + { + throw LLCertValidationHostnameException(validation_params[CERT_HOSTNAME].asString(), + (*current_cert)); + } + } + + + int depth = 0; + LLPointer previous_cert; + // loop through the cert chain, validating the current cert against the next one. + while(current_cert != end()) + { + + int local_validation_policy = validation_policy; + if(current_cert == begin()) + { + // for the child cert, we don't validate CA stuff + local_validation_policy &= ~(VALIDATION_POLICY_CA_KU | + VALIDATION_POLICY_CA_BASIC_CONSTRAINTS); + } + else + { + // for non-child certs, we don't validate SSL Key usage + local_validation_policy &= ~VALIDATION_POLICY_SSL_KU; + if(!_verify_signature((*current_cert), + previous_cert)) + { + throw LLCertValidationInvalidSignatureException(previous_cert); + } + } + _validateCert(local_validation_policy, + (*current_cert), + validation_params, + depth); + + // look for a CA in the CA store that may belong to this chain. + LLSD cert_llsd = (*current_cert)->getLLSD(); + LLSD cert_search_params = LLSD::emptyMap(); + // is the cert itself in the store? + cert_search_params[CERT_SHA1_DIGEST] = cert_llsd[CERT_SHA1_DIGEST]; + LLCertificateStore::iterator found_store_cert = ca_store->find(cert_search_params); + if(found_store_cert != ca_store->end()) + { + return; + } + + // is the parent in the cert store? + + cert_search_params = LLSD::emptyMap(); + cert_search_params[CERT_SUBJECT_NAME_STRING] = cert_llsd[CERT_ISSUER_NAME_STRING]; + if (cert_llsd.has(CERT_AUTHORITY_KEY_IDENTIFIER)) + { + LLSD cert_aki = cert_llsd[CERT_AUTHORITY_KEY_IDENTIFIER]; + if(cert_aki.has(CERT_AUTHORITY_KEY_IDENTIFIER_ID)) + { + cert_search_params[CERT_SUBJECT_KEY_IDENTFIER] = cert_aki[CERT_AUTHORITY_KEY_IDENTIFIER_ID]; + } + if(cert_aki.has(CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL)) + { + cert_search_params[CERT_SERIAL_NUMBER] = cert_aki[CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL]; + } + } + found_store_cert = ca_store->find(cert_search_params); + + if(found_store_cert != ca_store->end()) + { + LLSD foo = (*found_store_cert)->getLLSD(); + // validate the store cert against the depth + _validateCert(validation_policy & VALIDATION_POLICY_CA_BASIC_CONSTRAINTS, + (*found_store_cert), + LLSD(), + depth); + + // verify the signature of the CA + if(!_verify_signature((*found_store_cert), + (*current_cert))) + { + throw LLCertValidationInvalidSignatureException(*current_cert); + } + // successfully validated. + return; + } + previous_cert = (*current_cert); + current_cert++; + depth++; + } + if (validation_policy & VALIDATION_POLICY_TRUSTED) + { + LLPointer untrusted_ca_cert = (*this)[size()-1]; + // we reached the end without finding a trusted cert. + throw LLCertValidationTrustException((*this)[size()-1]); + + } +} + + +// LLSecAPIBasicHandler Class +// Interface handler class for the various security storage handlers. + +// We read the file on construction, and write it on destruction. This +// means multiple processes cannot modify the datastore. +LLSecAPIBasicHandler::LLSecAPIBasicHandler(const std::string& protected_data_file, + const std::string& legacy_password_path) +{ + mProtectedDataFilename = protected_data_file; + mProtectedDataMap = LLSD::emptyMap(); + mLegacyPasswordPath = legacy_password_path; + +} + +LLSecAPIBasicHandler::LLSecAPIBasicHandler() +{ +} + + +void LLSecAPIBasicHandler::init() +{ + mProtectedDataMap = LLSD::emptyMap(); + if (mProtectedDataFilename.length() == 0) + { + mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "bin_conf.dat"); + mLegacyPasswordPath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "password.dat"); + + mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "bin_conf.dat"); + std::string store_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "CA.pem"); + // copy the CA file to a user writable location so we can manipulate it. + // for this provider, by using a user writable file, there is a risk that + // an attacking program can modify the file, but OS dependent providers + // will reduce that risk. + // by using a user file, modifications will be limited to one user if + // we read-only the main file + if (!LLFile::isfile(store_file)) + { + + std::string ca_file_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); + llifstream ca_file(ca_file_path.c_str(), llifstream::binary | llifstream::in); + llofstream copied_store_file(store_file.c_str(), llofstream::binary | llofstream::out); + + while(!ca_file.fail()) + { + char buffer[BUFFER_READ_SIZE]; + ca_file.read(buffer, sizeof(buffer)); + copied_store_file.write(buffer, ca_file.gcount()); + } + ca_file.close(); + copied_store_file.close(); + } + LL_INFOS("SECAPI") << "Loading certificate store from " << store_file << LL_ENDL; + mStore = new LLBasicCertificateStore(store_file); + } + _readProtectedData(); // initialize mProtectedDataMap + // may throw LLProtectedDataException if saved datamap is not decryptable +} +LLSecAPIBasicHandler::~LLSecAPIBasicHandler() +{ + _writeProtectedData(); +} + +void LLSecAPIBasicHandler::_readProtectedData() +{ + // attempt to load the file into our map + LLPointer parser = new LLSDXMLParser(); + llifstream protected_data_stream(mProtectedDataFilename.c_str(), + llifstream::binary); + + if (!protected_data_stream.fail()) { + int offset; + U8 salt[STORE_SALT_SIZE]; + U8 buffer[BUFFER_READ_SIZE]; + U8 decrypted_buffer[BUFFER_READ_SIZE]; + int decrypted_length; + unsigned char MACAddress[MAC_ADDRESS_BYTES]; + LLUUID::getNodeID(MACAddress); + LLXORCipher cipher(MACAddress, MAC_ADDRESS_BYTES); + + // read in the salt and key + protected_data_stream.read((char *)salt, STORE_SALT_SIZE); + offset = 0; + if (protected_data_stream.gcount() < STORE_SALT_SIZE) + { + throw LLProtectedDataException("Config file too short."); + } + + cipher.decrypt(salt, STORE_SALT_SIZE); + + // totally lame. As we're not using the OS level protected data, we need to + // at least obfuscate the data. We do this by using a salt stored at the head of the file + // to encrypt the data, therefore obfuscating it from someone using simple existing tools. + // We do include the MAC address as part of the obfuscation, which would require an + // attacker to get the MAC address as well as the protected store, which improves things + // somewhat. It would be better to use the password, but as this store + // will be used to store the SL password when the user decides to have SL remember it, + // so we can't use that. OS-dependent store implementations will use the OS password/storage + // mechanisms and are considered to be more secure. + // We've a strong intent to move to OS dependent protected data stores. + + + // read in the rest of the file. + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_DecryptInit(&ctx, EVP_rc4(), salt, NULL); + // allocate memory: + std::string decrypted_data; + + while(protected_data_stream.good()) { + // read data as a block: + protected_data_stream.read((char *)buffer, BUFFER_READ_SIZE); + + EVP_DecryptUpdate(&ctx, decrypted_buffer, &decrypted_length, + buffer, protected_data_stream.gcount()); + decrypted_data.append((const char *)decrypted_buffer, protected_data_stream.gcount()); + } + + // RC4 is a stream cipher, so we don't bother to EVP_DecryptFinal, as there is + // no block padding. + EVP_CIPHER_CTX_cleanup(&ctx); + std::istringstream parse_stream(decrypted_data); + if (parser->parse(parse_stream, mProtectedDataMap, + LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) + { + throw LLProtectedDataException("Config file cannot be decrypted."); + } + } +} + +void LLSecAPIBasicHandler::_writeProtectedData() +{ + std::ostringstream formatted_data_ostream; + U8 salt[STORE_SALT_SIZE]; + U8 buffer[BUFFER_READ_SIZE]; + U8 encrypted_buffer[BUFFER_READ_SIZE]; + + + if(mProtectedDataMap.isUndefined()) + { + LLFile::remove(mProtectedDataFilename); + return; + } + // create a string with the formatted data. + LLSDSerialize::toXML(mProtectedDataMap, formatted_data_ostream); + std::istringstream formatted_data_istream(formatted_data_ostream.str()); + // generate the seed + RAND_bytes(salt, STORE_SALT_SIZE); + + + // write to a temp file so we don't clobber the initial file if there is + // an error. + std::string tmp_filename = mProtectedDataFilename + ".tmp"; + + llofstream protected_data_stream(tmp_filename.c_str(), + llofstream::binary); + try + { + + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_EncryptInit(&ctx, EVP_rc4(), salt, NULL); + unsigned char MACAddress[MAC_ADDRESS_BYTES]; + LLUUID::getNodeID(MACAddress); + LLXORCipher cipher(MACAddress, MAC_ADDRESS_BYTES); + cipher.encrypt(salt, STORE_SALT_SIZE); + protected_data_stream.write((const char *)salt, STORE_SALT_SIZE); + + while (formatted_data_istream.good()) + { + formatted_data_istream.read((char *)buffer, BUFFER_READ_SIZE); + if(formatted_data_istream.gcount() == 0) + { + break; + } + int encrypted_length; + EVP_EncryptUpdate(&ctx, encrypted_buffer, &encrypted_length, + buffer, formatted_data_istream.gcount()); + protected_data_stream.write((const char *)encrypted_buffer, encrypted_length); + } + + // no EVP_EncrypteFinal, as this is a stream cipher + EVP_CIPHER_CTX_cleanup(&ctx); + + protected_data_stream.close(); + } + catch (...) + { + // it's good practice to clean up any secure information on error + // (even though this file isn't really secure. Perhaps in the future + // it may be, however. + LLFile::remove(tmp_filename); + throw LLProtectedDataException("Error writing Protected Data Store"); + } + + // move the temporary file to the specified file location. + if((((LLFile::isfile(mProtectedDataFilename) != 0) && + (LLFile::remove(mProtectedDataFilename) != 0))) || + (LLFile::rename(tmp_filename, mProtectedDataFilename))) + { + LLFile::remove(tmp_filename); + throw LLProtectedDataException("Could not overwrite protected data store"); + } +} + +// instantiate a certificate from a pem string +LLPointer LLSecAPIBasicHandler::getCertificate(const std::string& pem_cert) +{ + LLPointer result = new LLBasicCertificate(pem_cert); + return result; +} + + + +// instiate a certificate from an openssl X509 structure +LLPointer LLSecAPIBasicHandler::getCertificate(X509* openssl_cert) +{ + LLPointer result = new LLBasicCertificate(openssl_cert); + return result; +} + +// instantiate a chain from an X509_STORE_CTX +LLPointer LLSecAPIBasicHandler::getCertificateChain(const X509_STORE_CTX* chain) +{ + LLPointer result = new LLBasicCertificateChain(chain); + return result; +} + +// instantiate a cert store given it's id. if a persisted version +// exists, it'll be loaded. If not, one will be created (but not +// persisted) +LLPointer LLSecAPIBasicHandler::getCertificateStore(const std::string& store_id) +{ + return mStore; +} + +// retrieve protected data +LLSD LLSecAPIBasicHandler::getProtectedData(const std::string& data_type, + const std::string& data_id) +{ + + if (mProtectedDataMap.has(data_type) && + mProtectedDataMap[data_type].isMap() && + mProtectedDataMap[data_type].has(data_id)) + { + return mProtectedDataMap[data_type][data_id]; + } + + return LLSD(); +} + +void LLSecAPIBasicHandler::deleteProtectedData(const std::string& data_type, + const std::string& data_id) +{ + if (mProtectedDataMap.has(data_type) && + mProtectedDataMap[data_type].isMap() && + mProtectedDataMap[data_type].has(data_id)) + { + mProtectedDataMap[data_type].erase(data_id); + } +} + + +// +// persist data in a protected store +// +void LLSecAPIBasicHandler::setProtectedData(const std::string& data_type, + const std::string& data_id, + const LLSD& data) +{ + if (!mProtectedDataMap.has(data_type) || !mProtectedDataMap[data_type].isMap()) { + mProtectedDataMap[data_type] = LLSD::emptyMap(); + } + + mProtectedDataMap[data_type][data_id] = data; +} + +// +// Create a credential object from an identifier and authenticator. credentials are +// per grid. +LLPointer LLSecAPIBasicHandler::createCredential(const std::string& grid, + const LLSD& identifier, + const LLSD& authenticator) +{ + LLPointer result = new LLSecAPIBasicCredential(grid); + result->setCredentialData(identifier, authenticator); + return result; +} + +// Load a credential from the credential store, given the grid +LLPointer LLSecAPIBasicHandler::loadCredential(const std::string& grid) +{ + LLSD credential = getProtectedData("credential", grid); + LLPointer result = new LLSecAPIBasicCredential(grid); + if(credential.isMap() && + credential.has("identifier")) + { + + LLSD identifier = credential["identifier"]; + LLSD authenticator; + if (credential.has("authenticator")) + { + authenticator = credential["authenticator"]; + } + result->setCredentialData(identifier, authenticator); + } + else + { + // credential was not in protected storage, so pull the credential + // from the legacy store. + std::string first_name = gSavedSettings.getString("FirstName"); + std::string last_name = gSavedSettings.getString("LastName"); + + if ((first_name != "") && + (last_name != "")) + { + LLSD identifier = LLSD::emptyMap(); + LLSD authenticator; + identifier["type"] = "agent"; + identifier["first_name"] = first_name; + identifier["last_name"] = last_name; + + std::string legacy_password = _legacyLoadPassword(); + if (legacy_password.length() > 0) + { + authenticator = LLSD::emptyMap(); + authenticator["type"] = "hash"; + authenticator["algorithm"] = "md5"; + authenticator["secret"] = legacy_password; + } + result->setCredentialData(identifier, authenticator); + } + } + return result; +} + +// Save the credential to the credential store. Save the authenticator also if requested. +// That feature is used to implement the 'remember password' functionality. +void LLSecAPIBasicHandler::saveCredential(LLPointer cred, bool save_authenticator) +{ + LLSD credential = LLSD::emptyMap(); + credential["identifier"] = cred->getIdentifier(); + if (save_authenticator) + { + credential["authenticator"] = cred->getAuthenticator(); + } + LL_INFOS("SECAPI") << "Saving Credential " << cred->getGrid() << ":" << cred->userID() << " " << save_authenticator << LL_ENDL; + setProtectedData("credential", cred->getGrid(), credential); + //*TODO: If we're saving Agni credentials, should we write the + // credentials to the legacy password.dat/etc? + _writeProtectedData(); +} + +// Remove a credential from the credential store. +void LLSecAPIBasicHandler::deleteCredential(LLPointer cred) +{ + LLSD undefVal; + deleteProtectedData("credential", cred->getGrid()); + cred->setCredentialData(undefVal, undefVal); + _writeProtectedData(); +} + +// load the legacy hash for agni, and decrypt it given the +// mac address +std::string LLSecAPIBasicHandler::_legacyLoadPassword() +{ + const S32 HASHED_LENGTH = 32; + std::vector buffer(HASHED_LENGTH); + llifstream password_file(mLegacyPasswordPath, llifstream::binary); + + if(password_file.fail()) + { + return std::string(""); + } + + password_file.read((char*)&buffer[0], buffer.size()); + if(password_file.gcount() != buffer.size()) + { + return std::string(""); + } + + // Decipher with MAC address + unsigned char MACAddress[MAC_ADDRESS_BYTES]; + LLUUID::getNodeID(MACAddress); + LLXORCipher cipher(MACAddress, 6); + cipher.decrypt(&buffer[0], buffer.size()); + + return std::string((const char*)&buffer[0], buffer.size()); +} + + +// return an identifier for the user +std::string LLSecAPIBasicCredential::userID() const +{ + if (!mIdentifier.isMap()) + { + return mGrid + "(null)"; + } + else if ((std::string)mIdentifier["type"] == "agent") + { + return (std::string)mIdentifier["first_name"] + "_" + (std::string)mIdentifier["last_name"]; + } + else if ((std::string)mIdentifier["type"] == "account") + { + return (std::string)mIdentifier["account_name"]; + } + + return "unknown"; + +} + +// return a printable user identifier +std::string LLSecAPIBasicCredential::asString() const +{ + if (!mIdentifier.isMap()) + { + return mGrid + ":(null)"; + } + else if ((std::string)mIdentifier["type"] == "agent") + { + return mGrid + ":" + (std::string)mIdentifier["first_name"] + " " + (std::string)mIdentifier["last_name"]; + } + else if ((std::string)mIdentifier["type"] == "account") + { + return mGrid + ":" + (std::string)mIdentifier["account_name"]; + } + + return mGrid + ":(unknown type)"; +} + + +bool valueCompareLLSD(const LLSD& lhs, const LLSD& rhs) +{ + if (lhs.type() != rhs.type()) + { + return FALSE; + } + if (lhs.isMap()) + { + // iterate through the map, verifying the right hand side has all of the + // values that the left hand side has. + for (LLSD::map_const_iterator litt = lhs.beginMap(); + litt != lhs.endMap(); + litt++) + { + if (!rhs.has(litt->first)) + { + return FALSE; + } + } + + // Now validate that the left hand side has everything the + // right hand side has, and that the values are equal. + for (LLSD::map_const_iterator ritt = rhs.beginMap(); + ritt != rhs.endMap(); + ritt++) + { + if (!lhs.has(ritt->first)) + { + return FALSE; + } + if (!valueCompareLLSD(lhs[ritt->first], ritt->second)) + { + return FALSE; + } + } + return TRUE; + } + else if (lhs.isArray()) + { + LLSD::array_const_iterator ritt = rhs.beginArray(); + // iterate through the array, comparing + for (LLSD::array_const_iterator litt = lhs.beginArray(); + litt != lhs.endArray(); + litt++) + { + if (!valueCompareLLSD(*ritt, *litt)) + { + return FALSE; + } + ritt++; + } + + return (ritt == rhs.endArray()); + } + else + { + // simple type, compare as string + return (lhs.asString() == rhs.asString()); + } + +} diff --git a/indra/newview/llsechandler_basic.h b/indra/newview/llsechandler_basic.h new file mode 100644 index 0000000000..4bbb73f062 --- /dev/null +++ b/indra/newview/llsechandler_basic.h @@ -0,0 +1,285 @@ +/** + * @file llsechandler_basic.h + * @brief Security API for services such as certificate handling + * secure local storage, etc. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LLSECHANDLER_BASIC +#define LLSECHANDLER_BASIC + +#include "llsecapi.h" +#include +#include + +// helpers +extern LLSD cert_name_from_X509_NAME(X509_NAME* name); +extern std::string cert_string_name_from_X509_NAME(X509_NAME* name); +extern std::string cert_string_from_asn1_integer(ASN1_INTEGER* value); +extern LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time); +extern std::string cert_get_digest(const std::string& digest_type, X509 *cert); + + +// class LLCertificate +// +class LLBasicCertificate : public LLCertificate +{ +public: + LOG_CLASS(LLBasicCertificate); + + LLBasicCertificate(const std::string& pem_cert); + LLBasicCertificate(X509* openSSLX509); + + virtual ~LLBasicCertificate(); + + virtual std::string getPem() const; + virtual std::vector getBinary() const; + virtual LLSD getLLSD() const; + + virtual X509* getOpenSSLX509() const; + + // set llsd elements for testing + void setLLSD(const std::string name, const LLSD& value) { mLLSDInfo[name] = value; } +protected: + + // certificates are stored as X509 objects, as validation and + // other functionality is via openssl + X509* mCert; + + LLSD& _initLLSD(); + LLSD mLLSDInfo; +}; + + +// class LLBasicCertificateVector +// Class representing a list of certificates +// This implementation uses a stl vector of certificates. +class LLBasicCertificateVector : virtual public LLCertificateVector +{ + +public: + LLBasicCertificateVector() {} + + virtual ~LLBasicCertificateVector() {} + + // Implementation of the basic iterator implementation. + // The implementation uses a vector iterator derived from + // the vector in the LLBasicCertificateVector class + class BasicIteratorImpl : public iterator_impl + { + public: + BasicIteratorImpl(std::vector >::iterator _iter) { mIter = _iter;} + virtual ~BasicIteratorImpl() {}; + // seek forward or back. Used by the operator++/operator-- implementations + virtual void seek(bool incr) + { + if(incr) + { + mIter++; + } + else + { + mIter--; + } + } + // create a copy of the iterator implementation class, used by the iterator copy constructor + virtual LLPointer clone() const + { + return new BasicIteratorImpl(mIter); + } + + virtual bool equals(const LLPointer& _iter) const + { + const BasicIteratorImpl *rhs_iter = dynamic_cast(_iter.get()); + return (mIter == rhs_iter->mIter); + } + virtual LLPointer get() + { + return *mIter; + } + protected: + friend class LLBasicCertificateVector; + std::vector >::iterator mIter; + }; + + // numeric index of the vector + virtual LLPointer operator[](int _index) { return mCerts[_index];} + + // Iteration + virtual iterator begin() { return iterator(new BasicIteratorImpl(mCerts.begin())); } + + virtual iterator end() { return iterator(new BasicIteratorImpl(mCerts.end())); } + + // find a cert given params + virtual iterator find(const LLSD& params); + + // return the number of certs in the store + virtual int size() const { return mCerts.size(); } + + // insert the cert to the store. if a copy of the cert already exists in the store, it is removed first + virtual void add(LLPointer cert) { insert(end(), cert); } + + // insert the cert to the store. if a copy of the cert already exists in the store, it is removed first + virtual void insert(iterator _iter, LLPointer cert); + + // remove a certificate from the store + virtual LLPointer erase(iterator _iter); + +protected: + std::vector >mCerts; +}; + +// class LLCertificateStore +// represents a store of certificates, typically a store of root CA +// certificates. The store can be persisted, and can be used to validate +// a cert chain +// +class LLBasicCertificateStore : virtual public LLBasicCertificateVector, public LLCertificateStore +{ +public: + LLBasicCertificateStore(const std::string& filename); + void load_from_file(const std::string& filename); + + virtual ~LLBasicCertificateStore(); + + // persist the store + virtual void save(); + + // return the store id + virtual std::string storeId() const; + +protected: + std::vector >mCerts; + std::string mFilename; +}; + +// class LLCertificateChain +// Class representing a chain of certificates in order, with the +// first element being the child cert. +class LLBasicCertificateChain : virtual public LLBasicCertificateVector, public LLCertificateChain +{ + +public: + LLBasicCertificateChain(const X509_STORE_CTX * store); + + virtual ~LLBasicCertificateChain() {} + + // validate a certificate chain against a certificate store, using the + // given validation policy. + virtual void validate(int validation_policy, + LLPointer ca_store, + const LLSD& validation_params); +}; + + + +// LLSecAPIBasicCredential class +class LLSecAPIBasicCredential : public LLCredential +{ +public: + LLSecAPIBasicCredential(const std::string& grid) : LLCredential(grid) {} + virtual ~LLSecAPIBasicCredential() {} + // return a value representing the user id, (could be guid, name, whatever) + virtual std::string userID() const; + + // printible string identifying the credential. + virtual std::string asString() const; +}; + +// LLSecAPIBasicHandler Class +// Interface handler class for the various security storage handlers. +class LLSecAPIBasicHandler : public LLSecAPIHandler +{ +public: + + LLSecAPIBasicHandler(const std::string& protected_data_filename, + const std::string& legacy_password_path); + LLSecAPIBasicHandler(); + + void init(); + + virtual ~LLSecAPIBasicHandler(); + + // instantiate a certificate from a pem string + virtual LLPointer getCertificate(const std::string& pem_cert); + + + // instiate a certificate from an openssl X509 structure + virtual LLPointer getCertificate(X509* openssl_cert); + + // instantiate a chain from an X509_STORE_CTX + virtual LLPointer getCertificateChain(const X509_STORE_CTX* chain); + + // instantiate a cert store given it's id. if a persisted version + // exists, it'll be loaded. If not, one will be created (but not + // persisted) + virtual LLPointer getCertificateStore(const std::string& store_id); + + // persist data in a protected store + virtual void setProtectedData(const std::string& data_type, + const std::string& data_id, + const LLSD& data); + + // retrieve protected data + virtual LLSD getProtectedData(const std::string& data_type, + const std::string& data_id); + + // delete a protected data item from the store + virtual void deleteProtectedData(const std::string& data_type, + const std::string& data_id); + + // credential management routines + + virtual LLPointer createCredential(const std::string& grid, + const LLSD& identifier, + const LLSD& authenticator); + + virtual LLPointer loadCredential(const std::string& grid); + + virtual void saveCredential(LLPointer cred, bool save_authenticator); + + virtual void deleteCredential(LLPointer cred); + +protected: + void _readProtectedData(); + void _writeProtectedData(); + std::string _legacyLoadPassword(); + + std::string mProtectedDataFilename; + LLSD mProtectedDataMap; + LLPointer mStore; + + std::string mLegacyPasswordPath; +}; + +bool valueCompareLLSD(const LLSD& lhs, const LLSD& rhs); + +#endif // LLSECHANDLER_BASIC + + + diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index d03a492cd1..3ef810c3e9 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -2435,7 +2435,7 @@ BOOL LLSelectMgr::selectGetCreator(LLUUID& result_id, std::string& name) if (identical) { - name = LLSLURL::buildCommand("agent", first_id, "inspect"); + name = LLSLURL("agent", first_id, "inspect").getSLURLString(); } else { @@ -2494,11 +2494,11 @@ BOOL LLSelectMgr::selectGetOwner(LLUUID& result_id, std::string& name) BOOL public_owner = (first_id.isNull() && !first_group_owned); if (first_group_owned) { - name = LLSLURL::buildCommand("group", first_id, "inspect"); + name = LLSLURL("group", first_id, "inspect").getSLURLString(); } else if(!public_owner) { - name = LLSLURL::buildCommand("agent", first_id, "inspect"); + name = LLSLURL("agent", first_id, "inspect").getSLURLString(); } else { @@ -2558,7 +2558,7 @@ BOOL LLSelectMgr::selectGetLastOwner(LLUUID& result_id, std::string& name) BOOL public_owner = (first_id.isNull()); if(!public_owner) { - name = LLSLURL::buildCommand("agent", first_id, "inspect"); + name = LLSLURL("agent", first_id, "inspect").getSLURLString(); } else { diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp index 5d20e280b5..ff7e479368 100644 --- a/indra/newview/llslurl.cpp +++ b/indra/newview/llslurl.cpp @@ -1,10 +1,11 @@ /** - * @file llslurl.cpp - * @brief SLURL manipulation + * @file llurlsimstring.cpp (was llsimurlstring.cpp) + * @brief Handles "SLURL fragments" like Ahern/123/45 for + * startup processing, login screen, prefs, etc. * - * $LicenseInfo:firstyear=2009&license=viewergpl$ + * $LicenseInfo:firstyear=2010&license=viewergpl$ * - * Copyright (c) 2009, Linden Research, Inc. + * Copyright (c) 2006-2010, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -12,13 +13,12 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -34,155 +34,443 @@ #include "llslurl.h" -#include "llweb.h" - -#include "llurlregistry.h" - -const std::string LLSLURL::PREFIX_SL_HELP = "secondlife://app."; -const std::string LLSLURL::PREFIX_SL = "sl://"; -const std::string LLSLURL::PREFIX_SECONDLIFE = "secondlife://"; -const std::string LLSLURL::PREFIX_SLURL_OLD = "http://slurl.com/secondlife/"; - -// For DnD - even though www.slurl.com redirects to slurl.com in a browser, you can copy and drag +#include "llpanellogin.h" +#include "llviewercontrol.h" +#include "llviewernetwork.h" +#include "llfiltersd2xmlrpc.h" +#include "curl/curl.h" +const char* LLSLURL::SLURL_HTTP_SCHEME = "http"; +const char* LLSLURL::SLURL_HTTPS_SCHEME = "https"; +const char* LLSLURL::SLURL_SECONDLIFE_SCHEME = "secondlife"; +const char* LLSLURL::SLURL_SECONDLIFE_PATH = "secondlife"; +const char* LLSLURL::SLURL_COM = "slurl.com"; +// For DnD - even though www.slurl.com redirects to slurl.com in a browser, you can copy and drag // text with www.slurl.com or a link explicitly pointing at www.slurl.com so testing for this // version is required also. -const std::string LLSLURL::PREFIX_SLURL_WWW = "http://www.slurl.com/secondlife/"; -const std::string LLSLURL::PREFIX_SLURL = "http://maps.secondlife.com/secondlife/"; +const char* LLSLURL::WWW_SLURL_COM = "www.slurl.com"; +const char* LLSLURL::MAPS_SECONDLIFE_COM = "maps.secondlife.com"; +const char* LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME = "x-grid-location-info"; +const char* LLSLURL::SLURL_APP_PATH = "app"; +const char* LLSLURL::SLURL_REGION_PATH = "region"; +const char* LLSLURL::SIM_LOCATION_HOME = "home"; +const char* LLSLURL::SIM_LOCATION_LAST = "last"; -const std::string LLSLURL::APP_TOKEN = "app/"; - -// static -std::string LLSLURL::stripProtocol(const std::string& url) +// resolve a simstring from a slurl +LLSLURL::LLSLURL(const std::string& slurl) { - std::string stripped = url; - if (matchPrefix(stripped, PREFIX_SL_HELP)) - { - stripped.erase(0, PREFIX_SL_HELP.length()); - } - else if (matchPrefix(stripped, PREFIX_SL)) - { - stripped.erase(0, PREFIX_SL.length()); - } - else if (matchPrefix(stripped, PREFIX_SECONDLIFE)) - { - stripped.erase(0, PREFIX_SECONDLIFE.length()); - } - else if (matchPrefix(stripped, PREFIX_SLURL)) + // by default we go to agni. + mType = INVALID; + LL_INFOS("AppInit") << "SLURL: " << slurl << LL_ENDL; + if(slurl == SIM_LOCATION_HOME) { - stripped.erase(0, PREFIX_SLURL.length()); + mType = HOME_LOCATION; } - else if (matchPrefix(stripped, PREFIX_SLURL_OLD)) + else if(slurl.empty() || (slurl == SIM_LOCATION_LAST)) { - stripped.erase(0, PREFIX_SLURL_OLD.length()); + + mType = LAST_LOCATION; } - else if (matchPrefix(stripped, PREFIX_SLURL_WWW)) + else { - stripped.erase(0, PREFIX_SLURL_WWW.length()); + LLURI slurl_uri; + // parse the slurl as a uri + if(slurl.find(':') == std::string::npos) + { + // There may be no scheme ('secondlife:' etc.) passed in. In that case + // we want to normalize the slurl by putting the appropriate scheme + // in front of the slurl. So, we grab the appropriate slurl base + // from the grid manager which may be http://slurl.com/secondlife/ for maingrid, or + // https:///region/ for Standalone grid (the word region, not the region name) + // these slurls are typically passed in from the 'starting location' box on the login panel, + // where the user can type in /// + std::string fixed_slurl = LLGridManager::getInstance()->getSLURLBase(); + // the slurl that was passed in might have a prepended /, or not. So, + // we strip off the prepended '/' so we don't end up with http://slurl.com/secondlife//// + // or some such. + + if(slurl[0] == '/') + { + fixed_slurl += slurl.substr(1); + } + else + { + fixed_slurl += slurl; + } + // We then load the slurl into a LLURI form + slurl_uri = LLURI(fixed_slurl); + } + else + { + // as we did have a scheme, implying a URI style slurl, we + // simply parse it as a URI + slurl_uri = LLURI(slurl); + } + + LLSD path_array = slurl_uri.pathArray(); + + // determine whether it's a maingrid URI or an Standalone/open style URI + // by looking at the scheme. If it's a 'secondlife:' slurl scheme or + // 'sl:' scheme, we know it's maingrid + + // At the end of this if/else block, we'll have determined the grid, + // and the slurl type (APP or LOCATION) + if(slurl_uri.scheme() == LLSLURL::SLURL_SECONDLIFE_SCHEME) + { + // parse a maingrid style slurl. We know the grid is maingrid + // so grab it. + // A location slurl for maingrid (with the special schemes) can be in the form + // secondlife:///// + // or + // secondlife:///secondlife//// + // where if grid is empty, it specifies Agni + + // An app style slurl for maingrid can be + // secondlife:///app/ + // where an empty grid implies Agni + + // we'll start by checking the top of the 'path' which will be + // either 'app', 'secondlife', or . + + // default to maingrid + + mGrid = MAINGRID; + + if ((path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) || + (path_array[0].asString() == LLSLURL::SLURL_APP_PATH)) + { + // it's in the form secondlife:///(app|secondlife) + // so parse the grid name to derive the grid ID + if (!slurl_uri.hostName().empty()) + { + mGrid = LLGridManager::getInstance()->getGridByLabel(slurl_uri.hostName()); + } + else if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) + { + // If the slurl is in the form secondlife:///secondlife/ form, + // then we are in fact on maingrid. + mGrid = MAINGRID; + } + else if(path_array[0].asString() == LLSLURL::SLURL_APP_PATH) + { + // for app style slurls, where no grid name is specified, assume the currently + // selected or logged in grid. + mGrid = LLGridManager::getInstance()->getGrid(); + } + + if(mGrid.empty()) + { + // we couldn't find the grid in the grid manager, so bail + return; + } + // set the type as appropriate. + if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) + { + mType = LOCATION; + } + else + { + mType = APP; + } + path_array.erase(0); + } + else + { + // it wasn't a /secondlife/ or /app/, so it must be secondlife:// + // therefore the hostname will be the region name, and it's a location type + mType = LOCATION; + // 'normalize' it so the region name is in fact the head of the path_array + path_array.insert(0, slurl_uri.hostName()); + } + } + else if((slurl_uri.scheme() == LLSLURL::SLURL_HTTP_SCHEME) || + (slurl_uri.scheme() == LLSLURL::SLURL_HTTPS_SCHEME) || + (slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME)) + { + // We're dealing with either a Standalone style slurl or slurl.com slurl + if ((slurl_uri.hostName() == LLSLURL::SLURL_COM) || + (slurl_uri.hostName() == LLSLURL::WWW_SLURL_COM) || + (slurl_uri.hostName() == LLSLURL::MAPS_SECONDLIFE_COM)) + { + // slurl.com implies maingrid + mGrid = MAINGRID; + } + else + { + // As it's a Standalone grid/open, we will always have a hostname, as Standalone/open style + // urls are properly formed, unlike the stinky maingrid style + mGrid = slurl_uri.hostName(); + } + if (path_array.size() == 0) + { + // um, we need a path... + return; + } + + // we need to normalize the urls so + // the path portion starts with the 'command' that we want to do + // it can either be region or app. + if ((path_array[0].asString() == LLSLURL::SLURL_REGION_PATH) || + (path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH)) + { + // strip off 'region' or 'secondlife' + path_array.erase(0); + // it's a location + mType = LOCATION; + } + else if (path_array[0].asString() == LLSLURL::SLURL_APP_PATH) + { + mType = APP; + path_array.erase(0); + // leave app appended. + } + else + { + // not a valid https/http/x-grid-location-info slurl, so it'll likely just be a URL + return; + } + } + else + { + // invalid scheme, so bail + return; + } + + + if(path_array.size() == 0) + { + // we gotta have some stuff after the specifier as to whether it's a region or command + return; + } + + // now that we know whether it's an app slurl or a location slurl, + // parse the slurl into the proper data structures. + if(mType == APP) + { + // grab the app command type and strip it (could be a command to jump somewhere, + // or whatever ) + mAppCmd = path_array[0].asString(); + path_array.erase(0); + + // Grab the parameters + mAppPath = path_array; + // and the query + mAppQuery = slurl_uri.query(); + mAppQueryMap = slurl_uri.queryMap(); + return; + } + else if(mType == LOCATION) + { + // at this point, head of the path array should be [ , , , ] where x, y and z + // are collectively optional + // are optional + mRegion = LLURI::unescape(path_array[0].asString()); + path_array.erase(0); + + // parse the x, y, z + if(path_array.size() >= 3) + { + + mPosition = LLVector3(path_array); + if((F32(mPosition[VX]) < 0.f) || + (mPosition[VX] > REGION_WIDTH_METERS) || + (F32(mPosition[VY]) < 0.f) || + (mPosition[VY] > REGION_WIDTH_METERS) || + (F32(mPosition[VZ]) < 0.f) || + (mPosition[VZ] > REGION_HEIGHT_METERS)) + { + mType = INVALID; + return; + } + + } + else + { + // if x, y and z were not fully passed in, go to the middle of the region. + // teleport will adjust the actual location to make sure you're on the ground + // and such + mPosition = LLVector3(REGION_WIDTH_METERS/2, REGION_WIDTH_METERS/2, 0); + } + } } - - return stripped; } -// static -bool LLSLURL::isSLURL(const std::string& url) + +// Create a slurl for the middle of the region +LLSLURL::LLSLURL(const std::string& grid, + const std::string& region) { - if (matchPrefix(url, PREFIX_SL_HELP)) return true; - if (matchPrefix(url, PREFIX_SL)) return true; - if (matchPrefix(url, PREFIX_SECONDLIFE)) return true; - if (matchPrefix(url, PREFIX_SLURL)) return true; - if (matchPrefix(url, PREFIX_SLURL_OLD)) return true; - if (matchPrefix(url, PREFIX_SLURL_WWW)) return true; - - return false; + mGrid = grid; + mRegion = region; + mType = LOCATION; + mPosition = LLVector3((F64)REGION_WIDTH_METERS/2, (F64)REGION_WIDTH_METERS/2, 0); } -bool LLSLURL::isValidSLURL(const std::string& url) + + +// create a slurl given the position. The position will be modded with the region +// width handling global positions as well +LLSLURL::LLSLURL(const std::string& grid, + const std::string& region, + const LLVector3& position) { - std::string temp_url(url); - //"www." may appear in DnD- see description of PREFIX_SLURL_WWW. - // If it is found, we remove it because it isn't expected in regexp. - if (matchPrefix(url, PREFIX_SLURL_WWW)) - { - size_t position = url.find("www."); - temp_url.erase(position,4); - } - - return LLUrlRegistry::getInstance()->isUrl(temp_url); + mGrid = grid; + mRegion = region; + S32 x = llround( (F32)fmod( position[VX], (F32)REGION_WIDTH_METERS ) ); + S32 y = llround( (F32)fmod( position[VY], (F32)REGION_WIDTH_METERS ) ); + S32 z = llround( (F32)position[VZ] ); + mType = LOCATION; + mPosition = LLVector3(x, y, z); } -// static -bool LLSLURL::isSLURLCommand(const std::string& url) -{ - if (matchPrefix(url, PREFIX_SL + APP_TOKEN) || - matchPrefix(url, PREFIX_SECONDLIFE + "/" + APP_TOKEN) || - matchPrefix(url, PREFIX_SLURL + APP_TOKEN) || - matchPrefix(url, PREFIX_SLURL_WWW + APP_TOKEN) || - matchPrefix(url, PREFIX_SLURL_OLD + APP_TOKEN) ) - { - return true; - } - return false; +// create a simstring +LLSLURL::LLSLURL(const std::string& region, + const LLVector3& position) +{ + *this = LLSLURL(LLGridManager::getInstance()->getGrid(), + region, position); } -// static -bool LLSLURL::isSLURLHelp(const std::string& url) +// create a slurl from a global position +LLSLURL::LLSLURL(const std::string& grid, + const std::string& region, + const LLVector3d& global_position) { - return matchPrefix(url, PREFIX_SL_HELP); + *this = LLSLURL(grid, + region, LLVector3(global_position.mdV[VX], + global_position.mdV[VY], + global_position.mdV[VZ])); } -// static -std::string LLSLURL::buildSLURL(const std::string& regionname, S32 x, S32 y, S32 z) +// create a slurl from a global position +LLSLURL::LLSLURL(const std::string& region, + const LLVector3d& global_position) { - std::string slurl = PREFIX_SLURL + regionname + llformat("/%d/%d/%d",x,y,z); - slurl = LLWeb::escapeURL( slurl ); - return slurl; + *this = LLSLURL(LLGridManager::getInstance()->getGrid(), + region, global_position); } -// static -std::string LLSLURL::buildCommand(const char* noun, const LLUUID& id, const char* verb) +LLSLURL::LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb) { - std::string slurl = llformat("secondlife:///app/%s/%s/%s", - noun, id.asString().c_str(), verb); - return slurl; + mType = APP; + mAppCmd = command; + mAppPath = LLSD::emptyArray(); + mAppPath.append(LLSD(id)); + mAppPath.append(LLSD(verb)); } -// static -std::string LLSLURL::buildUnescapedSLURL(const std::string& regionname, S32 x, S32 y, S32 z) + +std::string LLSLURL::getSLURLString() const { - std::string unescapedslurl = PREFIX_SLURL + regionname + llformat("/%d/%d/%d",x,y,z); - return unescapedslurl; + switch(mType) + { + case HOME_LOCATION: + return SIM_LOCATION_HOME; + case LAST_LOCATION: + return SIM_LOCATION_LAST; + case LOCATION: + { + // lookup the grid + S32 x = llround( (F32)mPosition[VX] ); + S32 y = llround( (F32)mPosition[VY] ); + S32 z = llround( (F32)mPosition[VZ] ); + return LLGridManager::getInstance()->getSLURLBase(mGrid) + + LLURI::escape(mRegion) + llformat("/%d/%d/%d",x,y,z); + } + case APP: + { + std::ostringstream app_url; + app_url << LLGridManager::getInstance()->getAppSLURLBase() << "/" << mAppCmd; + for(LLSD::array_const_iterator i = mAppPath.beginArray(); + i != mAppPath.endArray(); + i++) + { + app_url << "/" << i->asString(); + } + if(mAppQuery.length() > 0) + { + app_url << "?" << mAppQuery; + } + return app_url.str(); + } + default: + LL_WARNS("AppInit") << "Unexpected SLURL type for SLURL string" << (int)mType << LL_ENDL; + return std::string(); + } } -// static -std::string LLSLURL::buildSLURLfromPosGlobal(const std::string& regionname, - const LLVector3d& global_pos, - bool escaped /*= true*/) +std::string LLSLURL::getLoginString() const { - S32 x, y, z; - globalPosToXYZ(global_pos, x, y, z); - if(escaped) + + std::stringstream unescaped_start; + switch(mType) { - return buildSLURL(regionname, x, y, z); + case LOCATION: + unescaped_start << "uri:" + << mRegion << "&" + << llround(mPosition[0]) << "&" + << llround(mPosition[1]) << "&" + << llround(mPosition[2]); + break; + case HOME_LOCATION: + unescaped_start << "home"; + break; + case LAST_LOCATION: + unescaped_start << "last"; + break; + default: + LL_WARNS("AppInit") << "Unexpected SLURL type for login string" << (int)mType << LL_ENDL; + break; } - else + return xml_escape_string(unescaped_start.str()); +} + +bool LLSLURL::operator==(const LLSLURL& rhs) +{ + if(rhs.mType != mType) return false; + switch(mType) { - return buildUnescapedSLURL(regionname, x, y, z); + case LOCATION: + return ((mGrid == rhs.mGrid) && + (mRegion == rhs.mRegion) && + (mPosition == rhs.mPosition)); + case APP: + return getSLURLString() == rhs.getSLURLString(); + + case HOME_LOCATION: + case LAST_LOCATION: + return true; + default: + return false; } } -// static -bool LLSLURL::matchPrefix(const std::string& url, const std::string& prefix) +bool LLSLURL::operator !=(const LLSLURL& rhs) { - std::string test_prefix = url.substr(0, prefix.length()); - LLStringUtil::toLower(test_prefix); - return test_prefix == prefix; + return !(*this == rhs); } -void LLSLURL::globalPosToXYZ(const LLVector3d& pos, S32& x, S32& y, S32& z) +std::string LLSLURL::getLocationString() const +{ + return llformat("%s/%d/%d/%d", + mRegion.c_str(), + (int)llround(mPosition[0]), + (int)llround(mPosition[1]), + (int)llround(mPosition[2])); +} +std::string LLSLURL::asString() const { - x = llround((F32)fmod(pos.mdV[VX], (F64)REGION_WIDTH_METERS)); - y = llround((F32)fmod(pos.mdV[VY], (F64)REGION_WIDTH_METERS)); - z = llround((F32)pos.mdV[VZ]); + std::ostringstream result; + result << " mAppCmd:" << getAppCmd() << + " mAppPath:" + getAppPath().asString() << + " mAppQueryMap:" + getAppQueryMap().asString() << + " mAppQuery: " + getAppQuery() << + " mGrid: " + getGrid() << + " mRegion: " + getRegion() << + " mPosition: " << + " mType: " << mType << + " mPosition: " << mPosition; + return result.str(); } + diff --git a/indra/newview/llslurl.h b/indra/newview/llslurl.h index a79a8fc97c..28c23561cf 100644 --- a/indra/newview/llslurl.h +++ b/indra/newview/llslurl.h @@ -1,10 +1,11 @@ -/** +/** * @file llslurl.h - * @brief SLURL manipulation + * @brief Handles "SLURL fragments" like Ahern/123/45 for + * startup processing, login screen, prefs, etc. * - * $LicenseInfo:firstyear=2009&license=viewergpl$ + * $LicenseInfo:firstyear=2010&license=viewergpl$ * - * Copyright (c) 2009, Linden Research, Inc. + * Copyright (c) 2006-2010, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -12,13 +13,12 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -29,85 +29,84 @@ * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ +#ifndef LLSLURL_H +#define LLSLURL_H -#ifndef LL_SLURL_H -#define LL_SLURL_H +#include "llstring.h" -#include -// IAN BUG: where should this live? -// IAN BUG: are static utility functions right? See LLUUID. -// question of whether to have a LLSLURL object or a -// some of this was moved from LLURLDispatcher +// represents a location in a grid -/** - * SLURL manipulation - */ class LLSLURL { public: - static const std::string PREFIX_SL_HELP; - static const std::string PREFIX_SL; - static const std::string PREFIX_SECONDLIFE; - static const std::string PREFIX_SLURL; - static const std::string PREFIX_SLURL_OLD; - static const std::string PREFIX_SLURL_WWW; - - static const std::string APP_TOKEN; - - /** - * Is this any sort of secondlife:// or sl:// URL? - */ - static bool isSLURL(const std::string& url); - - /** - * Returns true if url is proven valid by regexp check from LLUrlRegistry - */ - static bool isValidSLURL(const std::string& url); - - /** - * Is this a special secondlife://app/ URL? - */ - static bool isSLURLCommand(const std::string& url); - - /** - * Not sure what it is. - */ - static bool isSLURLHelp(const std::string& url); - - /** - * builds: http://slurl.com/secondlife/Region%20Name/x/y/z/ escaping result url. - */ - static std::string buildSLURL(const std::string& regionname, S32 x, S32 y, S32 z); - - /// Build a SLURL like secondlife:///app/agent//inspect - static std::string buildCommand(const char* noun, const LLUUID& id, const char* verb); - - /** - * builds: http://slurl.com/secondlife/Region Name/x/y/z/ without escaping result url. - */ - static std::string buildUnescapedSLURL(const std::string& regionname, S32 x, S32 y, S32 z); - - /** - * builds SLURL from global position. Returns escaped or unescaped url. - * Returns escaped url by default. - */ - static std::string buildSLURLfromPosGlobal(const std::string& regionname, - const LLVector3d& global_pos, - bool escaped = true); - /** - * Strip protocol part from the URL. - */ - static std::string stripProtocol(const std::string& url); - - /** - * Convert global position to X, Y Z - */ - static void globalPosToXYZ(const LLVector3d& pos, S32& x, S32& y, S32& z); - -private: - static bool matchPrefix(const std::string& url, const std::string& prefix); - + static const char* SLURL_HTTPS_SCHEME; + static const char* SLURL_HTTP_SCHEME; + static const char* SLURL_SL_SCHEME; + static const char* SLURL_SECONDLIFE_SCHEME; + static const char* SLURL_SECONDLIFE_PATH; + static const char* SLURL_COM; + static const char* WWW_SLURL_COM; + static const char* MAPS_SECONDLIFE_COM; + static const char* SLURL_X_GRID_LOCATION_INFO_SCHEME; + static LLSLURL START_LOCATION; + static const char* SIM_LOCATION_HOME; + static const char* SIM_LOCATION_LAST; + static const char* SLURL_APP_PATH; + static const char* SLURL_REGION_PATH; + + enum SLURL_TYPE { + INVALID, + LOCATION, + HOME_LOCATION, + LAST_LOCATION, + APP, + HELP + }; + + + LLSLURL(): mType(LAST_LOCATION) { } + LLSLURL(const std::string& slurl); + LLSLURL(const std::string& grid, const std::string& region); + LLSLURL(const std::string& region, const LLVector3& position); + LLSLURL(const std::string& grid, const std::string& region, const LLVector3& position); + LLSLURL(const std::string& grid, const std::string& region, const LLVector3d& global_position); + LLSLURL(const std::string& region, const LLVector3d& global_position); + LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb); + + SLURL_TYPE getType() const { return mType; } + + std::string getSLURLString() const; + std::string getLoginString() const; + std::string getLocationString() const; + std::string getGrid() const { return mGrid; } + std::string getRegion() const { return mRegion; } + LLVector3 getPosition() const { return mPosition; } + std::string getAppCmd() const { return mAppCmd; } + std::string getAppQuery() const { return mAppQuery; } + LLSD getAppQueryMap() const { return mAppQueryMap; } + LLSD getAppPath() const { return mAppPath; } + + bool isValid() const { return mType != INVALID; } + bool isSpatial() const { return (mType == LAST_LOCATION) || (mType == HOME_LOCATION) || (mType == LOCATION); } + + bool operator==(const LLSLURL& rhs); + bool operator!=(const LLSLURL&rhs); + + std::string asString() const ; + +protected: + SLURL_TYPE mType; + + // used for Apps and Help + std::string mAppCmd; + LLSD mAppPath; + LLSD mAppQueryMap; + std::string mAppQuery; + + std::string mGrid; // reference to grid manager grid + std::string mRegion; + LLVector3 mPosition; }; -#endif +#endif // LLSLURL_H diff --git a/indra/newview/llspeakbutton.cpp b/indra/newview/llspeakbutton.cpp index c5c311ed33..d7de050636 100644 --- a/indra/newview/llspeakbutton.cpp +++ b/indra/newview/llspeakbutton.cpp @@ -59,9 +59,9 @@ LLSpeakButton::Params::Params() void LLSpeakButton::draw() { - // gVoiceClient is the authoritative global source of info regarding our open-mic state, we merely reflect that state. - bool openmic = gVoiceClient->getUserPTTState(); - bool voiceenabled = gVoiceClient->voiceEnabled(); + // LLVoiceClient::getInstance() is the authoritative global source of info regarding our open-mic state, we merely reflect that state. + bool openmic = LLVoiceClient::getInstance()->getUserPTTState(); + bool voiceenabled = LLVoiceClient::getInstance()->voiceEnabled(); mSpeakBtn->setToggleState(openmic && voiceenabled); mOutputMonitor->setIsMuted(!voiceenabled); LLUICtrl::draw(); @@ -176,11 +176,11 @@ void LLSpeakButton::setLabelVisible(bool visible) void LLSpeakButton::onMouseDown_SpeakBtn() { bool down = true; - gVoiceClient->inputUserControlState(down); // this method knows/care about whether this translates into a toggle-to-talk or down-to-talk + LLVoiceClient::getInstance()->inputUserControlState(down); // this method knows/care about whether this translates into a toggle-to-talk or down-to-talk } void LLSpeakButton::onMouseUp_SpeakBtn() { bool down = false; - gVoiceClient->inputUserControlState(down); + LLVoiceClient::getInstance()->inputUserControlState(down); } diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 4573520647..b9534fac9a 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -299,7 +299,7 @@ LLPointer LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::strin void LLSpeakerMgr::update(BOOL resort_ok) { - if (!gVoiceClient) + if (!LLVoiceClient::getInstance()) { return; } @@ -313,7 +313,7 @@ void LLSpeakerMgr::update(BOOL resort_ok) } // update status of all current speakers - BOOL voice_channel_active = (!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()); + BOOL voice_channel_active = (!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()); for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end();) { LLUUID speaker_id = speaker_it->first; @@ -321,21 +321,21 @@ void LLSpeakerMgr::update(BOOL resort_ok) speaker_map_t::iterator cur_speaker_it = speaker_it++; - if (voice_channel_active && gVoiceClient->getVoiceEnabled(speaker_id)) + if (voice_channel_active && LLVoiceClient::getInstance()->getVoiceEnabled(speaker_id)) { - speakerp->mSpeechVolume = gVoiceClient->getCurrentPower(speaker_id); - BOOL moderator_muted_voice = gVoiceClient->getIsModeratorMuted(speaker_id); + speakerp->mSpeechVolume = LLVoiceClient::getInstance()->getCurrentPower(speaker_id); + BOOL moderator_muted_voice = LLVoiceClient::getInstance()->getIsModeratorMuted(speaker_id); if (moderator_muted_voice != speakerp->mModeratorMutedVoice) { speakerp->mModeratorMutedVoice = moderator_muted_voice; speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp)); } - if (gVoiceClient->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice) + if (LLVoiceClient::getInstance()->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice) { speakerp->mStatus = LLSpeaker::STATUS_MUTED; } - else if (gVoiceClient->getIsSpeaking(speaker_id)) + else if (LLVoiceClient::getInstance()->getIsSpeaking(speaker_id)) { // reset inactivity expiration if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING) @@ -417,19 +417,21 @@ void LLSpeakerMgr::update(BOOL resort_ok) void LLSpeakerMgr::updateSpeakerList() { // are we bound to the currently active voice channel? - if ((!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive())) - { - LLVoiceClient::participantMap* participants = gVoiceClient->getParticipantList(); - if(participants) + if ((!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive())) + { + std::set participants; + LLVoiceClient::getInstance()->getParticipantList(participants); + // add new participants to our list of known speakers + for (std::set::iterator participant_it = participants.begin(); + participant_it != participants.end(); + ++participant_it) { - LLVoiceClient::participantMap::iterator participant_it; + setSpeaker(*participant_it, + LLVoiceClient::getInstance()->getDisplayName(*participant_it), + LLSpeaker::STATUS_VOICE_ACTIVE, + (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL)); + - // add new participants to our list of known speakers - for (participant_it = participants->begin(); participant_it != participants->end(); ++participant_it) - { - LLVoiceClient::participantState* participantp = participant_it->second; - setSpeaker(participantp->mAvatarID, participantp->mDisplayName, LLSpeaker::STATUS_VOICE_ACTIVE, (participantp->isAvatar()?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL)); - } } } } @@ -519,7 +521,7 @@ void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) BOOL LLSpeakerMgr::isVoiceActive() { // mVoiceChannel = NULL means current voice channel, whatever it is - return LLVoiceClient::voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive(); + return LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive(); } diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp index cc06179481..29237946d2 100644 --- a/indra/newview/llspeakingindicatormanager.cpp +++ b/indra/newview/llspeakingindicatormanager.cpp @@ -158,7 +158,7 @@ void SpeakingIndicatorManager::registerSpeakingIndicator(const LLUUID& speaker_i mSpeakingIndicators.insert(value_type); speaker_ids_t speakers_uuids; - BOOL is_in_same_voice = LLVoiceClient::getInstance()->findParticipantByID(speaker_id) != NULL; + BOOL is_in_same_voice = LLVoiceClient::getInstance()->isParticipant(speaker_id); speakers_uuids.insert(speaker_id); switchSpeakerIndicators(speakers_uuids, is_in_same_voice); @@ -210,7 +210,7 @@ void SpeakingIndicatorManager::onChange() LL_DEBUGS("SpeakingIndicator") << "Voice participant list was changed, updating indicators" << LL_ENDL; speaker_ids_t speakers_uuids; - LLVoiceClient::getInstance()->getParticipantsUUIDSet(speakers_uuids); + LLVoiceClient::getInstance()->getParticipantList(speakers_uuids); LL_DEBUGS("SpeakingIndicator") << "Switching all OFF, count: " << mSwitchedIndicatorsOn.size() << LL_ENDL; // switch all indicators off diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index b5a73a3143..4f1bcde302 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -147,7 +147,7 @@ #include "lltrans.h" #include "llui.h" #include "llurldispatcher.h" -#include "llurlsimstring.h" +#include "llslurl.h" #include "llurlhistory.h" #include "llurlwhitelist.h" #include "llvieweraudio.h" @@ -192,6 +192,7 @@ #include "llinventorybridge.h" #include "llappearancemgr.h" #include "llavatariconctrl.h" +#include "llvoicechannel.h" #include "lllogin.h" #include "llevents.h" @@ -228,11 +229,11 @@ static std::string sInitialOutfitGender; // "male" or "female" static bool gUseCircuitCallbackCalled = false; EStartupState LLStartUp::gStartupState = STATE_FIRST; +LLSLURL LLStartUp::sStartSLURL; -// *NOTE:Mani - to reconcile with giab changes... -static std::string gFirstname; -static std::string gLastname; -static std::string gPassword; +static LLPointer gUserCredential; +static std::string gDisplayName; +static BOOL gRememberPassword = TRUE; static U64 gFirstSimHandle = 0; static LLHost gFirstSim; @@ -249,7 +250,6 @@ boost::scoped_ptr LLStartUp::sListener(new LLStartupListener( void login_show(); void login_callback(S32 option, void* userdata); -bool is_hex_string(U8* str, S32 len); void show_first_run_dialog(); bool first_run_dialog_callback(const LLSD& notification, const LLSD& response); void set_startup_status(const F32 frac, const std::string& string, const std::string& msg); @@ -262,6 +262,9 @@ bool callback_choose_gender(const LLSD& notification, const LLSD& response); void init_start_screen(S32 location_id); void release_start_screen(); void reset_login(); +LLSD transform_cert_args(LLPointer cert); +void general_cert_done(const LLSD& notification, const LLSD& response); +void trust_cert_done(const LLSD& notification, const LLSD& response); void apply_udp_blacklist(const std::string& csv); bool process_login_success_response(); void transition_back_to_login_panel(const std::string& emsg); @@ -364,7 +367,7 @@ bool idle_startup() if ( STATE_FIRST == LLStartUp::getStartupState() ) { - gViewerWindow->showCursor(); + gViewerWindow->showCursor(); gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT); ///////////////////////////////////////////////// @@ -662,69 +665,25 @@ bool idle_startup() // // Log on to system // - if (!LLStartUp::sSLURLCommand.empty()) - { - // this might be a secondlife:///app/login URL - gLoginHandler.parseDirectLogin(LLStartUp::sSLURLCommand); - } - if (!gLoginHandler.getFirstName().empty() - || !gLoginHandler.getLastName().empty() - /*|| !gLoginHandler.getWebLoginKey().isNull()*/ ) - { - // We have at least some login information on a SLURL - gFirstname = gLoginHandler.getFirstName(); - gLastname = gLoginHandler.getLastName(); - LL_DEBUGS("LLStartup") << "STATE_FIRST: setting gFirstname, gLastname from gLoginHandler: '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; - - // Show the login screen if we don't have everything - show_connect_box = - gFirstname.empty() || gLastname.empty(); - } - else if(gSavedSettings.getLLSD("UserLoginInfo").size() == 3) - { - LLSD cmd_line_login = gSavedSettings.getLLSD("UserLoginInfo"); - gFirstname = cmd_line_login[0].asString(); - gLastname = cmd_line_login[1].asString(); - LL_DEBUGS("LLStartup") << "Setting gFirstname, gLastname from gSavedSettings(\"UserLoginInfo\"): '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; - - LLMD5 pass((unsigned char*)cmd_line_login[2].asString().c_str()); - char md5pass[33]; /* Flawfinder: ignore */ - pass.hex_digest(md5pass); - gPassword = md5pass; - -#ifdef USE_VIEWER_AUTH - show_connect_box = true; -#else - show_connect_box = false; -#endif - gSavedSettings.setBOOL("AutoLogin", TRUE); - } - else if (gSavedSettings.getBOOL("AutoLogin")) - { - gFirstname = gSavedSettings.getString("FirstName"); - gLastname = gSavedSettings.getString("LastName"); - LL_DEBUGS("LLStartup") << "AutoLogin: setting gFirstname, gLastname from gSavedSettings(\"First|LastName\"): '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; - gPassword = LLStartUp::loadPasswordFromDisk(); - gSavedSettings.setBOOL("RememberPassword", TRUE); - -#ifdef USE_VIEWER_AUTH - show_connect_box = true; -#else - show_connect_box = false; -#endif + if (gUserCredential.isNull()) + { + gUserCredential = gLoginHandler.initializeLoginInfo(); } - else + if (gUserCredential.isNull()) { - // if not automatically logging in, display login dialog - // a valid grid is selected - gFirstname = gSavedSettings.getString("FirstName"); - gLastname = gSavedSettings.getString("LastName"); - LL_DEBUGS("LLStartup") << "normal login: setting gFirstname, gLastname from gSavedSettings(\"First|LastName\"): '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; - gPassword = LLStartUp::loadPasswordFromDisk(); - show_connect_box = true; + show_connect_box = TRUE; + } + else if (gSavedSettings.getBOOL("AutoLogin")) + { + gRememberPassword = TRUE; + gSavedSettings.setBOOL("RememberPassword", TRUE); + show_connect_box = false; + } + else + { + gRememberPassword = gSavedSettings.getBOOL("RememberPassword"); + show_connect_box = TRUE; } - - // Go to the next startup state LLStartUp::setStartupState( STATE_BROWSER_INIT ); return FALSE; @@ -756,8 +715,10 @@ bool idle_startup() // Load all the name information out of the login view // NOTE: Hits "Attempted getFields with no login view shown" warning, since we don't // show the login view until login_show() is called below. - // LLPanelLogin::getFields(gFirstname, gLastname, gPassword); - + if (gUserCredential.isNull()) + { + gUserCredential = gLoginHandler.initializeLoginInfo(); + } if (gNoRender) { LL_ERRS("AppInit") << "Need to autologin or use command line with norender!" << LL_ENDL; @@ -768,8 +729,10 @@ bool idle_startup() // Show the login dialog login_show(); // connect dialog is already shown, so fill in the names - LLPanelLogin::setFields( gFirstname, gLastname, gPassword); - + if (gUserCredential.notNull()) + { + LLPanelLogin::setFields( gUserCredential, gRememberPassword); + } LLPanelLogin::giveFocus(); gSavedSettings.setBOOL("FirstRunThisInstall", FALSE); @@ -839,39 +802,36 @@ bool idle_startup() // DEV-42215: Make sure they're not empty -- gFirstname and gLastname // might already have been set from gSavedSettings, and it's too bad // to overwrite valid values with empty strings. - if (! gLoginHandler.getFirstName().empty() && ! gLoginHandler.getLastName().empty()) - { - gFirstname = gLoginHandler.getFirstName(); - gLastname = gLoginHandler.getLastName(); - LL_DEBUGS("LLStartup") << "STATE_LOGIN_CLEANUP: setting gFirstname, gLastname from gLoginHandler: '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; - } if (show_connect_box) { // TODO if not use viewer auth // Load all the name information out of the login view - LLPanelLogin::getFields(&gFirstname, &gLastname, &gPassword); + LLPanelLogin::getFields(gUserCredential, gRememberPassword); // end TODO // HACK: Try to make not jump on login gKeyboard->resetKeys(); } - if (!gFirstname.empty() && !gLastname.empty()) - { - gSavedSettings.setString("FirstName", gFirstname); - gSavedSettings.setString("LastName", gLastname); - - LL_INFOS("AppInit") << "Attempting login as: " << gFirstname << " " << gLastname << LL_ENDL; - gDebugInfo["LoginName"] = gFirstname + " " + gLastname; + // save the credentials + std::string userid = "unknown"; + if(gUserCredential.notNull()) + { + userid = gUserCredential->userID(); + gSecAPIHandler->saveCredential(gUserCredential, gRememberPassword); } - + gSavedSettings.setBOOL("RememberPassword", gRememberPassword); + LL_INFOS("AppInit") << "Attempting login as: " << userid << LL_ENDL; + gDebugInfo["LoginName"] = userid; + // create necessary directories // *FIX: these mkdir's should error check - gDirUtilp->setLindenUserDir(gFirstname, gLastname); + gDirUtilp->setLindenUserDir(userid); LLFile::mkdir(gDirUtilp->getLindenUserDir()); - + // Set PerAccountSettingsFile to the default value. + std::string per_account_settings_file = LLAppViewer::instance()->getSettingsFilename("Default", "PerAccount"); gSavedSettings.setString("PerAccountSettingsFile", gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, LLAppViewer::instance()->getSettingsFilename("Default", "PerAccount"))); @@ -901,9 +861,8 @@ bool idle_startup() { gDirUtilp->setChatLogsDir(gSavedPerAccountSettings.getString("InstantMessageLogPath")); } + gDirUtilp->setPerAccountChatLogsDir(userid); - gDirUtilp->setPerAccountChatLogsDir(gFirstname, gLastname); - LLFile::mkdir(gDirUtilp->getChatLogsDir()); LLFile::mkdir(gDirUtilp->getPerAccountChatLogsDir()); @@ -924,11 +883,7 @@ bool idle_startup() if (show_connect_box) { - std::string location; - LLPanelLogin::getLocation( location ); - LLURLSimString::setString( location ); - - // END TODO + LLSLURL slurl; LLPanelLogin::closePanel(); } @@ -952,26 +907,21 @@ bool idle_startup() // their last location, or some URL "-url //sim/x/y[/z]" // All accounts have both a home and a last location, and we don't support // more locations than that. Choose the appropriate one. JC - if (LLURLSimString::parse()) - { - // a startup URL was specified - agent_location_id = START_LOCATION_ID_URL; - - // doesn't really matter what location_which is, since - // gAgentStartLookAt will be overwritten when the - // UserLoginLocationReply arrives - location_which = START_LOCATION_ID_LAST; - } - else if (gSavedSettings.getString("LoginLocation") == "last" ) - { - agent_location_id = START_LOCATION_ID_LAST; // last location - location_which = START_LOCATION_ID_LAST; - } - else - { - agent_location_id = START_LOCATION_ID_HOME; // home - location_which = START_LOCATION_ID_HOME; - } + switch (LLStartUp::getStartSLURL().getType()) + { + case LLSLURL::LOCATION: + agent_location_id = START_LOCATION_ID_URL; + location_which = START_LOCATION_ID_LAST; + break; + case LLSLURL::LAST_LOCATION: + agent_location_id = START_LOCATION_ID_LAST; + location_which = START_LOCATION_ID_LAST; + break; + default: + agent_location_id = START_LOCATION_ID_HOME; + location_which = START_LOCATION_ID_HOME; + break; + } gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT); @@ -998,7 +948,7 @@ bool idle_startup() if(STATE_LOGIN_AUTH_INIT == LLStartUp::getStartupState()) { - gDebugInfo["GridName"] = LLViewerLogin::getInstance()->getGridLabel(); + gDebugInfo["GridName"] = LLGridManager::getInstance()->getGridLabel(); // Update progress status and the display loop. auth_desc = LLTrans::getString("LoginInProgress"); @@ -1022,11 +972,7 @@ bool idle_startup() // This call to LLLoginInstance::connect() starts the // authentication process. - LLSD credentials; - credentials["first"] = gFirstname; - credentials["last"] = gLastname; - credentials["passwd"] = gPassword; - login->connect(credentials); + login->connect(gUserCredential); LLStartUp::setStartupState( STATE_LOGIN_CURL_UNSTUCK ); return FALSE; @@ -1051,10 +997,11 @@ bool idle_startup() { LL_INFOS("LLStartup") << "Login failed, LLLoginInstance::getResponse(): " << LLLoginInstance::getInstance()->getResponse() << LL_ENDL; + LLSD response = LLLoginInstance::getInstance()->getResponse(); // Still have error conditions that may need some // sort of handling. - std::string reason_response = LLLoginInstance::getInstance()->getResponse("reason"); - std::string message_response = LLLoginInstance::getInstance()->getResponse("message"); + std::string reason_response = response["reason"]; + std::string message_response = response["message"]; if(!message_response.empty()) { @@ -1074,8 +1021,8 @@ bool idle_startup() if(reason_response == "key") { // Couldn't login because user/password is wrong - // Clear the password - gPassword = ""; + // Clear the credential + gUserCredential->clearAuthenticator(); } if(reason_response == "update" @@ -1088,18 +1035,65 @@ bool idle_startup() LLLoginInstance::getInstance()->disconnect(); LLAppViewer::instance()->forceQuit(); } - else + else { - // Don't pop up a notification in the TOS case because - // LLFloaterTOS::onCancel() already scolded the user. - if (reason_response != "tos") + if (reason_response != "tos") { - LLSD args; - args["ERROR_MESSAGE"] = emsg.str(); - LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; - LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); + // Don't pop up a notification in the TOS case because + // LLFloaterTOS::onCancel() already scolded the user. + std::string error_code; + if(response.has("errorcode")) + { + error_code = response["errorcode"].asString(); + } + if ((reason_response == "CURLError") && + (error_code == "SSL_CACERT" || error_code == "SSL_PEER_CERTIFICATE") && + response.has("certificate")) + { + // This was a certificate error, so grab the certificate + // and throw up the appropriate dialog. + LLPointer certificate = gSecAPIHandler->getCertificate(response["certificate"]); + if(certificate) + { + LLSD args = transform_cert_args(certificate); + + if(error_code == "SSL_CACERT") + { + // if we are handling an untrusted CA, throw up the dialog + // with the 'trust this CA' button. + LLNotificationsUtil::add("TrustCertificateError", args, response, + trust_cert_done); + + show_connect_box = true; + } + else + { + // the certificate exception returns a unique string for each type of exception. + // we grab this string via the LLUserAuth object, and use that to grab the localized + // string. + args["REASON"] = LLTrans::getString(message_response); + + LLNotificationsUtil::add("GeneralCertificateError", args, response, + general_cert_done); + + reset_login(); + gSavedSettings.setBOOL("AutoLogin", FALSE); + show_connect_box = true; + + } + + } + } + else + { + // This wasn't a certificate error, so throw up the normal + // notificatioin message. + LLSD args; + args["ERROR_MESSAGE"] = emsg.str(); + LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; + LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); + } } - //setup map of datetime strings to codes and slt & local time offset from utc // *TODO: Does this need to be here? LLStringOps::setupDatetimeInfo (false); @@ -1112,7 +1106,12 @@ bool idle_startup() if(process_login_success_response()) { // Pass the user information to the voice chat server interface. - gVoiceClient->userAuthorized(gFirstname, gLastname, gAgentID); + LLVoiceClient::getInstance()->userAuthorized(gUserCredential->userID(), gAgentID); + // create the default proximal channel + LLVoiceChannel::initClass(); + // update the voice settings + LLVoiceClient::getInstance()->updateSettings(); + LLGridManager::getInstance()->setFavorite(); LLStartUp::setStartupState( STATE_WORLD_INIT); } else @@ -1123,6 +1122,7 @@ bool idle_startup() LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); transition_back_to_login_panel(emsg.str()); show_connect_box = true; + return FALSE; } } return FALSE; @@ -1807,9 +1807,12 @@ bool idle_startup() // thus, do not show this alert. if (!gAgent.isFirstLogin()) { - bool url_ok = LLURLSimString::sInstance.parse(); - if ((url_ok && gAgentStartLocation == "url") || - (!url_ok && ((gAgentStartLocation == gSavedSettings.getString("LoginLocation"))))) + llinfos << "gAgentStartLocation : " << gAgentStartLocation << llendl; + LLSLURL start_slurl = LLStartUp::getStartSLURL(); + + if (((start_slurl.getType() == LLSLURL::LOCATION) && (gAgentStartLocation == "url")) || + ((start_slurl.getType() == LLSLURL::LAST_LOCATION) && (gAgentStartLocation == "last")) || + ((start_slurl.getType() == LLSLURL::HOME_LOCATION) && (gAgentStartLocation == "home"))) { // Start location is OK // Disabled code to restore camera location and focus if logging in to default location @@ -1831,17 +1834,23 @@ bool idle_startup() else { std::string msg; - if (url_ok) - { - msg = "AvatarMovedDesired"; - } - else if (gSavedSettings.getString("LoginLocation") == "home") - { - msg = "AvatarMovedHome"; - } - else + switch(start_slurl.getType()) { - msg = "AvatarMovedLast"; + case LLSLURL::LOCATION: + { + + msg = "AvatarMovedDesired"; + break; + } + case LLSLURL::HOME_LOCATION: + { + msg = "AvatarMovedHome"; + break; + } + default: + { + msg = "AvatarMovedLast"; + } } LLNotificationsUtil::add(msg); } @@ -2057,20 +2066,9 @@ void login_show() #endif LLPanelLogin::show( gViewerWindow->getWindowRectScaled(), - bUseDebugLogin, + bUseDebugLogin || gSavedSettings.getBOOL("SecondLifeEnterprise"), login_callback, NULL ); - // UI textures have been previously loaded in doPreloadImages() - - LL_DEBUGS("AppInit") << "Setting Servers" << LL_ENDL; - - LLPanelLogin::addServer(LLViewerLogin::getInstance()->getGridLabel(), LLViewerLogin::getInstance()->getGridChoice()); - - LLViewerLogin* vl = LLViewerLogin::getInstance(); - for(int grid_index = GRID_INFO_ADITI; grid_index < GRID_INFO_OTHER; ++grid_index) - { - LLPanelLogin::addServer(vl->getKnownGridLabel((EGridInfo)grid_index), grid_index); - } } // Callback for when login screen is closed. Option 0 = connect, option 1 = quit. @@ -2086,9 +2084,6 @@ void login_callback(S32 option, void *userdata) } else if (QUIT_OPTION == option) // *TODO: THIS CODE SEEMS TO BE UNREACHABLE!!!!! login_callback is never called with option equal to QUIT_OPTION { - // Make sure we don't save the password if the user is trying to clear it. - std::string first, last, password; - LLPanelLogin::getFields(&first, &last, &password); if (!gSavedSettings.getBOOL("RememberPassword")) { // turn off the setting and write out to disk @@ -2111,142 +2106,6 @@ void login_callback(S32 option, void *userdata) } } - -// static -std::string LLStartUp::loadPasswordFromDisk() -{ - // Only load password if we also intend to save it (otherwise the user - // wonders what we're doing behind his back). JC - BOOL remember_password = gSavedSettings.getBOOL("RememberPassword"); - if (!remember_password) - { - return std::string(""); - } - - std::string hashed_password(""); - - // Look for legacy "marker" password from settings.ini - hashed_password = gSavedSettings.getString("Marker"); - if (!hashed_password.empty()) - { - // Stomp the Marker entry. - gSavedSettings.setString("Marker", ""); - - // Return that password. - return hashed_password; - } - - std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, - "password.dat"); - LLFILE* fp = LLFile::fopen(filepath, "rb"); /* Flawfinder: ignore */ - if (!fp) - { - return hashed_password; - } - - // UUID is 16 bytes, written into ASCII is 32 characters - // without trailing \0 - const S32 HASHED_LENGTH = 32; - U8 buffer[HASHED_LENGTH+1]; - - if (1 != fread(buffer, HASHED_LENGTH, 1, fp)) - { - return hashed_password; - } - - fclose(fp); - - // Decipher with MAC address - LLXORCipher cipher(gMACAddress, 6); - cipher.decrypt(buffer, HASHED_LENGTH); - - buffer[HASHED_LENGTH] = '\0'; - - // Check to see if the mac address generated a bad hashed - // password. It should be a hex-string or else the mac adress has - // changed. This is a security feature to make sure that if you - // get someone's password.dat file, you cannot hack their account. - if(is_hex_string(buffer, HASHED_LENGTH)) - { - hashed_password.assign((char*)buffer); - } - - return hashed_password; -} - - -// static -void LLStartUp::savePasswordToDisk(const std::string& hashed_password) -{ - std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, - "password.dat"); - LLFILE* fp = LLFile::fopen(filepath, "wb"); /* Flawfinder: ignore */ - if (!fp) - { - return; - } - - // Encipher with MAC address - const S32 HASHED_LENGTH = 32; - U8 buffer[HASHED_LENGTH+1]; - - LLStringUtil::copy((char*)buffer, hashed_password.c_str(), HASHED_LENGTH+1); - - LLXORCipher cipher(gMACAddress, 6); - cipher.encrypt(buffer, HASHED_LENGTH); - - if (fwrite(buffer, HASHED_LENGTH, 1, fp) != 1) - { - LL_WARNS("AppInit") << "Short write" << LL_ENDL; - } - - fclose(fp); -} - - -// static -void LLStartUp::deletePasswordFromDisk() -{ - std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, - "password.dat"); - LLFile::remove(filepath); -} - - -bool is_hex_string(U8* str, S32 len) -{ - bool rv = true; - U8* c = str; - while(rv && len--) - { - switch(*c) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - ++c; - break; - default: - rv = false; - break; - } - } - return rv; -} - void show_first_run_dialog() { LLNotificationsUtil::add("FirstRun", LLSD(), LLSD(), first_run_dialog_callback); @@ -2288,7 +2147,7 @@ bool login_alert_status(const LLSD& notification, const LLSD& response) // break; case 2: // Teleport // Restart the login process, starting at our home locaton - LLURLSimString::setString("home"); + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); break; default: @@ -2508,30 +2367,35 @@ void asset_callback_nothing(LLVFS*, const LLUUID&, LLAssetType::EType, void*, S3 const std::string COMMON_GESTURES_FOLDER = "Common Gestures"; const std::string MALE_GESTURES_FOLDER = "Male Gestures"; const std::string FEMALE_GESTURES_FOLDER = "Female Gestures"; -const std::string MALE_OUTFIT_FOLDER = "Male Shape & Outfit"; -const std::string FEMALE_OUTFIT_FOLDER = "Female Shape & Outfit"; const S32 OPT_CLOSED_WINDOW = -1; const S32 OPT_MALE = 0; const S32 OPT_FEMALE = 1; - +const S32 OPT_TRUST_CERT = 0; +const S32 OPT_CANCEL_TRUST = 1; + bool callback_choose_gender(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); +{ + + // These defaults are returned from the server on login. They are set in login.xml. + // If no default is returned from the server, they are retrieved from settings.xml. + + S32 option = LLNotification::getSelectedOption(notification, response); switch(option) { - case OPT_MALE: - LLStartUp::loadInitialOutfit( MALE_OUTFIT_FOLDER, "male" ); - break; - - case OPT_FEMALE: - case OPT_CLOSED_WINDOW: - default: - LLStartUp::loadInitialOutfit( FEMALE_OUTFIT_FOLDER, "female" ); - break; + case OPT_MALE: + LLStartUp::loadInitialOutfit( gSavedSettings.getString("DefaultMaleAvatar"), "male" ); + break; + + case OPT_FEMALE: + case OPT_CLOSED_WINDOW: + default: + LLStartUp::loadInitialOutfit( gSavedSettings.getString("DefaultFemaleAvatar"), "female" ); + break; } return false; } + void LLStartUp::loadInitialOutfit( const std::string& outfit_folder_name, const std::string& gender_name ) { @@ -2746,7 +2610,6 @@ void reset_login() //--------------------------------------------------------------------------- -std::string LLStartUp::sSLURLCommand; bool LLStartUp::canGoFullscreen() { @@ -2779,41 +2642,145 @@ void LLStartUp::fontInit() bool LLStartUp::dispatchURL() { // ok, if we've gotten this far and have a startup URL - if (!sSLURLCommand.empty()) + if (!getStartSLURL().isValid()) { - LLMediaCtrl* web = NULL; - const bool trusted_browser = false; - LLURLDispatcher::dispatch(sSLURLCommand, web, trusted_browser); + return false; } - else if (LLURLSimString::parse()) - { + if(getStartSLURL().getType() != LLSLURL::APP) + { + // If we started with a location, but we're already // at that location, don't pop dialogs open. LLVector3 pos = gAgent.getPositionAgent(); - F32 dx = pos.mV[VX] - (F32)LLURLSimString::sInstance.mX; - F32 dy = pos.mV[VY] - (F32)LLURLSimString::sInstance.mY; + LLVector3 slurlpos = getStartSLURL().getPosition(); + F32 dx = pos.mV[VX] - slurlpos.mV[VX]; + F32 dy = pos.mV[VY] - slurlpos.mV[VY]; const F32 SLOP = 2.f; // meters - if( LLURLSimString::sInstance.mSimName != gAgent.getRegion()->getName() + if( getStartSLURL().getRegion() != gAgent.getRegion()->getName() || (dx*dx > SLOP*SLOP) || (dy*dy > SLOP*SLOP) ) { - std::string url = LLURLSimString::getURL(); - LLMediaCtrl* web = NULL; - const bool trusted_browser = false; - LLURLDispatcher::dispatch(url, web, trusted_browser); + LLURLDispatcher::dispatch(getStartSLURL().getSLURLString(), + NULL, false); } return true; } return false; } +void LLStartUp::setStartSLURL(const LLSLURL& slurl) +{ + sStartSLURL = slurl; + switch(slurl.getType()) + { + case LLSLURL::HOME_LOCATION: + { + gSavedSettings.setString("LoginLocation", LLSLURL::SIM_LOCATION_HOME); + break; + } + case LLSLURL::LAST_LOCATION: + { + gSavedSettings.setString("LoginLocation", LLSLURL::SIM_LOCATION_LAST); + break; + } + default: + LLGridManager::getInstance()->setGridChoice(slurl.getGrid()); + break; + } +} + bool login_alert_done(const LLSD& notification, const LLSD& response) { LLPanelLogin::giveFocus(); return false; } +// parse the certificate information into args for the +// certificate notifications +LLSD transform_cert_args(LLPointer cert) +{ + LLSD args = LLSD::emptyMap(); + std::string value; + LLSD cert_info = cert->getLLSD(); + // convert all of the elements in the cert into + // args for the xml dialog, so we have flexability to + // display various parts of the cert by only modifying + // the cert alert dialog xml. + for(LLSD::map_iterator iter = cert_info.beginMap(); + iter != cert_info.endMap(); + iter++) + { + // key usage and extended key usage + // are actually arrays, and we want to format them as comma separated + // strings, so special case those. + LLSDSerialize::toXML(cert_info[iter->first], std::cout); + if((iter->first== std::string(CERT_KEY_USAGE)) | + (iter->first == std::string(CERT_EXTENDED_KEY_USAGE))) + { + value = ""; + LLSD usage = cert_info[iter->first]; + for (LLSD::array_iterator usage_iter = usage.beginArray(); + usage_iter != usage.endArray(); + usage_iter++) + { + + if(usage_iter != usage.beginArray()) + { + value += ", "; + } + + value += (*usage_iter).asString(); + } + + } + else + { + value = iter->second.asString(); + } + + std::string name = iter->first; + std::transform(name.begin(), name.end(), name.begin(), + (int(*)(int))toupper); + args[name.c_str()] = value; + } + return args; +} + + +// when we handle a cert error, give focus back to the login panel +void general_cert_done(const LLSD& notification, const LLSD& response) +{ + LLStartUp::setStartupState( STATE_LOGIN_SHOW ); + LLPanelLogin::giveFocus(); +} + +// check to see if the user wants to trust the cert. +// if they do, add it to the cert store and +void trust_cert_done(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + switch(option) + { + case OPT_TRUST_CERT: + { + LLPointer cert = gSecAPIHandler->getCertificate(notification["payload"]["certificate"]); + LLPointer store = gSecAPIHandler->getCertificateStore(gSavedSettings.getString("CertStore")); + store->add(cert); + store->save(); + LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); + break; + } + case OPT_CANCEL_TRUST: + reset_login(); + gSavedSettings.setBOOL("AutoLogin", FALSE); + LLStartUp::setStartupState( STATE_LOGIN_SHOW ); + default: + LLPanelLogin::giveFocus(); + break; + } + +} void apply_udp_blacklist(const std::string& csv) { @@ -2861,33 +2828,45 @@ bool process_login_success_response() text = response["secure_session_id"].asString(); if(!text.empty()) gAgent.mSecureSessionID.set(text); - text = response["first_name"].asString(); - if(!text.empty()) - { - // Remove quotes from string. Login.cgi sends these to force - // names that look like numbers into strings. - gFirstname.assign(text); - LLStringUtil::replaceChar(gFirstname, '"', ' '); - LLStringUtil::trim(gFirstname); - } - text = response["last_name"].asString(); - if(!text.empty()) + // if the response contains a display name, use that, + // otherwise if the response contains a first and/or last name, + // use those. Otherwise use the credential identifier + + gDisplayName = ""; + if (response.has("display_name")) { - gLastname.assign(text); + gDisplayName.assign(response["display_name"].asString()); + if(!gDisplayName.empty()) + { + // Remove quotes from string. Login.cgi sends these to force + // names that look like numbers into strings. + LLStringUtil::replaceChar(gDisplayName, '"', ' '); + LLStringUtil::trim(gDisplayName); + } } - gSavedSettings.setString("FirstName", gFirstname); - gSavedSettings.setString("LastName", gLastname); - - if (gSavedSettings.getBOOL("RememberPassword")) + if(gDisplayName.empty()) { - // Successful login means the password is valid, so save it. - LLStartUp::savePasswordToDisk(gPassword); + if(response.has("first_name")) + { + gDisplayName.assign(response["first_name"].asString()); + LLStringUtil::replaceChar(gDisplayName, '"', ' '); + LLStringUtil::trim(gDisplayName); + } + if(response.has("last_name")) + { + text.assign(response["last_name"].asString()); + LLStringUtil::replaceChar(text, '"', ' '); + LLStringUtil::trim(text); + if(!gDisplayName.empty()) + { + gDisplayName += " "; + } + gDisplayName += text; + } } - else + if(gDisplayName.empty()) { - // Don't leave password from previous session sitting around - // during this login session. - LLStartUp::deletePasswordFromDisk(); + gDisplayName.assign(gUserCredential->asString()); } // this is their actual ability to access content @@ -2981,7 +2960,7 @@ bool process_login_success_response() // replace the default help URL format gSavedSettings.setString("HelpURLFormat",text); - // don't fall back to Nebraska's pre-connection static help + // don't fall back to Standalone's pre-connection static help gSavedSettings.setBOOL("HelpUseLocal", false); } @@ -3043,7 +3022,44 @@ bool process_login_success_response() //setup map of datetime strings to codes and slt & local time offset from utc LLStringOps::setupDatetimeInfo(pacific_daylight_time); } - + + static const char* CONFIG_OPTIONS[] = {"voice-config", "newuser-config"}; + for (int i = 0; i < sizeof(CONFIG_OPTIONS)/sizeof(CONFIG_OPTIONS[0]); i++) + { + LLSD options = response[CONFIG_OPTIONS[i]]; + if (!options.isArray() && (options.size() < 1) && !options[0].isMap()) + { + continue; + } + llinfos << "config option " << CONFIG_OPTIONS[i][0] << "response " << options << llendl; + for(LLSD::map_iterator option_it = options[0].beginMap(); + option_it != options[0].endMap(); + option_it++) + { + llinfos << "trying option " << option_it->first << llendl; + LLPointer control = gSavedSettings.getControl(option_it->first); + if(control.notNull()) + { + if(control->isType(TYPE_BOOLEAN)) + { + llinfos << "Setting BOOL from login " << option_it->first << " " << option_it->second << llendl; + + gSavedSettings.setBOOL(option_it->first, !((option_it->second == "F") || + (option_it->second == "false") || + (!option_it->second))); + } + else if (control->isType(TYPE_STRING)) + { + llinfos << "Setting String from login " << option_it->first << " " << option_it->second << llendl; + gSavedSettings.setString(option_it->first, option_it->second); + } + // we don't support other types now + + } + + } + } + LLSD initial_outfit = response["initial-outfit"][0]; if(initial_outfit.size()) { @@ -3097,7 +3113,7 @@ bool process_login_success_response() bool success = false; // JC: gesture loading done below, when we have an asset system - // in place. Don't delete/clear user_credentials until then. + // in place. Don't delete/clear gUserCredentials until then. if(gAgentID.notNull() && gAgentSessionID.notNull() && gMessageSystem->mOurCircuitCode diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index 92fe9521d3..16cc74504f 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -38,6 +38,7 @@ class LLViewerTexture ; class LLEventPump; class LLStartupListener; +class LLSLURL; // functions bool idle_startup(); @@ -101,26 +102,18 @@ public: static void loadInitialOutfit( const std::string& outfit_folder_name, const std::string& gender_name ); - // Load MD5 of user's password from local disk file. - static std::string loadPasswordFromDisk(); - - // Record MD5 of user's password for subsequent login. - static void savePasswordToDisk(const std::string& hashed_password); - - // Delete the saved password local disk file. - static void deletePasswordFromDisk(); static bool dispatchURL(); // if we have a SLURL or sim string ("Ahern/123/45") that started // the viewer, dispatch it - static std::string sSLURLCommand; - // *HACK: On startup, if we were passed a secondlife://app/do/foo - // command URL, store it for later processing. - static void postStartupState(); + static void setStartSLURL(const LLSLURL& slurl); + static LLSLURL& getStartSLURL() { return sStartSLURL; } private: + static LLSLURL sStartSLURL; + static std::string startupStateToString(EStartupState state); static EStartupState gStartupState; // Do not set directly, use LLStartup::setStartupState static boost::scoped_ptr sStateWatcher; diff --git a/indra/newview/llstylemap.cpp b/indra/newview/llstylemap.cpp index 61705c4eb3..8fab3bb361 100644 --- a/indra/newview/llstylemap.cpp +++ b/indra/newview/llstylemap.cpp @@ -51,7 +51,7 @@ const LLStyle::Params &LLStyleMap::lookupAgent(const LLUUID &source) style_params.color.control = "HTMLLinkColor"; style_params.readonly_color.control = "HTMLLinkColor"; style_params.link_href = - LLSLURL::buildCommand("agent", source, "inspect"); + LLSLURL("agent", source, "inspect").getSLURLString(); } else { diff --git a/indra/newview/llurl.cpp b/indra/newview/llurl.cpp index ab65ead4c5..83a5839a93 100644 --- a/indra/newview/llurl.cpp +++ b/indra/newview/llurl.cpp @@ -286,5 +286,11 @@ const char * LLURL::getFullPath() return(sReturnString); } +const char * LLURL::getAuthority() +{ + strncpy(LLURL::sReturnString,mAuthority, LL_MAX_PATH -1); /* Flawfinder: ignore */ + LLURL::sReturnString[LL_MAX_PATH -1] = '\0'; + return(sReturnString); +} char LLURL::sReturnString[LL_MAX_PATH] = ""; diff --git a/indra/newview/llurl.h b/indra/newview/llurl.h index 9a089dd835..e41b83d29f 100644 --- a/indra/newview/llurl.h +++ b/indra/newview/llurl.h @@ -79,6 +79,7 @@ public: virtual const char *getFQURL() const; virtual const char *getFullPath(); + virtual const char *getAuthority(); virtual const char *updateRelativePath(const LLURL &url); diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index b88069cd48..a31c3a0f1b 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2007&license=viewergpl$ * - * Copyright (c) 2007-2009, Linden Research, Inc. + * Copyright (c) 2010, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -12,13 +12,12 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -45,10 +44,10 @@ #include "llsidetray.h" #include "llslurl.h" #include "llstartup.h" // gStartupState -#include "llurlsimstring.h" #include "llweb.h" #include "llworldmapmessage.h" #include "llurldispatcherlistener.h" +#include "llviewernetwork.h" // library includes #include "llnotificationsutil.h" @@ -59,25 +58,25 @@ static LLURLDispatcherListener sURLDispatcherListener; class LLURLDispatcherImpl { public: - static bool dispatch(const std::string& url, + static bool dispatch(const LLSLURL& slurl, LLMediaCtrl* web, bool trusted_browser); // returns true if handled or explicitly blocked. - static bool dispatchRightClick(const std::string& url); + static bool dispatchRightClick(const LLSLURL& slurl); private: - static bool dispatchCore(const std::string& url, + static bool dispatchCore(const LLSLURL& slurl, bool right_mouse, LLMediaCtrl* web, bool trusted_browser); // handles both left and right click - static bool dispatchHelp(const std::string& url, bool right_mouse); + static bool dispatchHelp(const LLSLURL& slurl, bool right_mouse); // Handles sl://app.floater.html.help by showing Help floater. // Returns true if handled. - static bool dispatchApp(const std::string& url, + static bool dispatchApp(const LLSLURL& slurl, bool right_mouse, LLMediaCtrl* web, bool trusted_browser); @@ -85,16 +84,16 @@ private: // by showing panel in Search floater. // Returns true if handled or explicitly blocked. - static bool dispatchRegion(const std::string& url, bool right_mouse); + static bool dispatchRegion(const LLSLURL& slurl, bool right_mouse); // handles secondlife://Ahern/123/45/67/ // Returns true if handled. - static void regionHandleCallback(U64 handle, const std::string& url, + static void regionHandleCallback(U64 handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport); // Called by LLWorldMap when a location has been resolved to a // region name - static void regionNameCallback(U64 handle, const std::string& url, + static void regionNameCallback(U64 handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport); // Called by LLWorldMap when a region name has been resolved to a // location in-world, used by places-panel display. @@ -103,65 +102,57 @@ private: }; // static -bool LLURLDispatcherImpl::dispatchCore(const std::string& url, +bool LLURLDispatcherImpl::dispatchCore(const LLSLURL& slurl, bool right_mouse, LLMediaCtrl* web, bool trusted_browser) { - if (url.empty()) return false; - //if (dispatchHelp(url, right_mouse)) return true; - if (dispatchApp(url, right_mouse, web, trusted_browser)) return true; - if (dispatchRegion(url, right_mouse)) return true; + //if (dispatchHelp(slurl, right_mouse)) return true; + switch(slurl.getType()) + { + case LLSLURL::APP: + return dispatchApp(slurl, right_mouse, web, trusted_browser); + case LLSLURL::LOCATION: + return dispatchRegion(slurl, right_mouse); + default: + return false; + } /* // Inform the user we can't handle this std::map args; - args["SLURL"] = url; + args["SLURL"] = slurl; r; */ - - return false; } // static -bool LLURLDispatcherImpl::dispatch(const std::string& url, +bool LLURLDispatcherImpl::dispatch(const LLSLURL& slurl, LLMediaCtrl* web, bool trusted_browser) { - llinfos << "url: " << url << llendl; const bool right_click = false; - return dispatchCore(url, right_click, web, trusted_browser); + return dispatchCore(slurl, right_click, web, trusted_browser); } // static -bool LLURLDispatcherImpl::dispatchRightClick(const std::string& url) +bool LLURLDispatcherImpl::dispatchRightClick(const LLSLURL& slurl) { - llinfos << "url: " << url << llendl; const bool right_click = true; LLMediaCtrl* web = NULL; const bool trusted_browser = false; - return dispatchCore(url, right_click, web, trusted_browser); + return dispatchCore(slurl, right_click, web, trusted_browser); } // static -bool LLURLDispatcherImpl::dispatchApp(const std::string& url, +bool LLURLDispatcherImpl::dispatchApp(const LLSLURL& slurl, bool right_mouse, LLMediaCtrl* web, bool trusted_browser) { - // ensure the URL is in the secondlife:///app/ format - if (!LLSLURL::isSLURLCommand(url)) - { - return false; - } - - LLURI uri(url); - LLSD pathArray = uri.pathArray(); - pathArray.erase(0); // erase "app" - std::string cmd = pathArray.get(0); - pathArray.erase(0); // erase "cmd" + llinfos << "cmd: " << slurl.getAppCmd() << " path: " << slurl.getAppPath() << " query: " << slurl.getAppQuery() << llendl; bool handled = LLCommandDispatcher::dispatch( - cmd, pathArray, uri.queryMap(), web, trusted_browser); + slurl.getAppCmd(), slurl.getAppPath(), slurl.getAppQuery(), web, trusted_browser); // alert if we didn't handle this secondlife:///app/ SLURL // (but still return true because it is a valid app SLURL) @@ -173,81 +164,72 @@ bool LLURLDispatcherImpl::dispatchApp(const std::string& url, } // static -bool LLURLDispatcherImpl::dispatchRegion(const std::string& url, bool right_mouse) +bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, bool right_mouse) { - if (!LLSLURL::isSLURL(url)) - { - return false; - } - - std::string sim_string = LLSLURL::stripProtocol(url); - std::string region_name; - S32 x = 128; - S32 y = 128; - S32 z = 0; - if (! LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z)) - { - return false; - } - + if(slurl.getType() != LLSLURL::LOCATION) + { + return false; + } // Before we're logged in, need to update the startup screen // to tell the user where they are going. if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) { - // Parse it and stash in globals, it will be dispatched in - // STATE_CLEANUP. - LLURLSimString::setString(url); // We're at the login screen, so make sure user can see // the login location box to know where they are going. - LLPanelLogin::refreshLocation( true ); + LLPanelLogin::setLocation(slurl); return true; } // LLFloaterURLDisplay functionality moved to LLPanelPlaces in Side Tray. - //LLFloaterURLDisplay* url_displayp = LLFloaterReg::getTypedInstance("preview_url",LLSD()); - //if(url_displayp) url_displayp->setName(region_name); + //LLFloaterURLDisplay* slurl_displayp = LLFloaterReg::getTypedInstance("preview_url",LLSD()); + //if(slurl_displayp) slurl_displayp->setName(region_name); // Request a region handle by name - LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, - LLURLDispatcherImpl::regionNameCallback, - url, - false); // don't teleport + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(slurl.getRegion(), + LLURLDispatcherImpl::regionNameCallback, + slurl.getSLURLString(), + false); // don't teleport return true; } /*static*/ -void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport) +void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport) { - std::string sim_string = LLSLURL::stripProtocol(url); - std::string region_name; - S32 x = 128; - S32 y = 128; - S32 z = 0; - - if (LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z)) - { - regionHandleCallback(region_handle, url, snapshot_id, teleport); - } + + if(slurl.getType() == LLSLURL::LOCATION) + { + regionHandleCallback(region_handle, slurl, snapshot_id, teleport); + } } /* static */ -void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport) +void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport) { - std::string sim_string = LLSLURL::stripProtocol(url); - std::string region_name; - S32 x = 128; - S32 y = 128; - S32 z = 0; - LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z); - - LLVector3 local_pos; - local_pos.mV[VX] = (F32)x; - local_pos.mV[VY] = (F32)y; - local_pos.mV[VZ] = (F32)z; + // we can't teleport cross grid at this point + if((!LLGridManager::getInstance()->isSystemGrid(slurl.getGrid()) || !LLGridManager::getInstance()->isSystemGrid()) && + (slurl.getGrid() != LLGridManager::getInstance()->getGrid())) + { + LLSD args; + args["SLURL"] = slurl.getLocationString(); + args["CURRENT_GRID"] = LLGridManager::getInstance()->getGridLabel(); + LLSD grid_info = LLGridManager::getInstance()->getGridInfo(slurl.getGrid()); + + if(grid_info.has(GRID_LABEL_VALUE)) + { + args["GRID"] = grid_info[GRID_LABEL_VALUE].asString(); + } + else + { + args["GRID"] = slurl.getGrid(); + } + LLNotificationsUtil::add("CantTeleportToGrid", args); + return; + } + LLVector3d global_pos = from_region_handle(region_handle); - global_pos += LLVector3d(local_pos); + global_pos += LLVector3d(slurl.getPosition()); if (teleport) { @@ -271,8 +253,8 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::str // LLFloaterURLDisplay functionality moved to LLPanelPlaces in Side Tray. // // display informational floater, allow user to click teleport btn -// LLFloaterURLDisplay* url_displayp = LLFloaterReg::getTypedInstance("preview_url",LLSD()); -// if(url_displayp) +// LLFloaterURLDisplay* slurl_displayp = LLFloaterReg::getTypedInstance("preview_url",LLSD()); +// if(slurl_displayp) // { // url_displayp->displayParcelInfo(region_handle, local_pos); // if(snapshot_id.notNull()) @@ -287,7 +269,7 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::str //--------------------------------------------------------------------------- // Teleportation links are handled here because they are tightly coupled -// to URL parsing and sim-fragment parsing +// to SLURL parsing and sim-fragment parsing class LLTeleportHandler : public LLCommandHandler { public: @@ -303,18 +285,21 @@ public: // a global position, and teleport to it if (tokens.size() < 1) return false; - // Region names may be %20 escaped. - std::string region_name = LLURLSimString::unescapeRegionName(tokens[0]); - - // build secondlife://De%20Haro/123/45/67 for use in callback - std::string url = LLSLURL::PREFIX_SECONDLIFE; - for (int i = 0; i < tokens.size(); ++i) + LLVector3 coords(128, 128, 0); + if (tokens.size() <= 4) { - url += tokens[i].asString() + "/"; + coords = LLVector3(tokens[1].asReal(), + tokens[2].asReal(), + tokens[3].asReal()); } + + // Region names may be %20 escaped. + + std::string region_name = LLURI::unescape(tokens[0]); + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, LLURLDispatcherImpl::regionHandleCallback, - url, + LLSLURL(region_name, coords).getSLURLString(), true); // teleport return true; } @@ -324,21 +309,21 @@ LLTeleportHandler gTeleportHandler; //--------------------------------------------------------------------------- // static -bool LLURLDispatcher::dispatch(const std::string& url, +bool LLURLDispatcher::dispatch(const std::string& slurl, LLMediaCtrl* web, bool trusted_browser) { - return LLURLDispatcherImpl::dispatch(url, web, trusted_browser); + return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), web, trusted_browser); } // static -bool LLURLDispatcher::dispatchRightClick(const std::string& url) +bool LLURLDispatcher::dispatchRightClick(const std::string& slurl) { - return LLURLDispatcherImpl::dispatchRightClick(url); + return LLURLDispatcherImpl::dispatchRightClick(LLSLURL(slurl)); } // static -bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url) +bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl) { // *NOTE: Text editors are considered sources of trusted URLs // in order to make avatar profile links in chat history work. @@ -348,5 +333,7 @@ bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url) // *TODO: Make this trust model more refined. JC const bool trusted_browser = true; LLMediaCtrl* web = NULL; - return LLURLDispatcherImpl::dispatch(url, web, trusted_browser); + return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), web, trusted_browser); } + + diff --git a/indra/newview/llurldispatcher.h b/indra/newview/llurldispatcher.h index ff8a351253..407e417e58 100644 --- a/indra/newview/llurldispatcher.h +++ b/indra/newview/llurldispatcher.h @@ -2,9 +2,9 @@ * @file llurldispatcher.h * @brief Central registry for all SL URL handlers * - * $LicenseInfo:firstyear=2007&license=viewergpl$ + * $LicenseInfo:firstyear=2010&license=viewergpl$ * - * Copyright (c) 2007-2009, Linden Research, Inc. + * Copyright (c) 2007-2010, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -12,13 +12,12 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -31,16 +30,16 @@ */ #ifndef LLURLDISPATCHER_H #define LLURLDISPATCHER_H - class LLMediaCtrl; class LLURLDispatcher { public: - static bool dispatch(const std::string& url, + + static bool dispatch(const std::string& slurl, LLMediaCtrl* web, - bool trusted_browser); + bool trusted_browser); // At startup time and on clicks in internal web browsers, // teleport, open map, or run requested command. // @param url @@ -54,9 +53,9 @@ public: // that navigates to trusted (Linden Lab) pages. // Returns true if someone handled the URL. - static bool dispatchRightClick(const std::string& url); + static bool dispatchRightClick(const std::string& slurl); - static bool dispatchFromTextEditor(const std::string& url); + static bool dispatchFromTextEditor(const std::string& slurl); }; #endif diff --git a/indra/newview/llurllineeditorctrl.cpp b/indra/newview/llurllineeditorctrl.cpp index 1d2687a8c2..8488527185 100644 --- a/indra/newview/llurllineeditorctrl.cpp +++ b/indra/newview/llurllineeditorctrl.cpp @@ -89,7 +89,7 @@ void LLURLLineEditor::copyEscapedURLToClipboard() const std::string unescaped_text = wstring_to_utf8str(mText.getWString().substr(left_pos, length)); LLWString text_to_copy; - if (LLSLURL::isSLURL(unescaped_text)) + if (LLSLURL(unescaped_text).isValid()) text_to_copy = utf8str_to_wstring(LLWeb::escapeURL(unescaped_text)); else text_to_copy = utf8str_to_wstring(unescaped_text); diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index 2661c9f32b..ef6f4194e0 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -157,21 +157,21 @@ void audio_update_volume(bool force_update) LLViewerMedia::setVolume( media_muted ? 0.0f : media_volume ); // Voice - if (gVoiceClient) + if (LLVoiceClient::getInstance()) { F32 voice_volume = gSavedSettings.getF32("AudioLevelVoice"); voice_volume = mute_volume * master_volume * voice_volume; BOOL voice_mute = gSavedSettings.getBOOL("MuteVoice"); - gVoiceClient->setVoiceVolume(voice_mute ? 0.f : voice_volume); - gVoiceClient->setMicGain(voice_mute ? 0.f : gSavedSettings.getF32("AudioLevelMic")); + LLVoiceClient::getInstance()->setVoiceVolume(voice_mute ? 0.f : voice_volume); + LLVoiceClient::getInstance()->setMicGain(voice_mute ? 0.f : gSavedSettings.getF32("AudioLevelMic")); if (!gViewerWindow->getActive() && (gSavedSettings.getBOOL("MuteWhenMinimized"))) { - gVoiceClient->setMuteMic(true); + LLVoiceClient::getInstance()->setMuteMic(true); } else { - gVoiceClient->setMuteMic(false); + LLVoiceClient::getInstance()->setMuteMic(false); } } } diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index b2b7e653e4..8627f08891 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -413,9 +413,9 @@ bool handleHighResSnapshotChanged(const LLSD& newvalue) bool handleVoiceClientPrefsChanged(const LLSD& newvalue) { - if(gVoiceClient) + if(LLVoiceClient::getInstance()) { - gVoiceClient->updateSettings(); + LLVoiceClient::getInstance()->updateSettings(); } return true; } @@ -446,7 +446,7 @@ bool handleVelocityInterpolate(const LLSD& newvalue) bool handleForceShowGrid(const LLSD& newvalue) { - LLPanelLogin::refreshLocation( false ); + LLPanelLogin::updateServer( ); return true; } diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index b42d25c1d8..17221219eb 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -36,7 +36,6 @@ #include "llnotificationsutil.h" #include "llsdserialize.h" #include "message.h" -#include "indra_constants.h" #include "llagent.h" #include "llagentcamera.h" @@ -264,10 +263,14 @@ void LLViewerInventoryItem::fetchFromServer(void) const // we have to check region. It can be null after region was destroyed. See EXT-245 if (region) { - if( ALEXANDRIA_LINDEN_ID.getString() == mPermissions.getOwner().getString()) - url = region->getCapability("FetchLib"); - else - url = region->getCapability("FetchInventory"); + if(gAgent.getID() != mPermissions.getOwner()) + { + url = region->getCapability("FetchLib"); + } + else + { + url = region->getCapability("FetchInventory"); + } } else { diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 7d87f06794..a8b1257cf6 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -429,7 +429,7 @@ void init_menus() gPopupMenuView->setBackgroundColor( color ); // If we are not in production, use a different color to make it apparent. - if (LLViewerLogin::getInstance()->isInProductionGrid()) + if (LLGridManager::getInstance()->isInProductionGrid()) { color = LLUIColorTable::instance().getColor( "MenuBarBgColor" ); } @@ -445,7 +445,7 @@ void init_menus() menu_bar_holder->addChild(gMenuBarView); gViewerWindow->setMenuBackgroundColor(false, - LLViewerLogin::getInstance()->isInProductionGrid()); + LLGridManager::getInstance()->isInProductionGrid()); // Assume L$10 for now, the server will tell us the real cost at login // *TODO:Also fix cost in llfolderview.cpp for Inventory menus @@ -3467,7 +3467,7 @@ void set_god_level(U8 god_level) if(gViewerWindow) { gViewerWindow->setMenuBackgroundColor(god_level > GOD_NOT, - LLViewerLogin::getInstance()->isInProductionGrid()); + LLGridManager::getInstance()->isInProductionGrid()); } LLSD args; @@ -3507,7 +3507,7 @@ BOOL check_toggle_hacked_godmode(void*) bool enable_toggle_hacked_godmode(void*) { - return !LLViewerLogin::getInstance()->isInProductionGrid(); + return !LLGridManager::getInstance()->isInProductionGrid(); } #endif @@ -4378,7 +4378,7 @@ BOOL enable_take() return TRUE; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLViewerLogin::getInstance()->isInProductionGrid() + if (!LLGridManager::getInstance()->isInProductionGrid() && gAgent.isGodlike()) { return TRUE; @@ -4991,7 +4991,7 @@ bool enable_object_delete() TRUE; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - (!LLViewerLogin::getInstance()->isInProductionGrid() + (!LLGridManager::getInstance()->isInProductionGrid() && gAgent.isGodlike()) || # endif LLSelectMgr::getInstance()->canDoDelete(); @@ -6627,7 +6627,7 @@ bool enable_object_take_copy() all_valid = true; #ifndef HACKED_GODLIKE_VIEWER # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (LLViewerLogin::getInstance()->isInProductionGrid() + if (LLGridManager::getInstance()->isInProductionGrid() || !gAgent.isGodlike()) # endif { @@ -6689,7 +6689,7 @@ BOOL enable_save_into_inventory(void*) return TRUE; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLViewerLogin::getInstance()->isInProductionGrid() + if (!LLGridManager::getInstance()->isInProductionGrid() && gAgent.isGodlike()) { return TRUE; diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1426c0b9e2..7346b2a76e 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -1573,9 +1573,9 @@ void inventory_offer_handler(LLOfferInfo* info) payload["give_inventory_notification"] = FALSE; args["OBJECTFROMNAME"] = info->mFromName; args["NAME"] = info->mFromName; - args["NAME_SLURL"] = LLSLURL::buildCommand("agent", info->mFromID, "about"); + args["NAME_SLURL"] = LLSLURL("agent", info->mFromID, "about").getSLURLString(); std::string verb = "select?name=" + LLURI::escape(msg); - args["ITEM_SLURL"] = LLSLURL::buildCommand("inventory", info->mObjectID, verb.c_str()); + args["ITEM_SLURL"] = LLSLURL("inventory", info->mObjectID, verb.c_str()).getSLURLString(); LLNotification::Params p("ObjectGiveItem"); @@ -2244,10 +2244,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) query_string["groupowned"] = "true"; } - std::ostringstream link; - link << "secondlife:///app/objectim/" << session_id << LLURI::mapToQueryString(query_string); - - chat.mURL = link.str(); + chat.mURL = LLSLURL("objectim", session_id, "").getSLURLString(); chat.mText = message; chat.mSourceType = CHAT_SOURCE_OBJECT; @@ -2330,7 +2327,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) { LLSD args; // *TODO: Translate -> [FIRST] [LAST] (maybe) - args["NAME_SLURL"] = LLSLURL::buildCommand("agent", from_id, "about"); + args["NAME_SLURL"] = LLSLURL("agent", from_id, "about").getSLURLString(); args["MESSAGE"] = message; LLSD payload; payload["from_id"] = from_id; @@ -2396,7 +2393,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) } else { - args["NAME_SLURL"] = LLSLURL::buildCommand("agent", from_id, "about"); + args["NAME_SLURL"] = LLSLURL("agent", from_id, "about").getSLURLString(); if(message.empty()) { //support for frienship offers from clients before July 2008 @@ -3155,7 +3152,9 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) { // Chat the "back" SLURL. (DEV-4907) - LLSD substitution = LLSD().with("[T_SLURL]", gAgent.getTeleportSourceSLURL()); + LLSLURL slurl; + gAgent.getTeleportSourceSLURL(slurl); + LLSD substitution = LLSD().with("[T_SLURL]", slurl.getSLURLString()); std::string completed_from = LLAgent::sTeleportProgressMessages["completed_from"]; LLStringUtil::format(completed_from, substitution); @@ -5548,7 +5547,9 @@ void send_group_notice(const LLUUID& group_id, bool handle_lure_callback(const LLSD& notification, const LLSD& response) { std::string text = response["message"].asString(); - text.append("\r\n").append(LLAgentUI::buildSLURL()); + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + text.append("\r\n").append(slurl.getSLURLString()); S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if(0 == option) @@ -5991,7 +5992,7 @@ void process_covenant_reply(LLMessageSystem* msg, void**) LLFloaterBuyLand::updateEstateName(estate_name); std::string owner_name = - LLSLURL::buildCommand("agent", estate_owner_id, "inspect"); + LLSLURL("agent", estate_owner_id, "inspect").getSLURLString(); LLPanelEstateCovenant::updateEstateOwnerName(owner_name); LLPanelLandCovenant::updateEstateOwnerName(owner_name); LLFloaterBuyLand::updateEstateOwnerName(owner_name); diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index 987d23630a..d7bb4efe85 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -5,7 +5,7 @@ * * $LicenseInfo:firstyear=2006&license=viewergpl$ * - * Copyright (c) 2006-2009, Linden Research, Inc. + * Copyright (c) 2006-2010, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -13,13 +13,12 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -34,303 +33,478 @@ #include "llviewerprecompiledheaders.h" #include "llviewernetwork.h" +#include "llviewercontrol.h" +#include "llsdserialize.h" +#include "llweb.h" -#include "llevents.h" -#include "net.h" + +const char* DEFAULT_LOGIN_PAGE = "http://secondlife.com/app/login/"; -#include "llviewercontrol.h" -#include "lllogin.h" +const char* SYSTEM_GRID_SLURL_BASE = "secondlife://%s/secondlife/"; +const char* MAIN_GRID_SLURL_BASE = "http://maps.secondlife.com/secondlife/"; +const char* SYSTEM_GRID_APP_SLURL_BASE = "secondlife:///app"; -struct LLGridData -{ - const char* mLabel; - const char* mName; - const char* mLoginURI; - const char* mHelperURI; -}; +const char* DEFAULT_SLURL_BASE = "https://%s/region/"; +const char* DEFAULT_APP_SLURL_BASE = "x-grid-location-info://%s/app"; -static LLGridData gGridInfo[GRID_INFO_COUNT] = +LLGridManager::LLGridManager() { - { "None", "", "", ""}, - { "Aditi", - "util.aditi.lindenlab.com", - "https://login.aditi.lindenlab.com/cgi-bin/login.cgi", - "http://aditi-secondlife.webdev.lindenlab.com/helpers/" }, - { "Agni", - "util.agni.lindenlab.com", - "https://login.agni.lindenlab.com/cgi-bin/login.cgi", - "https://secondlife.com/helpers/" }, - { "Aruna", - "util.aruna.lindenlab.com", - "https://login.aruna.lindenlab.com/cgi-bin/login.cgi", - "http://aruna-secondlife.webdev.lindenlab.com/helpers/" }, - { "Bharati", - "util.bharati.lindenlab.com", - "https://login.bharati.lindenlab.com/cgi-bin/login.cgi", - "http://bharati-secondlife.webdev.lindenlab.com/helpers/" }, - { "Chandra", - "util.chandra.lindenlab.com", - "https://login.chandra.lindenlab.com/cgi-bin/login.cgi", - "http://chandra-secondlife.webdev.lindenlab.com/helpers/" }, - { "Damballah", - "util.damballah.lindenlab.com", - "https://login.damballah.lindenlab.com/cgi-bin/login.cgi", - "http://damballah-secondlife.webdev.lindenlab.com/helpers/" }, - { "Danu", - "util.danu.lindenlab.com", - "https://login.danu.lindenlab.com/cgi-bin/login.cgi", - "http://danu-secondlife.webdev.lindenlab.com/helpers/" }, - { "Durga", - "util.durga.lindenlab.com", - "https://login.durga.lindenlab.com/cgi-bin/login.cgi", - "http://durga-secondlife.webdev.lindenlab.com/helpers/" }, - { "Ganga", - "util.ganga.lindenlab.com", - "https://login.ganga.lindenlab.com/cgi-bin/login.cgi", - "http://ganga-secondlife.webdev.lindenlab.com/helpers/" }, - { "Mitra", - "util.mitra.lindenlab.com", - "https://login.mitra.lindenlab.com/cgi-bin/login.cgi", - "http://mitra-secondlife.webdev.lindenlab.com/helpers/" }, - { "Mohini", - "util.mohini.lindenlab.com", - "https://login.mohini.lindenlab.com/cgi-bin/login.cgi", - "http://mohini-secondlife.webdev.lindenlab.com/helpers/" }, - { "Nandi", - "util.nandi.lindenlab.com", - "https://login.nandi.lindenlab.com/cgi-bin/login.cgi", - "http://nandi-secondlife.webdev.lindenlab.com/helpers/" }, - { "Parvati", - "util.parvati.lindenlab.com", - "https://login.parvati.lindenlab.com/cgi-bin/login.cgi", - "http://parvati-secondlife.webdev.lindenlab.com/helpers/" }, - { "Radha", - "util.radha.lindenlab.com", - "https://login.radha.lindenlab.com/cgi-bin/login.cgi", - "http://radha-secondlife.webdev.lindenlab.com/helpers/" }, - { "Ravi", - "util.ravi.lindenlab.com", - "https://login.ravi.lindenlab.com/cgi-bin/login.cgi", - "http://ravi-secondlife.webdev.lindenlab.com/helpers/" }, - { "Siva", - "util.siva.lindenlab.com", - "https://login.siva.lindenlab.com/cgi-bin/login.cgi", - "http://siva-secondlife.webdev.lindenlab.com/helpers/" }, - { "Shakti", - "util.shakti.lindenlab.com", - "https://login.shakti.lindenlab.com/cgi-bin/login.cgi", - "http://shakti-secondlife.webdev.lindenlab.com/helpers/" }, - { "Skanda", - "util.skanda.lindenlab.com", - "https://login.skanda.lindenlab.com/cgi-bin/login.cgi", - "http://skanda-secondlife.webdev.lindenlab.com/helpers/" }, - { "Soma", - "util.soma.lindenlab.com", - "https://login.soma.lindenlab.com/cgi-bin/login.cgi", - "http://soma-secondlife.webdev.lindenlab.com/helpers/" }, - { "Uma", - "util.uma.lindenlab.com", - "https://login.uma.lindenlab.com/cgi-bin/login.cgi", - "http://uma-secondlife.webdev.lindenlab.com/helpers/" }, - { "Vaak", - "util.vaak.lindenlab.com", - "https://login.vaak.lindenlab.com/cgi-bin/login.cgi", - "http://vaak-secondlife.webdev.lindenlab.com/helpers/" }, - { "Yami", - "util.yami.lindenlab.com", - "https://login.yami.lindenlab.com/cgi-bin/login.cgi", - "http://yami-secondlife.webdev.lindenlab.com/helpers/" }, - { "Local", - "localhost", - "https://login.dmz.lindenlab.com/cgi-bin/login.cgi", - "" }, - { "Other", - "", - "https://login.dmz.lindenlab.com/cgi-bin/login.cgi", - "" } -}; - -const EGridInfo DEFAULT_GRID_CHOICE = GRID_INFO_AGNI; - - -unsigned char gMACAddress[MAC_ADDRESS_BYTES]; /* Flawfinder: ignore */ - -LLViewerLogin::LLViewerLogin() : - mGridChoice(DEFAULT_GRID_CHOICE) + // by default, we use the 'grids.xml' file in the user settings directory + // this file is an LLSD file containing multiple grid definitions. + // This file does not contain definitions for secondlife.com grids, + // as that would be a security issue when they are overwritten by + // an attacker. Don't want someone snagging a password. + std::string grid_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "grids.xml"); + initialize(grid_file); + +} + + +LLGridManager::LLGridManager(const std::string& grid_file) { + // initialize with an explicity grid file for testing. + initialize(grid_file); } - LLViewerLogin::~LLViewerLogin() - { - } +// +// LLGridManager - class for managing the list of known grids, and the current +// selection +// + -void LLViewerLogin::setGridChoice(EGridInfo grid) -{ - if(grid < 0 || grid >= GRID_INFO_COUNT) +// +// LLGridManager::initialze - initialize the list of known grids based +// on the fixed list of linden grids (fixed for security reasons) +// the grids.xml file +// and the command line. +void LLGridManager::initialize(const std::string& grid_file) +{ + // default grid list. + // Don't move to a modifiable file for security reasons, + mGrid.clear() ; + // set to undefined + mGridList = LLSD(); + mGridFile = grid_file; + // as we don't want an attacker to override our grid list + // to point the default grid to an invalid grid + addSystemGrid("None", "", "", "", DEFAULT_LOGIN_PAGE); + + + +#ifndef LL_RELEASE_FOR_DOWNLOAD + addSystemGrid("Agni", + MAINGRID, + "https://login.agni.lindenlab.com/cgi-bin/login.cgi", + "https://secondlife.com/helpers/", + DEFAULT_LOGIN_PAGE); +#else + addSystemGrid("Secondlife.com", + MAINGRID, + "https://login.agni.lindenlab.com/cgi-bin/login.cgi", + "https://secondlife.com/helpers/", + DEFAULT_LOGIN_PAGE, + "Agni"); +#endif // LL_RELEASE_FOR_DOWNLOAD + addSystemGrid("Aditi", + "util.aditi.lindenlab.com", + "https://login.aditi.lindenlab.com/cgi-bin/login.cgi", + "http://aditi-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Aruna", + "util.aruna.lindenlab.com", + "https://login.aruna.lindenlab.com/cgi-bin/login.cgi", + "http://aruna-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Durga", + "util.durga.lindenlab.com", + "https://login.durga.lindenlab.com/cgi-bin/login.cgi", + "http://durga-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Ganga", + "util.ganga.lindenlab.com", + "https://login.ganga.lindenlab.com/cgi-bin/login.cgi", + "http://ganga-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Mitra", + "util.mitra.lindenlab.com", + "https://login.mitra.lindenlab.com/cgi-bin/login.cgi", + "http://mitra-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Mohini", + "util.mohini.lindenlab.com", + "https://login.mohini.lindenlab.com/cgi-bin/login.cgi", + "http://mohini-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Nandi", + "util.nandi.lindenlab.com", + "https://login.nandi.lindenlab.com/cgi-bin/login.cgi", + "http://nandi-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Radha", + "util.radha.lindenlab.com", + "https://login.radha.lindenlab.com/cgi-bin/login.cgi", + "http://radha-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Ravi", + "util.ravi.lindenlab.com", + "https://login.ravi.lindenlab.com/cgi-bin/login.cgi", + "http://ravi-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Siva", + "util.siva.lindenlab.com", + "https://login.siva.lindenlab.com/cgi-bin/login.cgi", + "http://siva-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Shakti", + "util.shakti.lindenlab.com", + "https://login.shakti.lindenlab.com/cgi-bin/login.cgi", + "http://shakti-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Soma", + "util.soma.lindenlab.com", + "https://login.soma.lindenlab.com/cgi-bin/login.cgi", + "http://soma-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + + addSystemGrid("Uma", + "util.uma.lindenlab.com", + "https://login.uma.lindenlab.com/cgi-bin/login.cgi", + "http://uma-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Vaak", + "util.vaak.lindenlab.com", + "https://login.vaak.lindenlab.com/cgi-bin/login.cgi", + "http://vaak-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Yami", + "util.yami.lindenlab.com", + "https://login.yami.lindenlab.com/cgi-bin/login.cgi", + "http://yami-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Local (Linden)", + "localhost", + "https://login.dmz.lindenlab.com/cgi-bin/login.cgi", + "", + DEFAULT_LOGIN_PAGE); + + + LLSD other_grids; + llifstream llsd_xml; + if (!grid_file.empty()) { - llerrs << "Invalid grid index specified." << llendl; - return; + llsd_xml.open( grid_file.c_str(), std::ios::in | std::ios::binary ); + + // parse through the gridfile, inserting grids into the list unless + // they overwrite a linden grid. + if( llsd_xml.is_open()) + { + LLSDSerialize::fromXMLDocument( other_grids, llsd_xml ); + if(other_grids.isMap()) + { + for(LLSD::map_iterator grid_itr = other_grids.beginMap(); + grid_itr != other_grids.endMap(); + ++grid_itr) + { + LLSD::String key_name = grid_itr->first; + LLSD grid = grid_itr->second; + // TODO: Make sure gridfile specified label is not + // a system grid label + LL_INFOS("GridManager") << "reading: " << key_name << LL_ENDL; + if (mGridList.has(key_name) && + mGridList[key_name].has(GRID_IS_SYSTEM_GRID_VALUE)) + { + LL_INFOS("GridManager") << "Cannot override grid " << key_name << " as it's a system grid" << LL_ENDL; + // If the system grid does exist in the grids file, and it's marked as a favorite, set it as a favorite. + if(grid_itr->second.has(GRID_IS_FAVORITE_VALUE) && grid_itr->second[GRID_IS_FAVORITE_VALUE].asBoolean() ) + { + mGridList[key_name][GRID_IS_FAVORITE_VALUE] = TRUE; + } + } + else + { + try + { + addGrid(grid); + LL_INFOS("GridManager") << "Added grid: " << key_name << LL_ENDL; + } + catch (...) + { + } + } + } + llsd_xml.close(); + } + } } + + // load a grid from the command line. + // if the actual grid name is specified from the command line, + // set it as the 'selected' grid. + mGrid = gSavedSettings.getString("CmdLineGridChoice"); + LL_INFOS("GridManager") << "Grid Name: " << mGrid << LL_ENDL; + + // If a command line login URI was passed in, so we should add the command + // line grid to the list of grids - if(mGridChoice != grid || gSavedSettings.getS32("ServerChoice") != grid) + LLSD cmd_line_login_uri = gSavedSettings.getLLSD("CmdLineLoginURI"); + if (cmd_line_login_uri.isString()) { - mGridChoice = grid; - if(GRID_INFO_LOCAL == mGridChoice) + LL_INFOS("GridManager") << "adding cmd line login uri" << LL_ENDL; + // grab the other related URI values + std::string cmd_line_helper_uri = gSavedSettings.getString("CmdLineHelperURI"); + std::string cmd_line_login_page = gSavedSettings.getString("LoginPage"); + + // we've a cmd line login, so add a grid for the command line, + // overwriting any existing grids + LLSD grid = LLSD::emptyMap(); + grid[GRID_LOGIN_URI_VALUE] = LLSD::emptyArray(); + grid[GRID_LOGIN_URI_VALUE].append(cmd_line_login_uri); + LL_INFOS("GridManager") << "cmd line login uri: " << cmd_line_login_uri.asString() << LL_ENDL; + LLURI uri(cmd_line_login_uri.asString()); + if (mGrid.empty()) { - mGridName = LOOPBACK_ADDRESS_STRING; + // if a grid name was not passed in via the command line, + // then set the grid name based on the hostname of the + // login uri + mGrid = uri.hostName(); } - else if(GRID_INFO_OTHER == mGridChoice) + + grid[GRID_VALUE] = mGrid; + + if (mGridList.has(mGrid) && mGridList[mGrid].has(GRID_LABEL_VALUE)) { - // *FIX:Mani - could this possibly be valid? - mGridName = "other"; + grid[GRID_LABEL_VALUE] = mGridList[mGrid][GRID_LABEL_VALUE]; } else { - mGridName = gGridInfo[mGridChoice].mLabel; + grid[GRID_LABEL_VALUE] = mGrid; + } + if(!cmd_line_helper_uri.empty()) + { + grid[GRID_HELPER_URI_VALUE] = cmd_line_helper_uri; } - gSavedSettings.setS32("ServerChoice", mGridChoice); - gSavedSettings.setString("CustomServer", ""); + if(!cmd_line_login_page.empty()) + { + grid[GRID_LOGIN_PAGE_VALUE] = cmd_line_login_page; + } + // if the login page, helper URI value, and so on are not specified, + // add grid will generate them. + + // Also, we will override a system grid if values are passed in via the command + // line, for testing. These values will not be remembered though. + if (mGridList.has(mGrid) && mGridList[mGrid].has(GRID_IS_SYSTEM_GRID_VALUE)) + { + grid[GRID_IS_SYSTEM_GRID_VALUE] = TRUE; + } + addGrid(grid); } -} + + // if a grid was not passed in via the command line, grab it from the CurrentGrid setting. + if (mGrid.empty()) + { + + mGrid = gSavedSettings.getString("CurrentGrid"); + } + + if (mGrid.empty() || !mGridList.has(mGrid)) + { + // the grid name was empty, or the grid isn't actually in the list, then set it to the + // appropriate default. + LL_INFOS("GridManager") << "Resetting grid as grid name " << mGrid << " is not in the list" << LL_ENDL; +#if LL_RELEASE_FOR_DOWNLOAD + mGrid = MAINGRID; +#else + mGrid = ""; +#endif + } + LL_INFOS("GridManager") << "Selected grid is " << mGrid << LL_ENDL; + gSavedSettings.setString("CurrentGrid", mGrid); -void LLViewerLogin::setGridChoice(const std::string& grid_name) -{ - // Set the grid choice based on a string. - // The string can be: - // - a grid label from the gGridInfo table - // - an ip address - if(!grid_name.empty()) - { - // find the grid choice from the user setting. - int grid_index = GRID_INFO_NONE; - for(;grid_index < GRID_INFO_OTHER; ++grid_index) - { - if(0 == LLStringUtil::compareInsensitive(gGridInfo[grid_index].mLabel, grid_name)) - { - // Founding a matching label in the list... - setGridChoice((EGridInfo)grid_index); - break; - } - } - - if(GRID_INFO_OTHER == grid_index) - { - // *FIX:MEP Can and should we validate that this is an IP address? - mGridChoice = GRID_INFO_OTHER; - mGridName = grid_name; - gSavedSettings.setS32("ServerChoice", mGridChoice); - gSavedSettings.setString("CustomServer", mGridName); - } - } } -void LLViewerLogin::resetURIs() +LLGridManager::~LLGridManager() { - // Clear URIs when picking a new server - gSavedSettings.setLLSD("CmdLineLoginURI", LLSD::emptyArray()); - gSavedSettings.setString("CmdLineHelperURI", ""); + saveFavorites(); } -EGridInfo LLViewerLogin::getGridChoice() const +// +// LLGridManager::addGrid - add a grid to the grid list, populating the needed values +// if they're not populated yet. +// + +void LLGridManager::addGrid(LLSD& grid_data) { - return mGridChoice; + if (grid_data.isMap() && grid_data.has(GRID_VALUE)) + { + std::string grid = utf8str_tolower(grid_data[GRID_VALUE]); + + // grid should be in the form of a dns address + if (!grid.empty() && + grid.find_first_not_of("abcdefghijklmnopqrstuvwxyz1234567890-_. ") != std::string::npos) + { + printf("grid name: %s", grid.c_str()); + throw LLInvalidGridName(grid); + } + + // populate the other values if they don't exist + if (!grid_data.has(GRID_LABEL_VALUE)) + { + grid_data[GRID_LABEL_VALUE] = grid; + } + if (!grid_data.has(GRID_ID_VALUE)) + { + grid_data[GRID_ID_VALUE] = grid; + } + + // if the grid data doesn't include any of the URIs, then + // generate them from the grid, which should be a dns address + if (!grid_data.has(GRID_LOGIN_URI_VALUE)) + { + grid_data[GRID_LOGIN_URI_VALUE] = LLSD::emptyArray(); + grid_data[GRID_LOGIN_URI_VALUE].append(std::string("https://") + + grid + "/cgi-bin/login.cgi"); + } + // Populate to the default values + if (!grid_data.has(GRID_LOGIN_PAGE_VALUE)) + { + grid_data[GRID_LOGIN_PAGE_VALUE] = std::string("http://") + grid + "/app/login/"; + } + if (!grid_data.has(GRID_HELPER_URI_VALUE)) + { + grid_data[GRID_HELPER_URI_VALUE] = std::string("https://") + grid + "/helpers/"; + } + LL_INFOS("GridManager") << "ADDING: " << grid << LL_ENDL; + mGridList[grid] = grid_data; + } } -std::string LLViewerLogin::getGridLabel() const +// +// LLGridManager::addSystemGrid - helper for adding a system grid. +void LLGridManager::addSystemGrid(const std::string& label, + const std::string& name, + const std::string& login, + const std::string& helper, + const std::string& login_page, + const std::string& login_id) { - if(mGridChoice == GRID_INFO_NONE) + LLSD grid = LLSD::emptyMap(); + grid[GRID_VALUE] = name; + grid[GRID_LABEL_VALUE] = label; + grid[GRID_HELPER_URI_VALUE] = helper; + grid[GRID_LOGIN_URI_VALUE] = LLSD::emptyArray(); + grid[GRID_LOGIN_URI_VALUE].append(login); + grid[GRID_LOGIN_PAGE_VALUE] = login_page; + grid[GRID_IS_SYSTEM_GRID_VALUE] = TRUE; + grid[GRID_LOGIN_CREDENTIAL_PAGE_TYPE_VALUE] = GRID_LOGIN_CREDENTIAL_PAGE_TYPE_AGENT; + + grid[GRID_APP_SLURL_BASE] = SYSTEM_GRID_APP_SLURL_BASE; + if (login_id.empty()) { - return "None"; + grid[GRID_ID_VALUE] = name; } - else if(mGridChoice < GRID_INFO_OTHER) + else { - return gGridInfo[mGridChoice].mLabel; + grid[GRID_ID_VALUE] = login_id; } - - return mGridName; -} - -std::string LLViewerLogin::getKnownGridLabel(EGridInfo grid_index) const -{ - if(grid_index > GRID_INFO_NONE && grid_index < GRID_INFO_OTHER) + + // only add the system grids beyond agni to the visible list + // if we're building a debug version. + if (name == std::string(MAINGRID)) + { + grid[GRID_SLURL_BASE] = MAIN_GRID_SLURL_BASE; + grid[GRID_IS_FAVORITE_VALUE] = TRUE; + } + else { - return gGridInfo[grid_index].mLabel; + grid[GRID_SLURL_BASE] = llformat(SYSTEM_GRID_SLURL_BASE, label.c_str()); } - return gGridInfo[GRID_INFO_NONE].mLabel; + addGrid(grid); } -void LLViewerLogin::getLoginURIs(std::vector& uris) const +// return a list of grid name -> grid label mappings for UI purposes +std::map LLGridManager::getKnownGrids(bool favorite_only) { - // return the login uri set on the command line. - LLControlVariable* c = gSavedSettings.getControl("CmdLineLoginURI"); - if(c) + std::map result; + for(LLSD::map_iterator grid_iter = mGridList.beginMap(); + grid_iter != mGridList.endMap(); + grid_iter++) { - LLSD v = c->getValue(); - if(v.isArray()) + if(!favorite_only || grid_iter->second.has(GRID_IS_FAVORITE_VALUE)) { - for(LLSD::array_const_iterator itr = v.beginArray(); - itr != v.endArray(); ++itr) - { - std::string uri = itr->asString(); - if(!uri.empty()) - { - uris.push_back(uri); - } - } - } - else - { - std::string uri = v.asString(); - if(!uri.empty()) - { - uris.push_back(uri); - } + result[grid_iter->first] = grid_iter->second[GRID_LABEL_VALUE].asString(); } } - // If there was no command line uri... - if(uris.empty()) + return result; +} + +void LLGridManager::setGridChoice(const std::string& grid) +{ + // Set the grid choice based on a string. + // The string can be: + // - a grid label from the gGridInfo table + // - a hostname + // - an ip address + + // loop through. We could do just a hash lookup but we also want to match + // on label + for(LLSD::map_iterator grid_iter = mGridList.beginMap(); + grid_iter != mGridList.endMap(); + grid_iter++) { - // If its a known grid choice, get the uri from the table, - // else try the grid name. - if(mGridChoice > GRID_INFO_NONE && mGridChoice < GRID_INFO_OTHER) + if((grid == grid_iter->first) || + (grid == grid_iter->second[GRID_LABEL_VALUE].asString())) { - uris.push_back(gGridInfo[mGridChoice].mLoginURI); - } - else - { - uris.push_back(mGridName); + mGrid = grid_iter->second[GRID_VALUE].asString(); + gSavedSettings.setString("CurrentGrid", grid_iter->second[GRID_VALUE]); + return; + } } + LLSD grid_data = LLSD::emptyMap(); + grid_data[GRID_VALUE] = grid; + addGrid(grid_data); + mGrid = grid; + gSavedSettings.setString("CurrentGrid", grid); } -std::string LLViewerLogin::getHelperURI() const +std::string LLGridManager::getGridByLabel( const std::string &grid_label) { - std::string helper_uri = gSavedSettings.getString("CmdLineHelperURI"); - if (helper_uri.empty()) + for(LLSD::map_iterator grid_iter = mGridList.beginMap(); + grid_iter != mGridList.endMap(); + grid_iter++) { - // grab URI from selected grid - if(mGridChoice > GRID_INFO_NONE && mGridChoice < GRID_INFO_OTHER) + if (grid_iter->second.has(GRID_LABEL_VALUE) && (grid_iter->second[GRID_LABEL_VALUE].asString() == grid_label)) { - helper_uri = gGridInfo[mGridChoice].mHelperURI; + return grid_iter->first; } + } + return std::string(); +} - if (helper_uri.empty()) - { - // what do we do with unnamed/miscellaneous grids? - // for now, operations that rely on the helper URI (currency/land purchasing) will fail - } +void LLGridManager::getLoginURIs(std::vector& uris) +{ + uris.clear(); + for (LLSD::array_iterator llsd_uri = mGridList[mGrid][GRID_LOGIN_URI_VALUE].beginArray(); + llsd_uri != mGridList[mGrid][GRID_LOGIN_URI_VALUE].endArray(); + llsd_uri++) + { + uris.push_back(llsd_uri->asString()); } - return helper_uri; } -bool LLViewerLogin::isInProductionGrid() +bool LLGridManager::isInProductionGrid() { // *NOTE:Mani This used to compare GRID_INFO_AGNI to gGridChoice, // but it seems that loginURI trumps that. std::vector uris; getLoginURIs(uris); + if (uris.size() < 1) + { + return 1; + } LLStringUtil::toLower(uris[0]); if((uris[0].find("agni") != std::string::npos)) { @@ -339,3 +513,51 @@ bool LLViewerLogin::isInProductionGrid() return false; } + +void LLGridManager::saveFavorites() +{ + // filter out just those marked as favorites + LLSD output_grid_list = LLSD::emptyMap(); + for(LLSD::map_iterator grid_iter = mGridList.beginMap(); + grid_iter != mGridList.endMap(); + grid_iter++) + { + if(grid_iter->second.has(GRID_IS_FAVORITE_VALUE)) + { + output_grid_list[grid_iter->first] = grid_iter->second; + } + } + llofstream llsd_xml; + llsd_xml.open( mGridFile.c_str(), std::ios::out | std::ios::binary); + LLSDSerialize::toPrettyXML(output_grid_list, llsd_xml); + llsd_xml.close(); +} + + +// build a slurl for the given region within the selected grid +std::string LLGridManager::getSLURLBase(const std::string& grid) +{ + std::string grid_base; + if(mGridList.has(grid) && mGridList[grid].has(GRID_SLURL_BASE)) + { + return mGridList[grid][GRID_SLURL_BASE].asString(); + } + else + { + return llformat(DEFAULT_SLURL_BASE, grid.c_str()); + } +} + +// build a slurl for the given region within the selected grid +std::string LLGridManager::getAppSLURLBase(const std::string& grid) +{ + std::string grid_base; + if(mGridList.has(grid) && mGridList[grid].has(GRID_APP_SLURL_BASE)) + { + return mGridList[grid][GRID_APP_SLURL_BASE].asString(); + } + else + { + return llformat(DEFAULT_APP_SLURL_BASE, grid.c_str()); + } +} diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h index edae6dc47b..46f21bf20f 100644 --- a/indra/newview/llviewernetwork.h +++ b/indra/newview/llviewernetwork.h @@ -5,7 +5,7 @@ * * $LicenseInfo:firstyear=2006&license=viewergpl$ * - * Copyright (c) 2006-2009, Linden Research, Inc. + * Copyright (c) 2006-2010, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -13,13 +13,12 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -33,83 +32,136 @@ #ifndef LL_LLVIEWERNETWORK_H #define LL_LLVIEWERNETWORK_H + +extern const char* DEFAULT_LOGIN_PAGE; + +#define GRID_VALUE "name" +#define GRID_LABEL_VALUE "label" +#define GRID_ID_VALUE "grid_login_id" +#define GRID_LOGIN_URI_VALUE "login_uri" +#define GRID_HELPER_URI_VALUE "helper_uri" +#define GRID_LOGIN_PAGE_VALUE "login_page" +#define GRID_IS_SYSTEM_GRID_VALUE "system_grid" +#define GRID_IS_FAVORITE_VALUE "favorite" +#define GRID_LOGIN_CREDENTIAL_PAGE_TYPE_VALUE "credential_type" +#define GRID_LOGIN_CREDENTIAL_PAGE_TYPE_AGENT "agent" +#define GRID_LOGIN_CREDENTIAL_PAGE_TYPE_ACCOUNT "account" +#define MAINGRID "util.agni.lindenlab.com" -#include +// defines slurl formats associated with various grids. +// we need to continue to support existing forms, as slurls +// are shared between viewers that may not understand newer +// forms. +#define GRID_SLURL_BASE "slurl_base" +#define GRID_APP_SLURL_BASE "app_slurl_base" -class LLHost; -class LLLogin; - -enum EGridInfo +class LLInvalidGridName { - GRID_INFO_NONE, - GRID_INFO_ADITI, - GRID_INFO_AGNI, - GRID_INFO_ARUNA, - GRID_INFO_BHARATI, - GRID_INFO_CHANDRA, - GRID_INFO_DAMBALLAH, - GRID_INFO_DANU, - GRID_INFO_DURGA, - GRID_INFO_GANGA, - GRID_INFO_MITRA, - GRID_INFO_MOHINI, - GRID_INFO_NANDI, - GRID_INFO_PARVATI, - GRID_INFO_RADHA, - GRID_INFO_RAVI, - GRID_INFO_SIVA, - GRID_INFO_SHAKTI, - GRID_INFO_SKANDA, - GRID_INFO_SOMA, - GRID_INFO_UMA, - GRID_INFO_VAAK, - GRID_INFO_YAMI, - GRID_INFO_LOCAL, - GRID_INFO_OTHER, // IP address set via command line option - GRID_INFO_COUNT +public: + LLInvalidGridName(std::string grid) : mGrid(grid) + { + } +protected: + std::string mGrid; }; + /** - * @brief A class to manage the viewer's login state. + * @brief A class to manage the grids available to the viewer + * including persistance. This class also maintains the currently + * selected grid. * **/ -class LLViewerLogin : public LLSingleton +class LLGridManager : public LLSingleton { public: - LLViewerLogin(); - ~LLViewerLogin(); - - void setGridChoice(EGridInfo grid); - void setGridChoice(const std::string& grid_name); - void resetURIs(); + + // when the grid manager is instantiated, the default grids are automatically + // loaded, and the grids favorites list is loaded from the xml file. + LLGridManager(const std::string& grid_file); + LLGridManager(); + ~LLGridManager(); + + void initialize(const std::string& grid_file); + // grid list management + + // add a grid to the list of grids + void addGrid(LLSD& grid_info); - /** - * @brief Get the enumeration of the grid choice. - * Should only return values > 0 && < GRID_INFO_COUNT - **/ - EGridInfo getGridChoice() const; - - /** - * @brief Get a readable label for the grid choice. - * Returns the readable name for the grid choice. - * If the grid is 'other', returns something - * the string used to specifiy the grid. - **/ - std::string getGridLabel() const; - - std::string getKnownGridLabel(EGridInfo grid_index) const; - - void getLoginURIs(std::vector& uris) const; - std::string getHelperURI() const; + // retrieve a map of grid-name <-> label + // by default only return the user visible grids + std::map getKnownGrids(bool favorites_only=FALSE); + + LLSD getGridInfo(const std::string& grid) + { + if(mGridList.has(grid)) + { + return mGridList[grid]; + } + else + { + return LLSD(); + } + } + + // current grid management + // select a given grid as the current grid. If the grid + // is not a known grid, then it's assumed to be a dns name for the + // grid, and the various URIs will be automatically generated. + void setGridChoice(const std::string& grid); + + + std::string getGridLabel() { return mGridList[mGrid][GRID_LABEL_VALUE]; } + std::string getGrid() const { return mGrid; } + void getLoginURIs(std::vector& uris); + std::string getHelperURI() {return mGridList[mGrid][GRID_HELPER_URI_VALUE];} + std::string getLoginPage() {return mGridList[mGrid][GRID_LOGIN_PAGE_VALUE];} + std::string getGridLoginID() { return mGridList[mGrid][GRID_ID_VALUE]; } + std::string getLoginPage(const std::string& grid) { return mGridList[grid][GRID_LOGIN_PAGE_VALUE]; } + + // build a slurl for the given region within the selected grid + std::string getSLURLBase(const std::string& grid); + std::string getSLURLBase() { return getSLURLBase(mGrid); } + + std::string getAppSLURLBase(const std::string& grid); + std::string getAppSLURLBase() { return getAppSLURLBase(mGrid); } + + LLSD getGridInfo() { return mGridList[mGrid]; } + + std::string getGridByLabel( const std::string &grid_label); + + bool isSystemGrid(const std::string& grid) + { + return mGridList.has(grid) && + mGridList[grid].has(GRID_IS_SYSTEM_GRID_VALUE) && + mGridList[grid][GRID_IS_SYSTEM_GRID_VALUE].asBoolean(); + } + bool isSystemGrid() { return isSystemGrid(mGrid); } + // Mark this grid as a favorite that should be persisited on 'save' + // this is currently used to persist a grid after a successful login + void setFavorite() { mGridList[mGrid][GRID_IS_FAVORITE_VALUE] = TRUE; } + bool isInProductionGrid(); + void saveFavorites(); + void clearFavorites(); + +protected: -private: - EGridInfo mGridChoice; - std::string mGridName; + // helper function for adding the predefined grids + void addSystemGrid(const std::string& label, + const std::string& name, + const std::string& login, + const std::string& helper, + const std::string& login_page, + const std::string& login_id = ""); + + + std::string mGrid; + std::string mGridFile; + LLSD mGridList; }; const S32 MAC_ADDRESS_BYTES = 6; -extern unsigned char gMACAddress[MAC_ADDRESS_BYTES]; /* Flawfinder: ignore */ #endif diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 8860b734bb..e6d14079c9 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -4726,7 +4726,7 @@ BOOL LLViewerObject::permYouOwner() const return TRUE; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLViewerLogin::getInstance()->isInProductionGrid() + if (!LLGridManager::getInstance()->isInProductionGrid() && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) { return TRUE; @@ -4763,7 +4763,7 @@ BOOL LLViewerObject::permOwnerModify() const return TRUE; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLViewerLogin::getInstance()->isInProductionGrid() + if (!LLGridManager::getInstance()->isInProductionGrid() && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) { return TRUE; @@ -4787,7 +4787,7 @@ BOOL LLViewerObject::permModify() const return TRUE; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLViewerLogin::getInstance()->isInProductionGrid() + if (!LLGridManager::getInstance()->isInProductionGrid() && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) { return TRUE; @@ -4811,7 +4811,7 @@ BOOL LLViewerObject::permCopy() const return TRUE; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLViewerLogin::getInstance()->isInProductionGrid() + if (!LLGridManager::getInstance()->isInProductionGrid() && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) { return TRUE; @@ -4835,7 +4835,7 @@ BOOL LLViewerObject::permMove() const return TRUE; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLViewerLogin::getInstance()->isInProductionGrid() + if (!LLGridManager::getInstance()->isInProductionGrid() && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) { return TRUE; @@ -4859,7 +4859,7 @@ BOOL LLViewerObject::permTransfer() const return TRUE; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLViewerLogin::getInstance()->isInProductionGrid() + if (!LLGridManager::getInstance()->isInProductionGrid() && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) { return TRUE; diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index b7c265be59..bdc34d0f18 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -768,9 +768,11 @@ void send_stats() system["ram"] = (S32) gSysMemory.getPhysicalMemoryKB(); system["os"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); system["cpu"] = gSysCPU.getCPUString(); + unsigned char MACAddress[MAC_ADDRESS_BYTES]; + LLUUID::getNodeID(MACAddress); std::string macAddressString = llformat("%02x-%02x-%02x-%02x-%02x-%02x", - gMACAddress[0],gMACAddress[1],gMACAddress[2], - gMACAddress[3],gMACAddress[4],gMACAddress[5]); + MACAddress[0],MACAddress[1],MACAddress[2], + MACAddress[3],MACAddress[4],MACAddress[5]); system["mac_address"] = macAddressString; system["serial_number"] = LLAppViewer::instance()->getSerialNumber(); std::string gpu_desc = llformat( diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index ae3f680cbf..4c6a02db87 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -85,7 +85,6 @@ #include "lltooltip.h" #include "llmediaentry.h" #include "llurldispatcher.h" -#include "llurlsimstring.h" // newview includes #include "llagent.h" @@ -799,7 +798,7 @@ BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK m BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = TRUE; - gVoiceClient->middleMouseState(true); + LLVoiceClient::getInstance()->middleMouseState(true); handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); // Always handled as far as the OS is concerned. @@ -826,20 +825,15 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi if (slurl_dnd_enabled) { - - // special case SLURLs - // isValidSLURL() call was added here to make sure that dragged SLURL is valid (EXT-4964) - if ( LLSLURL::isSLURL( data ) && LLSLURL::isValidSLURL( data ) ) + LLSLURL dropped_slurl(data); + if(dropped_slurl.isSpatial()) { if (drop) { - LLURLDispatcher::dispatch( data, NULL, true ); - LLURLSimString::setStringRaw( LLSLURL::stripProtocol( data ) ); - LLPanelLogin::refreshLocation( true ); - LLPanelLogin::updateLocationUI(); + LLURLDispatcher::dispatch( dropped_slurl.getSLURLString(), NULL, true ); + return LLWindowCallbacks::DND_MOVE; } - return LLWindowCallbacks::DND_MOVE; - }; + } } if (prim_media_dnd_enabled) @@ -957,7 +951,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = FALSE; - gVoiceClient->middleMouseState(false); + LLVoiceClient::getInstance()->middleMouseState(false); handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); // Always handled as far as the OS is concerned. @@ -1074,7 +1068,7 @@ void LLViewerWindow::handleFocusLost(LLWindow *window) BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) { // Let the voice chat code check for its PTT key. Note that this never affects event processing. - gVoiceClient->keyDown(key, mask); + LLVoiceClient::getInstance()->keyDown(key, mask); if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME) { @@ -1096,7 +1090,7 @@ BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask) { // Let the voice chat code check for its PTT key. Note that this never affects event processing. - gVoiceClient->keyUp(key, mask); + LLVoiceClient::getInstance()->keyUp(key, mask); return FALSE; } @@ -1955,7 +1949,7 @@ void LLViewerWindow::setNormalControlsVisible( BOOL visible ) // ...and set the menu color appropriately. setMenuBackgroundColor(gAgent.getGodLevel() > GOD_NOT, - LLViewerLogin::getInstance()->isInProductionGrid()); + LLGridManager::getInstance()->isInProductionGrid()); } if ( gStatusBar ) @@ -1976,15 +1970,15 @@ void LLViewerWindow::setMenuBackgroundColor(bool god_mode, bool dev_grid) LLSD args; LLColor4 new_bg_color; - if(god_mode && LLViewerLogin::getInstance()->isInProductionGrid()) + if(god_mode && LLGridManager::getInstance()->isInProductionGrid()) { new_bg_color = LLUIColorTable::instance().getColor( "MenuBarGodBgColor" ); } - else if(god_mode && !LLViewerLogin::getInstance()->isInProductionGrid()) + else if(god_mode && !LLGridManager::getInstance()->isInProductionGrid()) { new_bg_color = LLUIColorTable::instance().getColor( "MenuNonProductionGodBgColor" ); } - else if(!god_mode && !LLViewerLogin::getInstance()->isInProductionGrid()) + else if(!god_mode && !LLGridManager::getInstance()->isInProductionGrid()) { new_bg_color = LLUIColorTable::instance().getColor( "MenuNonProductionBgColor" ); } @@ -2200,7 +2194,6 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) } return TRUE; } - // hidden edit menu for cut/copy/paste if (gEditMenu && gEditMenu->handleAcceleratorKey(key, mask)) { diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 0ce8894872..540cb47710 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1266,7 +1266,7 @@ void LLVOAvatar::initInstance(void) //VTPause(); // VTune - mVoiceVisualizer->setVoiceEnabled( gVoiceClient->getVoiceEnabled( mID ) ); + mVoiceVisualizer->setVoiceEnabled( LLVoiceClient::getInstance()->getVoiceEnabled( mID ) ); } const LLVector3 LLVOAvatar::getRenderPosition() const @@ -2197,8 +2197,8 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) } static LLUICachedControl visualizers_in_calls("ShowVoiceVisualizersInCalls", false); - bool voice_enabled = (visualizers_in_calls || gVoiceClient->inProximalChannel()) && - gVoiceClient->getVoiceEnabled(mID); + bool voice_enabled = (visualizers_in_calls || LLVoiceClient::getInstance()->inProximalChannel()) && + LLVoiceClient::getInstance()->getVoiceEnabled(mID); idleUpdateVoiceVisualizer( voice_enabled ); idleUpdateMisc( detailed_update ); @@ -2261,7 +2261,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) // Notice the calls to "gAwayTimer.reset()". This resets the timer that determines how long the avatar has been // "away", so that the avatar doesn't lapse into away-mode (and slump over) while the user is still talking. //----------------------------------------------------------------------------------------------------------------- - if (gVoiceClient->getIsSpeaking( mID )) + if (LLVoiceClient::getInstance()->getIsSpeaking( mID )) { if (!mVoiceVisualizer->getCurrentlySpeaking()) { @@ -2270,7 +2270,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) //printf( "gAwayTimer.reset();\n" ); } - mVoiceVisualizer->setSpeakingAmplitude( gVoiceClient->getCurrentPower( mID ) ); + mVoiceVisualizer->setSpeakingAmplitude( LLVoiceClient::getInstance()->getCurrentPower( mID ) ); if( isSelf() ) { @@ -2499,7 +2499,7 @@ F32 LLVOAvatar::calcMorphAmount() void LLVOAvatar::idleUpdateLipSync(bool voice_enabled) { // Use the Lipsync_Ooh and Lipsync_Aah morphs for lip sync - if ( voice_enabled && (gVoiceClient->lipSyncEnabled()) && gVoiceClient->getIsSpeaking( mID ) ) + if ( voice_enabled && (LLVoiceClient::getInstance()->lipSyncEnabled()) && LLVoiceClient::getInstance()->getIsSpeaking( mID ) ) { F32 ooh_morph_amount = 0.0f; F32 aah_morph_amount = 0.0f; diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index fac7fa6a18..338bc12f04 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -72,9 +72,9 @@ private: void LLVoiceCallCapResponder::error(U32 status, const std::string& reason) { - llwarns << "LLVoiceCallCapResponder::error(" + LL_WARNS("Voice") << "LLVoiceCallCapResponder::error(" << status << ": " << reason << ")" - << llendl; + << LL_ENDL; LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); if ( channelp ) { @@ -104,8 +104,8 @@ void LLVoiceCallCapResponder::result(const LLSD& content) LLSD::map_const_iterator iter; for(iter = content.beginMap(); iter != content.endMap(); ++iter) { - llinfos << "LLVoiceCallCapResponder::result got " - << iter->first << llendl; + LL_DEBUGS("Voice") << "LLVoiceCallCapResponder::result got " + << iter->first << LL_ENDL; } channelp->setChannelInfo( @@ -131,10 +131,8 @@ LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const std::string& sess { // a voice channel already exists for this session id, so this instance will be orphaned // the end result should simply be the failure to make voice calls - llwarns << "Duplicate voice channels registered for session_id " << session_id << llendl; + LL_WARNS("Voice") << "Duplicate voice channels registered for session_id " << session_id << LL_ENDL; } - - LLVoiceClient::getInstance()->addObserver(this); } LLVoiceChannel::~LLVoiceChannel() @@ -145,7 +143,7 @@ LLVoiceChannel::~LLVoiceChannel() // later in other destructors anyway). EXT-5524 if(LLVoiceClient::instanceExists()) { - gVoiceClient->removeObserver(this); + LLVoiceClient::getInstance()->removeObserver(this); } sVoiceChannelMap.erase(mSessionID); @@ -165,13 +163,13 @@ void LLVoiceChannel::setChannelInfo( if (mURI.empty()) { LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs); - llwarns << "Received empty URI for channel " << mSessionName << llendl; + LL_WARNS("Voice") << "Received empty URI for channel " << mSessionName << LL_ENDL; deactivate(); } else if (mCredentials.empty()) { LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs); - llwarns << "Received empty credentials for channel " << mSessionName << llendl; + LL_WARNS("Voice") << "Received empty credentials for channel " << mSessionName << LL_ENDL; deactivate(); } else @@ -286,13 +284,14 @@ void LLVoiceChannel::deactivate() //Default mic is OFF when leaving voice calls if (gSavedSettings.getBOOL("AutoDisengageMic") && sCurrentVoiceChannel == this && - gVoiceClient->getUserPTTState()) + LLVoiceClient::getInstance()->getUserPTTState()) { gSavedSettings.setBOOL("PTTCurrentlyEnabled", true); - gVoiceClient->inputUserControlState(true); + LLVoiceClient::getInstance()->inputUserControlState(true); } } - + LLVoiceClient::getInstance()->removeObserver(this); + if (sCurrentVoiceChannel == this) { // default channel is proximal channel @@ -332,7 +331,9 @@ void LLVoiceChannel::activate() { setState(STATE_CALL_STARTED); } - + + LLVoiceClient::getInstance()->addObserver(this); + //do not send earlier, channel should be initialized, should not be in STATE_NO_CHANNEL_INFO state sCurrentVoiceChannelChangedSignal(this->mSessionID); } @@ -374,6 +375,11 @@ LLVoiceChannel* LLVoiceChannel::getChannelByURI(std::string uri) } } +LLVoiceChannel* LLVoiceChannel::getCurrentVoiceChannel() +{ + return sCurrentVoiceChannel; +} + void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id) { sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID)); @@ -425,7 +431,6 @@ void LLVoiceChannel::initClass() sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); } - //static void LLVoiceChannel::suspend() { @@ -441,7 +446,7 @@ void LLVoiceChannel::resume() { if (sSuspended) { - if (gVoiceClient->voiceEnabled()) + if (LLVoiceClient::getInstance()->voiceEnabled()) { if (sSuspendedVoiceChannel) { @@ -511,9 +516,9 @@ void LLVoiceChannelGroup::activate() #endif //Mic default state is OFF on initiating/joining Ad-Hoc/Group calls - if (gVoiceClient->getUserPTTState() && gVoiceClient->getPTTIsToggle()) + if (LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle()) { - gVoiceClient->inputUserControlState(true); + LLVoiceClient::getInstance()->inputUserControlState(true); } } @@ -560,7 +565,7 @@ void LLVoiceChannelGroup::setChannelInfo( else { //*TODO: notify user - llwarns << "Received invalid credentials for channel " << mSessionName << llendl; + LL_WARNS("Voice") << "Received invalid credentials for channel " << mSessionName << LL_ENDL; deactivate(); } } @@ -659,7 +664,6 @@ void LLVoiceChannelGroup::setState(EState state) LLVoiceChannelProximal::LLVoiceChannelProximal() : LLVoiceChannel(LLUUID::null, LLStringUtil::null) { - activate(); } BOOL LLVoiceChannelProximal::isActive() @@ -671,13 +675,13 @@ void LLVoiceChannelProximal::activate() { if (callStarted()) return; - LLVoiceChannel::activate(); - - if (callStarted()) + if((LLVoiceChannel::sCurrentVoiceChannel != this) && (LLVoiceChannel::getState() == STATE_CONNECTED)) { - // this implicitly puts you back in the spatial channel - LLVoiceClient::getInstance()->leaveNonSpatialChannel(); + // we're connected to a non-spatial channel, so disconnect. + LLVoiceClient::getInstance()->leaveNonSpatialChannel(); } + LLVoiceChannel::activate(); + } void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal) @@ -707,7 +711,7 @@ void LLVoiceChannelProximal::handleStatusChange(EStatusType status) return; case STATUS_VOICE_DISABLED: //skip showing "Voice not available at your current location" when agent voice is disabled (EXT-4749) - if(LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking()) + if(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking()) { //TODO: remove or redirect this call status notification // LLCallInfoDialog::show("unavailable", mNotifyArgs); @@ -767,7 +771,7 @@ LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string void LLVoiceChannelP2P::handleStatusChange(EStatusType type) { - llinfos << "P2P CALL CHANNEL STATUS CHANGE: incoming=" << int(mReceivedCall) << " newstatus=" << LLVoiceClientStatusObserver::status2string(type) << " (mState=" << mState << ")" << llendl; + LL_INFOS("Voice") << "P2P CALL CHANNEL STATUS CHANGE: incoming=" << int(mReceivedCall) << " newstatus=" << LLVoiceClientStatusObserver::status2string(type) << " (mState=" << mState << ")" << LL_ENDL; // status updates switch(type) @@ -841,9 +845,9 @@ void LLVoiceChannelP2P::activate() LLRecentPeople::instance().add(mOtherUserID); //Default mic is ON on initiating/joining P2P calls - if (!gVoiceClient->getUserPTTState() && gVoiceClient->getPTTIsToggle()) + if (!LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle()) { - gVoiceClient->inputUserControlState(true); + LLVoiceClient::getInstance()->inputUserControlState(true); } } } @@ -906,7 +910,7 @@ void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::s void LLVoiceChannelP2P::setState(EState state) { - llinfos << "P2P CALL STATE CHANGE: incoming=" << int(mReceivedCall) << " oldstate=" << mState << " newstate=" << state << llendl; + LL_INFOS("Voice") << "P2P CALL STATE CHANGE: incoming=" << int(mReceivedCall) << " oldstate=" << mState << " newstate=" << state << LL_ENDL; if (mReceivedCall) // incoming call { diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 941cccacc3..573fab1f4f 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -98,7 +98,8 @@ public: static LLVoiceChannel* getChannelByID(const LLUUID& session_id); static LLVoiceChannel* getChannelByURI(std::string uri); - static LLVoiceChannel* getCurrentVoiceChannel() { return sCurrentVoiceChannel; } + static LLVoiceChannel* getCurrentVoiceChannel(); + static void initClass(); static void suspend(); diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 2238acd643..e067754e3e 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -1,6 +1,6 @@ /** * @file llvoiceclient.cpp - * @brief Implementation of LLVoiceClient class which is the interface to the voice client process. + * @brief Voice client delegation class implementation. * * $LicenseInfo:firstyear=2001&license=viewergpl$ * @@ -17,8 +17,7 @@ * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -32,7244 +31,730 @@ #include "llviewerprecompiledheaders.h" #include "llvoiceclient.h" +#include "llviewercontrol.h" +#include "llviewerwindow.h" +#include "llvoicedw.h" +#include "llvoicevivox.h" +#include "llviewernetwork.h" +#include "llhttpnode.h" +#include "llnotificationsutil.h" +#include "llsdserialize.h" +#include "llui.h" -#include - -// library includes -#include "llnotificationsutil.h" -#include "llsdserialize.h" -#include "llsdutil.h" - - -// project includes -#include "llvoavatar.h" -#include "llbufferstream.h" -#include "llfile.h" -#ifdef LL_STANDALONE -# include "expat.h" -#else -# include "expat/expat.h" -#endif -#include "llcallbacklist.h" -#include "llcallingcard.h" // for LLFriendObserver -#include "llviewerregion.h" -#include "llviewernetwork.h" // for gGridChoice -#include "llbase64.h" -#include "llviewercontrol.h" -#include "llkeyboard.h" -#include "llappviewer.h" // for gDisconnected, gDisableVoice -#include "llmutelist.h" // to check for muted avatars -#include "llagent.h" -#include "llvoavatarself.h" -#include "llcachename.h" -#include "llimview.h" // for LLIMMgr -#include "llparcel.h" -#include "llviewerparcelmgr.h" -//#include "llfirstuse.h" -#include "llspeakers.h" -#include "lltrans.h" -#include "llviewerwindow.h" -#include "llviewercamera.h" -#include "llvoavatarself.h" -#include "llvoicechannel.h" - -// for base64 decoding -#include "apr_base64.h" - -// for SHA1 hash -#include "apr_sha1.h" - -// for MD5 hash -#include "llmd5.h" - -#define USE_SESSION_GROUPS 0 - -static bool sConnectingToAgni = false; -F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f; - -const F32 LLVoiceClient::VOLUME_MIN = 0.f; -const F32 LLVoiceClient::VOLUME_DEFAULT = 0.5f; -const F32 LLVoiceClient::VOLUME_MAX = 1.0f; - -const F32 VOLUME_SCALE_VIVOX = 0.01f; - -const F32 SPEAKING_TIMEOUT = 1.f; - -const int VOICE_MAJOR_VERSION = 1; -const int VOICE_MINOR_VERSION = 0; - -LLVoiceClient *gVoiceClient = NULL; - -// Don't retry connecting to the daemon more frequently than this: -const F32 CONNECT_THROTTLE_SECONDS = 1.0f; - -// Don't send positional updates more frequently than this: -const F32 UPDATE_THROTTLE_SECONDS = 0.1f; - -const F32 LOGIN_RETRY_SECONDS = 10.0f; -const int MAX_LOGIN_RETRIES = 12; - -// Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine() -// which is treated as normal. If this number is exceeded we suspect there is a problem with connection -// to voice server (EXT-4313). When voice works correctly, there is from 1 to 15 times. 50 was chosen -// to make sure we don't make mistake when slight connection problems happen- situation when connection to server is -// blocked is VERY rare and it's better to sacrifice response time in this situation for the sake of stability. -const int MAX_NORMAL_JOINING_SPATIAL_NUM = 50; - -static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str) -{ - LLMD5 md5_uuid; - md5_uuid.update((const unsigned char*)str.data(), str.size()); - md5_uuid.finalize(); - md5_uuid.raw_digest(uuid.mData); -} - -static int scale_mic_volume(float volume) -{ - // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default. - // Map it to Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70 - return 30 + (int)(volume * 20.0f); -} - -static int scale_speaker_volume(float volume) -{ - // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default. - // Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70 - return 30 + (int)(volume * 40.0f); -} - -class LLViewerVoiceAccountProvisionResponder : - public LLHTTPClient::Responder -{ -public: - LLViewerVoiceAccountProvisionResponder(int retries) - { - mRetries = retries; - } - - virtual void error(U32 status, const std::string& reason) - { - if ( mRetries > 0 ) - { - LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, retrying. status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL; - if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision( - mRetries - 1); - } - else - { - LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, too many retries (giving up). status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL; - if ( gVoiceClient ) gVoiceClient->giveUp(); - } - } - - virtual void result(const LLSD& content) - { - if ( gVoiceClient ) - { - std::string voice_sip_uri_hostname; - std::string voice_account_server_uri; - - LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; - - if(content.has("voice_sip_uri_hostname")) - voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString(); - - // this key is actually misnamed -- it will be an entire URI, not just a hostname. - if(content.has("voice_account_server_name")) - voice_account_server_uri = content["voice_account_server_name"].asString(); - - gVoiceClient->login( - content["username"].asString(), - content["password"].asString(), - voice_sip_uri_hostname, - voice_account_server_uri); - } - } - -private: - int mRetries; -}; - -/** - * @class LLVivoxProtocolParser - * @brief This class helps construct new LLIOPipe specializations - * @see LLIOPipe - * - * THOROUGH_DESCRIPTION - */ -class LLVivoxProtocolParser : public LLIOPipe -{ - LOG_CLASS(LLVivoxProtocolParser); -public: - LLVivoxProtocolParser(); - virtual ~LLVivoxProtocolParser(); - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - - std::string mInput; - - // Expat control members - XML_Parser parser; - int responseDepth; - bool ignoringTags; - bool isEvent; - int ignoreDepth; - - // Members for processing responses. The values are transient and only valid within a call to processResponse(). - bool squelchDebugOutput; - int returnCode; - int statusCode; - std::string statusString; - std::string requestId; - std::string actionString; - std::string connectorHandle; - std::string versionID; - std::string accountHandle; - std::string sessionHandle; - std::string sessionGroupHandle; - std::string alias; - std::string applicationString; - - // Members for processing events. The values are transient and only valid within a call to processResponse(). - std::string eventTypeString; - int state; - std::string uriString; - bool isChannel; - bool incoming; - bool enabled; - std::string nameString; - std::string audioMediaString; - std::string displayNameString; - std::string deviceString; - int participantType; - bool isLocallyMuted; - bool isModeratorMuted; - bool isSpeaking; - int volume; - F32 energy; - std::string messageHeader; - std::string messageBody; - std::string notificationType; - bool hasText; - bool hasAudio; - bool hasVideo; - bool terminated; - std::string blockMask; - std::string presenceOnly; - std::string autoAcceptMask; - std::string autoAddAsBuddy; - int numberOfAliases; - std::string subscriptionHandle; - std::string subscriptionType; - - - // Members for processing text between tags - std::string textBuffer; - bool accumulateText; - - void reset(); - - void processResponse(std::string tag); - -static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr); -static void XMLCALL ExpatEndTag(void *data, const char *el); -static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len); - - void StartTag(const char *tag, const char **attr); - void EndTag(const char *tag); - void CharData(const char *buffer, int length); - -}; - -LLVivoxProtocolParser::LLVivoxProtocolParser() -{ - parser = NULL; - parser = XML_ParserCreate(NULL); - - reset(); -} - -void LLVivoxProtocolParser::reset() -{ - responseDepth = 0; - ignoringTags = false; - accumulateText = false; - energy = 0.f; - hasText = false; - hasAudio = false; - hasVideo = false; - terminated = false; - ignoreDepth = 0; - isChannel = false; - incoming = false; - enabled = false; - isEvent = false; - isLocallyMuted = false; - isModeratorMuted = false; - isSpeaking = false; - participantType = 0; - squelchDebugOutput = false; - returnCode = -1; - state = 0; - statusCode = 0; - volume = 0; - textBuffer.clear(); - alias.clear(); - numberOfAliases = 0; - applicationString.clear(); -} - -//virtual -LLVivoxProtocolParser::~LLVivoxProtocolParser() -{ - if (parser) - XML_ParserFree(parser); -} - -// virtual -LLIOPipe::EStatus LLVivoxProtocolParser::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LLBufferStream istr(channels, buffer.get()); - std::ostringstream ostr; - while (istr.good()) - { - char buf[1024]; - istr.read(buf, sizeof(buf)); - mInput.append(buf, istr.gcount()); - } - - // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser. - int start = 0; - int delim; - while((delim = mInput.find("\n\n\n", start)) != std::string::npos) - { - - // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser) - reset(); - - XML_ParserReset(parser, NULL); - XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag); - XML_SetCharacterDataHandler(parser, ExpatCharHandler); - XML_SetUserData(parser, this); - XML_Parse(parser, mInput.data() + start, delim - start, false); - - // If this message isn't set to be squelched, output the raw XML received. - if(!squelchDebugOutput) - { - LL_DEBUGS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL; - } - - start = delim + 3; - } - - if(start != 0) - mInput = mInput.substr(start); - - LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL; - - if(!gVoiceClient->mConnected) - { - // If voice has been disabled, we just want to close the socket. This does so. - LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL; - return STATUS_STOP; - } - - return STATUS_OK; -} - -void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr) -{ - if (data) - { - LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data; - object->StartTag(el, attr); - } -} - -// -------------------------------------------------------------------------------- - -void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void *data, const char *el) -{ - if (data) - { - LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data; - object->EndTag(el); - } -} - -// -------------------------------------------------------------------------------- - -void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len) -{ - if (data) - { - LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data; - object->CharData(s, len); - } -} - -// -------------------------------------------------------------------------------- - - -void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) -{ - // Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags - textBuffer.clear(); - // only accumulate text if we're not ignoring tags. - accumulateText = !ignoringTags; - - if (responseDepth == 0) - { - isEvent = !stricmp("Event", tag); - - if (!stricmp("Response", tag) || isEvent) - { - // Grab the attributes - while (*attr) - { - const char *key = *attr++; - const char *value = *attr++; - - if (!stricmp("requestId", key)) - { - requestId = value; - } - else if (!stricmp("action", key)) - { - actionString = value; - } - else if (!stricmp("type", key)) - { - eventTypeString = value; - } - } - } - LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; - } - else - { - if (ignoringTags) - { - LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; - } - else - { - LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; - - // Ignore the InputXml stuff so we don't get confused - if (!stricmp("InputXml", tag)) - { - ignoringTags = true; - ignoreDepth = responseDepth; - accumulateText = false; - - LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; - } - else if (!stricmp("CaptureDevices", tag)) - { - gVoiceClient->clearCaptureDevices(); - } - else if (!stricmp("RenderDevices", tag)) - { - gVoiceClient->clearRenderDevices(); - } - else if (!stricmp("CaptureDevice", tag)) - { - deviceString.clear(); - } - else if (!stricmp("RenderDevice", tag)) - { - deviceString.clear(); - } - else if (!stricmp("Buddies", tag)) - { - gVoiceClient->deleteAllBuddies(); - } - else if (!stricmp("BlockRules", tag)) - { - gVoiceClient->deleteAllBlockRules(); - } - else if (!stricmp("AutoAcceptRules", tag)) - { - gVoiceClient->deleteAllAutoAcceptRules(); - } - - } - } - responseDepth++; -} - -// -------------------------------------------------------------------------------- - -void LLVivoxProtocolParser::EndTag(const char *tag) -{ - const std::string& string = textBuffer; - - responseDepth--; - - if (ignoringTags) - { - if (ignoreDepth == responseDepth) - { - LL_DEBUGS("VivoxProtocolParser") << "end of ignore" << LL_ENDL; - ignoringTags = false; - } - else - { - LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; - } - } - - if (!ignoringTags) - { - LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; - - // Closing a tag. Finalize the text we've accumulated and reset - if (!stricmp("ReturnCode", tag)) - returnCode = strtol(string.c_str(), NULL, 10); - else if (!stricmp("SessionHandle", tag)) - sessionHandle = string; - else if (!stricmp("SessionGroupHandle", tag)) - sessionGroupHandle = string; - else if (!stricmp("StatusCode", tag)) - statusCode = strtol(string.c_str(), NULL, 10); - else if (!stricmp("StatusString", tag)) - statusString = string; - else if (!stricmp("ParticipantURI", tag)) - uriString = string; - else if (!stricmp("Volume", tag)) - volume = strtol(string.c_str(), NULL, 10); - else if (!stricmp("Energy", tag)) - energy = (F32)strtod(string.c_str(), NULL); - else if (!stricmp("IsModeratorMuted", tag)) - isModeratorMuted = !stricmp(string.c_str(), "true"); - else if (!stricmp("IsSpeaking", tag)) - isSpeaking = !stricmp(string.c_str(), "true"); - else if (!stricmp("Alias", tag)) - alias = string; - else if (!stricmp("NumberOfAliases", tag)) - numberOfAliases = strtol(string.c_str(), NULL, 10); - else if (!stricmp("Application", tag)) - applicationString = string; - else if (!stricmp("ConnectorHandle", tag)) - connectorHandle = string; - else if (!stricmp("VersionID", tag)) - versionID = string; - else if (!stricmp("AccountHandle", tag)) - accountHandle = string; - else if (!stricmp("State", tag)) - state = strtol(string.c_str(), NULL, 10); - else if (!stricmp("URI", tag)) - uriString = string; - else if (!stricmp("IsChannel", tag)) - isChannel = !stricmp(string.c_str(), "true"); - else if (!stricmp("Incoming", tag)) - incoming = !stricmp(string.c_str(), "true"); - else if (!stricmp("Enabled", tag)) - enabled = !stricmp(string.c_str(), "true"); - else if (!stricmp("Name", tag)) - nameString = string; - else if (!stricmp("AudioMedia", tag)) - audioMediaString = string; - else if (!stricmp("ChannelName", tag)) - nameString = string; - else if (!stricmp("DisplayName", tag)) - displayNameString = string; - else if (!stricmp("Device", tag)) - deviceString = string; - else if (!stricmp("AccountName", tag)) - nameString = string; - else if (!stricmp("ParticipantType", tag)) - participantType = strtol(string.c_str(), NULL, 10); - else if (!stricmp("IsLocallyMuted", tag)) - isLocallyMuted = !stricmp(string.c_str(), "true"); - else if (!stricmp("MicEnergy", tag)) - energy = (F32)strtod(string.c_str(), NULL); - else if (!stricmp("ChannelName", tag)) - nameString = string; - else if (!stricmp("ChannelURI", tag)) - uriString = string; - else if (!stricmp("BuddyURI", tag)) - uriString = string; - else if (!stricmp("Presence", tag)) - statusString = string; - else if (!stricmp("CaptureDevice", tag)) - { - gVoiceClient->addCaptureDevice(deviceString); - } - else if (!stricmp("RenderDevice", tag)) - { - gVoiceClient->addRenderDevice(deviceString); - } - else if (!stricmp("Buddy", tag)) - { - gVoiceClient->processBuddyListEntry(uriString, displayNameString); - } - else if (!stricmp("BlockRule", tag)) - { - gVoiceClient->addBlockRule(blockMask, presenceOnly); - } - else if (!stricmp("BlockMask", tag)) - blockMask = string; - else if (!stricmp("PresenceOnly", tag)) - presenceOnly = string; - else if (!stricmp("AutoAcceptRule", tag)) - { - gVoiceClient->addAutoAcceptRule(autoAcceptMask, autoAddAsBuddy); - } - else if (!stricmp("AutoAcceptMask", tag)) - autoAcceptMask = string; - else if (!stricmp("AutoAddAsBuddy", tag)) - autoAddAsBuddy = string; - else if (!stricmp("MessageHeader", tag)) - messageHeader = string; - else if (!stricmp("MessageBody", tag)) - messageBody = string; - else if (!stricmp("NotificationType", tag)) - notificationType = string; - else if (!stricmp("HasText", tag)) - hasText = !stricmp(string.c_str(), "true"); - else if (!stricmp("HasAudio", tag)) - hasAudio = !stricmp(string.c_str(), "true"); - else if (!stricmp("HasVideo", tag)) - hasVideo = !stricmp(string.c_str(), "true"); - else if (!stricmp("Terminated", tag)) - terminated = !stricmp(string.c_str(), "true"); - else if (!stricmp("SubscriptionHandle", tag)) - subscriptionHandle = string; - else if (!stricmp("SubscriptionType", tag)) - subscriptionType = string; - - textBuffer.clear(); - accumulateText= false; - - if (responseDepth == 0) - { - // We finished all of the XML, process the data - processResponse(tag); - } - } -} - -// -------------------------------------------------------------------------------- - -void LLVivoxProtocolParser::CharData(const char *buffer, int length) -{ - /* - This method is called for anything that isn't a tag, which can be text you - want that lies between tags, and a lot of stuff you don't want like file formatting - (tabs, spaces, CR/LF, etc). - - Only copy text if we are in accumulate mode... - */ - if (accumulateText) - textBuffer.append(buffer, length); -} - -// -------------------------------------------------------------------------------- - -void LLVivoxProtocolParser::processResponse(std::string tag) -{ - LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL; - - // SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success. This is a change vs. previous SDKs. - // According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned", - // so I believe this will give correct behavior. - - if(returnCode == 0) - statusCode = 0; - - if (isEvent) - { - const char *eventTypeCstr = eventTypeString.c_str(); - if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent")) - { - gVoiceClient->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state); - } - else if (!stricmp(eventTypeCstr, "SessionAddedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - sip:confctl-1408789@bhr.vivox.com - true - false - - - */ - gVoiceClient->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString); - } - else if (!stricmp(eventTypeCstr, "SessionRemovedEvent")) - { - gVoiceClient->sessionRemovedEvent(sessionHandle, sessionGroupHandle); - } - else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent")) - { - gVoiceClient->sessionGroupAddedEvent(sessionGroupHandle); - } - else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - 200 - OK - 2 - false - - */ - gVoiceClient->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); - } - else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==1 - true - 1 - true - - */ - gVoiceClient->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming); - } - else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 - sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.vivox.com - xI5auBZ60SJWIk606-1JGRQ== - - 0 - - */ - gVoiceClient->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType); - } - else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 - sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.vivox.com - xtx7YNV-3SGiG7rA1fo5Ndw== - - */ - gVoiceClient->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString); - } - else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com - false - true - 44 - 0.0879437 - - */ - - // These happen so often that logging them is pretty useless. - squelchDebugOutput = true; - - gVoiceClient->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy); - } - else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) - { - gVoiceClient->auxAudioPropertiesEvent(energy); - } - else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent")) - { - gVoiceClient->buddyPresenceEvent(uriString, alias, statusString, applicationString); - } - else if (!stricmp(eventTypeCstr, "BuddyAndGroupListChangedEvent")) - { - // The buddy list was updated during parsing. - // Need to recheck against the friends list. - gVoiceClient->buddyListChanged(); - } - else if (!stricmp(eventTypeCstr, "BuddyChangedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw== - sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com - Monroe Tester - - 0 - Set - - */ - // TODO: Question: Do we need to process this at all? - } - else if (!stricmp(eventTypeCstr, "MessageEvent")) - { - gVoiceClient->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString); - } - else if (!stricmp(eventTypeCstr, "SessionNotificationEvent")) - { - gVoiceClient->sessionNotificationEvent(sessionHandle, uriString, notificationType); - } - else if (!stricmp(eventTypeCstr, "SubscriptionEvent")) - { - gVoiceClient->subscriptionEvent(uriString, subscriptionHandle, alias, displayNameString, applicationString, subscriptionType); - } - else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - sip:confctl-9@bhd.vivox.com - 0 - 50 - 1 - 0 - 000 - 0 - - */ - // We don't need to process this, but we also shouldn't warn on it, since that confuses people. - } - - else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - - */ - // We don't need to process this, but we also shouldn't warn on it, since that confuses people. - } - else - { - LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL; - } - } - else - { - const char *actionCstr = actionString.c_str(); - if (!stricmp(actionCstr, "Connector.Create.1")) - { - gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID); - } - else if (!stricmp(actionCstr, "Account.Login.1")) - { - gVoiceClient->loginResponse(statusCode, statusString, accountHandle, numberOfAliases); - } - else if (!stricmp(actionCstr, "Session.Create.1")) - { - gVoiceClient->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle); - } - else if (!stricmp(actionCstr, "SessionGroup.AddSession.1")) - { - gVoiceClient->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle); - } - else if (!stricmp(actionCstr, "Session.Connect.1")) - { - gVoiceClient->sessionConnectResponse(requestId, statusCode, statusString); - } - else if (!stricmp(actionCstr, "Account.Logout.1")) - { - gVoiceClient->logoutResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1")) - { - gVoiceClient->connectorShutdownResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Account.ListBlockRules.1")) - { - gVoiceClient->accountListBlockRulesResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Account.ListAutoAcceptRules.1")) - { - gVoiceClient->accountListAutoAcceptRulesResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Session.Set3DPosition.1")) - { - // We don't need to process these, but they're so spammy we don't want to log them. - squelchDebugOutput = true; - } -/* - else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) - { - gVoiceClient->channelGetListResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Connector.AccountCreate.1")) - { - - } - else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1")) - { - - } - else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1")) - { - - } - else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1")) - { - - } - else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1")) - { - - } - else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1")) - { - - } - else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1")) - { - - } - else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1")) - { - - } - else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelCreate.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelUpdate.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelDelete.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1")) - { - - } -*/ - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////// - -class LLVoiceClientMuteListObserver : public LLMuteListObserver -{ - /* virtual */ void onChange() { gVoiceClient->muteListChanged();} -}; - -class LLVoiceClientFriendsObserver : public LLFriendObserver -{ -public: - /* virtual */ void changed(U32 mask) { gVoiceClient->updateFriends(mask);} -}; - -static LLVoiceClientMuteListObserver mutelist_listener; -static bool sMuteListListener_listening = false; - -static LLVoiceClientFriendsObserver *friendslist_listener = NULL; - -/////////////////////////////////////////////////////////////////////////////////////////////// - -class LLVoiceClientCapResponder : public LLHTTPClient::Responder -{ -public: - LLVoiceClientCapResponder(void){}; - - virtual void error(U32 status, const std::string& reason); // called with bad status codes - virtual void result(const LLSD& content); - -private: -}; - -void LLVoiceClientCapResponder::error(U32 status, const std::string& reason) -{ - LL_WARNS("Voice") << "LLVoiceClientCapResponder::error(" - << status << ": " << reason << ")" - << LL_ENDL; -} - -void LLVoiceClientCapResponder::result(const LLSD& content) -{ - LLSD::map_const_iterator iter; - - LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; - - if ( content.has("voice_credentials") ) - { - LLSD voice_credentials = content["voice_credentials"]; - std::string uri; - std::string credentials; - - if ( voice_credentials.has("channel_uri") ) - { - uri = voice_credentials["channel_uri"].asString(); - } - if ( voice_credentials.has("channel_credentials") ) - { - credentials = - voice_credentials["channel_credentials"].asString(); - } - - gVoiceClient->setSpatialChannel(uri, credentials); - } -} - - - -#if LL_WINDOWS -static HANDLE sGatewayHandle = 0; - -static bool isGatewayRunning() -{ - bool result = false; - if(sGatewayHandle != 0) - { - DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0); - if(waitresult != WAIT_OBJECT_0) - { - result = true; - } - } - return result; -} -static void killGateway() -{ - if(sGatewayHandle != 0) - { - TerminateProcess(sGatewayHandle,0); - } -} - -#else // Mac and linux - -static pid_t sGatewayPID = 0; -static bool isGatewayRunning() -{ - bool result = false; - if(sGatewayPID != 0) - { - // A kill with signal number 0 has no effect, just does error checking. It should return an error if the process no longer exists. - if(kill(sGatewayPID, 0) == 0) - { - result = true; - } - } - return result; -} - -static void killGateway() -{ - if(sGatewayPID != 0) - { - kill(sGatewayPID, SIGTERM); - } -} - -#endif - -class LLSpeakerVolumeStorage : public LLSingleton -{ - LOG_CLASS(LLSpeakerVolumeStorage); -public: - - /** - * Stores volume level for specified user. - * - * @param[in] speaker_id - LLUUID of user to store volume level for. - * @param[in] volume - volume level to be stored for user. - */ - void storeSpeakerVolume(const LLUUID& speaker_id, F32 volume); - - /** - * Gets stored volume level for specified speaker - * - * @param[in] speaker_id - LLUUID of user to retrieve volume level for. - * @param[out] volume - set to stored volume if found, otherwise unmodified. - * @return - true if a stored volume is found. - */ - bool getSpeakerVolume(const LLUUID& speaker_id, F32& volume); - - /** - * Removes stored volume level for specified user. - * - * @param[in] speaker_id - LLUUID of user to remove. - */ - void removeSpeakerVolume(const LLUUID& speaker_id); - -private: - friend class LLSingleton; - LLSpeakerVolumeStorage(); - ~LLSpeakerVolumeStorage(); - - const static std::string SETTINGS_FILE_NAME; - - void load(); - void save(); - - static F32 transformFromLegacyVolume(F32 volume_in); - static F32 transformToLegacyVolume(F32 volume_in); - - typedef std::map speaker_data_map_t; - speaker_data_map_t mSpeakersData; -}; - -const std::string LLSpeakerVolumeStorage::SETTINGS_FILE_NAME = "volume_settings.xml"; - -LLSpeakerVolumeStorage::LLSpeakerVolumeStorage() -{ - load(); -} - -LLSpeakerVolumeStorage::~LLSpeakerVolumeStorage() -{ - save(); -} - -void LLSpeakerVolumeStorage::storeSpeakerVolume(const LLUUID& speaker_id, F32 volume) -{ - if ((volume >= LLVoiceClient::VOLUME_MIN) && (volume <= LLVoiceClient::VOLUME_MAX)) - { - mSpeakersData[speaker_id] = volume; - - // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. - // LL_DEBUGS("Voice") << "Stored volume = " << volume << " for " << id << LL_ENDL; - } - else - { - LL_WARNS("Voice") << "Attempted to store out of range volume " << volume << " for " << speaker_id << LL_ENDL; - llassert(0); - } -} - -bool LLSpeakerVolumeStorage::getSpeakerVolume(const LLUUID& speaker_id, F32& volume) -{ - speaker_data_map_t::const_iterator it = mSpeakersData.find(speaker_id); - - if (it != mSpeakersData.end()) - { - volume = it->second; - - // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. - // LL_DEBUGS("Voice") << "Retrieved stored volume = " << volume << " for " << id << LL_ENDL; - - return true; - } - - return false; -} - -void LLSpeakerVolumeStorage::removeSpeakerVolume(const LLUUID& speaker_id) -{ - mSpeakersData.erase(speaker_id); - - // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. - // LL_DEBUGS("Voice") << "Removing stored volume for " << id << LL_ENDL; -} - -/* static */ F32 LLSpeakerVolumeStorage::transformFromLegacyVolume(F32 volume_in) -{ - // Convert to linear-logarithmic [0.0..1.0] with 0.5 = 0dB - // from legacy characteristic composed of two square-curves - // that intersect at volume_in = 0.5, volume_out = 0.56 - - F32 volume_out = 0.f; - volume_in = llclamp(volume_in, 0.f, 1.0f); - - if (volume_in <= 0.5f) - { - volume_out = volume_in * volume_in * 4.f * 0.56f; - } - else - { - volume_out = (1.f - 0.56f) * (4.f * volume_in * volume_in - 1.f) / 3.f + 0.56f; - } - - return volume_out; -} - -/* static */ F32 LLSpeakerVolumeStorage::transformToLegacyVolume(F32 volume_in) -{ - // Convert from linear-logarithmic [0.0..1.0] with 0.5 = 0dB - // to legacy characteristic composed of two square-curves - // that intersect at volume_in = 0.56, volume_out = 0.5 - - F32 volume_out = 0.f; - volume_in = llclamp(volume_in, 0.f, 1.0f); - - if (volume_in <= 0.56f) - { - volume_out = sqrt(volume_in / (4.f * 0.56f)); - } - else - { - volume_out = sqrt((3.f * (volume_in - 0.56f) / (1.f - 0.56f) + 1.f) / 4.f); - } - - return volume_out; -} - -void LLSpeakerVolumeStorage::load() -{ - // load per-resident voice volume information - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME); - - LL_INFOS("Voice") << "Loading stored speaker volumes from: " << filename << LL_ENDL; - - LLSD settings_llsd; - llifstream file; - file.open(filename); - if (file.is_open()) - { - LLSDSerialize::fromXML(settings_llsd, file); - } - - for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); - iter != settings_llsd.endMap(); ++iter) - { - // Maintain compatibility with 1.23 non-linear saved volume levels - F32 volume = transformFromLegacyVolume((F32)iter->second.asReal()); - - storeSpeakerVolume(LLUUID(iter->first), volume); - } -} - -void LLSpeakerVolumeStorage::save() -{ - // If we quit from the login screen we will not have an SL account - // name. Don't try to save, otherwise we'll dump a file in - // C:\Program Files\SecondLife\ or similar. JC - std::string user_dir = gDirUtilp->getLindenUserDir(); - if (!user_dir.empty()) - { - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME); - LLSD settings_llsd; - - LL_INFOS("Voice") << "Saving stored speaker volumes to: " << filename << LL_ENDL; - - for(speaker_data_map_t::const_iterator iter = mSpeakersData.begin(); iter != mSpeakersData.end(); ++iter) - { - // Maintain compatibility with 1.23 non-linear saved volume levels - F32 volume = transformToLegacyVolume(iter->second); - - settings_llsd[iter->first.asString()] = volume; - } - - llofstream file; - file.open(filename); - LLSDSerialize::toPrettyXML(settings_llsd, file); - } -} - - -/////////////////////////////////////////////////////////////////////////////////////////////// - -LLVoiceClient::LLVoiceClient() : - mState(stateDisabled), - mSessionTerminateRequested(false), - mRelogRequested(false), - mConnected(false), - mPump(NULL), - mSpatialJoiningNum(0), - - mTuningMode(false), - mTuningEnergy(0.0f), - mTuningMicVolume(0), - mTuningMicVolumeDirty(true), - mTuningSpeakerVolume(0), - mTuningSpeakerVolumeDirty(true), - mTuningExitState(stateDisabled), - - mAreaVoiceDisabled(false), - mAudioSession(NULL), - mAudioSessionChanged(false), - mNextAudioSession(NULL), - - mCurrentParcelLocalID(0), - mNumberOfAliases(0), - mCommandCookie(0), - mLoginRetryCount(0), - - mBuddyListMapPopulated(false), - mBlockRulesListReceived(false), - mAutoAcceptRulesListReceived(false), - mCaptureDeviceDirty(false), - mRenderDeviceDirty(false), - mSpatialCoordsDirty(false), - - mPTTDirty(true), - mPTT(true), - mUsePTT(true), - mPTTIsMiddleMouse(false), - mPTTKey(0), - mPTTIsToggle(false), - mUserPTTState(false), - mMuteMic(false), - mFriendsListDirty(true), - - mEarLocation(0), - mSpeakerVolumeDirty(true), - mSpeakerMuteDirty(true), - mMicVolume(0), - mMicVolumeDirty(true), - - mVoiceEnabled(false), - mWriteInProgress(false), - - mLipSyncEnabled(false) -{ - gVoiceClient = this; - - mAPIVersion = LLTrans::getString("NotConnected"); - - mSpeakerVolume = scale_speaker_volume(0); - -#if LL_DARWIN || LL_LINUX || LL_SOLARIS - // HACK: THIS DOES NOT BELONG HERE - // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. - // This should cause us to ignore SIGPIPE and handle the error through proper channels. - // This should really be set up elsewhere. Where should it go? - signal(SIGPIPE, SIG_IGN); - - // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes. - // Ignoring SIGCHLD should prevent zombies from being created. Alternately, we could use wait(), but I'd rather not do that. - signal(SIGCHLD, SIG_IGN); -#endif - - // set up state machine - setState(stateDisabled); - - gIdleCallbacks.addFunction(idle, this); -} - -//--------------------------------------------------- - -LLVoiceClient::~LLVoiceClient() -{ -} - -//---------------------------------------------- - -void LLVoiceClient::init(LLPumpIO *pump) -{ - // constructor will set up gVoiceClient - LLVoiceClient::getInstance()->mPump = pump; - LLVoiceClient::getInstance()->updateSettings(); -} - -void LLVoiceClient::terminate() -{ - if(gVoiceClient) - { -// gVoiceClient->leaveAudioSession(); - gVoiceClient->logout(); - // As of SDK version 4885, this should no longer be necessary. It will linger after the socket close if it needs to. - // ms_sleep(2000); - gVoiceClient->connectorShutdown(); - gVoiceClient->closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. - - // This will do unpleasant things on windows. -// killGateway(); - - // Don't do this anymore -- LLSingleton will take care of deleting the object. -// delete gVoiceClient; - - // Hint to other code not to access the voice client anymore. - gVoiceClient = NULL; - } -} - -//--------------------------------------------------- - -void LLVoiceClient::updateSettings() -{ - setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat")); - setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled")); - std::string keyString = gSavedSettings.getString("PushToTalkButton"); - setPTTKey(keyString); - setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); - setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); - - std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); - setCaptureDevice(inputDevice); - std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); - setRenderDevice(outputDevice); - F32 mic_level = gSavedSettings.getF32("AudioLevelMic"); - setMicGain(mic_level); - setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); -} - -///////////////////////////// -// utility functions - -bool LLVoiceClient::writeString(const std::string &str) -{ - bool result = false; - if(mConnected) - { - apr_status_t err; - apr_size_t size = (apr_size_t)str.size(); - apr_size_t written = size; - - //MARK: Turn this on to log outgoing XML -// LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL; - - // check return code - sockets will fail (broken, etc.) - err = apr_socket_send( - mSocket->getSocket(), - (const char*)str.data(), - &written); - - if(err == 0) - { - // Success. - result = true; - } - // TODO: handle partial writes (written is number of bytes written) - // Need to set socket to non-blocking before this will work. -// else if(APR_STATUS_IS_EAGAIN(err)) -// { -// // -// } - else - { - // Assume any socket error means something bad. For now, just close the socket. - char buf[MAX_STRING]; - LL_WARNS("Voice") << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL; - daemonDied(); - } - } - - return result; -} - - -///////////////////////////// -// session control messages -void LLVoiceClient::connectorCreate() -{ - std::ostringstream stream; - std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); - std::string loglevel = "0"; - - // Transition to stateConnectorStarted when the connector handle comes back. - setState(stateConnectorStarting); - - std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel"); - - if(savedLogLevel != "-1") - { - LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL; - loglevel = "10"; - } - - stream - << "" - << "V2 SDK" - << "" << mVoiceAccountServerURI << "" - << "Normal" - << "" - << "" << logpath << "" - << "Connector" - << ".log" - << "" << loglevel << "" - << "" - << "SecondLifeViewer.1" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::connectorShutdown() -{ - setState(stateConnectorStopping); - - if(!mConnectorHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mConnectorHandle << "" - << "" - << "\n\n\n"; - - mConnectorHandle.clear(); - - writeString(stream.str()); - } -} - -void LLVoiceClient::userAuthorized(const std::string& firstName, const std::string& lastName, const LLUUID &agentID) -{ - mAccountFirstName = firstName; - mAccountLastName = lastName; - - mAccountDisplayName = firstName; - mAccountDisplayName += " "; - mAccountDisplayName += lastName; - - LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL; - - sConnectingToAgni = LLViewerLogin::getInstance()->isInProductionGrid(); - - mAccountName = nameFromID(agentID); -} - -void LLVoiceClient::requestVoiceAccountProvision(S32 retries) -{ - if ( gAgent.getRegion() && mVoiceEnabled ) - { - std::string url = - gAgent.getRegion()->getCapability( - "ProvisionVoiceAccountRequest"); - - if ( url == "" ) return; - - LLHTTPClient::post( - url, - LLSD(), - new LLViewerVoiceAccountProvisionResponder(retries)); - } -} - -void LLVoiceClient::login( - const std::string& account_name, - const std::string& password, - const std::string& voice_sip_uri_hostname, - const std::string& voice_account_server_uri) -{ - mVoiceSIPURIHostName = voice_sip_uri_hostname; - mVoiceAccountServerURI = voice_account_server_uri; - - if(!mAccountHandle.empty()) - { - // Already logged in. - LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL; - - // Don't process another login. - return; - } - else if ( account_name != mAccountName ) - { - //TODO: error? - LL_WARNS("Voice") << "Wrong account name! " << account_name - << " instead of " << mAccountName << LL_ENDL; - } - else - { - mAccountPassword = password; - } - - std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName"); - - if( !debugSIPURIHostName.empty() ) - { - mVoiceSIPURIHostName = debugSIPURIHostName; - } - - if( mVoiceSIPURIHostName.empty() ) - { - // we have an empty account server name - // so we fall back to hardcoded defaults - - if(sConnectingToAgni) - { - // Use the release account server - mVoiceSIPURIHostName = "bhr.vivox.com"; - } - else - { - // Use the development account server - mVoiceSIPURIHostName = "bhd.vivox.com"; - } - } - - std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI"); - - if( !debugAccountServerURI.empty() ) - { - mVoiceAccountServerURI = debugAccountServerURI; - } - - if( mVoiceAccountServerURI.empty() ) - { - // If the account server URI isn't specified, construct it from the SIP URI hostname - mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/"; - } -} - -void LLVoiceClient::idle(void* user_data) -{ - LLVoiceClient* self = (LLVoiceClient*)user_data; - self->stateMachine(); -} - -std::string LLVoiceClient::state2string(LLVoiceClient::state inState) -{ - std::string result = "UNKNOWN"; - - // Prevent copy-paste errors when updating this list... -#define CASE(x) case x: result = #x; break - - switch(inState) - { - CASE(stateDisableCleanup); - CASE(stateDisabled); - CASE(stateStart); - CASE(stateDaemonLaunched); - CASE(stateConnecting); - CASE(stateConnected); - CASE(stateIdle); - CASE(stateMicTuningStart); - CASE(stateMicTuningRunning); - CASE(stateMicTuningStop); - CASE(stateConnectorStart); - CASE(stateConnectorStarting); - CASE(stateConnectorStarted); - CASE(stateLoginRetry); - CASE(stateLoginRetryWait); - CASE(stateNeedsLogin); - CASE(stateLoggingIn); - CASE(stateLoggedIn); - CASE(stateCreatingSessionGroup); - CASE(stateNoChannel); - CASE(stateJoiningSession); - CASE(stateSessionJoined); - CASE(stateRunning); - CASE(stateLeavingSession); - CASE(stateSessionTerminated); - CASE(stateLoggingOut); - CASE(stateLoggedOut); - CASE(stateConnectorStopping); - CASE(stateConnectorStopped); - CASE(stateConnectorFailed); - CASE(stateConnectorFailedWaiting); - CASE(stateLoginFailed); - CASE(stateLoginFailedWaiting); - CASE(stateJoinSessionFailed); - CASE(stateJoinSessionFailedWaiting); - CASE(stateJail); - } - -#undef CASE - - return result; -} - -std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus) -{ - std::string result = "UNKNOWN"; - - // Prevent copy-paste errors when updating this list... -#define CASE(x) case x: result = #x; break - - switch(inStatus) - { - CASE(STATUS_LOGIN_RETRY); - CASE(STATUS_LOGGED_IN); - CASE(STATUS_JOINING); - CASE(STATUS_JOINED); - CASE(STATUS_LEFT_CHANNEL); - CASE(STATUS_VOICE_DISABLED); - CASE(STATUS_VOICE_ENABLED); - CASE(BEGIN_ERROR_STATUS); - CASE(ERROR_CHANNEL_FULL); - CASE(ERROR_CHANNEL_LOCKED); - CASE(ERROR_NOT_AVAILABLE); - CASE(ERROR_UNKNOWN); - default: - break; - } - -#undef CASE - - return result; -} - -void LLVoiceClient::setState(state inState) -{ - LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL; - - mState = inState; -} - -void LLVoiceClient::stateMachine() -{ - if(gDisconnected) - { - // The viewer has been disconnected from the sim. Disable voice. - setVoiceEnabled(false); - } - - if(mVoiceEnabled) - { - updatePosition(); - } - else if(mTuningMode) - { - // Tuning mode is special -- it needs to launch SLVoice even if voice is disabled. - } - else - { - if((getState() != stateDisabled) && (getState() != stateDisableCleanup)) - { - // User turned off voice support. Send the cleanup messages, close the socket, and reset. - if(!mConnected) - { - // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill. - LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL; - killGateway(); - } - - logout(); - connectorShutdown(); - - setState(stateDisableCleanup); - } - } - - // Check for parcel boundary crossing - { - LLViewerRegion *region = gAgent.getRegion(); - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - - if(region && parcel) - { - S32 parcelLocalID = parcel->getLocalID(); - std::string regionName = region->getName(); - std::string capURI = region->getCapability("ParcelVoiceInfoRequest"); - -// LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL; - - // The region name starts out empty and gets filled in later. - // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes. - // If either is empty, wait for the next time around. - if(!regionName.empty()) - { - if(!capURI.empty()) - { - if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName)) - { - // We have changed parcels. Initiate a parcel channel lookup. - mCurrentParcelLocalID = parcelLocalID; - mCurrentRegionName = regionName; - - parcelChanged(); - } - } - else - { - LL_WARNS_ONCE("Voice") << "region doesn't have ParcelVoiceInfoRequest capability. This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL; - } - } - } - } - - switch(getState()) - { - //MARK: stateDisableCleanup - case stateDisableCleanup: - // Clean up and reset everything. - closeSocket(); - deleteAllSessions(); - deleteAllBuddies(); - - mConnectorHandle.clear(); - mAccountHandle.clear(); - mAccountPassword.clear(); - mVoiceAccountServerURI.clear(); - - setState(stateDisabled); - break; - - //MARK: stateDisabled - case stateDisabled: - if(mTuningMode || (mVoiceEnabled && !mAccountName.empty())) - { - setState(stateStart); - } - break; - - //MARK: stateStart - case stateStart: - if(gSavedSettings.getBOOL("CmdLineDisableVoice")) - { - // Voice is locked out, we must not launch the vivox daemon. - setState(stateJail); - } - else if(!isGatewayRunning()) - { - if(true) - { - // Launch the voice daemon - - // *FIX:Mani - Using the executable dir instead - // of mAppRODataDir, the working directory from which the app - // is launched. - //std::string exe_path = gDirUtilp->getAppRODataDir(); - std::string exe_path = gDirUtilp->getExecutableDir(); - exe_path += gDirUtilp->getDirDelimiter(); -#if LL_WINDOWS - exe_path += "SLVoice.exe"; -#elif LL_DARWIN - exe_path += "../Resources/SLVoice"; -#else - exe_path += "SLVoice"; -#endif - // See if the vivox executable exists - llstat s; - if(!LLFile::stat(exe_path, &s)) - { - // vivox executable exists. Build the command line and launch the daemon. - // SLIM SDK: these arguments are no longer necessary. -// std::string args = " -p tcp -h -c"; - std::string args; - std::string loglevel = gSavedSettings.getString("VivoxDebugLevel"); - - if(loglevel.empty()) - { - loglevel = "-1"; // turn logging off completely - } - - args += " -ll "; - args += loglevel; - - LL_DEBUGS("Voice") << "Args for SLVoice: " << args << LL_ENDL; - -#if LL_WINDOWS - PROCESS_INFORMATION pinfo; - STARTUPINFOW sinfo; - memset(&sinfo, 0, sizeof(sinfo)); - - std::string exe_dir = gDirUtilp->getExecutableDir(); - - llutf16string exe_path16 = utf8str_to_utf16str(exe_path); - llutf16string exe_dir16 = utf8str_to_utf16str(exe_dir); - llutf16string args16 = utf8str_to_utf16str(args); - // Create a writeable copy to keep Windows happy. - U16 *argscpy_16 = new U16[args16.size() + 1]; - wcscpy_s(argscpy_16,args16.size()+1,args16.c_str()); - if(!CreateProcessW(exe_path16.c_str(), argscpy_16, NULL, NULL, FALSE, 0, NULL, exe_dir16.c_str(), &sinfo, &pinfo)) - { -// DWORD dwErr = GetLastError(); - } - else - { - // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on - // CloseHandle(pinfo.hProcess); // stops leaks - nothing else - sGatewayHandle = pinfo.hProcess; - CloseHandle(pinfo.hThread); // stops leaks - nothing else - } - - delete[] argscpy_16; -#else // LL_WINDOWS - // This should be the same for mac and linux - { - std::vector arglist; - arglist.push_back(exe_path); - - // Split the argument string into separate strings for each argument - typedef boost::tokenizer > tokenizer; - boost::char_separator sep(" "); - tokenizer tokens(args, sep); - tokenizer::iterator token_iter; - - for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) - { - arglist.push_back(*token_iter); - } - - // create an argv vector for the child process - char **fakeargv = new char*[arglist.size() + 1]; - int i; - for(i=0; i < arglist.size(); i++) - fakeargv[i] = const_cast(arglist[i].c_str()); - - fakeargv[i] = NULL; - - fflush(NULL); // flush all buffers before the child inherits them - pid_t id = vfork(); - if(id == 0) - { - // child - execv(exe_path.c_str(), fakeargv); - - // If we reach this point, the exec failed. - // Use _exit() instead of exit() per the vfork man page. - _exit(0); - } - - // parent - delete[] fakeargv; - sGatewayPID = id; - } -#endif // LL_WINDOWS - mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost").c_str(), gSavedSettings.getU32("VoicePort")); - } - else - { - LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL; - } - } - else - { - // SLIM SDK: port changed from 44124 to 44125. - // We can connect to a client gateway running on another host. This is useful for testing. - // To do this, launch the gateway on a nearby host like this: - // vivox-gw.exe -p tcp -i 0.0.0.0:44125 - // and put that host's IP address here. - mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost"), gSavedSettings.getU32("VoicePort")); - } - - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); - - setState(stateDaemonLaunched); - - // Dirty the states we'll need to sync with the daemon when it comes up. - mPTTDirty = true; - mMicVolumeDirty = true; - mSpeakerVolumeDirty = true; - mSpeakerMuteDirty = true; - // These only need to be set if they're not default (i.e. empty string). - mCaptureDeviceDirty = !mCaptureDevice.empty(); - mRenderDeviceDirty = !mRenderDevice.empty(); - - mMainSessionGroupHandle.clear(); - } - break; - - //MARK: stateDaemonLaunched - case stateDaemonLaunched: - if(mUpdateTimer.hasExpired()) - { - LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL; - - mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); - - if(!mSocket) - { - mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); - } - - mConnected = mSocket->blockingConnect(mDaemonHost); - if(mConnected) - { - setState(stateConnecting); - } - else - { - // If the connect failed, the socket may have been put into a bad state. Delete it. - closeSocket(); - } - } - break; - - //MARK: stateConnecting - case stateConnecting: - // Can't do this until we have the pump available. - if(mPump) - { - // MBW -- Note to self: pumps and pipes examples in - // indra/test/io.cpp - // indra/test/llpipeutil.{cpp|h} - - // Attach the pumps and pipes - - LLPumpIO::chain_t readChain; - - readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket))); - readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser())); - - mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS); - - setState(stateConnected); - } - - break; - - //MARK: stateConnected - case stateConnected: - // Initial devices query - getCaptureDevicesSendMessage(); - getRenderDevicesSendMessage(); - - mLoginRetryCount = 0; - - setState(stateIdle); - break; - - //MARK: stateIdle - case stateIdle: - // This is the idle state where we're connected to the daemon but haven't set up a connector yet. - if(mTuningMode) - { - mTuningExitState = stateIdle; - setState(stateMicTuningStart); - } - else if(!mVoiceEnabled) - { - // We never started up the connector. This will shut down the daemon. - setState(stateConnectorStopped); - } - else if(!mAccountName.empty()) - { - LLViewerRegion *region = gAgent.getRegion(); - - if(region) - { - if ( region->getCapability("ProvisionVoiceAccountRequest") != "" ) - { - if ( mAccountPassword.empty() ) - { - requestVoiceAccountProvision(); - } - setState(stateConnectorStart); - } - else - { - LL_WARNS_ONCE("Voice") << "region doesn't have ProvisionVoiceAccountRequest capability!" << LL_ENDL; - } - } - } - break; - - //MARK: stateMicTuningStart - case stateMicTuningStart: - if(mUpdateTimer.hasExpired()) - { - if(mCaptureDeviceDirty || mRenderDeviceDirty) - { - // These can't be changed while in tuning mode. Set them before starting. - std::ostringstream stream; - - buildSetCaptureDevice(stream); - buildSetRenderDevice(stream); - - if(!stream.str().empty()) - { - writeString(stream.str()); - } - - // This will come around again in the same state and start the capture, after the timer expires. - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); - } - else - { - // duration parameter is currently unused, per Mike S. - tuningCaptureStartSendMessage(10000); - - setState(stateMicTuningRunning); - } - } - - break; - - //MARK: stateMicTuningRunning - case stateMicTuningRunning: - if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty) - { - // All of these conditions make us leave tuning mode. - setState(stateMicTuningStop); - } - else - { - // process mic/speaker volume changes - if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty) - { - std::ostringstream stream; - - if(mTuningMicVolumeDirty) - { - LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL; - stream - << "" - << "" << mTuningMicVolume << "" - << "\n\n\n"; - } - - if(mTuningSpeakerVolumeDirty) - { - stream - << "" - << "" << mTuningSpeakerVolume << "" - << "\n\n\n"; - } - - mTuningMicVolumeDirty = false; - mTuningSpeakerVolumeDirty = false; - - if(!stream.str().empty()) - { - writeString(stream.str()); - } - } - } - break; - - //MARK: stateMicTuningStop - case stateMicTuningStop: - { - // transition out of mic tuning - tuningCaptureStopSendMessage(); - - setState(mTuningExitState); - - // if we exited just to change devices, this will keep us from re-entering too fast. - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); - - } - break; - - //MARK: stateConnectorStart - case stateConnectorStart: - if(!mVoiceEnabled) - { - // We were never logged in. This will shut down the connector. - setState(stateLoggedOut); - } - else if(!mVoiceAccountServerURI.empty()) - { - connectorCreate(); - } - break; - - //MARK: stateConnectorStarting - case stateConnectorStarting: // waiting for connector handle - // connectorCreateResponse() will transition from here to stateConnectorStarted. - break; - - //MARK: stateConnectorStarted - case stateConnectorStarted: // connector handle received - if(!mVoiceEnabled) - { - // We were never logged in. This will shut down the connector. - setState(stateLoggedOut); - } - else - { - // The connector is started. Send a login message. - setState(stateNeedsLogin); - } - break; - - //MARK: stateLoginRetry - case stateLoginRetry: - if(mLoginRetryCount == 0) - { - // First retry -- display a message to the user - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY); - } - - mLoginRetryCount++; - - if(mLoginRetryCount > MAX_LOGIN_RETRIES) - { - LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL; - setState(stateLoginFailed); - } - else - { - LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL; - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS); - setState(stateLoginRetryWait); - } - break; - - //MARK: stateLoginRetryWait - case stateLoginRetryWait: - if(mUpdateTimer.hasExpired()) - { - setState(stateNeedsLogin); - } - break; - - //MARK: stateNeedsLogin - case stateNeedsLogin: - if(!mAccountPassword.empty()) - { - setState(stateLoggingIn); - loginSendMessage(); - } - break; - - //MARK: stateLoggingIn - case stateLoggingIn: // waiting for account handle - // loginResponse() will transition from here to stateLoggedIn. - break; - - //MARK: stateLoggedIn - case stateLoggedIn: // account handle received - - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - - // request the current set of block rules (we'll need them when updating the friends list) - accountListBlockRulesSendMessage(); - - // request the current set of auto-accept rules - accountListAutoAcceptRulesSendMessage(); - - // Set up the mute list observer if it hasn't been set up already. - if((!sMuteListListener_listening)) - { - LLMuteList::getInstance()->addObserver(&mutelist_listener); - sMuteListListener_listening = true; - } - - // Set up the friends list observer if it hasn't been set up already. - if(friendslist_listener == NULL) - { - friendslist_listener = new LLVoiceClientFriendsObserver; - LLAvatarTracker::instance().addObserver(friendslist_listener); - } - - // Set the initial state of mic mute, local speaker volume, etc. - { - std::ostringstream stream; - - buildLocalAudioUpdates(stream); - - if(!stream.str().empty()) - { - writeString(stream.str()); - } - } - -#if USE_SESSION_GROUPS - // create the main session group - sessionGroupCreateSendMessage(); - - setState(stateCreatingSessionGroup); -#else - // Not using session groups -- skip the stateCreatingSessionGroup state. - setState(stateNoChannel); - - // Initial kick-off of channel lookup logic - parcelChanged(); -#endif - break; - - //MARK: stateCreatingSessionGroup - case stateCreatingSessionGroup: - if(mSessionTerminateRequested || !mVoiceEnabled) - { - // TODO: Question: is this the right way out of this state - setState(stateSessionTerminated); - } - else if(!mMainSessionGroupHandle.empty()) - { - setState(stateNoChannel); - - // Start looped recording (needed for "panic button" anti-griefing tool) - recordingLoopStart(); - - // Initial kick-off of channel lookup logic - parcelChanged(); - } - break; - - //MARK: stateNoChannel - case stateNoChannel: - - mSpatialJoiningNum = 0; - // Do this here as well as inside sendPositionalUpdate(). - // Otherwise, if you log in but don't join a proximal channel (such as when your login location has voice disabled), your friends list won't sync. - sendFriendsListUpdates(); - - if(mSessionTerminateRequested || !mVoiceEnabled) - { - // TODO: Question: Is this the right way out of this state? - setState(stateSessionTerminated); - } - else if(mTuningMode) - { - mTuningExitState = stateNoChannel; - setState(stateMicTuningStart); - } - else if(sessionNeedsRelog(mNextAudioSession)) - { - requestRelog(); - setState(stateSessionTerminated); - } - else if(mNextAudioSession) - { - sessionState *oldSession = mAudioSession; - - mAudioSession = mNextAudioSession; - if(!mAudioSession->mReconnect) - { - mNextAudioSession = NULL; - } - - // The old session may now need to be deleted. - reapSession(oldSession); - - if(!mAudioSession->mHandle.empty()) - { - // Connect to a session by session handle - - sessionMediaConnectSendMessage(mAudioSession); - } - else - { - // Connect to a session by URI - sessionCreateSendMessage(mAudioSession, true, false); - } - - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); - setState(stateJoiningSession); - } - else if(!mSpatialSessionURI.empty()) - { - // If we're not headed elsewhere and have a spatial URI, return to spatial. - switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); - } - break; - - //MARK: stateJoiningSession - case stateJoiningSession: // waiting for session handle - - // If this is true we have problem with connection to voice server (EXT-4313). - // See descriptions of mSpatialJoiningNum and MAX_NORMAL_JOINING_SPATIAL_NUM. - if(mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM) - { - // Notify observers to let them know there is problem with voice - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); - llwarns << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << llendl; - } - - // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for - // example for p2p many times while waiting for response, so it can't be used to detect errors - if(mAudioSession && mAudioSession->mIsSpatial) - { - mSpatialJoiningNum++; - } - - // joinedAudioSession() will transition from here to stateSessionJoined. - if(!mVoiceEnabled) - { - // User bailed out during connect -- jump straight to teardown. - setState(stateSessionTerminated); - } - else if(mSessionTerminateRequested) - { - if(mAudioSession && !mAudioSession->mHandle.empty()) - { - // Only allow direct exits from this state in p2p calls (for cancelling an invite). - // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. - if(mAudioSession->mIsP2P) - { - sessionMediaDisconnectSendMessage(mAudioSession); - setState(stateSessionTerminated); - } - } - } - break; - - //MARK: stateSessionJoined - case stateSessionJoined: // session handle received - - mSpatialJoiningNum = 0; - // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4 - // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck. - // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined. - // This is a cheap way to make sure both have happened before proceeding. - if(mAudioSession && mAudioSession->mVoiceEnabled) - { - // Dirty state that may need to be sync'ed with the daemon. - mPTTDirty = true; - mSpeakerVolumeDirty = true; - mSpatialCoordsDirty = true; - - setState(stateRunning); - - // Start the throttle timer - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); - - // Events that need to happen when a session is joined could go here. - // Maybe send initial spatial data? - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); - - } - else if(!mVoiceEnabled) - { - // User bailed out during connect -- jump straight to teardown. - setState(stateSessionTerminated); - } - else if(mSessionTerminateRequested) - { - // Only allow direct exits from this state in p2p calls (for cancelling an invite). - // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. - if(mAudioSession && mAudioSession->mIsP2P) - { - sessionMediaDisconnectSendMessage(mAudioSession); - setState(stateSessionTerminated); - } - } - break; - - //MARK: stateRunning - case stateRunning: // steady state - // Disabling voice or disconnect requested. - if(!mVoiceEnabled || mSessionTerminateRequested) - { - leaveAudioSession(); - } - else - { - - // Figure out whether the PTT state needs to change - { - bool newPTT; - if(mUsePTT) - { - // If configured to use PTT, track the user state. - newPTT = mUserPTTState; - } - else - { - // If not configured to use PTT, it should always be true (otherwise the user will be unable to speak). - newPTT = true; - } - - if(mMuteMic) - { - // This always overrides any other PTT setting. - newPTT = false; - } - - // Dirty if state changed. - if(newPTT != mPTT) - { - mPTT = newPTT; - mPTTDirty = true; - } - } - - if(!inSpatialChannel()) - { - // When in a non-spatial channel, never send positional updates. - mSpatialCoordsDirty = false; - } - else - { - // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position) - enforceTether(); - } - - // Send an update only if the ptt or mute state has changed (which shouldn't be able to happen that often - // -- the user can only click so fast) or every 10hz, whichever is sooner. - // Sending for every volume update causes an excessive flood of messages whenever a volume slider is dragged. - if((mAudioSession && mAudioSession->mMuteDirty) || mPTTDirty || mUpdateTimer.hasExpired()) - { - mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); - sendPositionalUpdate(); - } - } - break; - - //MARK: stateLeavingSession - case stateLeavingSession: // waiting for terminate session response - // The handler for the Session.Terminate response will transition from here to stateSessionTerminated. - break; - - //MARK: stateSessionTerminated - case stateSessionTerminated: - - // Must do this first, since it uses mAudioSession. - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - - if(mAudioSession) - { - sessionState *oldSession = mAudioSession; - - mAudioSession = NULL; - // We just notified status observers about this change. Don't do it again. - mAudioSessionChanged = false; - - // The old session may now need to be deleted. - reapSession(oldSession); - } - else - { - LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL; - } - - // Always reset the terminate request flag when we get here. - mSessionTerminateRequested = false; - - if(mVoiceEnabled && !mRelogRequested) - { - // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state). - setState(stateNoChannel); - } - else - { - // Shutting down voice, continue with disconnecting. - logout(); - - // The state machine will take it from here - mRelogRequested = false; - } - - break; - - //MARK: stateLoggingOut - case stateLoggingOut: // waiting for logout response - // The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut. - break; - - //MARK: stateLoggedOut - case stateLoggedOut: // logout response received - - // Once we're logged out, all these things are invalid. - mAccountHandle.clear(); - deleteAllSessions(); - deleteAllBuddies(); - - if(mVoiceEnabled && !mRelogRequested) - { - // User was logged out, but wants to be logged in. Send a new login request. - setState(stateNeedsLogin); - } - else - { - // shut down the connector - connectorShutdown(); - } - break; - - //MARK: stateConnectorStopping - case stateConnectorStopping: // waiting for connector stop - // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped. - break; - - //MARK: stateConnectorStopped - case stateConnectorStopped: // connector stop received - setState(stateDisableCleanup); - break; - - //MARK: stateConnectorFailed - case stateConnectorFailed: - setState(stateConnectorFailedWaiting); - break; - //MARK: stateConnectorFailedWaiting - case stateConnectorFailedWaiting: - if(!mVoiceEnabled) - { - setState(stateDisableCleanup); - } - break; - - //MARK: stateLoginFailed - case stateLoginFailed: - setState(stateLoginFailedWaiting); - break; - //MARK: stateLoginFailedWaiting - case stateLoginFailedWaiting: - if(!mVoiceEnabled) - { - setState(stateDisableCleanup); - } - break; - - //MARK: stateJoinSessionFailed - case stateJoinSessionFailed: - // Transition to error state. Send out any notifications here. - if(mAudioSession) - { - LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL; - } - else - { - LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL; - } - - notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - setState(stateJoinSessionFailedWaiting); - break; - - //MARK: stateJoinSessionFailedWaiting - case stateJoinSessionFailedWaiting: - // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message. - // Region crossings may leave this state and try the join again. - if(mSessionTerminateRequested) - { - setState(stateSessionTerminated); - } - break; - - //MARK: stateJail - case stateJail: - // We have given up. Do nothing. - break; - - } - - if(mAudioSession && mAudioSession->mParticipantsChanged) - { - mAudioSession->mParticipantsChanged = false; - mAudioSessionChanged = true; - } - - if(mAudioSessionChanged) - { - mAudioSessionChanged = false; - notifyParticipantObservers(); - } -} - -void LLVoiceClient::closeSocket(void) -{ - mSocket.reset(); - mConnected = false; -} - -void LLVoiceClient::loginSendMessage() -{ - std::ostringstream stream; - - bool autoPostCrashDumps = gSavedSettings.getBOOL("VivoxAutoPostCrashDumps"); - - stream - << "" - << "" << mConnectorHandle << "" - << "" << mAccountName << "" - << "" << mAccountPassword << "" - << "VerifyAnswer" - << "true" - << "Application" - << "5" - << (autoPostCrashDumps?"true":"") - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::logout() -{ - // Ensure that we'll re-request provisioning before logging in again - mAccountPassword.clear(); - mVoiceAccountServerURI.clear(); - - setState(stateLoggingOut); - logoutSendMessage(); -} - -void LLVoiceClient::logoutSendMessage() -{ - if(!mAccountHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mAccountHandle << "" - << "" - << "\n\n\n"; - - mAccountHandle.clear(); - - writeString(stream.str()); - } -} - -void LLVoiceClient::accountListBlockRulesSendMessage() -{ - if(!mAccountHandle.empty()) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL; - - stream - << "" - << "" << mAccountHandle << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVoiceClient::accountListAutoAcceptRulesSendMessage() -{ - if(!mAccountHandle.empty()) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL; - - stream - << "" - << "" << mAccountHandle << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVoiceClient::sessionGroupCreateSendMessage() -{ - if(!mAccountHandle.empty()) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "creating session group" << LL_ENDL; - - stream - << "" - << "" << mAccountHandle << "" - << "Normal" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText) -{ - LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; - - session->mCreateInProgress = true; - if(startAudio) - { - session->mMediaConnectInProgress = true; - } - - std::ostringstream stream; - stream - << "mSIPURI << "\" action=\"Session.Create.1\">" - << "" << mAccountHandle << "" - << "" << session->mSIPURI << ""; - - static const std::string allowed_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789" - "-._~"; - - if(!session->mHash.empty()) - { - stream - << "" << LLURI::escape(session->mHash, allowed_chars) << "" - << "SHA1UserName"; - } - - stream - << "" << (startAudio?"true":"false") << "" - << "" << (startText?"true":"false") << "" - << "" << mChannelName << "" - << "\n\n\n"; - writeString(stream.str()); -} - -void LLVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText) -{ - LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; - - session->mCreateInProgress = true; - if(startAudio) - { - session->mMediaConnectInProgress = true; - } - - std::string password; - if(!session->mHash.empty()) - { - static const std::string allowed_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789" - "-._~" - ; - password = LLURI::escape(session->mHash, allowed_chars); - } - - std::ostringstream stream; - stream - << "mSIPURI << "\" action=\"SessionGroup.AddSession.1\">" - << "" << session->mGroupHandle << "" - << "" << session->mSIPURI << "" - << "" << mChannelName << "" - << "" << (startAudio?"true":"false") << "" - << "" << (startText?"true":"false") << "" - << "" << password << "" - << "SHA1UserName" - << "\n\n\n" - ; - - writeString(stream.str()); -} - -void LLVoiceClient::sessionMediaConnectSendMessage(sessionState *session) -{ - LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL; - - session->mMediaConnectInProgress = true; - - std::ostringstream stream; - - stream - << "mHandle << "\" action=\"Session.MediaConnect.1\">" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "Audio" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::sessionTextConnectSendMessage(sessionState *session) -{ - LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL; - - std::ostringstream stream; - - stream - << "mHandle << "\" action=\"Session.TextConnect.1\">" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::sessionTerminate() -{ - mSessionTerminateRequested = true; -} - -void LLVoiceClient::requestRelog() -{ - mSessionTerminateRequested = true; - mRelogRequested = true; -} - - -void LLVoiceClient::leaveAudioSession() -{ - if(mAudioSession) - { - LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; - - switch(getState()) - { - case stateNoChannel: - // In this case, we want to pretend the join failed so our state machine doesn't get stuck. - // Skip the join failed transition state so we don't send out error notifications. - setState(stateJoinSessionFailedWaiting); - break; - case stateJoiningSession: - case stateSessionJoined: - case stateRunning: - if(!mAudioSession->mHandle.empty()) - { - -#if RECORD_EVERYTHING - // HACK: for testing only - // Save looped recording - std::string savepath("/tmp/vivoxrecording"); - { - time_t now = time(NULL); - const size_t BUF_SIZE = 64; - char time_str[BUF_SIZE]; /* Flawfinder: ignore */ - - strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); - savepath += time_str; - } - recordingLoopSave(savepath); -#endif - - sessionMediaDisconnectSendMessage(mAudioSession); - setState(stateLeavingSession); - } - else - { - LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; - setState(stateSessionTerminated); - } - break; - case stateJoinSessionFailed: - case stateJoinSessionFailedWaiting: - setState(stateSessionTerminated); - break; - - default: - LL_WARNS("Voice") << "called from unknown state" << LL_ENDL; - break; - } - } - else - { - LL_WARNS("Voice") << "called with no active session" << LL_ENDL; - setState(stateSessionTerminated); - } -} - -void LLVoiceClient::sessionTerminateSendMessage(sessionState *session) -{ - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL; - stream - << "" - << "" << session->mHandle << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::sessionGroupTerminateSendMessage(sessionState *session) -{ - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL; - stream - << "" - << "" << session->mGroupHandle << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session) -{ - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL; - stream - << "" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "Audio" - << "\n\n\n"; - - writeString(stream.str()); - -} - -void LLVoiceClient::sessionTextDisconnectSendMessage(sessionState *session) -{ - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL; - stream - << "" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::getCaptureDevicesSendMessage() -{ - std::ostringstream stream; - stream - << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::getRenderDevicesSendMessage() -{ - std::ostringstream stream; - stream - << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::clearCaptureDevices() -{ - LL_DEBUGS("Voice") << "called" << LL_ENDL; - mCaptureDevices.clear(); -} - -void LLVoiceClient::addCaptureDevice(const std::string& name) -{ - LL_DEBUGS("Voice") << name << LL_ENDL; - - mCaptureDevices.push_back(name); -} - -LLVoiceClient::deviceList *LLVoiceClient::getCaptureDevices() -{ - return &mCaptureDevices; -} - -void LLVoiceClient::setCaptureDevice(const std::string& name) -{ - if(name == "Default") - { - if(!mCaptureDevice.empty()) - { - mCaptureDevice.clear(); - mCaptureDeviceDirty = true; - } - } - else - { - if(mCaptureDevice != name) - { - mCaptureDevice = name; - mCaptureDeviceDirty = true; - } - } -} - -void LLVoiceClient::clearRenderDevices() -{ - LL_DEBUGS("Voice") << "called" << LL_ENDL; - mRenderDevices.clear(); -} - -void LLVoiceClient::addRenderDevice(const std::string& name) -{ - LL_DEBUGS("Voice") << name << LL_ENDL; - mRenderDevices.push_back(name); -} - -LLVoiceClient::deviceList *LLVoiceClient::getRenderDevices() -{ - return &mRenderDevices; -} - -void LLVoiceClient::setRenderDevice(const std::string& name) -{ - if(name == "Default") - { - if(!mRenderDevice.empty()) - { - mRenderDevice.clear(); - mRenderDeviceDirty = true; - } - } - else - { - if(mRenderDevice != name) - { - mRenderDevice = name; - mRenderDeviceDirty = true; - } - } - -} - -void LLVoiceClient::tuningStart() -{ - mTuningMode = true; - if(getState() >= stateNoChannel) - { - sessionTerminate(); - } -} - -void LLVoiceClient::tuningStop() -{ - mTuningMode = false; -} - -bool LLVoiceClient::inTuningMode() -{ - bool result = false; - switch(getState()) - { - case stateMicTuningRunning: - result = true; - break; - default: - break; - } - return result; -} - -void LLVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop) -{ - mTuningAudioFile = name; - std::ostringstream stream; - stream - << "" - << "" << mTuningAudioFile << "" - << "" << (loop?"1":"0") << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::tuningRenderStopSendMessage() -{ - std::ostringstream stream; - stream - << "" - << "" << mTuningAudioFile << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::tuningCaptureStartSendMessage(int duration) -{ - LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL; - - std::ostringstream stream; - stream - << "" - << "" << duration << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::tuningCaptureStopSendMessage() -{ - LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL; - - std::ostringstream stream; - stream - << "" - << "\n\n\n"; - - writeString(stream.str()); - - mTuningEnergy = 0.0f; -} - -void LLVoiceClient::tuningSetMicVolume(float volume) -{ - int scaled_volume = scale_mic_volume(volume); - - if(scaled_volume != mTuningMicVolume) - { - mTuningMicVolume = scaled_volume; - mTuningMicVolumeDirty = true; - } -} - -void LLVoiceClient::tuningSetSpeakerVolume(float volume) -{ - int scaled_volume = scale_speaker_volume(volume); - - if(scaled_volume != mTuningSpeakerVolume) - { - mTuningSpeakerVolume = scaled_volume; - mTuningSpeakerVolumeDirty = true; - } -} - -float LLVoiceClient::tuningGetEnergy(void) -{ - return mTuningEnergy; -} - -bool LLVoiceClient::deviceSettingsAvailable() -{ - bool result = true; - - if(!mConnected) - result = false; - - if(mRenderDevices.empty()) - result = false; - - return result; -} - -void LLVoiceClient::refreshDeviceLists(bool clearCurrentList) -{ - if(clearCurrentList) - { - clearCaptureDevices(); - clearRenderDevices(); - } - getCaptureDevicesSendMessage(); - getRenderDevicesSendMessage(); -} - -void LLVoiceClient::daemonDied() -{ - // The daemon died, so the connection is gone. Reset everything and start over. - LL_WARNS("Voice") << "Connection to vivox daemon lost. Resetting state."<< LL_ENDL; - - // Try to relaunch the daemon - setState(stateDisableCleanup); -} - -void LLVoiceClient::giveUp() -{ - // All has failed. Clean up and stop trying. - closeSocket(); - deleteAllSessions(); - deleteAllBuddies(); - - setState(stateJail); -} - -static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel) -{ - F32 nat[3], nup[3], nl[3], nvel[3]; // the new at, up, left vectors and the new position and velocity - F64 npos[3]; - - // The original XML command was sent like this: - /* - << "" - << "" << pos[VX] << "" - << "" << pos[VZ] << "" - << "" << pos[VY] << "" - << "" - << "" - << "" << mAvatarVelocity[VX] << "" - << "" << mAvatarVelocity[VZ] << "" - << "" << mAvatarVelocity[VY] << "" - << "" - << "" - << "" << l.mV[VX] << "" - << "" << u.mV[VX] << "" - << "" << a.mV[VX] << "" - << "" - << "" - << "" << l.mV[VZ] << "" - << "" << u.mV[VY] << "" - << "" << a.mV[VZ] << "" - << "" - << "" - << "" << l.mV [VY] << "" - << "" << u.mV [VZ] << "" - << "" << a.mV [VY] << "" - << ""; - */ - -#if 1 - // This was the original transform done when building the XML command - nat[0] = left.mV[VX]; - nat[1] = up.mV[VX]; - nat[2] = at.mV[VX]; - - nup[0] = left.mV[VZ]; - nup[1] = up.mV[VY]; - nup[2] = at.mV[VZ]; - - nl[0] = left.mV[VY]; - nl[1] = up.mV[VZ]; - nl[2] = at.mV[VY]; - - npos[0] = pos.mdV[VX]; - npos[1] = pos.mdV[VZ]; - npos[2] = pos.mdV[VY]; - - nvel[0] = vel.mV[VX]; - nvel[1] = vel.mV[VZ]; - nvel[2] = vel.mV[VY]; - - for(int i=0;i<3;++i) { - at.mV[i] = nat[i]; - up.mV[i] = nup[i]; - left.mV[i] = nl[i]; - pos.mdV[i] = npos[i]; - } - - // This was the original transform done in the SDK - nat[0] = at.mV[2]; - nat[1] = 0; // y component of at vector is always 0, this was up[2] - nat[2] = -1 * left.mV[2]; - - // We override whatever the application gives us - nup[0] = 0; // x component of up vector is always 0 - nup[1] = 1; // y component of up vector is always 1 - nup[2] = 0; // z component of up vector is always 0 - - nl[0] = at.mV[0]; - nl[1] = 0; // y component of left vector is always zero, this was up[0] - nl[2] = -1 * left.mV[0]; - - npos[2] = pos.mdV[2] * -1.0; - npos[1] = pos.mdV[1]; - npos[0] = pos.mdV[0]; - - for(int i=0;i<3;++i) { - at.mV[i] = nat[i]; - up.mV[i] = nup[i]; - left.mV[i] = nl[i]; - pos.mdV[i] = npos[i]; - } -#else - // This is the compose of the two transforms (at least, that's what I'm trying for) - nat[0] = at.mV[VX]; - nat[1] = 0; // y component of at vector is always 0, this was up[2] - nat[2] = -1 * up.mV[VZ]; - - // We override whatever the application gives us - nup[0] = 0; // x component of up vector is always 0 - nup[1] = 1; // y component of up vector is always 1 - nup[2] = 0; // z component of up vector is always 0 - - nl[0] = left.mV[VX]; - nl[1] = 0; // y component of left vector is always zero, this was up[0] - nl[2] = -1 * left.mV[VY]; - - npos[0] = pos.mdV[VX]; - npos[1] = pos.mdV[VZ]; - npos[2] = pos.mdV[VY] * -1.0; - - nvel[0] = vel.mV[VX]; - nvel[1] = vel.mV[VZ]; - nvel[2] = vel.mV[VY]; - - for(int i=0;i<3;++i) { - at.mV[i] = nat[i]; - up.mV[i] = nup[i]; - left.mV[i] = nl[i]; - pos.mdV[i] = npos[i]; - } - -#endif -} - -void LLVoiceClient::sendPositionalUpdate(void) -{ - std::ostringstream stream; - - if(mSpatialCoordsDirty) - { - LLVector3 l, u, a, vel; - LLVector3d pos; - - mSpatialCoordsDirty = false; - - // Always send both speaker and listener positions together. - stream << "" - << "" << getAudioSessionHandle() << ""; - - stream << ""; - -// LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; - l = mAvatarRot.getLeftRow(); - u = mAvatarRot.getUpRow(); - a = mAvatarRot.getFwdRow(); - pos = mAvatarPosition; - vel = mAvatarVelocity; - - // SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore. - // The old transform is replicated by this function. - oldSDKTransform(l, u, a, pos, vel); - - stream - << "" - << "" << pos.mdV[VX] << "" - << "" << pos.mdV[VY] << "" - << "" << pos.mdV[VZ] << "" - << "" - << "" - << "" << vel.mV[VX] << "" - << "" << vel.mV[VY] << "" - << "" << vel.mV[VZ] << "" - << "" - << "" - << "" << a.mV[VX] << "" - << "" << a.mV[VY] << "" - << "" << a.mV[VZ] << "" - << "" - << "" - << "" << u.mV[VX] << "" - << "" << u.mV[VY] << "" - << "" << u.mV[VZ] << "" - << "" - << "" - << "" << l.mV [VX] << "" - << "" << l.mV [VY] << "" - << "" << l.mV [VZ] << "" - << ""; - - stream << ""; - - stream << ""; - - LLVector3d earPosition; - LLVector3 earVelocity; - LLMatrix3 earRot; - - switch(mEarLocation) - { - case earLocCamera: - default: - earPosition = mCameraPosition; - earVelocity = mCameraVelocity; - earRot = mCameraRot; - break; - - case earLocAvatar: - earPosition = mAvatarPosition; - earVelocity = mAvatarVelocity; - earRot = mAvatarRot; - break; - - case earLocMixed: - earPosition = mAvatarPosition; - earVelocity = mAvatarVelocity; - earRot = mCameraRot; - break; - } - - l = earRot.getLeftRow(); - u = earRot.getUpRow(); - a = earRot.getFwdRow(); - pos = earPosition; - vel = earVelocity; - -// LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL; - - oldSDKTransform(l, u, a, pos, vel); - - stream - << "" - << "" << pos.mdV[VX] << "" - << "" << pos.mdV[VY] << "" - << "" << pos.mdV[VZ] << "" - << "" - << "" - << "" << vel.mV[VX] << "" - << "" << vel.mV[VY] << "" - << "" << vel.mV[VZ] << "" - << "" - << "" - << "" << a.mV[VX] << "" - << "" << a.mV[VY] << "" - << "" << a.mV[VZ] << "" - << "" - << "" - << "" << u.mV[VX] << "" - << "" << u.mV[VY] << "" - << "" << u.mV[VZ] << "" - << "" - << "" - << "" << l.mV [VX] << "" - << "" << l.mV [VY] << "" - << "" << l.mV [VZ] << "" - << ""; - - - stream << ""; - - stream << "\n\n\n"; - } - - if(mAudioSession && (mAudioSession->mVolumeDirty || mAudioSession->mMuteDirty)) - { - participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); - - mAudioSession->mVolumeDirty = false; - mAudioSession->mMuteDirty = false; - - for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) - { - participantState *p = iter->second; - - if(p->mVolumeDirty) - { - // Can't set volume/mute for yourself - if(!p->mIsSelf) - { - // scale from the range 0.0-1.0 to vivox volume in the range 0-100 - S32 volume = llround(p->mVolume / VOLUME_SCALE_VIVOX); - - bool mute = p->mOnMuteList; - - if(mute) - { - // SetParticipantMuteForMe doesn't work in p2p sessions. - // If we want the user to be muted, set their volume to 0 as well. - // This isn't perfect, but it will at least reduce their volume to a minimum. - volume = 0; - - // Mark the current volume level as set to prevent incoming events - // changing it to 0, so that we can return to it when unmuting. - p->mVolumeSet = true; - } - - if(volume == 0) - { - mute = true; - } - - LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL; - - // SLIM SDK: Send both volume and mute commands. - - // Send a "volume for me" command for the user. - stream << "" - << "" << getAudioSessionHandle() << "" - << "" << p->mURI << "" - << "" << volume << "" - << "\n\n\n"; - - if(!mAudioSession->mIsP2P) - { - // Send a "mute for me" command for the user - // Doesn't work in P2P sessions - stream << "" - << "" << getAudioSessionHandle() << "" - << "" << p->mURI << "" - << "" << (mute?"1":"0") << "" - << "Audio" - << "\n\n\n"; - } - } - - p->mVolumeDirty = false; - } - } - } - - buildLocalAudioUpdates(stream); - - if(!stream.str().empty()) - { - writeString(stream.str()); - } - - // Friends list updates can be huge, especially on the first voice login of an account with lots of friends. - // Batching them all together can choke SLVoice, so send them in separate writes. - sendFriendsListUpdates(); -} - -void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream) -{ - if(mCaptureDeviceDirty) - { - LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL; - - stream - << "" - << "" << mCaptureDevice << "" - << "" - << "\n\n\n"; - - mCaptureDeviceDirty = false; - } -} - -void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream) -{ - if(mRenderDeviceDirty) - { - LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL; - - stream - << "" - << "" << mRenderDevice << "" - << "" - << "\n\n\n"; - mRenderDeviceDirty = false; - } -} - -void LLVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream) -{ - buildSetCaptureDevice(stream); - - buildSetRenderDevice(stream); - - if(mPTTDirty) - { - mPTTDirty = false; - - // Send a local mute command. - // NOTE that the state of "PTT" is the inverse of "local mute". - // (i.e. when PTT is true, we send a mute command with "false", and vice versa) - - LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << LL_ENDL; - - stream << "" - << "" << mConnectorHandle << "" - << "" << (mPTT?"false":"true") << "" - << "\n\n\n"; - - } - - if(mSpeakerMuteDirty) - { - const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0))?"true":"false"); - - mSpeakerMuteDirty = false; - - LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL; - - stream << "" - << "" << mConnectorHandle << "" - << "" << muteval << "" - << "\n\n\n"; - - } - - if(mSpeakerVolumeDirty) - { - mSpeakerVolumeDirty = false; - - LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL; - - stream << "" - << "" << mConnectorHandle << "" - << "" << mSpeakerVolume << "" - << "\n\n\n"; - - } - - if(mMicVolumeDirty) - { - mMicVolumeDirty = false; - - LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL; - - stream << "" - << "" << mConnectorHandle << "" - << "" << mMicVolume << "" - << "\n\n\n"; - } - - -} - -void LLVoiceClient::checkFriend(const LLUUID& id) -{ - std::string name; - buddyListEntry *buddy = findBuddy(id); - - // Make sure we don't add a name before it's been looked up. - if(gCacheName->getFullName(id, name)) - { - - const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id); - bool canSeeMeOnline = false; - if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)) - canSeeMeOnline = true; - - // When we get here, mNeedsSend is true and mInSLFriends is false. Change them as necessary. - - if(buddy) - { - // This buddy is already in both lists. - - if(name != buddy->mDisplayName) - { - // The buddy is in the list with the wrong name. Update it with the correct name. - LL_WARNS("Voice") << "Buddy " << id << " has wrong name (\"" << buddy->mDisplayName << "\" should be \"" << name << "\"), updating."<< LL_ENDL; - buddy->mDisplayName = name; - buddy->mNeedsNameUpdate = true; // This will cause the buddy to be resent. - } - } - else - { - // This buddy was not in the vivox list, needs to be added. - buddy = addBuddy(sipURIFromID(id), name); - buddy->mUUID = id; - } - - // In all the above cases, the buddy is in the SL friends list (which is how we got here). - buddy->mInSLFriends = true; - buddy->mCanSeeMeOnline = canSeeMeOnline; - buddy->mNameResolved = true; - - } - else - { - // This name hasn't been looked up yet. Don't do anything with this buddy list entry until it has. - if(buddy) - { - buddy->mNameResolved = false; - } - - // Initiate a lookup. - // The "lookup completed" callback will ensure that the friends list is rechecked after it completes. - lookupName(id); - } -} - -void LLVoiceClient::clearAllLists() -{ - // FOR TESTING ONLY - - // This will send the necessary commands to delete ALL buddies, autoaccept rules, and block rules SLVoice tells us about. - buddyListMap::iterator buddy_it; - for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();) - { - buddyListEntry *buddy = buddy_it->second; - buddy_it++; - - std::ostringstream stream; - - if(buddy->mInVivoxBuddies) - { - // delete this entry from the vivox buddy list - buddy->mInVivoxBuddies = false; - LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - } - - if(buddy->mHasBlockListEntry) - { - // Delete the associated block list entry (so the block list doesn't fill up with junk) - buddy->mHasBlockListEntry = false; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - } - if(buddy->mHasAutoAcceptListEntry) - { - // Delete the associated auto-accept list entry (so the auto-accept list doesn't fill up with junk) - buddy->mHasAutoAcceptListEntry = false; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - } - - writeString(stream.str()); - - } -} - -void LLVoiceClient::sendFriendsListUpdates() -{ - if(mBuddyListMapPopulated && mBlockRulesListReceived && mAutoAcceptRulesListReceived && mFriendsListDirty) - { - mFriendsListDirty = false; - - if(0) - { - // FOR TESTING ONLY -- clear all buddy list, block list, and auto-accept list entries. - clearAllLists(); - return; - } - - LL_INFOS("Voice") << "Checking vivox buddy list against friends list..." << LL_ENDL; - - buddyListMap::iterator buddy_it; - for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) - { - // reset the temp flags in the local buddy list - buddy_it->second->mInSLFriends = false; - } - - // correlate with the friends list - { - LLCollectAllBuddies collect; - LLAvatarTracker::instance().applyFunctor(collect); - LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin(); - LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end(); - - for ( ; it != end; ++it) - { - checkFriend(it->second); - } - it = collect.mOffline.begin(); - end = collect.mOffline.end(); - for ( ; it != end; ++it) - { - checkFriend(it->second); - } - } - - LL_INFOS("Voice") << "Sending friend list updates..." << LL_ENDL; - - for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();) - { - buddyListEntry *buddy = buddy_it->second; - buddy_it++; - - // Ignore entries that aren't resolved yet. - if(buddy->mNameResolved) - { - std::ostringstream stream; - - if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate)) - { - if(mNumberOfAliases > 0) - { - // Add (or update) this entry in the vivox buddy list - buddy->mInVivoxBuddies = true; - buddy->mNeedsNameUpdate = false; - LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; - stream - << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "" << buddy->mDisplayName << "" - << "" // Without this, SLVoice doesn't seem to parse the command. - << "0" - << "\n\n\n"; - } - } - else if(!buddy->mInSLFriends) - { - // This entry no longer exists in your SL friends list. Remove all traces of it from the Vivox buddy list. - if(buddy->mInVivoxBuddies) - { - // delete this entry from the vivox buddy list - buddy->mInVivoxBuddies = false; - LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - } - - if(buddy->mHasBlockListEntry) - { - // Delete the associated block list entry, if any - buddy->mHasBlockListEntry = false; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - } - if(buddy->mHasAutoAcceptListEntry) - { - // Delete the associated auto-accept list entry, if any - buddy->mHasAutoAcceptListEntry = false; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - } - } - - if(buddy->mInSLFriends) - { - - if(buddy->mCanSeeMeOnline) - { - // Buddy should not be blocked. - - // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent. - - // If the buddy has a block list entry, delete it. - if(buddy->mHasBlockListEntry) - { - buddy->mHasBlockListEntry = false; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - - - // If we just deleted a block list entry, add an auto-accept entry. - if(!buddy->mHasAutoAcceptListEntry) - { - buddy->mHasAutoAcceptListEntry = true; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "0" - << "\n\n\n"; - } - } - } - else - { - // Buddy should be blocked. - - // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent. - - // If this buddy has an autoaccept entry, delete it - if(buddy->mHasAutoAcceptListEntry) - { - buddy->mHasAutoAcceptListEntry = false; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - - // If we just deleted an auto-accept entry, add a block list entry. - if(!buddy->mHasBlockListEntry) - { - buddy->mHasBlockListEntry = true; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "1" - << "\n\n\n"; - } - } - } - - if(!buddy->mInSLFriends && !buddy->mInVivoxBuddies) - { - // Delete this entry from the local buddy list. This should NOT invalidate the iterator, - // since it has already been incremented to the next entry. - deleteBuddy(buddy->mURI); - } - - } - writeString(stream.str()); - } - } - } -} - -///////////////////////////// -// Response/Event handlers - -void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID) -{ - if(statusCode != 0) - { - LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL; - setState(stateConnectorFailed); - } - else - { - // Connector created, move forward. - LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL; - mAPIVersion = versionID; - mConnectorHandle = connectorHandle; - if(getState() == stateConnectorStarting) - { - setState(stateConnectorStarted); - } - } -} - -void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases) -{ - LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL; - - // Status code of 20200 means "bad password". We may want to special-case that at some point. - - if ( statusCode == 401 ) - { - // Login failure which is probably caused by the delay after a user's password being updated. - LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; - setState(stateLoginRetry); - } - else if(statusCode != 0) - { - LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; - setState(stateLoginFailed); - } - else - { - // Login succeeded, move forward. - mAccountHandle = accountHandle; - mNumberOfAliases = numberOfAliases; - // This needs to wait until the AccountLoginStateChangeEvent is received. -// if(getState() == stateLoggingIn) -// { -// setState(stateLoggedIn); -// } - } -} - -void LLVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) -{ - sessionState *session = findSessionBeingCreatedByURI(requestId); - - if(session) - { - session->mCreateInProgress = false; - } - - if(statusCode != 0) - { - LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL; - if(session) - { - session->mErrorStatusCode = statusCode; - session->mErrorStatusString = statusString; - if(session == mAudioSession) - { - setState(stateJoinSessionFailed); - } - else - { - reapSession(session); - } - } - } - else - { - LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL; - if(session) - { - setSessionHandle(session, sessionHandle); - } - } -} - -void LLVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) -{ - sessionState *session = findSessionBeingCreatedByURI(requestId); - - if(session) - { - session->mCreateInProgress = false; - } - - if(statusCode != 0) - { - LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL; - if(session) - { - session->mErrorStatusCode = statusCode; - session->mErrorStatusString = statusString; - if(session == mAudioSession) - { - setState(stateJoinSessionFailed); - } - else - { - reapSession(session); - } - } - } - else - { - LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL; - if(session) - { - setSessionHandle(session, sessionHandle); - } - } -} - -void LLVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString) -{ - sessionState *session = findSession(requestId); - if(statusCode != 0) - { - LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; - if(session) - { - session->mMediaConnectInProgress = false; - session->mErrorStatusCode = statusCode; - session->mErrorStatusString = statusString; - if(session == mAudioSession) - setState(stateJoinSessionFailed); - } - } - else - { - LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL; - } -} - -void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString) -{ - if(statusCode != 0) - { - LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; - // Should this ever fail? do we care if it does? - } -} - -void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString) -{ - if(statusCode != 0) - { - LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL; - // Should this ever fail? do we care if it does? - } - - mConnected = false; - - if(getState() == stateConnectorStopping) - { - setState(stateConnectorStopped); - } -} - -void LLVoiceClient::sessionAddedEvent( - std::string &uriString, - std::string &alias, - std::string &sessionHandle, - std::string &sessionGroupHandle, - bool isChannel, - bool incoming, - std::string &nameString, - std::string &applicationString) -{ - sessionState *session = NULL; - - LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL; - - session = addSession(uriString, sessionHandle); - if(session) - { - session->mGroupHandle = sessionGroupHandle; - session->mIsChannel = isChannel; - session->mIncoming = incoming; - session->mAlias = alias; - - // Generate a caller UUID -- don't need to do this for channels - if(!session->mIsChannel) - { - if(IDFromName(session->mSIPURI, session->mCallerID)) - { - // Normal URI(base64-encoded UUID) - } - else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID)) - { - // Wrong URI, but an alias is available. Stash the incoming URI as an alternate - session->mAlternateSIPURI = session->mSIPURI; - - // and generate a proper URI from the ID. - setSessionURI(session, sipURIFromID(session->mCallerID)); - } - else - { - LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL; - setUUIDFromStringHash(session->mCallerID, session->mSIPURI); - session->mSynthesizedCallerID = true; - - // Can't look up the name in this case -- we have to extract it from the URI. - std::string namePortion = nameFromsipURI(session->mSIPURI); - if(namePortion.empty()) - { - // Didn't seem to be a SIP URI, just use the whole provided name. - namePortion = nameString; - } - - // Some incoming names may be separated with an underscore instead of a space. Fix this. - LLStringUtil::replaceChar(namePortion, '_', ' '); - - // Act like we just finished resolving the name (this stores it in all the right places) - avatarNameResolved(session->mCallerID, namePortion); - } - - LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL; - - if(!session->mSynthesizedCallerID) - { - // If we got here, we don't have a proper name. Initiate a lookup. - lookupName(session->mCallerID); - } - } - } -} - -void LLVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) -{ - LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL; - -#if USE_SESSION_GROUPS - if(mMainSessionGroupHandle.empty()) - { - // This is the first (i.e. "main") session group. Save its handle. - mMainSessionGroupHandle = sessionGroupHandle; - } - else - { - LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL; - } -#endif -} - -void LLVoiceClient::joinedAudioSession(sessionState *session) -{ - if(mAudioSession != session) - { - sessionState *oldSession = mAudioSession; - - mAudioSession = session; - mAudioSessionChanged = true; - - // The old session may now need to be deleted. - reapSession(oldSession); - } - - // This is the session we're joining. - if(getState() == stateJoiningSession) - { - setState(stateSessionJoined); - - // SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now. - // Add the current user as a participant here. - participantState *participant = session->addParticipant(sipURIFromName(mAccountName)); - if(participant) - { - participant->mIsSelf = true; - lookupName(participant->mAvatarID); - - LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName - << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; - } - - if(!session->mIsChannel) - { - // this is a p2p session. Make sure the other end is added as a participant. - participantState *participant = session->addParticipant(session->mSIPURI); - if(participant) - { - if(participant->mAvatarIDValid) - { - lookupName(participant->mAvatarID); - } - else if(!session->mName.empty()) - { - participant->mDisplayName = session->mName; - avatarNameResolved(participant->mAvatarID, session->mName); - } - - // TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here? - LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName - << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; - } - } - } -} - -void LLVoiceClient::sessionRemovedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle) -{ - LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL; - - sessionState *session = findSession(sessionHandle); - if(session) - { - leftAudioSession(session); - - // This message invalidates the session's handle. Set it to empty. - setSessionHandle(session); - - // This also means that the session's session group is now empty. - // Terminate the session group so it doesn't leak. - sessionGroupTerminateSendMessage(session); - - // Reset the media state (we now have no info) - session->mMediaStreamState = streamStateUnknown; - session->mTextStreamState = streamStateUnknown; - - // Conditionally delete the session - reapSession(session); - } - else - { - LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL; - } -} - -void LLVoiceClient::reapSession(sessionState *session) -{ - if(session) - { - if(!session->mHandle.empty()) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL; - } - else if(session->mCreateInProgress) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL; - } - else if(session->mMediaConnectInProgress) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL; - } - else if(session == mAudioSession) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL; - } - else if(session == mNextAudioSession) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL; - } - else - { - // TODO: Question: Should we check for queued text messages here? - // We don't have a reason to keep tracking this session, so just delete it. - LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL; - deleteSession(session); - session = NULL; - } - } - else - { -// LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL; - } -} - -// Returns true if the session seems to indicate we've moved to a region on a different voice server -bool LLVoiceClient::sessionNeedsRelog(sessionState *session) -{ - bool result = false; - - if(session != NULL) - { - // Only make this check for spatial channels (so it won't happen for group or p2p calls) - if(session->mIsSpatial) - { - std::string::size_type atsign; - - atsign = session->mSIPURI.find("@"); - - if(atsign != std::string::npos) - { - std::string urihost = session->mSIPURI.substr(atsign + 1); - if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str())) - { - // The hostname in this URI is different from what we expect. This probably means we need to relog. - - // We could make a ProvisionVoiceAccountRequest and compare the result with the current values of - // mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator. - - result = true; - } - } - } - } - - return result; -} - -void LLVoiceClient::leftAudioSession( - sessionState *session) -{ - if(mAudioSession == session) - { - switch(getState()) - { - case stateJoiningSession: - case stateSessionJoined: - case stateRunning: - case stateLeavingSession: - case stateJoinSessionFailed: - case stateJoinSessionFailedWaiting: - // normal transition - LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; - setState(stateSessionTerminated); - break; - - case stateSessionTerminated: - // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state. - LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; - break; - - default: - LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL; - setState(stateSessionTerminated); - break; - } - } -} - -void LLVoiceClient::accountLoginStateChangeEvent( - std::string &accountHandle, - int statusCode, - std::string &statusString, - int state) -{ - LL_DEBUGS("Voice") << "state is " << state << LL_ENDL; - /* - According to Mike S., status codes for this event are: - login_state_logged_out=0, - login_state_logged_in = 1, - login_state_logging_in = 2, - login_state_logging_out = 3, - login_state_resetting = 4, - login_state_error=100 - */ - - switch(state) - { - case 1: - if(getState() == stateLoggingIn) - { - setState(stateLoggedIn); - } - break; - - case 3: - // The user is in the process of logging out. - setState(stateLoggingOut); - break; - - case 0: - // The user has been logged out. - setState(stateLoggedOut); - break; - - default: - //Used to be a commented out warning - LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL; - break; - } -} - -void LLVoiceClient::mediaStreamUpdatedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - int statusCode, - std::string &statusString, - int state, - bool incoming) -{ - sessionState *session = findSession(sessionHandle); - - LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL; - - if(session) - { - // We know about this session - - // Save the state for later use - session->mMediaStreamState = state; - - switch(statusCode) - { - case 0: - case 200: - // generic success - // Don't change the saved error code (it may have been set elsewhere) - break; - default: - // save the status code for later - session->mErrorStatusCode = statusCode; - break; - } - - switch(state) - { - case streamStateIdle: - // Standard "left audio session" - session->mVoiceEnabled = false; - session->mMediaConnectInProgress = false; - leftAudioSession(session); - break; - - case streamStateConnected: - session->mVoiceEnabled = true; - session->mMediaConnectInProgress = false; - joinedAudioSession(session); - break; - - case streamStateRinging: - if(incoming) - { - // Send the voice chat invite to the GUI layer - // *TODO: Question: Should we correlate with the mute list here? - session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID); - session->mVoiceInvitePending = true; - if(session->mName.empty()) - { - lookupName(session->mCallerID); - } - else - { - // Act like we just finished resolving the name - avatarNameResolved(session->mCallerID, session->mName); - } - } - break; - - default: - LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; - break; - - } - - } - else - { - LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL; - } -} - -void LLVoiceClient::textStreamUpdatedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - bool enabled, - int state, - bool incoming) -{ - sessionState *session = findSession(sessionHandle); - - if(session) - { - // Save the state for later use - session->mTextStreamState = state; - - // We know about this session - switch(state) - { - case 0: // We see this when the text stream closes - LL_DEBUGS("Voice") << "stream closed" << LL_ENDL; - break; - - case 1: // We see this on an incoming call from the Connector - // Try to send any text messages queued for this session. - sendQueuedTextMessages(session); - - // Send the text chat invite to the GUI layer - // TODO: Question: Should we correlate with the mute list here? - session->mTextInvitePending = true; - if(session->mName.empty()) - { - lookupName(session->mCallerID); - } - else - { - // Act like we just finished resolving the name - avatarNameResolved(session->mCallerID, session->mName); - } - break; - - default: - LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; - break; - - } - } -} - -void LLVoiceClient::participantAddedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, - std::string &nameString, - std::string &displayNameString, - int participantType) -{ - sessionState *session = findSession(sessionHandle); - if(session) - { - participantState *participant = session->addParticipant(uriString); - if(participant) - { - participant->mAccountName = nameString; - - LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName - << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; - - if(participant->mAvatarIDValid) - { - // Initiate a lookup - lookupName(participant->mAvatarID); - } - else - { - // If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work. - std::string namePortion = nameFromsipURI(uriString); - if(namePortion.empty()) - { - // Problem with the SIP URI, fall back to the display name - namePortion = displayNameString; - } - if(namePortion.empty()) - { - // Problems with both of the above, fall back to the account name - namePortion = nameString; - } - - // Set the display name (which is a hint to the active speakers window not to do its own lookup) - participant->mDisplayName = namePortion; - avatarNameResolved(participant->mAvatarID, namePortion); - } - } - } -} - -void LLVoiceClient::participantRemovedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, - std::string &nameString) -{ - sessionState *session = findSession(sessionHandle); - if(session) - { - participantState *participant = session->findParticipant(uriString); - if(participant) - { - session->removeParticipant(participant); - } - else - { - LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL; - } - } - else - { - LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; - } -} - - -void LLVoiceClient::participantUpdatedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, - bool isModeratorMuted, - bool isSpeaking, - int volume, - F32 energy) -{ - sessionState *session = findSession(sessionHandle); - if(session) - { - participantState *participant = session->findParticipant(uriString); - - if(participant) - { - participant->mIsSpeaking = isSpeaking; - participant->mIsModeratorMuted = isModeratorMuted; - - // SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false - if (isSpeaking) - { - participant->mSpeakingTimeout.reset(); - participant->mPower = energy; - } - else - { - participant->mPower = 0.0f; - } - - // Ignore incoming volume level if it has been explicitly set, or there - // is a volume or mute change pending. - if ( !participant->mVolumeSet && !participant->mVolumeDirty) - { - participant->mVolume = (F32)volume * VOLUME_SCALE_VIVOX; - } - - // *HACK: mantipov: added while working on EXT-3544 - /* - Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE - LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER. - - participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted - Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug. - Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates. - - But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post() - voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager - and event is not fired. - - So, we have to call LLSpeakerMgr::update() here. In any case it is better than call it - in LLCallFloater::draw() - */ - LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel(); - - // ignore session ID of local chat - if (voice_cnl && voice_cnl->getSessionID().notNull()) - { - LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID()); - if (speaker_manager) - { - speaker_manager->update(true); - } - } - } - else - { - LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL; - } - } - else - { - LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL; - } -} - -void LLVoiceClient::buddyPresenceEvent( - std::string &uriString, - std::string &alias, - std::string &statusString, - std::string &applicationString) -{ - buddyListEntry *buddy = findBuddy(uriString); - - if(buddy) - { - LL_DEBUGS("Voice") << "Presence event for " << buddy->mDisplayName << " status \"" << statusString << "\", application \"" << applicationString << "\""<< LL_ENDL; - LL_DEBUGS("Voice") << "before: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL; - - if(applicationString.empty()) - { - // This presence event is from a client that doesn't set up the Application string. Do things the old-skool way. - // NOTE: this will be needed to support people who aren't on the 3010-class SDK yet. - - if ( stricmp("Unknown", statusString.c_str())== 0) - { - // User went offline with a non-SLim-enabled viewer. - buddy->mOnlineSL = false; - } - else if ( stricmp("Online", statusString.c_str())== 0) - { - // User came online with a non-SLim-enabled viewer. - buddy->mOnlineSL = true; - } - else - { - // If the user is online through SLim, their status will be "Online-slc", "Away", or something else. - // NOTE: we should never see this unless someone is running an OLD version of SLim -- the versions that should be in use now all set the application string. - buddy->mOnlineSLim = true; - } - } - else if(applicationString.find("SecondLifeViewer") != std::string::npos) - { - // This presence event is from a viewer that sets the application string - if ( stricmp("Unknown", statusString.c_str())== 0) - { - // Viewer says they're offline - buddy->mOnlineSL = false; - } - else - { - // Viewer says they're online - buddy->mOnlineSL = true; - } - } - else - { - // This presence event is from something which is NOT the SL viewer (assume it's SLim). - if ( stricmp("Unknown", statusString.c_str())== 0) - { - // SLim says they're offline - buddy->mOnlineSLim = false; - } - else - { - // SLim says they're online - buddy->mOnlineSLim = true; - } - } - - LL_DEBUGS("Voice") << "after: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL; - - // HACK -- increment the internal change serial number in the LLRelationship (without changing the actual status), so the UI notices the change. - LLAvatarTracker::instance().setBuddyOnline(buddy->mUUID,LLAvatarTracker::instance().isBuddyOnline(buddy->mUUID)); - - notifyFriendObservers(); - } - else - { - LL_DEBUGS("Voice") << "Presence for unknown buddy " << uriString << LL_ENDL; - } -} - -void LLVoiceClient::messageEvent( - std::string &sessionHandle, - std::string &uriString, - std::string &alias, - std::string &messageHeader, - std::string &messageBody, - std::string &applicationString) -{ - LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL; -// LL_DEBUGS("Voice") << " header " << messageHeader << ", body: \n" << messageBody << LL_ENDL; - - if(messageHeader.find("text/html") != std::string::npos) - { - std::string message; - - { - const std::string startMarker = ", try looking for a instead. - start = messageBody.find(startSpan); - start = messageBody.find(startMarker2, start); - end = messageBody.find(endSpan); - - if(start != std::string::npos) - { - start += startMarker2.size(); - - if(end != std::string::npos) - end -= start; - - message.assign(messageBody, start, end); - } - } - } - -// LL_DEBUGS("Voice") << " raw message = \n" << message << LL_ENDL; - - // strip formatting tags - { - std::string::size_type start; - std::string::size_type end; - - while((start = message.find('<')) != std::string::npos) - { - if((end = message.find('>', start + 1)) != std::string::npos) - { - // Strip out the tag - message.erase(start, (end + 1) - start); - } - else - { - // Avoid an infinite loop - break; - } - } - } - - // Decode ampersand-escaped chars - { - std::string::size_type mark = 0; - - // The text may contain text encoded with <, >, and & - mark = 0; - while((mark = message.find("<", mark)) != std::string::npos) - { - message.replace(mark, 4, "<"); - mark += 1; - } - - mark = 0; - while((mark = message.find(">", mark)) != std::string::npos) - { - message.replace(mark, 4, ">"); - mark += 1; - } - - mark = 0; - while((mark = message.find("&", mark)) != std::string::npos) - { - message.replace(mark, 5, "&"); - mark += 1; - } - } - - // strip leading/trailing whitespace (since we always seem to get a couple newlines) - LLStringUtil::trim(message); - -// LL_DEBUGS("Voice") << " stripped message = \n" << message << LL_ENDL; - - sessionState *session = findSession(sessionHandle); - if(session) - { - bool is_busy = gAgent.getBusy(); - bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat); - bool is_linden = LLMuteList::getInstance()->isLinden(session->mName); - bool quiet_chat = false; - LLChat chat; - - chat.mMuted = is_muted && !is_linden; - - if(!chat.mMuted) - { - chat.mFromID = session->mCallerID; - chat.mFromName = session->mName; - chat.mSourceType = CHAT_SOURCE_AGENT; - - if(is_busy && !is_linden) - { - quiet_chat = true; - // TODO: Question: Return busy mode response here? Or maybe when session is started instead? - } - - LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL; - gIMMgr->addMessage(session->mIMSessionID, - session->mCallerID, - session->mName.c_str(), - message.c_str(), - LLStringUtil::null, // default arg - IM_NOTHING_SPECIAL, // default arg - 0, // default arg - LLUUID::null, // default arg - LLVector3::zero, // default arg - true); // prepend name and make it a link to the user's profile - } - } - } -} - -void LLVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType) -{ - sessionState *session = findSession(sessionHandle); - - if(session) - { - participantState *participant = session->findParticipant(uriString); - if(participant) - { - if (!stricmp(notificationType.c_str(), "Typing")) - { - // Other end started typing - // TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart(). - // It requires an LLIMInfo for the message, which we don't have here. - } - else if (!stricmp(notificationType.c_str(), "NotTyping")) - { - // Other end stopped typing - // TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop(). - // It requires an LLIMInfo for the message, which we don't have here. - } - else - { - LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; - } - } - else - { - LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; - } - } - else - { - LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL; - } -} - -void LLVoiceClient::subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType) -{ - buddyListEntry *buddy = findBuddy(buddyURI); - - if(!buddy) - { - // Couldn't find buddy by URI, try converting the alias... - if(!alias.empty()) - { - LLUUID id; - if(IDFromName(alias, id)) - { - buddy = findBuddy(id); - } - } - } - - if(buddy) - { - std::ostringstream stream; - - if(buddy->mCanSeeMeOnline) - { - // Sending the response will create an auto-accept rule - buddy->mHasAutoAcceptListEntry = true; - } - else - { - // Sending the response will create a block rule - buddy->mHasBlockListEntry = true; - } - - if(buddy->mInSLFriends) - { - buddy->mInVivoxBuddies = true; - } - - stream - << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "" << (buddy->mCanSeeMeOnline?"Allow":"Hide") << "" - << ""<< (buddy->mInSLFriends?"1":"0")<< "" - << "" << subscriptionHandle << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVoiceClient::auxAudioPropertiesEvent(F32 energy) -{ - LL_DEBUGS("Voice") << "got energy " << energy << LL_ENDL; - mTuningEnergy = energy; -} - -void LLVoiceClient::buddyListChanged() -{ - // This is called after we receive a BuddyAndGroupListChangedEvent. - mBuddyListMapPopulated = true; - mFriendsListDirty = true; -} - -void LLVoiceClient::muteListChanged() -{ - // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. - if(mAudioSession) - { - participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); - - for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) - { - participantState *p = iter->second; - - // Check to see if this participant is on the mute list already - if(p->updateMuteState()) - mAudioSession->mMuteDirty = true; - } - } -} - -void LLVoiceClient::updateFriends(U32 mask) -{ - if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::POWERS)) - { - // Just resend the whole friend list to the daemon - mFriendsListDirty = true; - } -} - -///////////////////////////// -// Managing list of participants -LLVoiceClient::participantState::participantState(const std::string &uri) : - mURI(uri), - mPTT(false), - mIsSpeaking(false), - mIsModeratorMuted(false), - mLastSpokeTimestamp(0.f), - mPower(0.f), - mVolume(VOLUME_DEFAULT), - mOnMuteList(false), - mVolumeSet(false), - mVolumeDirty(false), - mAvatarIDValid(false), - mIsSelf(false) -{ -} - -LLVoiceClient::participantState *LLVoiceClient::sessionState::addParticipant(const std::string &uri) -{ - participantState *result = NULL; - bool useAlternateURI = false; - - // Note: this is mostly the body of LLVoiceClient::sessionState::findParticipant(), but since we need to know if it - // matched the alternate SIP URI (so we can add it properly), we need to reproduce it here. - { - participantMap::iterator iter = mParticipantsByURI.find(&uri); - - if(iter == mParticipantsByURI.end()) - { - if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) - { - // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. - // Use mSIPURI instead, since it will be properly encoded. - iter = mParticipantsByURI.find(&(mSIPURI)); - useAlternateURI = true; - } - } - - if(iter != mParticipantsByURI.end()) - { - result = iter->second; - } - } - - if(!result) - { - // participant isn't already in one list or the other. - result = new participantState(useAlternateURI?mSIPURI:uri); - mParticipantsByURI.insert(participantMap::value_type(&(result->mURI), result)); - mParticipantsChanged = true; - - // Try to do a reverse transform on the URI to get the GUID back. - { - LLUUID id; - if(IDFromName(result->mURI, id)) - { - result->mAvatarIDValid = true; - result->mAvatarID = id; - - if(result->updateMuteState()) - mMuteDirty = true; - } - else - { - // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. - // This tells both code in LLVoiceClient and code in llfloateractivespeakers.cpp that the ID will not be in the name cache. - setUUIDFromStringHash(result->mAvatarID, uri); - } - } - - mParticipantsByUUID.insert(participantUUIDMap::value_type(&(result->mAvatarID), result)); - - if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) - { - result->mVolumeDirty = true; - mVolumeDirty = true; - } - - LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; - } - - return result; -} - -bool LLVoiceClient::participantState::updateMuteState() -{ - bool result = false; - - if(mAvatarIDValid) - { - bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); - if(mOnMuteList != isMuted) - { - mOnMuteList = isMuted; - mVolumeDirty = true; - result = true; - } - } - return result; -} - -bool LLVoiceClient::participantState::isAvatar() -{ - return mAvatarIDValid; -} - -void LLVoiceClient::sessionState::removeParticipant(LLVoiceClient::participantState *participant) -{ - if(participant) - { - participantMap::iterator iter = mParticipantsByURI.find(&(participant->mURI)); - participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(&(participant->mAvatarID)); - - LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; - - if(iter == mParticipantsByURI.end()) - { - LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL; - } - else if(iter2 == mParticipantsByUUID.end()) - { - LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; - } - else if(iter->second != iter2->second) - { - LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; - } - else - { - mParticipantsByURI.erase(iter); - mParticipantsByUUID.erase(iter2); - - delete participant; - mParticipantsChanged = true; - } - } -} - -void LLVoiceClient::sessionState::removeAllParticipants() -{ - LL_DEBUGS("Voice") << "called" << LL_ENDL; - - while(!mParticipantsByURI.empty()) - { - removeParticipant(mParticipantsByURI.begin()->second); - } - - if(!mParticipantsByUUID.empty()) - { - LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; - } -} - -LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void) -{ - participantMap *result = NULL; - if(mAudioSession) - { - result = &(mAudioSession->mParticipantsByURI); - } - return result; -} - -void LLVoiceClient::getParticipantsUUIDSet(std::set& participant_uuids) -{ - if (NULL == mAudioSession) return; - - participantUUIDMap::const_iterator it = mAudioSession->mParticipantsByUUID.begin(), - it_end = mAudioSession->mParticipantsByUUID.end(); - for (; it != it_end; ++it) - { - participant_uuids.insert((*(*it).first)); - } -} - -LLVoiceClient::participantState *LLVoiceClient::sessionState::findParticipant(const std::string &uri) -{ - participantState *result = NULL; - - participantMap::iterator iter = mParticipantsByURI.find(&uri); - - if(iter == mParticipantsByURI.end()) - { - if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) - { - // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. - // Look up the other URI - iter = mParticipantsByURI.find(&(mSIPURI)); - } - } - - if(iter != mParticipantsByURI.end()) - { - result = iter->second; - } - - return result; -} - -LLVoiceClient::participantState* LLVoiceClient::sessionState::findParticipantByID(const LLUUID& id) -{ - participantState * result = NULL; - participantUUIDMap::iterator iter = mParticipantsByUUID.find(&id); - - if(iter != mParticipantsByUUID.end()) - { - result = iter->second; - } - - return result; -} - -LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id) -{ - participantState * result = NULL; - - if(mAudioSession) - { - result = mAudioSession->findParticipantByID(id); - } - - return result; -} - - -void LLVoiceClient::parcelChanged() -{ - if(getState() >= stateNoChannel) - { - // If the user is logged in, start a channel lookup. - LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; - - std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); - LLSD data; - LLHTTPClient::post( - url, - data, - new LLVoiceClientCapResponder); - } - else - { - // The transition to stateNoChannel needs to kick this off again. - LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL; - } -} - -void LLVoiceClient::switchChannel( - std::string uri, - bool spatial, - bool no_reconnect, - bool is_p2p, - std::string hash) -{ - bool needsSwitch = false; - - LL_DEBUGS("Voice") - << "called in state " << state2string(getState()) - << " with uri \"" << uri << "\"" - << (spatial?", spatial is true":", spatial is false") - << LL_ENDL; - - switch(getState()) - { - case stateJoinSessionFailed: - case stateJoinSessionFailedWaiting: - case stateNoChannel: - // Always switch to the new URI from these states. - needsSwitch = true; - break; - - default: - if(mSessionTerminateRequested) - { - // If a terminate has been requested, we need to compare against where the URI we're already headed to. - if(mNextAudioSession) - { - if(mNextAudioSession->mSIPURI != uri) - needsSwitch = true; - } - else - { - // mNextAudioSession is null -- this probably means we're on our way back to spatial. - if(!uri.empty()) - { - // We do want to process a switch in this case. - needsSwitch = true; - } - } - } - else - { - // Otherwise, compare against the URI we're in now. - if(mAudioSession) - { - if(mAudioSession->mSIPURI != uri) - { - needsSwitch = true; - } - } - else - { - if(!uri.empty()) - { - // mAudioSession is null -- it's not clear what case would cause this. - // For now, log it as a warning and see if it ever crops up. - LL_WARNS("Voice") << "No current audio session." << LL_ENDL; - } - } - } - break; - } - - if(needsSwitch) - { - if(uri.empty()) - { - // Leave any channel we may be in - LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; - - sessionState *oldSession = mNextAudioSession; - mNextAudioSession = NULL; - - // The old session may now need to be deleted. - reapSession(oldSession); - - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); - } - else - { - LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; - - mNextAudioSession = addSession(uri); - mNextAudioSession->mHash = hash; - mNextAudioSession->mIsSpatial = spatial; - mNextAudioSession->mReconnect = !no_reconnect; - mNextAudioSession->mIsP2P = is_p2p; - } - - if(getState() <= stateNoChannel) - { - // We're already set up to join a channel, just needed to fill in the session URI - } - else - { - // State machine will come around and rejoin if uri/handle is not empty. - sessionTerminate(); - } - } -} - -void LLVoiceClient::joinSession(sessionState *session) -{ - mNextAudioSession = session; - - if(getState() <= stateNoChannel) - { - // We're already set up to join a channel, just needed to fill in the session handle - } - else - { - // State machine will come around and rejoin if uri/handle is not empty. - sessionTerminate(); - } -} - -void LLVoiceClient::setNonSpatialChannel( - const std::string &uri, - const std::string &credentials) -{ - switchChannel(uri, false, false, false, credentials); -} - -void LLVoiceClient::setSpatialChannel( - const std::string &uri, - const std::string &credentials) -{ - mSpatialSessionURI = uri; - mSpatialSessionCredentials = credentials; - mAreaVoiceDisabled = mSpatialSessionURI.empty(); - - LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; - - if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) - { - // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels. - LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; - } - else - { - switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); - } -} - -void LLVoiceClient::callUser(const LLUUID &uuid) -{ - std::string userURI = sipURIFromID(uuid); - - switchChannel(userURI, false, true, true); -} - -LLVoiceClient::sessionState* LLVoiceClient::startUserIMSession(const LLUUID &uuid) -{ - // Figure out if a session with the user already exists - sessionState *session = findSession(uuid); - if(!session) - { - // No session with user, need to start one. - std::string uri = sipURIFromID(uuid); - session = addSession(uri); - - llassert(session); - if (!session) return NULL; - - session->mIsSpatial = false; - session->mReconnect = false; - session->mIsP2P = true; - session->mCallerID = uuid; - } - - if(session->mHandle.empty()) - { - // Session isn't active -- start it up. - sessionCreateSendMessage(session, false, true); - } - else - { - // Session is already active -- start up text. - sessionTextConnectSendMessage(session); - } - - return session; -} - -bool LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message) -{ - bool result = false; - - // Attempt to locate the indicated session - sessionState *session = startUserIMSession(participant_id); - if(session) - { - // found the session, attempt to send the message - session->mTextMsgQueue.push(message); - - // Try to send queued messages (will do nothing if the session is not open yet) - sendQueuedTextMessages(session); - - // The message is queued, so we succeed. - result = true; - } - else - { - LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL; - } - - return result; -} - -void LLVoiceClient::sendQueuedTextMessages(sessionState *session) -{ - if(session->mTextStreamState == 1) - { - if(!session->mTextMsgQueue.empty()) - { - std::ostringstream stream; - - while(!session->mTextMsgQueue.empty()) - { - std::string message = session->mTextMsgQueue.front(); - session->mTextMsgQueue.pop(); - stream - << "" - << "" << session->mHandle << "" - << "text/HTML" - << "" << message << "" - << "" - << "\n\n\n"; - } - writeString(stream.str()); - } - } - else - { - // Session isn't connected yet, defer until later. - } -} - -void LLVoiceClient::endUserIMSession(const LLUUID &uuid) -{ - // Figure out if a session with the user exists - sessionState *session = findSession(uuid); - if(session) - { - // found the session - if(!session->mHandle.empty()) - { - sessionTextDisconnectSendMessage(session); - } - } - else - { - LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL; - } -} - -bool LLVoiceClient::answerInvite(std::string &sessionHandle) -{ - // this is only ever used to answer incoming p2p call invites. - - sessionState *session = findSession(sessionHandle); - if(session) - { - session->mIsSpatial = false; - session->mReconnect = false; - session->mIsP2P = true; - - joinSession(session); - return true; - } - - return false; -} - -bool LLVoiceClient::isOnlineSIP(const LLUUID &id) -{ - bool result = false; - buddyListEntry *buddy = findBuddy(id); - if(buddy) - { - result = buddy->mOnlineSLim; - LL_DEBUGS("Voice") << "Buddy " << buddy->mDisplayName << " is SIP " << (result?"online":"offline") << LL_ENDL; - } - - if(!result) - { - // This user isn't on the buddy list or doesn't show online status through the buddy list, but could be a participant in an existing session if they initiated a text IM. - sessionState *session = findSession(id); - if(session && !session->mHandle.empty()) - { - if((session->mTextStreamState != streamStateUnknown) || (session->mMediaStreamState > streamStateIdle)) - { - LL_DEBUGS("Voice") << "Open session with " << id << " found, returning SIP online state" << LL_ENDL; - // we have a p2p text session open with this user, so by definition they're online. - result = true; - } - } - } - - return result; -} - -// Returns true if the indicated participant in the current audio session is really an SL avatar. -// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. -bool LLVoiceClient::isParticipantAvatar(const LLUUID &id) -{ - bool result = true; - sessionState *session = findSession(id); - - if(session != NULL) - { - // this is a p2p session with the indicated caller, or the session with the specified UUID. - if(session->mSynthesizedCallerID) - result = false; - } - else - { - // Didn't find a matching session -- check the current audio session for a matching participant - if(mAudioSession != NULL) - { - participantState *participant = findParticipantByID(id); - if(participant != NULL) - { - result = participant->isAvatar(); - } - } - } - - return result; -} - -// Returns true if calling back the session URI after the session has closed is possible. -// Currently this will be false only for PSTN P2P calls. -bool LLVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) -{ - bool result = true; - sessionState *session = findSession(session_id); - - if(session != NULL) - { - result = session->isCallBackPossible(); - } - - return result; -} - -// Returns true if the session can accepte text IM's. -// Currently this will be false only for PSTN P2P calls. -bool LLVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) -{ - bool result = true; - sessionState *session = findSession(session_id); - - if(session != NULL) - { - result = session->isTextIMPossible(); - } - - return result; -} - - -void LLVoiceClient::declineInvite(std::string &sessionHandle) -{ - sessionState *session = findSession(sessionHandle); - if(session) - { - sessionMediaDisconnectSendMessage(session); - } -} - -void LLVoiceClient::leaveNonSpatialChannel() -{ - LL_DEBUGS("Voice") - << "called in state " << state2string(getState()) - << LL_ENDL; - - // Make sure we don't rejoin the current session. - sessionState *oldNextSession = mNextAudioSession; - mNextAudioSession = NULL; - - // Most likely this will still be the current session at this point, but check it anyway. - reapSession(oldNextSession); - - verifySessionState(); - - sessionTerminate(); -} - -std::string LLVoiceClient::getCurrentChannel() -{ - std::string result; - - if((getState() == stateRunning) && !mSessionTerminateRequested) - { - result = getAudioSessionURI(); - } - - return result; -} - -bool LLVoiceClient::inProximalChannel() -{ - bool result = false; - - if((getState() == stateRunning) && !mSessionTerminateRequested) - { - result = inSpatialChannel(); - } - - return result; -} - -std::string LLVoiceClient::sipURIFromID(const LLUUID &id) -{ - std::string result; - result = "sip:"; - result += nameFromID(id); - result += "@"; - result += mVoiceSIPURIHostName; - - return result; -} - -std::string LLVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar) -{ - std::string result; - if(avatar) - { - result = "sip:"; - result += nameFromID(avatar->getID()); - result += "@"; - result += mVoiceSIPURIHostName; - } - - return result; -} - -std::string LLVoiceClient::nameFromAvatar(LLVOAvatar *avatar) -{ - std::string result; - if(avatar) - { - result = nameFromID(avatar->getID()); - } - return result; -} - -std::string LLVoiceClient::nameFromID(const LLUUID &uuid) -{ - std::string result; - - if (uuid.isNull()) { - //VIVOX, the uuid emtpy look for the mURIString and return that instead. - //result.assign(uuid.mURIStringName); - LLStringUtil::replaceChar(result, '_', ' '); - return result; - } - // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code. - result = "x"; - - // Base64 encode and replace the pieces of base64 that are less compatible - // with e-mail local-parts. - // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet" - result += LLBase64::encode(uuid.mData, UUID_BYTES); - LLStringUtil::replaceChar(result, '+', '-'); - LLStringUtil::replaceChar(result, '/', '_'); - - // If you need to transform a GUID to this form on the Mac OS X command line, this will do so: - // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-') - - // The reverse transform can be done with: - // echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p - - return result; -} +const F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f; -bool LLVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) +std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus) { - bool result = false; - - // SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com" - // If it is, convert to a bare name before doing the transform. - std::string name = nameFromsipURI(inName); - - // Doesn't look like a SIP URI, assume it's an actual name. - if(name.empty()) - name = inName; - - // This will only work if the name is of the proper form. - // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is: - // "xFnPP04IpREWNkuw1cOXlhw==" - - if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '=')) - { - // The name appears to have the right form. - - // Reverse the transforms done by nameFromID - std::string temp = name; - LLStringUtil::replaceChar(temp, '-', '+'); - LLStringUtil::replaceChar(temp, '_', '/'); - - U8 rawuuid[UUID_BYTES + 1]; - int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1); - if(len == UUID_BYTES) - { - // The decode succeeded. Stuff the bits into the result's UUID - memcpy(uuid.mData, rawuuid, UUID_BYTES); - result = true; - } - } + std::string result = "UNKNOWN"; - if(!result) - { - // VIVOX: not a standard account name, just copy the URI name mURIString field - // and hope for the best. bpj - uuid.setNull(); // VIVOX, set the uuid field to nulls - } + // Prevent copy-paste errors when updating this list... +#define CASE(x) case x: result = #x; break - return result; -} - -std::string LLVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar) -{ - return avatar->getFullname(); -} - -std::string LLVoiceClient::sipURIFromName(std::string &name) -{ - std::string result; - result = "sip:"; - result += name; - result += "@"; - result += mVoiceSIPURIHostName; - -// LLStringUtil::toLower(result); - - return result; -} - -std::string LLVoiceClient::nameFromsipURI(const std::string &uri) -{ - std::string result; - - std::string::size_type sipOffset, atOffset; - sipOffset = uri.find("sip:"); - atOffset = uri.find("@"); - if((sipOffset != std::string::npos) && (atOffset != std::string::npos)) + switch(inStatus) { - result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4)); + CASE(STATUS_LOGIN_RETRY); + CASE(STATUS_LOGGED_IN); + CASE(STATUS_JOINING); + CASE(STATUS_JOINED); + CASE(STATUS_LEFT_CHANNEL); + CASE(STATUS_VOICE_DISABLED); + CASE(BEGIN_ERROR_STATUS); + CASE(ERROR_CHANNEL_FULL); + CASE(ERROR_CHANNEL_LOCKED); + CASE(ERROR_NOT_AVAILABLE); + CASE(ERROR_UNKNOWN); + default: + break; } - return result; -} - -bool LLVoiceClient::inSpatialChannel(void) -{ - bool result = false; - - if(mAudioSession) - result = mAudioSession->mIsSpatial; - - return result; -} - -std::string LLVoiceClient::getAudioSessionURI() -{ - std::string result; - - if(mAudioSession) - result = mAudioSession->mSIPURI; - - return result; -} - -std::string LLVoiceClient::getAudioSessionHandle() -{ - std::string result; +#undef CASE - if(mAudioSession) - result = mAudioSession->mHandle; - return result; } -///////////////////////////// -// Sending updates of current state - -void LLVoiceClient::enforceTether(void) -{ - LLVector3d tethered = mCameraRequestedPosition; - - // constrain 'tethered' to within 50m of mAvatarPosition. - { - F32 max_dist = 50.0f; - LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition; - F32 camera_distance = (F32)camera_offset.magVec(); - if(camera_distance > max_dist) - { - tethered = mAvatarPosition + - (max_dist / camera_distance) * camera_offset; - } - } - - if(dist_vec(mCameraPosition, tethered) > 0.1) - { - mCameraPosition = tethered; - mSpatialCoordsDirty = true; - } -} - -void LLVoiceClient::updatePosition(void) -{ - if(gVoiceClient) - { - LLViewerRegion *region = gAgent.getRegion(); - if(region && isAgentAvatarValid()) - { - LLMatrix3 rot; - LLVector3d pos; - - // TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here... - // They're currently always set to zero. - - // Send the current camera position to the voice code - rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis()); - pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); - - gVoiceClient->setCameraPosition( - pos, // position - LLVector3::zero, // velocity - rot); // rotation matrix - - // Send the current avatar position to the voice code - rot = gAgentAvatarp->getRootJoint()->getWorldRotation().getMatrix3(); - - pos = gAgentAvatarp->getPositionGlobal(); - // TODO: Can we get the head offset from outside the LLVOAvatar? -// pos += LLVector3d(mHeadOffset); - pos += LLVector3d(0.f, 0.f, 1.f); - - gVoiceClient->setAvatarPosition( - pos, // position - LLVector3::zero, // velocity - rot); // rotation matrix - } - } -} - -void LLVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) -{ - mCameraRequestedPosition = position; - - if(mCameraVelocity != velocity) - { - mCameraVelocity = velocity; - mSpatialCoordsDirty = true; - } - - if(mCameraRot != rot) - { - mCameraRot = rot; - mSpatialCoordsDirty = true; - } -} - -void LLVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) -{ - if(dist_vec(mAvatarPosition, position) > 0.1) - { - mAvatarPosition = position; - mSpatialCoordsDirty = true; - } - - if(mAvatarVelocity != velocity) - { - mAvatarVelocity = velocity; - mSpatialCoordsDirty = true; - } - - if(mAvatarRot != rot) - { - mAvatarRot = rot; - mSpatialCoordsDirty = true; - } -} - -bool LLVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name) -{ - bool result = false; - - if(region) - { - name = region->getName(); - } - - if(!name.empty()) - result = true; - - return result; -} -void LLVoiceClient::leaveChannel(void) -{ - if(getState() == stateRunning) - { - LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL; - mChannelName.clear(); - sessionTerminate(); - } -} -void LLVoiceClient::setMuteMic(bool muted) -{ - mMuteMic = muted; -} +/////////////////////////////////////////////////////////////////////////////////////////////// -bool LLVoiceClient::getMuteMic() const +LLVoiceClient::LLVoiceClient() { - return mMuteMic; + mVoiceModule = NULL; } -void LLVoiceClient::setUserPTTState(bool ptt) -{ - mUserPTTState = ptt; -} +//--------------------------------------------------- +// Basic setup/shutdown -bool LLVoiceClient::getUserPTTState() +LLVoiceClient::~LLVoiceClient() { - return mUserPTTState; } -void LLVoiceClient::toggleUserPTTState(void) +void LLVoiceClient::init(LLPumpIO *pump) { - mUserPTTState = !mUserPTTState; + // Initialize all of the voice modules + m_servicePump = pump; } -void LLVoiceClient::setVoiceEnabled(bool enabled) +void LLVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID) { - if (enabled != mVoiceEnabled) + // In the future, we should change this to allow voice module registration + // with a table lookup of sorts. + std::string voice_server = gSavedSettings.getString("VoiceServerType"); + LL_DEBUGS("Voice") << "voice server type " << voice_server << LL_ENDL; + if(voice_server == "diamondware") { - mVoiceEnabled = enabled; - LLVoiceClientStatusObserver::EStatusType status; - - if (enabled) - { - LLVoiceChannel::getCurrentVoiceChannel()->activate(); - status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; - } - else - { - // Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it. - LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); - status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED; - } - - notifyStatusObservers(status); + mVoiceModule = (LLVoiceModuleInterface *)LLDiamondwareVoiceClient::getInstance(); } -} - -bool LLVoiceClient::voiceEnabled() -{ - return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice"); -} - -//AD *TODO: investigate possible merge of voiceWorking() and voiceEnabled() into one non-static method -bool LLVoiceClient::voiceWorking() -{ - //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) - // Condition with joining spatial num was added to take into account possible problems with connection to voice - // server(EXT-4313). See bug descriptions and comments for MAX_NORMAL_JOINING_SPATIAL_NUM for more info. - return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated); -} - -void LLVoiceClient::setLipSyncEnabled(BOOL enabled) -{ - mLipSyncEnabled = enabled; -} - -BOOL LLVoiceClient::lipSyncEnabled() -{ - - if ( mVoiceEnabled && stateDisabled != getState() ) + else if(voice_server == "vivox") { - return mLipSyncEnabled; + mVoiceModule = (LLVoiceModuleInterface *)LLVivoxVoiceClient::getInstance(); } else { - return FALSE; - } -} - -void LLVoiceClient::setUsePTT(bool usePTT) -{ - if(usePTT && !mUsePTT) - { - // When the user turns on PTT, reset the current state. - mUserPTTState = false; + mVoiceModule = NULL; + return; } - mUsePTT = usePTT; + mVoiceModule->init(m_servicePump); + mVoiceModule->userAuthorized(user_id, agentID); } -void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle) -{ - if(!PTTIsToggle && mPTTIsToggle) - { - // When the user turns off toggle, reset the current state. - mUserPTTState = false; - } - - mPTTIsToggle = PTTIsToggle; -} -bool LLVoiceClient::getPTTIsToggle() +void LLVoiceClient::terminate() { - return mPTTIsToggle; + if (mVoiceModule) mVoiceModule->terminate(); + mVoiceModule = NULL; } -void LLVoiceClient::setPTTKey(std::string &key) +const LLVoiceVersionInfo LLVoiceClient::getVersion() { - if(key == "MiddleMouse") + if (mVoiceModule) { - mPTTIsMiddleMouse = true; + return mVoiceModule->getVersion(); } else { - mPTTIsMiddleMouse = false; - if(!LLKeyboard::keyFromString(key, &mPTTKey)) - { - // If the call failed, don't match any key. - key = KEY_NONE; - } + LLVoiceVersionInfo result; + result.serverVersion = std::string(); + result.serverType = std::string(); + return result; } } -void LLVoiceClient::setEarLocation(S32 loc) +void LLVoiceClient::updateSettings() { - if(mEarLocation != loc) - { - LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; - - mEarLocation = loc; - mSpatialCoordsDirty = true; - } + if (mVoiceModule) mVoiceModule->updateSettings(); } -void LLVoiceClient::setVoiceVolume(F32 volume) -{ - int scaled_volume = scale_speaker_volume(volume); - - if(scaled_volume != mSpeakerVolume) - { - int min_volume = scale_speaker_volume(0); - if((scaled_volume == min_volume) || (mSpeakerVolume == min_volume)) - { - mSpeakerMuteDirty = true; - } - - mSpeakerVolume = scaled_volume; - mSpeakerVolumeDirty = true; - } -} +//-------------------------------------------------- +// tuning -void LLVoiceClient::setMicGain(F32 volume) +void LLVoiceClient::tuningStart() { - int scaled_volume = scale_mic_volume(volume); - - if(scaled_volume != mMicVolume) - { - mMicVolume = scaled_volume; - mMicVolumeDirty = true; - } + if (mVoiceModule) mVoiceModule->tuningStart(); } -void LLVoiceClient::keyDown(KEY key, MASK mask) -{ - if (gKeyboard->getKeyRepeated(key)) - { - // ignore auto-repeat keys - return; - } - - if(!mPTTIsMiddleMouse) - { - bool down = (mPTTKey != KEY_NONE) - && gKeyboard->getKeyDown(mPTTKey); - inputUserControlState(down); - } -} -void LLVoiceClient::keyUp(KEY key, MASK mask) -{ - if(!mPTTIsMiddleMouse) - { - bool down = (mPTTKey != KEY_NONE) - && gKeyboard->getKeyDown(mPTTKey); - inputUserControlState(down); - } -} -void LLVoiceClient::inputUserControlState(bool down) -{ - if(mPTTIsToggle) - { - if(down) // toggle open-mic state on 'down' - { - toggleUserPTTState(); - } - } - else // set open-mic state as an absolute - { - setUserPTTState(down); - } -} -void LLVoiceClient::middleMouseState(bool down) +void LLVoiceClient::tuningStop() { - if(mPTTIsMiddleMouse) - { - inputUserControlState(down); - } + if (mVoiceModule) mVoiceModule->tuningStop(); } -///////////////////////////// -// Accessors for data related to nearby speakers -BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id) +bool LLVoiceClient::inTuningMode() { - BOOL result = FALSE; - participantState *participant = findParticipantByID(id); - if(participant) + if (mVoiceModule) { - // I'm not sure what the semantics of this should be. - // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled. - result = TRUE; + return mVoiceModule->inTuningMode(); + } + else + { + return false; } - - return result; } -BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id) +void LLVoiceClient::tuningSetMicVolume(float volume) { - BOOL result = FALSE; - - participantState *participant = findParticipantByID(id); - if(participant) - { - if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) - { - participant->mIsSpeaking = FALSE; - } - result = participant->mIsSpeaking; - } - - return result; + if (mVoiceModule) mVoiceModule->tuningSetMicVolume(volume); } -BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id) +void LLVoiceClient::tuningSetSpeakerVolume(float volume) { - BOOL result = FALSE; + if (mVoiceModule) mVoiceModule->tuningSetSpeakerVolume(volume); +} - participantState *participant = findParticipantByID(id); - if(participant) +float LLVoiceClient::tuningGetEnergy(void) +{ + if (mVoiceModule) { - result = participant->mIsModeratorMuted; + return mVoiceModule->tuningGetEnergy(); } - - return result; -} - -F32 LLVoiceClient::getCurrentPower(const LLUUID& id) -{ - F32 result = 0; - participantState *participant = findParticipantByID(id); - if(participant) + else { - result = participant->mPower; + return 0.0; } - - return result; } -std::string LLVoiceClient::getDisplayName(const LLUUID& id) +//------------------------------------------------ +// devices + +bool LLVoiceClient::deviceSettingsAvailable() { - std::string result; - participantState *participant = findParticipantByID(id); - if(participant) + if (mVoiceModule) { - result = participant->mDisplayName; + return mVoiceModule->deviceSettingsAvailable(); + } + else + { + return false; } - - return result; } - -BOOL LLVoiceClient::getUsingPTT(const LLUUID& id) +void LLVoiceClient::refreshDeviceLists(bool clearCurrentList) { - BOOL result = FALSE; - - participantState *participant = findParticipantByID(id); - if(participant) - { - // I'm not sure what the semantics of this should be. - // Does "using PTT" mean they're configured with a push-to-talk button? - // For now, we know there's no PTT mechanism in place, so nobody is using it. - } - - return result; + if (mVoiceModule) mVoiceModule->refreshDeviceLists(clearCurrentList); } -BOOL LLVoiceClient::getOnMuteList(const LLUUID& id) +void LLVoiceClient::setCaptureDevice(const std::string& name) { - BOOL result = FALSE; + if (mVoiceModule) mVoiceModule->setCaptureDevice(name); - participantState *participant = findParticipantByID(id); - if(participant) - { - result = participant->mOnMuteList; - } +} - return result; +void LLVoiceClient::setRenderDevice(const std::string& name) +{ + if (mVoiceModule) mVoiceModule->setRenderDevice(name); } -// External accessors. -F32 LLVoiceClient::getUserVolume(const LLUUID& id) +const LLVoiceDeviceList& LLVoiceClient::getCaptureDevices() { - // Minimum volume will be returned for users with voice disabled - F32 result = VOLUME_MIN; - - participantState *participant = findParticipantByID(id); - if(participant) + static LLVoiceDeviceList nullCaptureDevices; + if (mVoiceModule) { - result = participant->mVolume; - - // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. - // LL_DEBUGS("Voice") << "mVolume = " << result << " for " << id << LL_ENDL; + return mVoiceModule->getCaptureDevices(); + } + else + { + return nullCaptureDevices; } - - return result; } -void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume) + +const LLVoiceDeviceList& LLVoiceClient::getRenderDevices() { - if(mAudioSession) + static LLVoiceDeviceList nullRenderDevices; + if (mVoiceModule) { - participantState *participant = findParticipantByID(id); - if (participant && !participant->mIsSelf) - { - if (!is_approx_equal(volume, VOLUME_DEFAULT)) - { - // Store this volume setting for future sessions if it has been - // changed from the default - LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume); - } - else - { - // Remove stored volume setting if it is returned to the default - LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id); - } - - participant->mVolume = llclamp(volume, VOLUME_MIN, VOLUME_MAX); - participant->mVolumeDirty = true; - mAudioSession->mVolumeDirty = true; - } + return mVoiceModule->getRenderDevices(); + } + else + { + return nullRenderDevices; } } -std::string LLVoiceClient::getGroupID(const LLUUID& id) -{ - std::string result; - participantState *participant = findParticipantByID(id); - if(participant) +//-------------------------------------------------- +// participants + +void LLVoiceClient::getParticipantList(std::set &participants) +{ + if (mVoiceModule) { - result = participant->mGroupID; + mVoiceModule->getParticipantList(participants); + } + else + { + participants = std::set(); } - - return result; } -BOOL LLVoiceClient::getAreaVoiceDisabled() +bool LLVoiceClient::isParticipant(const LLUUID &speaker_id) { - return mAreaVoiceDisabled; + if(mVoiceModule) + { + return mVoiceModule->isParticipant(speaker_id); + } + return false; } -void LLVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame) -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; - - if(!mMainSessionGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Start" - << "" << deltaFramesPerControlFrame << "" - << "" << "" << "" - << "false" - << "" << seconds << "" - << "\n\n\n"; +//-------------------------------------------------- +// text chat - writeString(stream.str()); - } -} -void LLVoiceClient::recordingLoopSave(const std::string& filename) +BOOL LLVoiceClient::isSessionTextIMPossible(const LLUUID& id) { -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + if (mVoiceModule) { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Flush" - << "" << filename << "" - << "\n\n\n"; - - writeString(stream.str()); + return mVoiceModule->isSessionTextIMPossible(id); } + else + { + return FALSE; + } } -void LLVoiceClient::recordingStop() +BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id) { -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + if (mVoiceModule) { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Stop" - << "\n\n\n"; - - writeString(stream.str()); + return mVoiceModule->isSessionCallBackPossible(id); } + else + { + return FALSE; + } } -void LLVoiceClient::filePlaybackStart(const std::string& filename) +BOOL LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message) { -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + if (mVoiceModule) { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Start" - << "" << filename << "" - << "\n\n\n"; - - writeString(stream.str()); + return mVoiceModule->sendTextMessage(participant_id, message); } + else + { + return FALSE; + } } -void LLVoiceClient::filePlaybackStop() +void LLVoiceClient::endUserIMSession(const LLUUID& participant_id) { -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + if (mVoiceModule) { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Stop" - << "\n\n\n"; - - writeString(stream.str()); + mVoiceModule->endUserIMSession(participant_id); } } -void LLVoiceClient::filePlaybackSetPaused(bool paused) -{ - // TODO: Implement once Vivox gives me a sample -} - -void LLVoiceClient::filePlaybackSetMode(bool vox, float speed) -{ - // TODO: Implement once Vivox gives me a sample -} - -LLVoiceClient::sessionState::sessionState() : - mErrorStatusCode(0), - mMediaStreamState(streamStateUnknown), - mTextStreamState(streamStateUnknown), - mCreateInProgress(false), - mMediaConnectInProgress(false), - mVoiceInvitePending(false), - mTextInvitePending(false), - mSynthesizedCallerID(false), - mIsChannel(false), - mIsSpatial(false), - mIsP2P(false), - mIncoming(false), - mVoiceEnabled(false), - mReconnect(false), - mVolumeDirty(false), - mMuteDirty(false), - mParticipantsChanged(false) -{ -} +//---------------------------------------------- +// channels -LLVoiceClient::sessionState::~sessionState() +bool LLVoiceClient::inProximalChannel() { - removeAllParticipants(); + if (mVoiceModule) + { + return mVoiceModule->inProximalChannel(); + } + else + { + return false; + } } -bool LLVoiceClient::sessionState::isCallBackPossible() +void LLVoiceClient::setNonSpatialChannel( + const std::string &uri, + const std::string &credentials) { - // This may change to be explicitly specified by vivox in the future... - // Currently, only PSTN P2P calls cannot be returned. - // Conveniently, this is also the only case where we synthesize a caller UUID. - return !mSynthesizedCallerID; + if (mVoiceModule) mVoiceModule->setNonSpatialChannel(uri, credentials); } -bool LLVoiceClient::sessionState::isTextIMPossible() +void LLVoiceClient::setSpatialChannel( + const std::string &uri, + const std::string &credentials) { - // This may change to be explicitly specified by vivox in the future... - return !mSynthesizedCallerID; + if (mVoiceModule) mVoiceModule->setSpatialChannel(uri, credentials); } - -LLVoiceClient::sessionIterator LLVoiceClient::sessionsBegin(void) +void LLVoiceClient::leaveNonSpatialChannel() { - return mSessions.begin(); + if (mVoiceModule) mVoiceModule->leaveNonSpatialChannel(); } -LLVoiceClient::sessionIterator LLVoiceClient::sessionsEnd(void) +void LLVoiceClient::leaveChannel(void) { - return mSessions.end(); + if (mVoiceModule) mVoiceModule->leaveChannel(); } - -LLVoiceClient::sessionState *LLVoiceClient::findSession(const std::string &handle) +std::string LLVoiceClient::getCurrentChannel() { - sessionState *result = NULL; - sessionMap::iterator iter = mSessionsByHandle.find(&handle); - if(iter != mSessionsByHandle.end()) + if (mVoiceModule) { - result = iter->second; + return mVoiceModule->getCurrentChannel(); } - - return result; -} - -LLVoiceClient::sessionState *LLVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) -{ - sessionState *result = NULL; - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + else { - sessionState *session = *iter; - if(session->mCreateInProgress && (session->mSIPURI == uri)) - { - result = session; - break; - } + return std::string(); } - - return result; } -LLVoiceClient::sessionState *LLVoiceClient::findSession(const LLUUID &participant_id) + +//--------------------------------------- +// invitations + +void LLVoiceClient::callUser(const LLUUID &uuid) { - sessionState *result = NULL; - - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *session = *iter; - if((session->mCallerID == participant_id) || (session->mIMSessionID == participant_id)) - { - result = session; - break; - } - } - - return result; + if (mVoiceModule) mVoiceModule->callUser(uuid); } -LLVoiceClient::sessionState *LLVoiceClient::addSession(const std::string &uri, const std::string &handle) +bool LLVoiceClient::answerInvite(std::string &channelHandle) { - sessionState *result = NULL; - - if(handle.empty()) - { - // No handle supplied. - // Check whether there's already a session with this URI - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *s = *iter; - if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri)) - { - // TODO: I need to think about this logic... it's possible that this case should raise an internal error. - result = s; - break; - } - } - } - else // (!handle.empty()) - { - // Check for an existing session with this handle - sessionMap::iterator iter = mSessionsByHandle.find(&handle); - - if(iter != mSessionsByHandle.end()) - { - result = iter->second; - } - } - - if(!result) + if (mVoiceModule) { - // No existing session found. - - LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL; - result = new sessionState(); - result->mSIPURI = uri; - result->mHandle = handle; - - mSessions.insert(result); - - if(!result->mHandle.empty()) - { - mSessionsByHandle.insert(sessionMap::value_type(&(result->mHandle), result)); - } + return mVoiceModule->answerInvite(channelHandle); } else { - // Found an existing session - - if(uri != result->mSIPURI) - { - // TODO: Should this be an internal error? - LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL; - setSessionURI(result, uri); - } - - if(handle != result->mHandle) - { - if(handle.empty()) - { - // There's at least one race condition where where addSession was clearing an existing session handle, which caused things to break. - LL_DEBUGS("Voice") << "NOT clearing handle " << result->mHandle << LL_ENDL; - } - else - { - // TODO: Should this be an internal error? - LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL; - setSessionHandle(result, handle); - } - } - - LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL; + return false; } - - verifySessionState(); - - return result; } -void LLVoiceClient::setSessionHandle(sessionState *session, const std::string &handle) +void LLVoiceClient::declineInvite(std::string &channelHandle) { - // Have to remove the session from the handle-indexed map before changing the handle, or things will break badly. - - if(!session->mHandle.empty()) - { - // Remove session from the map if it should have been there. - sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle)); - if(iter != mSessionsByHandle.end()) - { - if(iter->second != session) - { - LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL; - } + if (mVoiceModule) mVoiceModule->declineInvite(channelHandle); +} - mSessionsByHandle.erase(iter); - } - else - { - LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL; - } - } - - session->mHandle = handle; - if(!handle.empty()) - { - mSessionsByHandle.insert(sessionMap::value_type(&(session->mHandle), session)); - } +//------------------------------------------ +// Volume/gain - verifySessionState(); -} -void LLVoiceClient::setSessionURI(sessionState *session, const std::string &uri) +void LLVoiceClient::setVoiceVolume(F32 volume) { - // There used to be a map of session URIs to sessions, which made this complex.... - session->mSIPURI = uri; - - verifySessionState(); + if (mVoiceModule) mVoiceModule->setVoiceVolume(volume); } -void LLVoiceClient::deleteSession(sessionState *session) +void LLVoiceClient::setMicGain(F32 volume) { - // Remove the session from the handle map - if(!session->mHandle.empty()) - { - sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle)); - if(iter != mSessionsByHandle.end()) - { - if(iter->second != session) - { - LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL; - } - mSessionsByHandle.erase(iter); - } - } + if (mVoiceModule) mVoiceModule->setMicGain(volume); +} - // Remove the session from the URI map - mSessions.erase(session); - - // At this point, the session should be unhooked from all lists and all state should be consistent. - verifySessionState(); - // If this is the current audio session, clean up the pointer which will soon be dangling. - if(mAudioSession == session) +//------------------------------------------ +// enable/disable voice features + +bool LLVoiceClient::voiceEnabled() +{ + if (mVoiceModule) { - mAudioSession = NULL; - mAudioSessionChanged = true; + return mVoiceModule->voiceEnabled(); } - - // ditto for the next audio session - if(mNextAudioSession == session) + else { - mNextAudioSession = NULL; + return false; } +} - // delete the session - delete session; +void LLVoiceClient::setVoiceEnabled(bool enabled) +{ + if (mVoiceModule) mVoiceModule->setVoiceEnabled(enabled); } -void LLVoiceClient::deleteAllSessions() +void LLVoiceClient::setLipSyncEnabled(BOOL enabled) { - LL_DEBUGS("Voice") << "called" << LL_ENDL; + if (mVoiceModule) mVoiceModule->setLipSyncEnabled(enabled); +} - while(!mSessions.empty()) +BOOL LLVoiceClient::lipSyncEnabled() +{ + if (mVoiceModule) { - deleteSession(*(sessionsBegin())); + return mVoiceModule->lipSyncEnabled(); } - - if(!mSessionsByHandle.empty()) + else { - LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL; + return false; } } -void LLVoiceClient::verifySessionState(void) +void LLVoiceClient::setMuteMic(bool muted) { - // This is mostly intended for debugging problems with session state management. - LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL; + if (mVoiceModule) mVoiceModule->setMuteMic(muted); +} - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *session = *iter; - LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL; - - if(!session->mHandle.empty()) - { - // every session with a non-empty handle needs to be in the handle map - sessionMap::iterator i2 = mSessionsByHandle.find(&(session->mHandle)); - if(i2 == mSessionsByHandle.end()) - { - LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL; - } - else - { - if(i2->second != session) - { - LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL; - } - } - } - } - - // check that every entry in the handle map points to a valid session in the session set - for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++) +// ---------------------------------------------- +// PTT + +void LLVoiceClient::setUserPTTState(bool ptt) +{ + if (mVoiceModule) mVoiceModule->setUserPTTState(ptt); +} + +bool LLVoiceClient::getUserPTTState() +{ + if (mVoiceModule) { - sessionState *session = iter->second; - sessionIterator i2 = mSessions.find(session); - if(i2 == mSessions.end()) - { - LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL; - } - else - { - if(session->mHandle != (*i2)->mHandle) - { - LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL; - } - } + return mVoiceModule->getUserPTTState(); + } + else + { + return false; } } -LLVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) : - mURI(uri) +void LLVoiceClient::setUsePTT(bool usePTT) { - mOnlineSL = false; - mOnlineSLim = false; - mCanSeeMeOnline = true; - mHasBlockListEntry = false; - mHasAutoAcceptListEntry = false; - mNameResolved = false; - mInVivoxBuddies = false; - mInSLFriends = false; - mNeedsNameUpdate = false; + if (mVoiceModule) mVoiceModule->setUsePTT(usePTT); } -void LLVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName) +void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle) { - buddyListEntry *buddy = addBuddy(uri, displayName); - buddy->mInVivoxBuddies = true; + if (mVoiceModule) mVoiceModule->setPTTIsToggle(PTTIsToggle); } -LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri) +bool LLVoiceClient::getPTTIsToggle() { - std::string empty; - buddyListEntry *buddy = addBuddy(uri, empty); - if(buddy->mDisplayName.empty()) + if (mVoiceModule) { - buddy->mNameResolved = false; + return mVoiceModule->getPTTIsToggle(); } - return buddy; -} - -LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri, const std::string &displayName) -{ - buddyListEntry *result = NULL; - buddyListMap::iterator iter = mBuddyListMap.find(&uri); - - if(iter != mBuddyListMap.end()) - { - // Found a matching buddy already in the map. - LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL; - result = iter->second; + else { + return false; } - if(!result) - { - // participant isn't already in one list or the other. - LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL; - result = new buddyListEntry(uri); - result->mDisplayName = displayName; - - if(IDFromName(uri, result->mUUID)) - { - // Extracted UUID from name successfully. - } - else - { - LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " (\"" << displayName << "\")" << LL_ENDL; - } - - mBuddyListMap.insert(buddyListMap::value_type(&(result->mURI), result)); - } - - return result; } -LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const std::string &uri) +void LLVoiceClient::inputUserControlState(bool down) { - buddyListEntry *result = NULL; - buddyListMap::iterator iter = mBuddyListMap.find(&uri); - if(iter != mBuddyListMap.end()) - { - result = iter->second; - } - - return result; + if (mVoiceModule) mVoiceModule->inputUserControlState(down); } -LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const LLUUID &id) +void LLVoiceClient::toggleUserPTTState(void) { - buddyListEntry *result = NULL; - buddyListMap::iterator iter; - - for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++) - { - if(iter->second->mUUID == id) - { - result = iter->second; - break; - } - } - - return result; + if (mVoiceModule) mVoiceModule->toggleUserPTTState(); } -LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddyByDisplayName(const std::string &name) +void LLVoiceClient::keyDown(KEY key, MASK mask) +{ + if (mVoiceModule) mVoiceModule->keyDown(key, mask); +} +void LLVoiceClient::keyUp(KEY key, MASK mask) +{ + if (mVoiceModule) mVoiceModule->keyUp(key, mask); +} +void LLVoiceClient::middleMouseState(bool down) { - buddyListEntry *result = NULL; - buddyListMap::iterator iter; + if (mVoiceModule) mVoiceModule->middleMouseState(down); +} + + +//------------------------------------------- +// nearby speaker accessors - for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++) +BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id) +{ + if (mVoiceModule) { - if(iter->second->mDisplayName == name) - { - result = iter->second; - break; - } + return mVoiceModule->getVoiceEnabled(id); + } + else + { + return FALSE; } - - return result; } -void LLVoiceClient::deleteBuddy(const std::string &uri) +std::string LLVoiceClient::getDisplayName(const LLUUID& id) { - buddyListMap::iterator iter = mBuddyListMap.find(&uri); - if(iter != mBuddyListMap.end()) + if (mVoiceModule) { - LL_DEBUGS("Voice") << "deleting buddy " << uri << LL_ENDL; - buddyListEntry *buddy = iter->second; - mBuddyListMap.erase(iter); - delete buddy; + return mVoiceModule->getDisplayName(id); } else { - LL_DEBUGS("Voice") << "attempt to delete nonexistent buddy " << uri << LL_ENDL; + return std::string(); } - } -void LLVoiceClient::deleteAllBuddies(void) +bool LLVoiceClient::isVoiceWorking() { - while(!mBuddyListMap.empty()) + if (mVoiceModule) { - deleteBuddy(*(mBuddyListMap.begin()->first)); + return mVoiceModule->isVoiceWorking(); } - - // Don't want to correlate with friends list when we've emptied the buddy list. - mBuddyListMapPopulated = false; - - // Don't want to correlate with friends list when we've reset the block rules. - mBlockRulesListReceived = false; - mAutoAcceptRulesListReceived = false; + return false; } -void LLVoiceClient::deleteAllBlockRules(void) +BOOL LLVoiceClient::isParticipantAvatar(const LLUUID& id) { - // Clear the block list entry flags from all local buddy list entries - buddyListMap::iterator buddy_it; - for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) + if (mVoiceModule) { - buddy_it->second->mHasBlockListEntry = false; + return mVoiceModule->isParticipantAvatar(id); } -} - -void LLVoiceClient::deleteAllAutoAcceptRules(void) -{ - // Clear the auto-accept list entry flags from all local buddy list entries - buddyListMap::iterator buddy_it; - for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) + else { - buddy_it->second->mHasAutoAcceptListEntry = false; + return FALSE; } } -void LLVoiceClient::addBlockRule(const std::string &blockMask, const std::string &presenceOnly) +BOOL LLVoiceClient::isOnlineSIP(const LLUUID& id) { - buddyListEntry *buddy = NULL; - - // blockMask is the SIP URI of a friends list entry - buddyListMap::iterator iter = mBuddyListMap.find(&blockMask); - if(iter != mBuddyListMap.end()) + if (mVoiceModule) + { + return mVoiceModule->isOnlineSIP(id); + } + else { - LL_DEBUGS("Voice") << "block list entry for " << blockMask << LL_ENDL; - buddy = iter->second; + return FALSE; } +} - if(buddy == NULL) +BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id) +{ + if (mVoiceModule) { - LL_DEBUGS("Voice") << "block list entry for unknown buddy " << blockMask << LL_ENDL; - buddy = addBuddy(blockMask); + return mVoiceModule->getIsSpeaking(id); } - - if(buddy != NULL) + else { - buddy->mHasBlockListEntry = true; + return FALSE; } } -void LLVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy) +BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id) { - buddyListEntry *buddy = NULL; - - // blockMask is the SIP URI of a friends list entry - buddyListMap::iterator iter = mBuddyListMap.find(&autoAcceptMask); - if(iter != mBuddyListMap.end()) + if (mVoiceModule) { - LL_DEBUGS("Voice") << "auto-accept list entry for " << autoAcceptMask << LL_ENDL; - buddy = iter->second; + return mVoiceModule->getIsModeratorMuted(id); } - - if(buddy == NULL) + else { - LL_DEBUGS("Voice") << "auto-accept list entry for unknown buddy " << autoAcceptMask << LL_ENDL; - buddy = addBuddy(autoAcceptMask); + return FALSE; } +} - if(buddy != NULL) +F32 LLVoiceClient::getCurrentPower(const LLUUID& id) +{ + if (mVoiceModule) { - buddy->mHasAutoAcceptListEntry = true; + return mVoiceModule->getCurrentPower(id); + } + else + { + return 0.0; } } -void LLVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString) -{ - // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done. - mBlockRulesListReceived = true; -} - -void LLVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString) +BOOL LLVoiceClient::getOnMuteList(const LLUUID& id) { - // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done. - mAutoAcceptRulesListReceived = true; + if (mVoiceModule) + { + return mVoiceModule->getOnMuteList(id); + } + else + { + return FALSE; + } } -void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) +F32 LLVoiceClient::getUserVolume(const LLUUID& id) { - mParticipantObservers.insert(observer); + if (mVoiceModule) + { + return mVoiceModule->getUserVolume(id); + } + else + { + return 0.0; + } } -void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) +void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume) { - mParticipantObservers.erase(observer); + if (mVoiceModule) mVoiceModule->setUserVolume(id, volume); } -void LLVoiceClient::notifyParticipantObservers() -{ - for (observer_set_t::iterator it = mParticipantObservers.begin(); - it != mParticipantObservers.end(); - ) - { - LLVoiceClientParticipantObserver* observer = *it; - observer->onChange(); - // In case onChange() deleted an entry. - it = mParticipantObservers.upper_bound(observer); - } -} +//-------------------------------------------------- +// status observers void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) { - mStatusObservers.insert(observer); + if (mVoiceModule) mVoiceModule->addObserver(observer); } void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) { - mStatusObservers.erase(observer); -} - -void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) -{ - if(mAudioSession) - { - if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) - { - switch(mAudioSession->mErrorStatusCode) - { - case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; - case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; - case 20715: - //invalid channel, we may be using a set of poorly cached - //info - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - break; - case 1009: - //invalid username and password - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - break; - } - - // Reset the error code to make sure it won't be reused later by accident. - mAudioSession->mErrorStatusCode = 0; - } - else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) - { - switch(mAudioSession->mErrorStatusCode) - { - case 404: // NOT_FOUND - case 480: // TEMPORARILY_UNAVAILABLE - case 408: // REQUEST_TIMEOUT - // call failed because other user was not available - // treat this as an error case - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - - // Reset the error code to make sure it won't be reused later by accident. - mAudioSession->mErrorStatusCode = 0; - break; - } - } - } - - LL_DEBUGS("Voice") - << " " << LLVoiceClientStatusObserver::status2string(status) - << ", session URI " << getAudioSessionURI() - << (inSpatialChannel()?", proximal is true":", proximal is false") - << LL_ENDL; - - for (status_observer_set_t::iterator it = mStatusObservers.begin(); - it != mStatusObservers.end(); - ) - { - LLVoiceClientStatusObserver* observer = *it; - observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); - // In case onError() deleted an entry. - it = mStatusObservers.upper_bound(observer); - } - + if (mVoiceModule) mVoiceModule->removeObserver(observer); } void LLVoiceClient::addObserver(LLFriendObserver* observer) { - mFriendObservers.insert(observer); + if (mVoiceModule) mVoiceModule->addObserver(observer); } void LLVoiceClient::removeObserver(LLFriendObserver* observer) { - mFriendObservers.erase(observer); + if (mVoiceModule) mVoiceModule->removeObserver(observer); } -void LLVoiceClient::notifyFriendObservers() +void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) { - for (friend_observer_set_t::iterator it = mFriendObservers.begin(); - it != mFriendObservers.end(); - ) - { - LLFriendObserver* observer = *it; - it++; - // The only friend-related thing we notify on is online/offline transitions. - observer->changed(LLFriendObserver::ONLINE); - } + if (mVoiceModule) mVoiceModule->addObserver(observer); } -void LLVoiceClient::lookupName(const LLUUID &id) +void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) { - BOOL is_group = FALSE; - gCacheName->get(id, is_group, &LLVoiceClient::onAvatarNameLookup); + if (mVoiceModule) mVoiceModule->removeObserver(observer); } -//static -void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group) +std::string LLVoiceClient::sipURIFromID(const LLUUID &id) { - if(gVoiceClient) + if (mVoiceModule) + { + return mVoiceModule->sipURIFromID(id); + } + else { - std::string name = llformat("%s %s", first.c_str(), last.c_str()); - gVoiceClient->avatarNameResolved(id, name); + return std::string(); } } -void LLVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) + +/////////////////// +// version checking + +class LLViewerRequiredVoiceVersion : public LLHTTPNode { - // If the avatar whose name just resolved is on our friends list, resync the friends list. - if(LLAvatarTracker::instance().getBuddyInfo(id) != NULL) - { - mFriendsListDirty = true; - } - - // Iterate over all sessions. - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + static BOOL sAlertedUser; + virtual void post( + LLHTTPNode::ResponsePtr response, + const LLSD& context, + const LLSD& input) const { - sessionState *session = *iter; - - // Check for this user as a participant in this session - participantState *participant = session->findParticipantByID(id); - if(participant) - { - // Found -- fill in the name - participant->mAccountName = name; - // and post a "participants updated" message to listeners later. - session->mParticipantsChanged = true; - } - - // Check whether this is a p2p session whose caller name just resolved - if(session->mCallerID == id) + //You received this messsage (most likely on region cross or + //teleport) + if ( input.has("body") && input["body"].has("major_version") ) { - // this session's "caller ID" just resolved. Fill in the name. - session->mName = name; - if(session->mTextInvitePending) - { - session->mTextInvitePending = false; - - // We don't need to call gIMMgr->addP2PSession() here. The first incoming message will create the panel. - } - if(session->mVoiceInvitePending) + int major_voice_version = + input["body"]["major_version"].asInteger(); + // int minor_voice_version = + // input["body"]["minor_version"].asInteger(); + LLVoiceVersionInfo versionInfo = LLVoiceClient::getInstance()->getVersion(); + + if (major_voice_version > 1) { - session->mVoiceInvitePending = false; - - gIMMgr->inviteToSession( - session->mIMSessionID, - session->mName, - session->mCallerID, - session->mName, - IM_SESSION_P2P_INVITE, - LLIMMgr::INVITATION_TYPE_VOICE, - session->mHandle, - session->mSIPURI); + if (!sAlertedUser) + { + //sAlertedUser = TRUE; + LLNotificationsUtil::add("VoiceVersionMismatch"); + gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener + } } - } } -} +}; class LLViewerParcelVoiceInfo : public LLHTTPNode { virtual void post( - LLHTTPNode::ResponsePtr response, - const LLSD& context, - const LLSD& input) const + LLHTTPNode::ResponsePtr response, + const LLSD& context, + const LLSD& input) const { //the parcel you are in has changed something about its //voice information - + //this is a misnomer, as it can also be when you are not in //a parcel at all. Should really be something like //LLViewerVoiceInfoChanged..... if ( input.has("body") ) { LLSD body = input["body"]; - + //body has "region_name" (str), "parcel_local_id"(int), //"voice_credentials" (map). - + //body["voice_credentials"] has "channel_uri" (str), //body["voice_credentials"] has "channel_credentials" (str) - + //if we really wanted to be extra careful, //we'd check the supplied //local parcel id to make sure it's for the same parcel @@ -7279,7 +764,7 @@ class LLViewerParcelVoiceInfo : public LLHTTPNode LLSD voice_credentials = body["voice_credentials"]; std::string uri; std::string credentials; - + if ( voice_credentials.has("channel_uri") ) { uri = voice_credentials["channel_uri"].asString(); @@ -7287,51 +772,96 @@ class LLViewerParcelVoiceInfo : public LLHTTPNode if ( voice_credentials.has("channel_credentials") ) { credentials = - voice_credentials["channel_credentials"].asString(); + voice_credentials["channel_credentials"].asString(); } - - gVoiceClient->setSpatialChannel(uri, credentials); + + LLVoiceClient::getInstance()->setSpatialChannel(uri, credentials); } } } }; -class LLViewerRequiredVoiceVersion : public LLHTTPNode +const std::string LLSpeakerVolumeStorage::SETTINGS_FILE_NAME = "volume_settings.xml"; + +LLSpeakerVolumeStorage::LLSpeakerVolumeStorage() { - static BOOL sAlertedUser; - virtual void post( - LLHTTPNode::ResponsePtr response, - const LLSD& context, - const LLSD& input) const + load(); +} + +LLSpeakerVolumeStorage::~LLSpeakerVolumeStorage() +{ + save(); +} + +void LLSpeakerVolumeStorage::storeSpeakerVolume(const LLUUID& speaker_id, F32 volume) +{ + mSpeakersData[speaker_id] = volume; +} + +S32 LLSpeakerVolumeStorage::getSpeakerVolume(const LLUUID& speaker_id) +{ + // Return value of -1 indicates no level is stored for this speaker + S32 ret_val = -1; + speaker_data_map_t::const_iterator it = mSpeakersData.find(speaker_id); + + if (it != mSpeakersData.end()) { - //You received this messsage (most likely on region cross or - //teleport) - if ( input.has("body") && input["body"].has("major_version") ) - { - int major_voice_version = - input["body"]["major_version"].asInteger(); -// int minor_voice_version = -// input["body"]["minor_version"].asInteger(); + F32 f_val = it->second; + // volume can amplify by as much as 4x! + S32 ivol = (S32)(400.f * f_val * f_val); + ret_val = llclamp(ivol, 0, 400); + } + return ret_val; +} - if (gVoiceClient && - (major_voice_version > VOICE_MAJOR_VERSION) ) - { - if (!sAlertedUser) - { - //sAlertedUser = TRUE; - LLNotificationsUtil::add("VoiceVersionMismatch"); - gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener - } - } +void LLSpeakerVolumeStorage::load() +{ + // load per-resident voice volume information + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME); + + LLSD settings_llsd; + llifstream file; + file.open(filename); + if (file.is_open()) + { + LLSDSerialize::fromXML(settings_llsd, file); + } + + for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); + iter != settings_llsd.endMap(); ++iter) + { + mSpeakersData.insert(std::make_pair(LLUUID(iter->first), (F32)iter->second.asReal())); + } +} + +void LLSpeakerVolumeStorage::save() +{ + // If we quit from the login screen we will not have an SL account + // name. Don't try to save, otherwise we'll dump a file in + // C:\Program Files\SecondLife\ or similar. JC + std::string user_dir = gDirUtilp->getLindenUserDir(); + if (!user_dir.empty()) + { + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME); + LLSD settings_llsd; + + for(speaker_data_map_t::const_iterator iter = mSpeakersData.begin(); iter != mSpeakersData.end(); ++iter) + { + settings_llsd[iter->first.asString()] = iter->second; } + + llofstream file; + file.open(filename); + LLSDSerialize::toPrettyXML(settings_llsd, file); } -}; +} + BOOL LLViewerRequiredVoiceVersion::sAlertedUser = FALSE; LLHTTPRegistration - gHTTPRegistrationMessageParcelVoiceInfo( - "/message/ParcelVoiceInfo"); +gHTTPRegistrationMessageParcelVoiceInfo( + "/message/ParcelVoiceInfo"); LLHTTPRegistration - gHTTPRegistrationMessageRequiredVoiceVersion( - "/message/RequiredVoiceVersion"); +gHTTPRegistrationMessageRequiredVoiceVersion( + "/message/RequiredVoiceVersion"); diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index a29c386182..f1a7d3dbec 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -17,8 +17,7 @@ * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -33,7 +32,6 @@ #define LL_VOICE_CLIENT_H class LLVOAvatar; -class LLVivoxProtocolParser; #include "lliopipe.h" #include "llpumpio.h" @@ -42,9 +40,14 @@ class LLVivoxProtocolParser; #include "v3math.h" #include "llframetimer.h" #include "llviewerregion.h" -#include "m3math.h" // LLMatrix3 +#include "llcallingcard.h" // for LLFriendObserver +#include "llsecapi.h" + +// devices + +typedef std::vector LLVoiceDeviceList; + -class LLFriendObserver; class LLVoiceClientParticipantObserver { public: @@ -52,6 +55,9 @@ public: virtual void onChange() = 0; }; + +/////////////////////////////////// +/// @class LLVoiceClientStatusObserver class LLVoiceClientStatusObserver { public: @@ -65,11 +71,7 @@ public: STATUS_JOINED, STATUS_LEFT_CHANNEL, STATUS_VOICE_DISABLED, - - // Adding STATUS_VOICE_ENABLED as pair status for STATUS_VOICE_DISABLED - // See LLVoiceClient::setVoiceEnabled() STATUS_VOICE_ENABLED, - BEGIN_ERROR_STATUS, ERROR_CHANNEL_FULL, ERROR_CHANNEL_LOCKED, @@ -83,699 +85,367 @@ public: static std::string status2string(EStatusType inStatus); }; -class LLVoiceClient: public LLSingleton +struct LLVoiceVersionInfo { - LOG_CLASS(LLVoiceClient); - public: - LLVoiceClient(); - ~LLVoiceClient(); - - public: - static void init(LLPumpIO *pump); // Call this once at application startup (creates connector) - static void terminate(); // Call this to clean up during shutdown - - protected: - bool writeString(const std::string &str); - - public: - - static F32 OVERDRIVEN_POWER_LEVEL; + std::string serverType; + std::string serverVersion; +}; - static const F32 VOLUME_MIN; - static const F32 VOLUME_DEFAULT; - static const F32 VOLUME_MAX; +////////////////////////////////// +/// @class LLVoiceModuleInterface +/// @brief Voice module interface +/// +/// Voice modules should provide an implementation for this interface. +///////////////////////////////// - void updateSettings(); // call after loading settings and whenever they change +class LLVoiceModuleInterface +{ +public: + LLVoiceModuleInterface() {} + virtual ~LLVoiceModuleInterface() {} - void getCaptureDevicesSendMessage(); - void getRenderDevicesSendMessage(); - - void clearCaptureDevices(); - void addCaptureDevice(const std::string& name); - void setCaptureDevice(const std::string& name); - - void clearRenderDevices(); - void addRenderDevice(const std::string& name); - void setRenderDevice(const std::string& name); - - void tuningStart(); - void tuningStop(); - bool inTuningMode(); - bool inTuningStates(); - - void tuningRenderStartSendMessage(const std::string& name, bool loop); - void tuningRenderStopSendMessage(); - - void tuningCaptureStartSendMessage(int duration); - void tuningCaptureStopSendMessage(); - - void tuningSetMicVolume(float volume); - void tuningSetSpeakerVolume(float volume); - float tuningGetEnergy(void); - - // This returns true when it's safe to bring up the "device settings" dialog in the prefs. - // i.e. when the daemon is running and connected, and the device lists are populated. - bool deviceSettingsAvailable(); - - // Requery the vivox daemon for the current list of input/output devices. - // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed - // (use this if you want to know when it's done). - // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. - void refreshDeviceLists(bool clearCurrentList = true); - - // Call this if the connection to the daemon terminates unexpectedly. It will attempt to reset everything and relaunch. - void daemonDied(); - - // Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away. - void giveUp(); - - ///////////////////////////// - // Response/Event handlers - void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID); - void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases); - void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); - void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); - void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString); - void logoutResponse(int statusCode, std::string &statusString); - void connectorShutdownResponse(int statusCode, std::string &statusString); - - void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); - void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming); - void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming); - void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString); - void sessionGroupAddedEvent(std::string &sessionGroupHandle); - void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); - void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); - void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); - void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy); - void auxAudioPropertiesEvent(F32 energy); - void buddyPresenceEvent(std::string &uriString, std::string &alias, std::string &statusString, std::string &applicationString); - void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); - void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType); - void subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType); - - void buddyListChanged(); - void muteListChanged(); - void updateFriends(U32 mask); - - ///////////////////////////// - // Sending updates of current state -static void updatePosition(void); - void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); - void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); - bool channelFromRegion(LLViewerRegion *region, std::string &name); - void leaveChannel(void); // call this on logout or teleport begin - - - void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. - bool getMuteMic() const; - void setUserPTTState(bool ptt); - bool getUserPTTState(); - void toggleUserPTTState(void); - void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs - void setVoiceEnabled(bool enabled); - static bool voiceEnabled(); - // Checks is voice working judging from mState - // Returns true if vivox has successfully logged in and is not in error state - bool voiceWorking(); - void setUsePTT(bool usePTT); - void setPTTIsToggle(bool PTTIsToggle); - bool getPTTIsToggle(); - void setPTTKey(std::string &key); - void setEarLocation(S32 loc); - void setVoiceVolume(F32 volume); - void setMicGain(F32 volume); - void setUserVolume(const LLUUID& id, F32 volume); // sets volume for specified agent, from 0-1 (where .5 is nominal) - void setLipSyncEnabled(BOOL enabled); - BOOL lipSyncEnabled(); - - // PTT key triggering - void keyDown(KEY key, MASK mask); - void keyUp(KEY key, MASK mask); - void middleMouseState(bool down); - - // Return the version of the Vivox library - std::string getAPIVersion() const { return mAPIVersion; } - - ///////////////////////////// - // Accessors for data related to nearby speakers - BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar - BOOL getIsSpeaking(const LLUUID& id); - BOOL getIsModeratorMuted(const LLUUID& id); - F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - BOOL getOnMuteList(const LLUUID& id); - F32 getUserVolume(const LLUUID& id); - std::string getDisplayName(const LLUUID& id); - - // MBW -- XXX -- Not sure how to get this data out of the TVC - BOOL getUsingPTT(const LLUUID& id); - std::string getGroupID(const LLUUID& id); // group ID if the user is in group chat (empty string if not applicable) - - ///////////////////////////// - BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. - // Use this to determine whether to show a "no speech" icon in the menu bar. - - ///////////////////////////// - // Recording controls - void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200); - void recordingLoopSave(const std::string& filename); - void recordingStop(); - - // Playback controls - void filePlaybackStart(const std::string& filename); - void filePlaybackStop(); - void filePlaybackSetPaused(bool paused); - void filePlaybackSetMode(bool vox = false, float speed = 1.0f); - - - // This is used by the string-keyed maps below, to avoid storing the string twice. - // The 'const std::string *' in the key points to a string actually stored in the object referenced by the map. - // The add and delete operations for each map allocate and delete in the right order to avoid dangling references. - // The default compare operation would just compare pointers, which is incorrect, so they must use this comparitor instead. - struct stringMapComparitor - { - bool operator()(const std::string* a, const std::string * b) const - { - return a->compare(*b) < 0; - } - }; - - struct uuidMapComparitor - { - bool operator()(const LLUUID* a, const LLUUID * b) const - { - return *a < *b; - } - }; - - struct participantState - { - public: - participantState(const std::string &uri); - - bool updateMuteState(); // true if mute state has changed - bool isAvatar(); - - std::string mURI; - LLUUID mAvatarID; - std::string mAccountName; - std::string mDisplayName; - LLFrameTimer mSpeakingTimeout; - F32 mLastSpokeTimestamp; - F32 mPower; - F32 mVolume; - std::string mGroupID; - bool mPTT; - bool mIsSpeaking; - bool mIsModeratorMuted; - bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted) - bool mVolumeSet; // true if incoming volume messages should not change the volume - bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed) - bool mAvatarIDValid; - bool mIsSelf; - }; - typedef std::map participantMap; - - typedef std::map participantUUIDMap; - - enum streamState - { - streamStateUnknown = 0, - streamStateIdle = 1, - streamStateConnected = 2, - streamStateRinging = 3, - }; - - struct sessionState - { - public: - sessionState(); - ~sessionState(); - - participantState *addParticipant(const std::string &uri); - // Note: after removeParticipant returns, the participant* that was passed to it will have been deleted. - // Take care not to use the pointer again after that. - void removeParticipant(participantState *participant); - void removeAllParticipants(); - - participantState *findParticipant(const std::string &uri); - participantState *findParticipantByID(const LLUUID& id); - - bool isCallBackPossible(); - bool isTextIMPossible(); - - std::string mHandle; - std::string mGroupHandle; - std::string mSIPURI; - std::string mAlias; - std::string mName; - std::string mAlternateSIPURI; - std::string mHash; // Channel password - std::string mErrorStatusString; - std::queue mTextMsgQueue; - - LLUUID mIMSessionID; - LLUUID mCallerID; - int mErrorStatusCode; - int mMediaStreamState; - int mTextStreamState; - bool mCreateInProgress; // True if a Session.Create has been sent for this session and no response has been received yet. - bool mMediaConnectInProgress; // True if a Session.MediaConnect has been sent for this session and no response has been received yet. - bool mVoiceInvitePending; // True if a voice invite is pending for this session (usually waiting on a name lookup) - bool mTextInvitePending; // True if a text invite is pending for this session (usually waiting on a name lookup) - bool mSynthesizedCallerID; // True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup. - bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) - bool mIsSpatial; // True for spatial channels - bool mIsP2P; - bool mIncoming; - bool mVoiceEnabled; - bool mReconnect; // Whether we should try to reconnect to this session if it's dropped - // Set to true when the mute state of someone in the participant list changes. - // The code will have to walk the list to find the changed participant(s). - bool mVolumeDirty; - bool mMuteDirty; - - bool mParticipantsChanged; - participantMap mParticipantsByURI; - participantUUIDMap mParticipantsByUUID; - }; - - participantState *findParticipantByID(const LLUUID& id); - participantMap *getParticipantList(void); - void getParticipantsUUIDSet(std::set& participant_uuids); - - typedef std::map sessionMap; - typedef std::set sessionSet; - - typedef sessionSet::iterator sessionIterator; - sessionIterator sessionsBegin(void); - sessionIterator sessionsEnd(void); - - sessionState *findSession(const std::string &handle); - sessionState *findSessionBeingCreatedByURI(const std::string &uri); - sessionState *findSession(const LLUUID &participant_id); - sessionState *findSessionByCreateID(const std::string &create_id); - - sessionState *addSession(const std::string &uri, const std::string &handle = LLStringUtil::null); - void setSessionHandle(sessionState *session, const std::string &handle = LLStringUtil::null); - void setSessionURI(sessionState *session, const std::string &uri); - void deleteSession(sessionState *session); - void deleteAllSessions(void); + virtual void init(LLPumpIO *pump)=0; // Call this once at application startup (creates connector) + virtual void terminate()=0; // Call this to clean up during shutdown + + virtual void updateSettings()=0; // call after loading settings and whenever they change + + virtual bool isVoiceWorking()=0; // connected to a voice server and voice channel - void verifySessionState(void); + virtual const LLVoiceVersionInfo& getVersion()=0; + + ///////////////////// + /// @name Tuning + //@{ + virtual void tuningStart()=0; + virtual void tuningStop()=0; + virtual bool inTuningMode()=0; + + virtual void tuningSetMicVolume(float volume)=0; + virtual void tuningSetSpeakerVolume(float volume)=0; + virtual float tuningGetEnergy(void)=0; + //@} + + ///////////////////// + /// @name Devices + //@{ + // This returns true when it's safe to bring up the "device settings" dialog in the prefs. + // i.e. when the daemon is running and connected, and the device lists are populated. + virtual bool deviceSettingsAvailable()=0; + + // Requery the vivox daemon for the current list of input/output devices. + // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed + // (use this if you want to know when it's done). + // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. + virtual void refreshDeviceLists(bool clearCurrentList = true)=0; + + virtual void setCaptureDevice(const std::string& name)=0; + virtual void setRenderDevice(const std::string& name)=0; + + virtual LLVoiceDeviceList& getCaptureDevices()=0; + virtual LLVoiceDeviceList& getRenderDevices()=0; + + virtual void getParticipantList(std::set &participants)=0; + virtual bool isParticipant(const LLUUID& speaker_id)=0; + //@} + + //////////////////////////// + /// @ name Channel stuff + //@{ + // returns true iff the user is currently in a proximal (local spatial) channel. + // Note that gestures should only fire if this returns true. + virtual bool inProximalChannel()=0; + + virtual void setNonSpatialChannel(const std::string &uri, + const std::string &credentials)=0; + + virtual void setSpatialChannel(const std::string &uri, + const std::string &credentials)=0; + + virtual void leaveNonSpatialChannel()=0; + + virtual void leaveChannel(void)=0; + + // Returns the URI of the current channel, or an empty string if not currently in a channel. + // NOTE that it will return an empty string if it's in the process of joining a channel. + virtual std::string getCurrentChannel()=0; + //@} + + + ////////////////////////// + /// @name invitations + //@{ + // start a voice channel with the specified user + virtual void callUser(const LLUUID &uuid)=0; + virtual bool answerInvite(std::string &channelHandle)=0; + virtual void declineInvite(std::string &channelHandle)=0; + //@} + + ///////////////////////// + /// @name Volume/gain + //@{ + virtual void setVoiceVolume(F32 volume)=0; + virtual void setMicGain(F32 volume)=0; + //@} + + ///////////////////////// + /// @name enable disable voice and features + //@{ + virtual bool voiceEnabled()=0; + virtual void setVoiceEnabled(bool enabled)=0; + virtual void setLipSyncEnabled(BOOL enabled)=0; + virtual BOOL lipSyncEnabled()=0; + virtual void setMuteMic(bool muted)=0; // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. + //@} + + //////////////////////// + /// @name PTT + //@{ + virtual void setUserPTTState(bool ptt)=0; + virtual bool getUserPTTState()=0; + virtual void setUsePTT(bool usePTT)=0; + virtual void setPTTIsToggle(bool PTTIsToggle)=0; + virtual bool getPTTIsToggle()=0; + virtual void toggleUserPTTState(void)=0; + virtual void inputUserControlState(bool down)=0; // interpret any sort of up-down mic-open control input according to ptt-toggle prefs + + virtual void keyDown(KEY key, MASK mask)=0; + virtual void keyUp(KEY key, MASK mask)=0; + virtual void middleMouseState(bool down)=0; + //@} + + ////////////////////////// + /// @name nearby speaker accessors + //@{ + + + virtual BOOL getVoiceEnabled(const LLUUID& id)=0; // true if we've received data for this avatar + virtual std::string getDisplayName(const LLUUID& id)=0; + virtual BOOL isOnlineSIP(const LLUUID &id)=0; + virtual BOOL isParticipantAvatar(const LLUUID &id)=0; + virtual BOOL getIsSpeaking(const LLUUID& id)=0; + virtual BOOL getIsModeratorMuted(const LLUUID& id)=0; + virtual F32 getCurrentPower(const LLUUID& id)=0; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + virtual BOOL getOnMuteList(const LLUUID& id)=0; + virtual F32 getUserVolume(const LLUUID& id)=0; + virtual void setUserVolume(const LLUUID& id, F32 volume)=0; // set's volume for specified agent, from 0-1 (where .5 is nominal) + //@} + + ////////////////////////// + /// @name text chat + //@{ + virtual BOOL isSessionTextIMPossible(const LLUUID& id)=0; + virtual BOOL isSessionCallBackPossible(const LLUUID& id)=0; + virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message)=0; + virtual void endUserIMSession(const LLUUID &uuid)=0; + //@} + + // authorize the user + virtual void userAuthorized(const std::string& user_id, + const LLUUID &agentID)=0; + + ////////////////////////////// + /// @name Status notification + //@{ + virtual void addObserver(LLVoiceClientStatusObserver* observer)=0; + virtual void removeObserver(LLVoiceClientStatusObserver* observer)=0; + virtual void addObserver(LLFriendObserver* observer)=0; + virtual void removeObserver(LLFriendObserver* observer)=0; + virtual void addObserver(LLVoiceClientParticipantObserver* observer)=0; + virtual void removeObserver(LLVoiceClientParticipantObserver* observer)=0; + //@} + + virtual std::string sipURIFromID(const LLUUID &id)=0; + //@} + +}; - void joinedAudioSession(sessionState *session); - void leftAudioSession(sessionState *session); - // This is called in several places where the session _may_ need to be deleted. - // It contains logic for whether to delete the session or keep it around. - void reapSession(sessionState *session); - - // Returns true if the session seems to indicate we've moved to a region on a different voice server - bool sessionNeedsRelog(sessionState *session); - - struct buddyListEntry - { - buddyListEntry(const std::string &uri); - std::string mURI; - std::string mDisplayName; - LLUUID mUUID; - bool mOnlineSL; - bool mOnlineSLim; - bool mCanSeeMeOnline; - bool mHasBlockListEntry; - bool mHasAutoAcceptListEntry; - bool mNameResolved; - bool mInSLFriends; - bool mInVivoxBuddies; - bool mNeedsNameUpdate; - }; - - typedef std::map buddyListMap; - - // This should be called when parsing a buddy list entry sent by SLVoice. - void processBuddyListEntry(const std::string &uri, const std::string &displayName); - - buddyListEntry *addBuddy(const std::string &uri); - buddyListEntry *addBuddy(const std::string &uri, const std::string &displayName); - buddyListEntry *findBuddy(const std::string &uri); - buddyListEntry *findBuddy(const LLUUID &id); - buddyListEntry *findBuddyByDisplayName(const std::string &name); - void deleteBuddy(const std::string &uri); - void deleteAllBuddies(void); - - void deleteAllBlockRules(void); - void addBlockRule(const std::string &blockMask, const std::string &presenceOnly); - void deleteAllAutoAcceptRules(void); - void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy); - void accountListBlockRulesResponse(int statusCode, const std::string &statusString); - void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString); - - ///////////////////////////// - // session control messages - void connectorCreate(); - void connectorShutdown(); - - void requestVoiceAccountProvision(S32 retries = 3); - void userAuthorized( - const std::string& firstName, - const std::string& lastName, - const LLUUID &agentID); - void login( - const std::string& account_name, - const std::string& password, - const std::string& voice_sip_uri_hostname, - const std::string& voice_account_server_uri); - void loginSendMessage(); - void logout(); - void logoutSendMessage(); - - void accountListBlockRulesSendMessage(); - void accountListAutoAcceptRulesSendMessage(); - - void sessionGroupCreateSendMessage(); - void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false); - void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false); - void sessionMediaConnectSendMessage(sessionState *session); // just joins the audio session - void sessionTextConnectSendMessage(sessionState *session); // just joins the text session - void sessionTerminateSendMessage(sessionState *session); - void sessionGroupTerminateSendMessage(sessionState *session); - void sessionMediaDisconnectSendMessage(sessionState *session); - void sessionTextDisconnectSendMessage(sessionState *session); - - // Pokes the state machine to leave the audio session next time around. - void sessionTerminate(); - - // Pokes the state machine to shut down the connector and restart it. - void requestRelog(); - - // Does the actual work to get out of the audio session - void leaveAudioSession(); - - void addObserver(LLVoiceClientParticipantObserver* observer); - void removeObserver(LLVoiceClientParticipantObserver* observer); +class LLVoiceClient: public LLSingleton +{ + LOG_CLASS(LLVoiceClient); +public: + LLVoiceClient(); + ~LLVoiceClient(); - void addObserver(LLVoiceClientStatusObserver* observer); - void removeObserver(LLVoiceClientStatusObserver* observer); + void init(LLPumpIO *pump); // Call this once at application startup (creates connector) + void terminate(); // Call this to clean up during shutdown + + const LLVoiceVersionInfo getVersion(); + +static const F32 OVERDRIVEN_POWER_LEVEL; - void addObserver(LLFriendObserver* observer); - void removeObserver(LLFriendObserver* observer); - - void lookupName(const LLUUID &id); - static void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); - void avatarNameResolved(const LLUUID &id, const std::string &name); - - typedef std::vector deviceList; + void updateSettings(); // call after loading settings and whenever they change - deviceList *getCaptureDevices(); - deviceList *getRenderDevices(); - - void setNonSpatialChannel( - const std::string &uri, - const std::string &credentials); - void setSpatialChannel( - const std::string &uri, - const std::string &credentials); - // start a voice session with the specified user - void callUser(const LLUUID &uuid); - - // Send a text message to the specified user, initiating the session if necessary. - bool sendTextMessage(const LLUUID& participant_id, const std::string& message); - - // close any existing text IM session with the specified user - void endUserIMSession(const LLUUID &uuid); - - bool answerInvite(std::string &sessionHandle); - void declineInvite(std::string &sessionHandle); - void leaveNonSpatialChannel(); + bool isVoiceWorking(); // connected to a voice server and voice channel - // Returns the URI of the current channel, or an empty string if not currently in a channel. - // NOTE that it will return an empty string if it's in the process of joining a channel. - std::string getCurrentChannel(); + // tuning + void tuningStart(); + void tuningStop(); + bool inTuningMode(); - // returns true iff the user is currently in a proximal (local spatial) channel. - // Note that gestures should only fire if this returns true. - bool inProximalChannel(); - - std::string sipURIFromID(const LLUUID &id); + void tuningSetMicVolume(float volume); + void tuningSetSpeakerVolume(float volume); + float tuningGetEnergy(void); - // Returns true if the indicated user is online via SIP presence according to SLVoice. - // Note that we only get SIP presence data for other users that are in our vivox buddy list. - bool isOnlineSIP(const LLUUID &id); - - // Returns true if the indicated participant is really an SL avatar. - // This should be used to control the state of the "profile" button. - // Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. - bool isParticipantAvatar(const LLUUID &id); - - // Returns true if calling back the session URI after the session has closed is possible. - // Currently this will be false only for PSTN P2P calls. - // NOTE: this will return true if the session can't be found. - bool isSessionCallBackPossible(const LLUUID &session_id); - - // Returns true if the session can accepte text IM's. - // Currently this will be false only for PSTN P2P calls. - // NOTE: this will return true if the session can't be found. - bool isSessionTextIMPossible(const LLUUID &session_id); - - private: - - // internal state for a simple state machine. This is used to deal with the asynchronous nature of some of the messages. - // Note: if you change this list, please make corresponding changes to LLVoiceClient::state2string(). - enum state - { - stateDisableCleanup, - stateDisabled, // Voice is turned off. - stateStart, // Class is initialized, socket is created - stateDaemonLaunched, // Daemon has been launched - stateConnecting, // connect() call has been issued - stateConnected, // connection to the daemon has been made, send some initial setup commands. - stateIdle, // socket is connected, ready for messaging - stateMicTuningStart, - stateMicTuningRunning, - stateMicTuningStop, - stateConnectorStart, // connector needs to be started - stateConnectorStarting, // waiting for connector handle - stateConnectorStarted, // connector handle received - stateLoginRetry, // need to retry login (failed due to changing password) - stateLoginRetryWait, // waiting for retry timer - stateNeedsLogin, // send login request - stateLoggingIn, // waiting for account handle - stateLoggedIn, // account handle received - stateCreatingSessionGroup, // Creating the main session group - stateNoChannel, // - stateJoiningSession, // waiting for session handle - stateSessionJoined, // session handle received - stateRunning, // in session, steady state - stateLeavingSession, // waiting for terminate session response - stateSessionTerminated, // waiting for terminate session response - - stateLoggingOut, // waiting for logout response - stateLoggedOut, // logout response received - stateConnectorStopping, // waiting for connector stop - stateConnectorStopped, // connector stop received - - // We go to this state if the login fails because the account needs to be provisioned. - - // error states. No way to recover from these yet. - stateConnectorFailed, - stateConnectorFailedWaiting, - stateLoginFailed, - stateLoginFailedWaiting, - stateJoinSessionFailed, - stateJoinSessionFailedWaiting, - - stateJail // Go here when all else has failed. Nothing will be retried, we're done. - }; - - state mState; - bool mSessionTerminateRequested; - bool mRelogRequested; - // Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). - // The larger it is the greater is possibility there is a problem with connection to voice server. - // Introduced while fixing EXT-4313. - int mSpatialJoiningNum; - - void setState(state inState); - state getState(void) { return mState; }; - static std::string state2string(state inState); - - void stateMachine(); - static void idle(void *user_data); - - LLHost mDaemonHost; - LLSocket::ptr_t mSocket; - bool mConnected; - - void closeSocket(void); - - LLPumpIO *mPump; - friend class LLVivoxProtocolParser; - - std::string mAccountName; - std::string mAccountPassword; - std::string mAccountDisplayName; - std::string mAccountFirstName; - std::string mAccountLastName; - - bool mTuningMode; - float mTuningEnergy; - std::string mTuningAudioFile; - int mTuningMicVolume; - bool mTuningMicVolumeDirty; - int mTuningSpeakerVolume; - bool mTuningSpeakerVolumeDirty; - state mTuningExitState; // state to return to when we leave tuning mode. + // devices + + // This returns true when it's safe to bring up the "device settings" dialog in the prefs. + // i.e. when the daemon is running and connected, and the device lists are populated. + bool deviceSettingsAvailable(); - std::string mSpatialSessionURI; - std::string mSpatialSessionCredentials; + // Requery the vivox daemon for the current list of input/output devices. + // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed + // (use this if you want to know when it's done). + // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. + void refreshDeviceLists(bool clearCurrentList = true); - std::string mMainSessionGroupHandle; // handle of the "main" session group. - - std::string mChannelName; // Name of the channel to be looked up - bool mAreaVoiceDisabled; - sessionState *mAudioSession; // Session state for the current audio session - bool mAudioSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. + void setCaptureDevice(const std::string& name); + void setRenderDevice(const std::string& name); - sessionState *mNextAudioSession; // Session state for the audio session we're trying to join + const LLVoiceDeviceList& getCaptureDevices(); + const LLVoiceDeviceList& getRenderDevices(); -// std::string mSessionURI; // URI of the session we're in. -// std::string mSessionHandle; // returned by ? - - S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings - std::string mCurrentRegionName; // Used to detect parcel boundary crossings - - std::string mConnectorHandle; // returned by "Create Connector" message - std::string mAccountHandle; // returned by login message - int mNumberOfAliases; - U32 mCommandCookie; + //////////////////////////// + // Channel stuff + // + + // returns true iff the user is currently in a proximal (local spatial) channel. + // Note that gestures should only fire if this returns true. + bool inProximalChannel(); + void setNonSpatialChannel( + const std::string &uri, + const std::string &credentials); + void setSpatialChannel( + const std::string &uri, + const std::string &credentials); + void leaveNonSpatialChannel(); + + // Returns the URI of the current channel, or an empty string if not currently in a channel. + // NOTE that it will return an empty string if it's in the process of joining a channel. + std::string getCurrentChannel(); + // start a voice channel with the specified user + void callUser(const LLUUID &uuid); + bool answerInvite(std::string &channelHandle); + void declineInvite(std::string &channelHandle); + void leaveChannel(void); // call this on logout or teleport begin + + + ///////////////////////////// + // Sending updates of current state - std::string mVoiceAccountServerURI; - std::string mVoiceSIPURIHostName; - - int mLoginRetryCount; - - sessionMap mSessionsByHandle; // Active sessions, indexed by session handle. Sessions which are being initiated may not be in this map. - sessionSet mSessions; // All sessions, not indexed. This is the canonical session list. - - bool mBuddyListMapPopulated; - bool mBlockRulesListReceived; - bool mAutoAcceptRulesListReceived; - buddyListMap mBuddyListMap; - - deviceList mCaptureDevices; - deviceList mRenderDevices; - - std::string mCaptureDevice; - std::string mRenderDevice; - bool mCaptureDeviceDirty; - bool mRenderDeviceDirty; - - // This should be called when the code detects we have changed parcels. - // It initiates the call to the server that gets the parcel channel. - void parcelChanged(); - - void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); - void joinSession(sessionState *session); - -static std::string nameFromAvatar(LLVOAvatar *avatar); -static std::string nameFromID(const LLUUID &id); -static bool IDFromName(const std::string name, LLUUID &uuid); -static std::string displayNameFromAvatar(LLVOAvatar *avatar); - std::string sipURIFromAvatar(LLVOAvatar *avatar); - std::string sipURIFromName(std::string &name); - - // Returns the name portion of the SIP URI if the string looks vaguely like a SIP URI, or an empty string if not. -static std::string nameFromsipURI(const std::string &uri); - bool inSpatialChannel(void); - std::string getAudioSessionURI(); - std::string getAudioSessionHandle(); - - void sendPositionalUpdate(void); - - void buildSetCaptureDevice(std::ostringstream &stream); - void buildSetRenderDevice(std::ostringstream &stream); - void buildLocalAudioUpdates(std::ostringstream &stream); - - void clearAllLists(); - void checkFriend(const LLUUID& id); - void sendFriendsListUpdates(); - - // start a text IM session with the specified user - // This will be asynchronous, the session may be established at a future time. - sessionState* startUserIMSession(const LLUUID& uuid); - void sendQueuedTextMessages(sessionState *session); - - void enforceTether(void); - - bool mSpatialCoordsDirty; - - LLVector3d mCameraPosition; - LLVector3d mCameraRequestedPosition; - LLVector3 mCameraVelocity; - LLMatrix3 mCameraRot; - - LLVector3d mAvatarPosition; - LLVector3 mAvatarVelocity; - LLMatrix3 mAvatarRot; - - bool mPTTDirty; - bool mPTT; - - bool mUsePTT; - bool mPTTIsMiddleMouse; - KEY mPTTKey; - bool mPTTIsToggle; - bool mUserPTTState; - bool mMuteMic; - - // Set to true when the friends list is known to have changed. - bool mFriendsListDirty; - - enum - { - earLocCamera = 0, // ear at camera - earLocAvatar, // ear at avatar - earLocMixed // ear at avatar location/camera direction - }; - - S32 mEarLocation; - - bool mSpeakerVolumeDirty; - bool mSpeakerMuteDirty; - int mSpeakerVolume; + void setVoiceVolume(F32 volume); + void setMicGain(F32 volume); + void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) + bool voiceEnabled(); + void setLipSyncEnabled(BOOL enabled); + void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. + void setUserPTTState(bool ptt); + bool getUserPTTState(); + void toggleUserPTTState(void); + void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs + void setVoiceEnabled(bool enabled); + + void setUsePTT(bool usePTT); + void setPTTIsToggle(bool PTTIsToggle); + bool getPTTIsToggle(); + + BOOL lipSyncEnabled(); + + // PTT key triggering + void keyDown(KEY key, MASK mask); + void keyUp(KEY key, MASK mask); + void middleMouseState(bool down); + + + ///////////////////////////// + // Accessors for data related to nearby speakers + BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar + std::string getDisplayName(const LLUUID& id); + BOOL isOnlineSIP(const LLUUID &id); + BOOL isParticipantAvatar(const LLUUID &id); + BOOL getIsSpeaking(const LLUUID& id); + BOOL getIsModeratorMuted(const LLUUID& id); + F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + BOOL getOnMuteList(const LLUUID& id); + F32 getUserVolume(const LLUUID& id); + + ///////////////////////////// + BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. + // Use this to determine whether to show a "no speech" icon in the menu bar. + void getParticipantList(std::set &participants); + bool isParticipant(const LLUUID& speaker_id); + + ////////////////////////// + /// @name text chat + //@{ + BOOL isSessionTextIMPossible(const LLUUID& id); + BOOL isSessionCallBackPossible(const LLUUID& id); + BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message); + void endUserIMSession(const LLUUID &uuid); + //@} + - int mMicVolume; - bool mMicVolumeDirty; - - bool mVoiceEnabled; - bool mWriteInProgress; - std::string mWriteString; - - LLTimer mUpdateTimer; + void userAuthorized(const std::string& user_id, + const LLUUID &agentID); + + void addObserver(LLVoiceClientStatusObserver* observer); + void removeObserver(LLVoiceClientStatusObserver* observer); + void addObserver(LLFriendObserver* observer); + void removeObserver(LLFriendObserver* observer); + void addObserver(LLVoiceClientParticipantObserver* observer); + void removeObserver(LLVoiceClientParticipantObserver* observer); + + std::string sipURIFromID(const LLUUID &id); - BOOL mLipSyncEnabled; - - std::string mAPIVersion; - - typedef std::set observer_set_t; - observer_set_t mParticipantObservers; +protected: + LLVoiceModuleInterface* mVoiceModule; + LLPumpIO *m_servicePump; +}; - void notifyParticipantObservers(); +/** + * Speaker volume storage helper class + **/ - typedef std::set status_observer_set_t; - status_observer_set_t mStatusObservers; - - void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); +class LLSpeakerVolumeStorage : public LLSingleton +{ + LOG_CLASS(LLSpeakerVolumeStorage); +public: - typedef std::set friend_observer_set_t; - friend_observer_set_t mFriendObservers; - void notifyFriendObservers(); + /** + * Sets internal voluem level for specified user. + * + * @param[in] speaker_id - LLUUID of user to store volume level for + * @param[in] volume - external volume level to be stored for user. + */ + void storeSpeakerVolume(const LLUUID& speaker_id, F32 volume); + + /** + * Gets stored external volume level for specified speaker. + * + * If specified user is not found default level will be returned. It is equivalent of + * external level 0.5 from the 0.0..1.0 range. + * Default external level is calculated as: internal = 400 * external^2 + * Maps 0.0 to 1.0 to internal values 0-400 with default 0.5 == 100 + * + * @param[in] speaker_id - LLUUID of user to get his volume level + */ + S32 getSpeakerVolume(const LLUUID& speaker_id); + +private: + friend class LLSingleton; + LLSpeakerVolumeStorage(); + ~LLSpeakerVolumeStorage(); + + const static std::string SETTINGS_FILE_NAME; + + void load(); + void save(); + + typedef std::map speaker_data_map_t; + speaker_data_map_t mSpeakersData; }; -extern LLVoiceClient *gVoiceClient; - #endif //LL_VOICE_CLIENT_H diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp new file mode 100644 index 0000000000..a7efc49c67 --- /dev/null +++ b/indra/newview/llvoicevivox.cpp @@ -0,0 +1,6967 @@ + /** + * @file LLVivoxVoiceClient.cpp + * @brief Implementation of LLVivoxVoiceClient class which is the interface to the voice client process. + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llvoicevivox.h" + +#include + +#include "llsdutil.h" + +#include "llvoavatarself.h" +#include "llbufferstream.h" +#include "llfile.h" +#ifdef LL_STANDALONE +# include "expat.h" +#else +# include "expat/expat.h" +#endif +#include "llcallbacklist.h" +#include "llviewerregion.h" +#include "llviewernetwork.h" // for gGridChoice +#include "llbase64.h" +#include "llviewercontrol.h" +#include "llkeyboard.h" +#include "llappviewer.h" // for gDisconnected, gDisableVoice +#include "llmutelist.h" // to check for muted avatars +#include "llagent.h" +#include "llcachename.h" +#include "llimview.h" // for LLIMMgr +#include "llparcel.h" +#include "llviewerparcelmgr.h" +//#include "llfirstuse.h" +#include "llspeakers.h" +#include "llviewerwindow.h" +#include "llviewercamera.h" + +#include "llfloaterfriends.h" //VIVOX, inorder to refresh communicate panel +#include "llviewernetwork.h" +#include "llnotificationsutil.h" + +// for base64 decoding +#include "apr_base64.h" + +// for SHA1 hash +#include "apr_sha1.h" + +// for MD5 hash +#include "llmd5.h" + +#define USE_SESSION_GROUPS 0 + +const F32 SPEAKING_TIMEOUT = 1.f; + +static const std::string VOICE_SERVER_TYPE = "Vivox"; + +// Don't retry connecting to the daemon more frequently than this: +const F32 CONNECT_THROTTLE_SECONDS = 1.0f; + +// Don't send positional updates more frequently than this: +const F32 UPDATE_THROTTLE_SECONDS = 0.1f; + +const F32 LOGIN_RETRY_SECONDS = 10.0f; +const int MAX_LOGIN_RETRIES = 12; + +// Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine() +// which is treated as normal. If this number is exceeded we suspect there is a problem with connection +// to voice server (EXT-4313). When voice works correctly, there is from 1 to 15 times. 50 was chosen +// to make sure we don't make mistake when slight connection problems happen- situation when connection to server is +// blocked is VERY rare and it's better to sacrifice response time in this situation for the sake of stability. +const int MAX_NORMAL_JOINING_SPATIAL_NUM = 50; + + +static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str) +{ + LLMD5 md5_uuid; + md5_uuid.update((const unsigned char*)str.data(), str.size()); + md5_uuid.finalize(); + md5_uuid.raw_digest(uuid.mData); +} + +static int scale_mic_volume(float volume) +{ + // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default. + // Map it to Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70 + return 30 + (int)(volume * 20.0f); +} + +static int scale_speaker_volume(float volume) +{ + // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default. + // Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70 + return 30 + (int)(volume * 40.0f); + +} + +class LLVivoxVoiceAccountProvisionResponder : + public LLHTTPClient::Responder +{ +public: + LLVivoxVoiceAccountProvisionResponder(int retries) + { + mRetries = retries; + } + + virtual void error(U32 status, const std::string& reason) + { + if ( mRetries > 0 ) + { + LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, retrying. status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL; + LLVivoxVoiceClient::getInstance()->requestVoiceAccountProvision( + mRetries - 1); + } + else + { + LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, too many retries (giving up). status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL; + LLVivoxVoiceClient::getInstance()->giveUp(); + } + } + + virtual void result(const LLSD& content) + { + + std::string voice_sip_uri_hostname; + std::string voice_account_server_uri; + + LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; + + if(content.has("voice_sip_uri_hostname")) + voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString(); + + // this key is actually misnamed -- it will be an entire URI, not just a hostname. + if(content.has("voice_account_server_name")) + voice_account_server_uri = content["voice_account_server_name"].asString(); + + LLVivoxVoiceClient::getInstance()->login( + content["username"].asString(), + content["password"].asString(), + voice_sip_uri_hostname, + voice_account_server_uri); + + } + +private: + int mRetries; +}; + + + +/////////////////////////////////////////////////////////////////////////////////////////////// + +class LLVivoxVoiceClientMuteListObserver : public LLMuteListObserver +{ + /* virtual */ void onChange() { LLVivoxVoiceClient::getInstance()->muteListChanged();} +}; + +class LLVivoxVoiceClientFriendsObserver : public LLFriendObserver +{ +public: + /* virtual */ void changed(U32 mask) { LLVivoxVoiceClient::getInstance()->updateFriends(mask);} +}; + +static LLVivoxVoiceClientMuteListObserver mutelist_listener; +static bool sMuteListListener_listening = false; + +static LLVivoxVoiceClientFriendsObserver *friendslist_listener = NULL; + +/////////////////////////////////////////////////////////////////////////////////////////////// + +class LLVivoxVoiceClientCapResponder : public LLHTTPClient::Responder +{ +public: + LLVivoxVoiceClientCapResponder(void){}; + + virtual void error(U32 status, const std::string& reason); // called with bad status codes + virtual void result(const LLSD& content); + +private: +}; + +void LLVivoxVoiceClientCapResponder::error(U32 status, const std::string& reason) +{ + LL_WARNS("Voice") << "LLVivoxVoiceClientCapResponder::error(" + << status << ": " << reason << ")" + << LL_ENDL; +} + +void LLVivoxVoiceClientCapResponder::result(const LLSD& content) +{ + LLSD::map_const_iterator iter; + + LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; + + if ( content.has("voice_credentials") ) + { + LLSD voice_credentials = content["voice_credentials"]; + std::string uri; + std::string credentials; + + if ( voice_credentials.has("channel_uri") ) + { + uri = voice_credentials["channel_uri"].asString(); + } + if ( voice_credentials.has("channel_credentials") ) + { + credentials = + voice_credentials["channel_credentials"].asString(); + } + + LLVivoxVoiceClient::getInstance()->setSpatialChannel(uri, credentials); + } +} + + + +#if LL_WINDOWS +static HANDLE sGatewayHandle = 0; + +static bool isGatewayRunning() +{ + bool result = false; + if(sGatewayHandle != 0) + { + DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0); + if(waitresult != WAIT_OBJECT_0) + { + result = true; + } + } + return result; +} +static void killGateway() +{ + if(sGatewayHandle != 0) + { + TerminateProcess(sGatewayHandle,0); + } +} + +#else // Mac and linux + +static pid_t sGatewayPID = 0; +static bool isGatewayRunning() +{ + bool result = false; + if(sGatewayPID != 0) + { + // A kill with signal number 0 has no effect, just does error checking. It should return an error if the process no longer exists. + if(kill(sGatewayPID, 0) == 0) + { + result = true; + } + } + return result; +} + +static void killGateway() +{ + if(sGatewayPID != 0) + { + kill(sGatewayPID, SIGTERM); + } +} + +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////// + +LLVivoxVoiceClient::LLVivoxVoiceClient() : + mState(stateDisabled), + mSessionTerminateRequested(false), + mRelogRequested(false), + mConnected(false), + mPump(NULL), + mSpatialJoiningNum(0), + + mTuningMode(false), + mTuningEnergy(0.0f), + mTuningMicVolume(0), + mTuningMicVolumeDirty(true), + mTuningSpeakerVolume(0), + mTuningSpeakerVolumeDirty(true), + mTuningExitState(stateDisabled), + + mAreaVoiceDisabled(false), + mAudioSession(NULL), + mAudioSessionChanged(false), + mNextAudioSession(NULL), + + mCurrentParcelLocalID(0), + mNumberOfAliases(0), + mCommandCookie(0), + mLoginRetryCount(0), + + mBuddyListMapPopulated(false), + mBlockRulesListReceived(false), + mAutoAcceptRulesListReceived(false), + mCaptureDeviceDirty(false), + mRenderDeviceDirty(false), + mSpatialCoordsDirty(false), + + mPTTDirty(true), + mPTT(true), + mUsePTT(true), + mPTTIsMiddleMouse(false), + mPTTKey(0), + mPTTIsToggle(false), + mUserPTTState(false), + mMuteMic(false), + mFriendsListDirty(true), + + mEarLocation(0), + mSpeakerVolumeDirty(true), + mSpeakerMuteDirty(true), + mMicVolume(0), + mMicVolumeDirty(true), + + mVoiceEnabled(false), + mWriteInProgress(false), + + mLipSyncEnabled(false) + + + +{ + mSpeakerVolume = scale_speaker_volume(0); + + mVoiceVersion.serverVersion = ""; + mVoiceVersion.serverType = VOICE_SERVER_TYPE; + + // gMuteListp isn't set up at this point, so we defer this until later. +// gMuteListp->addObserver(&mutelist_listener); + + +#if LL_DARWIN || LL_LINUX || LL_SOLARIS + // HACK: THIS DOES NOT BELONG HERE + // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. + // This should cause us to ignore SIGPIPE and handle the error through proper channels. + // This should really be set up elsewhere. Where should it go? + signal(SIGPIPE, SIG_IGN); + + // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes. + // Ignoring SIGCHLD should prevent zombies from being created. Alternately, we could use wait(), but I'd rather not do that. + signal(SIGCHLD, SIG_IGN); +#endif + + // set up state machine + setState(stateDisabled); + + gIdleCallbacks.addFunction(idle, this); +} + +//--------------------------------------------------- + +LLVivoxVoiceClient::~LLVivoxVoiceClient() +{ +} + +//---------------------------------------------- + +void LLVivoxVoiceClient::init(LLPumpIO *pump) +{ + // constructor will set up LLVoiceClient::getInstance() + LLVivoxVoiceClient::getInstance()->mPump = pump; +} + +void LLVivoxVoiceClient::terminate() +{ + +// leaveAudioSession(); + logout(); + // As of SDK version 4885, this should no longer be necessary. It will linger after the socket close if it needs to. + // ms_sleep(2000); + connectorShutdown(); + closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. + + // This will do unpleasant things on windows. +// killGateway(); + + + +} + +const LLVoiceVersionInfo& LLVivoxVoiceClient::getVersion() +{ + return mVoiceVersion; +} + +//--------------------------------------------------- + +void LLVivoxVoiceClient::updateSettings() +{ + setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat")); + setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled")); + std::string keyString = gSavedSettings.getString("PushToTalkButton"); + setPTTKey(keyString); + setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); + setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); + + std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); + setCaptureDevice(inputDevice); + std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); + setRenderDevice(outputDevice); + F32 mic_level = gSavedSettings.getF32("AudioLevelMic"); + setMicGain(mic_level); + setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); +} + +///////////////////////////// +// utility functions + +bool LLVivoxVoiceClient::writeString(const std::string &str) +{ + bool result = false; + if(mConnected) + { + apr_status_t err; + apr_size_t size = (apr_size_t)str.size(); + apr_size_t written = size; + + //MARK: Turn this on to log outgoing XML +// LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL; + + // check return code - sockets will fail (broken, etc.) + err = apr_socket_send( + mSocket->getSocket(), + (const char*)str.data(), + &written); + + if(err == 0) + { + // Success. + result = true; + } + // TODO: handle partial writes (written is number of bytes written) + // Need to set socket to non-blocking before this will work. +// else if(APR_STATUS_IS_EAGAIN(err)) +// { +// // +// } + else + { + // Assume any socket error means something bad. For now, just close the socket. + char buf[MAX_STRING]; + LL_WARNS("Voice") << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL; + daemonDied(); + } + } + + return result; +} + + +///////////////////////////// +// session control messages +void LLVivoxVoiceClient::connectorCreate() +{ + std::ostringstream stream; + std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); + std::string loglevel = "0"; + + // Transition to stateConnectorStarted when the connector handle comes back. + setState(stateConnectorStarting); + + std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel"); + + if(savedLogLevel != "-1") + { + LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL; + loglevel = "10"; + } + + stream + << "" + << "V2 SDK" + << "" << mVoiceAccountServerURI << "" + << "Normal" + << "" + << "" << logpath << "" + << "Connector" + << ".log" + << "" << loglevel << "" + << "" + << "SecondLifeViewer.1" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::connectorShutdown() +{ + setState(stateConnectorStopping); + + if(!mConnectorHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mConnectorHandle << "" + << "" + << "\n\n\n"; + + mConnectorHandle.clear(); + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID) +{ + + mAccountDisplayName = user_id; + + LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL; + + mAccountName = nameFromID(agentID); +} + +void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries) +{ + if ( gAgent.getRegion() && mVoiceEnabled ) + { + std::string url = + gAgent.getRegion()->getCapability( + "ProvisionVoiceAccountRequest"); + + if ( url == "" ) return; + + LLHTTPClient::post( + url, + LLSD(), + new LLVivoxVoiceAccountProvisionResponder(retries)); + } +} + +void LLVivoxVoiceClient::login( + const std::string& account_name, + const std::string& password, + const std::string& voice_sip_uri_hostname, + const std::string& voice_account_server_uri) +{ + mVoiceSIPURIHostName = voice_sip_uri_hostname; + mVoiceAccountServerURI = voice_account_server_uri; + + if(!mAccountHandle.empty()) + { + // Already logged in. + LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL; + + // Don't process another login. + return; + } + else if ( account_name != mAccountName ) + { + //TODO: error? + LL_WARNS("Voice") << "Wrong account name! " << account_name + << " instead of " << mAccountName << LL_ENDL; + } + else + { + mAccountPassword = password; + } + + std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName"); + + if( !debugSIPURIHostName.empty() ) + { + mVoiceSIPURIHostName = debugSIPURIHostName; + } + + if( mVoiceSIPURIHostName.empty() ) + { + // we have an empty account server name + // so we fall back to hardcoded defaults + + if(LLGridManager::getInstance()->isInProductionGrid()) + { + // Use the release account server + mVoiceSIPURIHostName = "bhr.vivox.com"; + } + else + { + // Use the development account server + mVoiceSIPURIHostName = "bhd.vivox.com"; + } + } + + std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI"); + + if( !debugAccountServerURI.empty() ) + { + mVoiceAccountServerURI = debugAccountServerURI; + } + + if( mVoiceAccountServerURI.empty() ) + { + // If the account server URI isn't specified, construct it from the SIP URI hostname + mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/"; + } +} + +void LLVivoxVoiceClient::idle(void* user_data) +{ + LLVivoxVoiceClient* self = (LLVivoxVoiceClient*)user_data; + self->stateMachine(); +} + +std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState) +{ + std::string result = "UNKNOWN"; + + // Prevent copy-paste errors when updating this list... +#define CASE(x) case x: result = #x; break + + switch(inState) + { + CASE(stateDisableCleanup); + CASE(stateDisabled); + CASE(stateStart); + CASE(stateDaemonLaunched); + CASE(stateConnecting); + CASE(stateConnected); + CASE(stateIdle); + CASE(stateMicTuningStart); + CASE(stateMicTuningRunning); + CASE(stateMicTuningStop); + CASE(stateConnectorStart); + CASE(stateConnectorStarting); + CASE(stateConnectorStarted); + CASE(stateLoginRetry); + CASE(stateLoginRetryWait); + CASE(stateNeedsLogin); + CASE(stateLoggingIn); + CASE(stateLoggedIn); + CASE(stateCreatingSessionGroup); + CASE(stateNoChannel); + CASE(stateJoiningSession); + CASE(stateSessionJoined); + CASE(stateRunning); + CASE(stateLeavingSession); + CASE(stateSessionTerminated); + CASE(stateLoggingOut); + CASE(stateLoggedOut); + CASE(stateConnectorStopping); + CASE(stateConnectorStopped); + CASE(stateConnectorFailed); + CASE(stateConnectorFailedWaiting); + CASE(stateLoginFailed); + CASE(stateLoginFailedWaiting); + CASE(stateJoinSessionFailed); + CASE(stateJoinSessionFailedWaiting); + CASE(stateJail); + } + +#undef CASE + + return result; +} + + + +void LLVivoxVoiceClient::setState(state inState) +{ + LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL; + + mState = inState; +} + +void LLVivoxVoiceClient::stateMachine() +{ + if(gDisconnected) + { + // The viewer has been disconnected from the sim. Disable voice. + setVoiceEnabled(false); + } + + if(mVoiceEnabled) + { + updatePosition(); + } + else if(mTuningMode) + { + // Tuning mode is special -- it needs to launch SLVoice even if voice is disabled. + } + else + { + if((getState() != stateDisabled) && (getState() != stateDisableCleanup)) + { + // User turned off voice support. Send the cleanup messages, close the socket, and reset. + if(!mConnected) + { + // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill. + LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL; + killGateway(); + } + + logout(); + connectorShutdown(); + + setState(stateDisableCleanup); + } + } + + // Check for parcel boundary crossing + { + LLViewerRegion *region = gAgent.getRegion(); + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + + if(region && parcel) + { + S32 parcelLocalID = parcel->getLocalID(); + std::string regionName = region->getName(); + std::string capURI = region->getCapability("ParcelVoiceInfoRequest"); + +// LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL; + + // The region name starts out empty and gets filled in later. + // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes. + // If either is empty, wait for the next time around. + if(!regionName.empty()) + { + if(!capURI.empty()) + { + if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName)) + { + // We have changed parcels. Initiate a parcel channel lookup. + mCurrentParcelLocalID = parcelLocalID; + mCurrentRegionName = regionName; + + parcelChanged(); + } + } + else + { + LL_WARNS_ONCE("Voice") << "region doesn't have ParcelVoiceInfoRequest capability. This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL; + } + } + } + } + + switch(getState()) + { + //MARK: stateDisableCleanup + case stateDisableCleanup: + // Clean up and reset everything. + closeSocket(); + deleteAllSessions(); + deleteAllBuddies(); + + mConnectorHandle.clear(); + mAccountHandle.clear(); + mAccountPassword.clear(); + mVoiceAccountServerURI.clear(); + + setState(stateDisabled); + break; + + //MARK: stateDisabled + case stateDisabled: + if(mTuningMode || (mVoiceEnabled && !mAccountName.empty())) + { + setState(stateStart); + } + break; + + //MARK: stateStart + case stateStart: + if(gSavedSettings.getBOOL("CmdLineDisableVoice")) + { + // Voice is locked out, we must not launch the vivox daemon. + setState(stateJail); + } + else if(!isGatewayRunning()) + { + if(true) + { + // Launch the voice daemon + + // *FIX:Mani - Using the executable dir instead + // of mAppRODataDir, the working directory from which the app + // is launched. + //std::string exe_path = gDirUtilp->getAppRODataDir(); + std::string exe_path = gDirUtilp->getExecutableDir(); + exe_path += gDirUtilp->getDirDelimiter(); +#if LL_WINDOWS + exe_path += "SLVoice.exe"; +#elif LL_DARWIN + exe_path += "../Resources/SLVoice"; +#else + exe_path += "SLVoice"; +#endif + // See if the vivox executable exists + llstat s; + if(!LLFile::stat(exe_path, &s)) + { + // vivox executable exists. Build the command line and launch the daemon. + // SLIM SDK: these arguments are no longer necessary. +// std::string args = " -p tcp -h -c"; + std::string args; + std::string cmd; + std::string loglevel = gSavedSettings.getString("VivoxDebugLevel"); + + if(loglevel.empty()) + { + loglevel = "-1"; // turn logging off completely + } + + args += " -ll "; + args += loglevel; + + LL_DEBUGS("Voice") << "Args for SLVoice: " << args << LL_ENDL; + +#if LL_WINDOWS + PROCESS_INFORMATION pinfo; + STARTUPINFOA sinfo; + + memset(&sinfo, 0, sizeof(sinfo)); + + std::string exe_dir = gDirUtilp->getAppRODataDir(); + cmd = "SLVoice.exe"; + cmd += args; + + // So retarded. Windows requires that the second parameter to CreateProcessA be writable (non-const) string... + char *args2 = new char[args.size() + 1]; + strcpy(args2, args.c_str()); + if(!CreateProcessA(exe_path.c_str(), args2, NULL, NULL, FALSE, 0, NULL, exe_dir.c_str(), &sinfo, &pinfo)) + { +// DWORD dwErr = GetLastError(); + } + else + { + // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on + // CloseHandle(pinfo.hProcess); // stops leaks - nothing else + sGatewayHandle = pinfo.hProcess; + CloseHandle(pinfo.hThread); // stops leaks - nothing else + } + + delete[] args2; +#else // LL_WINDOWS + // This should be the same for mac and linux + { + std::vector arglist; + arglist.push_back(exe_path); + + // Split the argument string into separate strings for each argument + typedef boost::tokenizer > tokenizer; + boost::char_separator sep(" "); + tokenizer tokens(args, sep); + tokenizer::iterator token_iter; + + for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) + { + arglist.push_back(*token_iter); + } + + // create an argv vector for the child process + char **fakeargv = new char*[arglist.size() + 1]; + int i; + for(i=0; i < arglist.size(); i++) + fakeargv[i] = const_cast(arglist[i].c_str()); + + fakeargv[i] = NULL; + + fflush(NULL); // flush all buffers before the child inherits them + pid_t id = vfork(); + if(id == 0) + { + // child + execv(exe_path.c_str(), fakeargv); + + // If we reach this point, the exec failed. + // Use _exit() instead of exit() per the vfork man page. + _exit(0); + } + + // parent + delete[] fakeargv; + sGatewayPID = id; + } +#endif // LL_WINDOWS + mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort")); + } + else + { + LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL; + } + } + else + { + // SLIM SDK: port changed from 44124 to 44125. + // We can connect to a client gateway running on another host. This is useful for testing. + // To do this, launch the gateway on a nearby host like this: + // vivox-gw.exe -p tcp -i 0.0.0.0:44125 + // and put that host's IP address here. + mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost"), gSavedSettings.getU32("VivoxVoicePort")); + } + + mUpdateTimer.start(); + mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); + + setState(stateDaemonLaunched); + + // Dirty the states we'll need to sync with the daemon when it comes up. + mPTTDirty = true; + mMicVolumeDirty = true; + mSpeakerVolumeDirty = true; + mSpeakerMuteDirty = true; + // These only need to be set if they're not default (i.e. empty string). + mCaptureDeviceDirty = !mCaptureDevice.empty(); + mRenderDeviceDirty = !mRenderDevice.empty(); + + mMainSessionGroupHandle.clear(); + } + break; + + //MARK: stateDaemonLaunched + case stateDaemonLaunched: + if(mUpdateTimer.hasExpired()) + { + LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL; + + mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); + + if(!mSocket) + { + mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); + } + + mConnected = mSocket->blockingConnect(mDaemonHost); + if(mConnected) + { + setState(stateConnecting); + } + else + { + // If the connect failed, the socket may have been put into a bad state. Delete it. + closeSocket(); + } + } + break; + + //MARK: stateConnecting + case stateConnecting: + // Can't do this until we have the pump available. + if(mPump) + { + // MBW -- Note to self: pumps and pipes examples in + // indra/test/io.cpp + // indra/test/llpipeutil.{cpp|h} + + // Attach the pumps and pipes + + LLPumpIO::chain_t readChain; + + readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket))); + readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser())); + + mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS); + + setState(stateConnected); + } + + break; + + //MARK: stateConnected + case stateConnected: + // Initial devices query + getCaptureDevicesSendMessage(); + getRenderDevicesSendMessage(); + + mLoginRetryCount = 0; + + setState(stateIdle); + break; + + //MARK: stateIdle + case stateIdle: + // This is the idle state where we're connected to the daemon but haven't set up a connector yet. + if(mTuningMode) + { + mTuningExitState = stateIdle; + setState(stateMicTuningStart); + } + else if(!mVoiceEnabled) + { + // We never started up the connector. This will shut down the daemon. + setState(stateConnectorStopped); + } + else if(!mAccountName.empty()) + { + LLViewerRegion *region = gAgent.getRegion(); + + if(region) + { + if ( region->getCapability("ProvisionVoiceAccountRequest") != "" ) + { + if ( mAccountPassword.empty() ) + { + requestVoiceAccountProvision(); + } + setState(stateConnectorStart); + } + else + { + LL_WARNS_ONCE("Voice") << "region doesn't have ProvisionVoiceAccountRequest capability!" << LL_ENDL; + } + } + } + break; + + //MARK: stateMicTuningStart + case stateMicTuningStart: + if(mUpdateTimer.hasExpired()) + { + if(mCaptureDeviceDirty || mRenderDeviceDirty) + { + // These can't be changed while in tuning mode. Set them before starting. + std::ostringstream stream; + + buildSetCaptureDevice(stream); + buildSetRenderDevice(stream); + + if(!stream.str().empty()) + { + writeString(stream.str()); + } + + // This will come around again in the same state and start the capture, after the timer expires. + mUpdateTimer.start(); + mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); + } + else + { + // duration parameter is currently unused, per Mike S. + tuningCaptureStartSendMessage(10000); + + setState(stateMicTuningRunning); + } + } + + break; + + //MARK: stateMicTuningRunning + case stateMicTuningRunning: + if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty) + { + // All of these conditions make us leave tuning mode. + setState(stateMicTuningStop); + } + else + { + // process mic/speaker volume changes + if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty) + { + std::ostringstream stream; + + if(mTuningMicVolumeDirty) + { + LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL; + stream + << "" + << "" << mTuningMicVolume << "" + << "\n\n\n"; + } + + if(mTuningSpeakerVolumeDirty) + { + stream + << "" + << "" << mTuningSpeakerVolume << "" + << "\n\n\n"; + } + + mTuningMicVolumeDirty = false; + mTuningSpeakerVolumeDirty = false; + + if(!stream.str().empty()) + { + writeString(stream.str()); + } + } + } + break; + + //MARK: stateMicTuningStop + case stateMicTuningStop: + { + // transition out of mic tuning + tuningCaptureStopSendMessage(); + + setState(mTuningExitState); + + // if we exited just to change devices, this will keep us from re-entering too fast. + mUpdateTimer.start(); + mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); + + } + break; + + //MARK: stateConnectorStart + case stateConnectorStart: + if(!mVoiceEnabled) + { + // We were never logged in. This will shut down the connector. + setState(stateLoggedOut); + } + else if(!mVoiceAccountServerURI.empty()) + { + connectorCreate(); + } + break; + + //MARK: stateConnectorStarting + case stateConnectorStarting: // waiting for connector handle + // connectorCreateResponse() will transition from here to stateConnectorStarted. + break; + + //MARK: stateConnectorStarted + case stateConnectorStarted: // connector handle received + if(!mVoiceEnabled) + { + // We were never logged in. This will shut down the connector. + setState(stateLoggedOut); + } + else + { + // The connector is started. Send a login message. + setState(stateNeedsLogin); + } + break; + + //MARK: stateLoginRetry + case stateLoginRetry: + if(mLoginRetryCount == 0) + { + // First retry -- display a message to the user + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY); + } + + mLoginRetryCount++; + + if(mLoginRetryCount > MAX_LOGIN_RETRIES) + { + LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL; + setState(stateLoginFailed); + LLSD args; + std::stringstream errs; + errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000"; + args["HOSTID"] = errs.str(); + if (LLGridManager::getInstance()->isSystemGrid()) + { + LLNotificationsUtil::add("NoVoiceConnect", args); + } + else + { + LLNotificationsUtil::add("NoVoiceConnect-GIAB", args); + } + } + else + { + LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL; + mUpdateTimer.start(); + mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS); + setState(stateLoginRetryWait); + } + break; + + //MARK: stateLoginRetryWait + case stateLoginRetryWait: + if(mUpdateTimer.hasExpired()) + { + setState(stateNeedsLogin); + } + break; + + //MARK: stateNeedsLogin + case stateNeedsLogin: + if(!mAccountPassword.empty()) + { + setState(stateLoggingIn); + loginSendMessage(); + } + break; + + //MARK: stateLoggingIn + case stateLoggingIn: // waiting for account handle + // loginResponse() will transition from here to stateLoggedIn. + break; + + //MARK: stateLoggedIn + case stateLoggedIn: // account handle received + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + + // request the current set of block rules (we'll need them when updating the friends list) + accountListBlockRulesSendMessage(); + + // request the current set of auto-accept rules + accountListAutoAcceptRulesSendMessage(); + + // Set up the mute list observer if it hasn't been set up already. + if((!sMuteListListener_listening)) + { + LLMuteList::getInstance()->addObserver(&mutelist_listener); + sMuteListListener_listening = true; + } + + // Set up the friends list observer if it hasn't been set up already. + if(friendslist_listener == NULL) + { + friendslist_listener = new LLVivoxVoiceClientFriendsObserver; + LLAvatarTracker::instance().addObserver(friendslist_listener); + } + + // Set the initial state of mic mute, local speaker volume, etc. + { + std::ostringstream stream; + + buildLocalAudioUpdates(stream); + + if(!stream.str().empty()) + { + writeString(stream.str()); + } + } + +#if USE_SESSION_GROUPS + // create the main session group + sessionGroupCreateSendMessage(); + + setState(stateCreatingSessionGroup); +#else + // Not using session groups -- skip the stateCreatingSessionGroup state. + setState(stateNoChannel); + + // Initial kick-off of channel lookup logic + parcelChanged(); +#endif + break; + + //MARK: stateCreatingSessionGroup + case stateCreatingSessionGroup: + if(mSessionTerminateRequested || !mVoiceEnabled) + { + // *TODO: Question: is this the right way out of this state + setState(stateSessionTerminated); + } + else if(!mMainSessionGroupHandle.empty()) + { + setState(stateNoChannel); + + // Start looped recording (needed for "panic button" anti-griefing tool) + recordingLoopStart(); + + // Initial kick-off of channel lookup logic + parcelChanged(); + } + break; + + //MARK: stateNoChannel + case stateNoChannel: + + LL_DEBUGS("Voice") << "State No Channel" << LL_ENDL; + mSpatialJoiningNum = 0; + // Do this here as well as inside sendPositionalUpdate(). + // Otherwise, if you log in but don't join a proximal channel (such as when your login location has voice disabled), your friends list won't sync. + sendFriendsListUpdates(); + + if(mSessionTerminateRequested || !mVoiceEnabled) + { + // TODO: Question: Is this the right way out of this state? + setState(stateSessionTerminated); + } + else if(mTuningMode) + { + mTuningExitState = stateNoChannel; + setState(stateMicTuningStart); + } + else if(sessionNeedsRelog(mNextAudioSession)) + { + requestRelog(); + setState(stateSessionTerminated); + } + else if(mNextAudioSession) + { + sessionState *oldSession = mAudioSession; + + mAudioSession = mNextAudioSession; + if(!mAudioSession->mReconnect) + { + mNextAudioSession = NULL; + } + + // The old session may now need to be deleted. + reapSession(oldSession); + + if(!mAudioSession->mHandle.empty()) + { + // Connect to a session by session handle + + sessionMediaConnectSendMessage(mAudioSession); + } + else + { + // Connect to a session by URI + sessionCreateSendMessage(mAudioSession, true, false); + } + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); + setState(stateJoiningSession); + } + else if(!mSpatialSessionURI.empty()) + { + // If we're not headed elsewhere and have a spatial URI, return to spatial. + switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); + } + break; + + //MARK: stateJoiningSession + case stateJoiningSession: // waiting for session handle + + // If this is true we have problem with connection to voice server (EXT-4313). + // See descriptions of mSpatialJoiningNum and MAX_NORMAL_JOINING_SPATIAL_NUM. + if(mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM) + { + // Notify observers to let them know there is problem with voice + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); + llwarns << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << llendl; + } + + // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for + // example for p2p many times while waiting for response, so it can't be used to detect errors + if(mAudioSession && mAudioSession->mIsSpatial) + { + mSpatialJoiningNum++; + } + + // joinedAudioSession() will transition from here to stateSessionJoined. + if(!mVoiceEnabled) + { + // User bailed out during connect -- jump straight to teardown. + setState(stateSessionTerminated); + } + else if(mSessionTerminateRequested) + { + if(mAudioSession && !mAudioSession->mHandle.empty()) + { + // Only allow direct exits from this state in p2p calls (for cancelling an invite). + // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. + if(mAudioSession->mIsP2P) + { + sessionMediaDisconnectSendMessage(mAudioSession); + setState(stateSessionTerminated); + } + } + } + break; + + //MARK: stateSessionJoined + case stateSessionJoined: // session handle received + + mSpatialJoiningNum = 0; + // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4 + // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck. + // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined. + // This is a cheap way to make sure both have happened before proceeding. + if(mAudioSession && mAudioSession->mVoiceEnabled) + { + // Dirty state that may need to be sync'ed with the daemon. + mPTTDirty = true; + mSpeakerVolumeDirty = true; + mSpatialCoordsDirty = true; + + setState(stateRunning); + + // Start the throttle timer + mUpdateTimer.start(); + mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); + + // Events that need to happen when a session is joined could go here. + // Maybe send initial spatial data? + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); + + } + else if(!mVoiceEnabled) + { + // User bailed out during connect -- jump straight to teardown. + setState(stateSessionTerminated); + } + else if(mSessionTerminateRequested) + { + // Only allow direct exits from this state in p2p calls (for cancelling an invite). + // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. + if(mAudioSession && mAudioSession->mIsP2P) + { + sessionMediaDisconnectSendMessage(mAudioSession); + setState(stateSessionTerminated); + } + } + break; + + //MARK: stateRunning + case stateRunning: // steady state + // Disabling voice or disconnect requested. + if(!mVoiceEnabled || mSessionTerminateRequested) + { + leaveAudioSession(); + } + else + { + + // Figure out whether the PTT state needs to change + { + bool newPTT; + if(mUsePTT) + { + // If configured to use PTT, track the user state. + newPTT = mUserPTTState; + } + else + { + // If not configured to use PTT, it should always be true (otherwise the user will be unable to speak). + newPTT = true; + } + + if(mMuteMic) + { + // This always overrides any other PTT setting. + newPTT = false; + } + + // Dirty if state changed. + if(newPTT != mPTT) + { + mPTT = newPTT; + mPTTDirty = true; + } + } + + if(!inSpatialChannel()) + { + // When in a non-spatial channel, never send positional updates. + mSpatialCoordsDirty = false; + } + else + { + // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position) + enforceTether(); + } + + // Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast) + // or every 10hz, whichever is sooner. + if((mAudioSession && mAudioSession->mVolumeDirty) || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired()) + { + mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); + sendPositionalUpdate(); + } + } + break; + + //MARK: stateLeavingSession + case stateLeavingSession: // waiting for terminate session response + // The handler for the Session.Terminate response will transition from here to stateSessionTerminated. + break; + + //MARK: stateSessionTerminated + case stateSessionTerminated: + + // Must do this first, since it uses mAudioSession. + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + + if(mAudioSession) + { + sessionState *oldSession = mAudioSession; + + mAudioSession = NULL; + // We just notified status observers about this change. Don't do it again. + mAudioSessionChanged = false; + + // The old session may now need to be deleted. + reapSession(oldSession); + } + else + { + LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL; + } + + // Always reset the terminate request flag when we get here. + mSessionTerminateRequested = false; + + if(mVoiceEnabled && !mRelogRequested) + { + // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state). + setState(stateNoChannel); + } + else + { + // Shutting down voice, continue with disconnecting. + logout(); + + // The state machine will take it from here + mRelogRequested = false; + } + + break; + + //MARK: stateLoggingOut + case stateLoggingOut: // waiting for logout response + // The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut. + break; + + //MARK: stateLoggedOut + case stateLoggedOut: // logout response received + + // Once we're logged out, all these things are invalid. + mAccountHandle.clear(); + deleteAllSessions(); + deleteAllBuddies(); + + if(mVoiceEnabled && !mRelogRequested) + { + // User was logged out, but wants to be logged in. Send a new login request. + setState(stateNeedsLogin); + } + else + { + // shut down the connector + connectorShutdown(); + } + break; + + //MARK: stateConnectorStopping + case stateConnectorStopping: // waiting for connector stop + // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped. + break; + + //MARK: stateConnectorStopped + case stateConnectorStopped: // connector stop received + setState(stateDisableCleanup); + break; + + //MARK: stateConnectorFailed + case stateConnectorFailed: + setState(stateConnectorFailedWaiting); + break; + //MARK: stateConnectorFailedWaiting + case stateConnectorFailedWaiting: + if(!mVoiceEnabled) + { + setState(stateDisableCleanup); + } + break; + + //MARK: stateLoginFailed + case stateLoginFailed: + setState(stateLoginFailedWaiting); + break; + //MARK: stateLoginFailedWaiting + case stateLoginFailedWaiting: + if(!mVoiceEnabled) + { + setState(stateDisableCleanup); + } + break; + + //MARK: stateJoinSessionFailed + case stateJoinSessionFailed: + // Transition to error state. Send out any notifications here. + if(mAudioSession) + { + LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL; + } + else + { + LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL; + } + + notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + setState(stateJoinSessionFailedWaiting); + break; + + //MARK: stateJoinSessionFailedWaiting + case stateJoinSessionFailedWaiting: + // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message. + // Region crossings may leave this state and try the join again. + if(mSessionTerminateRequested) + { + setState(stateSessionTerminated); + } + break; + + //MARK: stateJail + case stateJail: + // We have given up. Do nothing. + break; + + } + + if(mAudioSession && mAudioSession->mParticipantsChanged) + { + mAudioSession->mParticipantsChanged = false; + mAudioSessionChanged = true; + } + + if(mAudioSessionChanged) + { + mAudioSessionChanged = false; + notifyParticipantObservers(); + } +} + +void LLVivoxVoiceClient::closeSocket(void) +{ + mSocket.reset(); + mConnected = false; +} + +void LLVivoxVoiceClient::loginSendMessage() +{ + std::ostringstream stream; + + bool autoPostCrashDumps = gSavedSettings.getBOOL("VivoxAutoPostCrashDumps"); + + stream + << "" + << "" << mConnectorHandle << "" + << "" << mAccountName << "" + << "" << mAccountPassword << "" + << "VerifyAnswer" + << "true" + << "Application" + << "5" + << (autoPostCrashDumps?"true":"") + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::logout() +{ + // Ensure that we'll re-request provisioning before logging in again + mAccountPassword.clear(); + mVoiceAccountServerURI.clear(); + + setState(stateLoggingOut); + logoutSendMessage(); +} + +void LLVivoxVoiceClient::logoutSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mAccountHandle << "" + << "" + << "\n\n\n"; + + mAccountHandle.clear(); + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::accountListBlockRulesSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL; + + stream + << "" + << "" << mAccountHandle << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::accountListAutoAcceptRulesSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL; + + stream + << "" + << "" << mAccountHandle << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::sessionGroupCreateSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "creating session group" << LL_ENDL; + + stream + << "" + << "" << mAccountHandle << "" + << "Normal" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText) +{ + LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; + + session->mCreateInProgress = true; + if(startAudio) + { + session->mMediaConnectInProgress = true; + } + + std::ostringstream stream; + stream + << "mSIPURI << "\" action=\"Session.Create.1\">" + << "" << mAccountHandle << "" + << "" << session->mSIPURI << ""; + + static const std::string allowed_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "-._~"; + + if(!session->mHash.empty()) + { + stream + << "" << LLURI::escape(session->mHash, allowed_chars) << "" + << "SHA1UserName"; + } + + stream + << "" << (startAudio?"true":"false") << "" + << "" << (startText?"true":"false") << "" + << "" << mChannelName << "" + << "\n\n\n"; + writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText) +{ + LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; + + session->mCreateInProgress = true; + if(startAudio) + { + session->mMediaConnectInProgress = true; + } + + std::string password; + if(!session->mHash.empty()) + { + static const std::string allowed_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "-._~" + ; + password = LLURI::escape(session->mHash, allowed_chars); + } + + std::ostringstream stream; + stream + << "mSIPURI << "\" action=\"SessionGroup.AddSession.1\">" + << "" << session->mGroupHandle << "" + << "" << session->mSIPURI << "" + << "" << mChannelName << "" + << "" << (startAudio?"true":"false") << "" + << "" << (startText?"true":"false") << "" + << "" << password << "" + << "SHA1UserName" + << "\n\n\n" + ; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session) +{ + LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL; + + session->mMediaConnectInProgress = true; + + std::ostringstream stream; + + stream + << "mHandle << "\" action=\"Session.MediaConnect.1\">" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "Audio" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionTextConnectSendMessage(sessionState *session) +{ + LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL; + + std::ostringstream stream; + + stream + << "mHandle << "\" action=\"Session.TextConnect.1\">" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionTerminate() +{ + mSessionTerminateRequested = true; +} + +void LLVivoxVoiceClient::requestRelog() +{ + mSessionTerminateRequested = true; + mRelogRequested = true; +} + + +void LLVivoxVoiceClient::leaveAudioSession() +{ + if(mAudioSession) + { + LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; + + switch(getState()) + { + case stateNoChannel: + // In this case, we want to pretend the join failed so our state machine doesn't get stuck. + // Skip the join failed transition state so we don't send out error notifications. + setState(stateJoinSessionFailedWaiting); + break; + case stateJoiningSession: + case stateSessionJoined: + case stateRunning: + if(!mAudioSession->mHandle.empty()) + { + +#if RECORD_EVERYTHING + // HACK: for testing only + // Save looped recording + std::string savepath("/tmp/vivoxrecording"); + { + time_t now = time(NULL); + const size_t BUF_SIZE = 64; + char time_str[BUF_SIZE]; /* Flawfinder: ignore */ + + strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); + savepath += time_str; + } + recordingLoopSave(savepath); +#endif + + sessionMediaDisconnectSendMessage(mAudioSession); + setState(stateLeavingSession); + } + else + { + LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; + setState(stateSessionTerminated); + } + break; + case stateJoinSessionFailed: + case stateJoinSessionFailedWaiting: + setState(stateSessionTerminated); + break; + + default: + LL_WARNS("Voice") << "called from unknown state" << LL_ENDL; + break; + } + } + else + { + LL_WARNS("Voice") << "called with no active session" << LL_ENDL; + setState(stateSessionTerminated); + } +} + +void LLVivoxVoiceClient::sessionTerminateSendMessage(sessionState *session) +{ + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL; + stream + << "" + << "" << session->mHandle << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(sessionState *session) +{ + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL; + stream + << "" + << "" << session->mGroupHandle << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session) +{ + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL; + stream + << "" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "Audio" + << "\n\n\n"; + + writeString(stream.str()); + +} + +void LLVivoxVoiceClient::sessionTextDisconnectSendMessage(sessionState *session) +{ + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL; + stream + << "" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::getCaptureDevicesSendMessage() +{ + std::ostringstream stream; + stream + << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::getRenderDevicesSendMessage() +{ + std::ostringstream stream; + stream + << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::clearCaptureDevices() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + mCaptureDevices.clear(); +} + +void LLVivoxVoiceClient::addCaptureDevice(const std::string& name) +{ + LL_DEBUGS("Voice") << name << LL_ENDL; + + mCaptureDevices.push_back(name); +} + +LLVoiceDeviceList& LLVivoxVoiceClient::getCaptureDevices() +{ + return mCaptureDevices; +} + +void LLVivoxVoiceClient::setCaptureDevice(const std::string& name) +{ + if(name == "Default") + { + if(!mCaptureDevice.empty()) + { + mCaptureDevice.clear(); + mCaptureDeviceDirty = true; + } + } + else + { + if(mCaptureDevice != name) + { + mCaptureDevice = name; + mCaptureDeviceDirty = true; + } + } +} + +void LLVivoxVoiceClient::clearRenderDevices() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + mRenderDevices.clear(); +} + +void LLVivoxVoiceClient::addRenderDevice(const std::string& name) +{ + LL_DEBUGS("Voice") << name << LL_ENDL; + mRenderDevices.push_back(name); +} + +LLVoiceDeviceList& LLVivoxVoiceClient::getRenderDevices() +{ + return mRenderDevices; +} + +void LLVivoxVoiceClient::setRenderDevice(const std::string& name) +{ + if(name == "Default") + { + if(!mRenderDevice.empty()) + { + mRenderDevice.clear(); + mRenderDeviceDirty = true; + } + } + else + { + if(mRenderDevice != name) + { + mRenderDevice = name; + mRenderDeviceDirty = true; + } + } + +} + +void LLVivoxVoiceClient::tuningStart() +{ + mTuningMode = true; + LL_DEBUGS("Voice") << "Starting tuning" << LL_ENDL; + if(getState() >= stateNoChannel) + { + LL_DEBUGS("Voice") << "no channel" << LL_ENDL; + sessionTerminate(); + } +} + +void LLVivoxVoiceClient::tuningStop() +{ + mTuningMode = false; +} + +bool LLVivoxVoiceClient::inTuningMode() +{ + bool result = false; + switch(getState()) + { + case stateMicTuningRunning: + result = true; + break; + default: + break; + } + return result; +} + +void LLVivoxVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop) +{ + mTuningAudioFile = name; + std::ostringstream stream; + stream + << "" + << "" << mTuningAudioFile << "" + << "" << (loop?"1":"0") << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::tuningRenderStopSendMessage() +{ + std::ostringstream stream; + stream + << "" + << "" << mTuningAudioFile << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int duration) +{ + LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL; + + std::ostringstream stream; + stream + << "" + << "" << duration << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::tuningCaptureStopSendMessage() +{ + LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL; + + std::ostringstream stream; + stream + << "" + << "\n\n\n"; + + writeString(stream.str()); + + mTuningEnergy = 0.0f; +} + +void LLVivoxVoiceClient::tuningSetMicVolume(float volume) +{ + int scaled_volume = scale_mic_volume(volume); + + if(scaled_volume != mTuningMicVolume) + { + mTuningMicVolume = scaled_volume; + mTuningMicVolumeDirty = true; + } +} + +void LLVivoxVoiceClient::tuningSetSpeakerVolume(float volume) +{ + int scaled_volume = scale_speaker_volume(volume); + + if(scaled_volume != mTuningSpeakerVolume) + { + mTuningSpeakerVolume = scaled_volume; + mTuningSpeakerVolumeDirty = true; + } +} + +float LLVivoxVoiceClient::tuningGetEnergy(void) +{ + return mTuningEnergy; +} + +bool LLVivoxVoiceClient::deviceSettingsAvailable() +{ + bool result = true; + + if(!mConnected) + result = false; + + if(mRenderDevices.empty()) + result = false; + + return result; +} + +void LLVivoxVoiceClient::refreshDeviceLists(bool clearCurrentList) +{ + if(clearCurrentList) + { + clearCaptureDevices(); + clearRenderDevices(); + } + getCaptureDevicesSendMessage(); + getRenderDevicesSendMessage(); +} + +void LLVivoxVoiceClient::daemonDied() +{ + // The daemon died, so the connection is gone. Reset everything and start over. + LL_WARNS("Voice") << "Connection to vivox daemon lost. Resetting state."<< LL_ENDL; + + // Try to relaunch the daemon + setState(stateDisableCleanup); +} + +void LLVivoxVoiceClient::giveUp() +{ + // All has failed. Clean up and stop trying. + closeSocket(); + deleteAllSessions(); + deleteAllBuddies(); + + setState(stateJail); +} + +static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel) +{ + F32 nat[3], nup[3], nl[3], nvel[3]; // the new at, up, left vectors and the new position and velocity + F64 npos[3]; + + // The original XML command was sent like this: + /* + << "" + << "" << pos[VX] << "" + << "" << pos[VZ] << "" + << "" << pos[VY] << "" + << "" + << "" + << "" << mAvatarVelocity[VX] << "" + << "" << mAvatarVelocity[VZ] << "" + << "" << mAvatarVelocity[VY] << "" + << "" + << "" + << "" << l.mV[VX] << "" + << "" << u.mV[VX] << "" + << "" << a.mV[VX] << "" + << "" + << "" + << "" << l.mV[VZ] << "" + << "" << u.mV[VY] << "" + << "" << a.mV[VZ] << "" + << "" + << "" + << "" << l.mV [VY] << "" + << "" << u.mV [VZ] << "" + << "" << a.mV [VY] << "" + << ""; + */ + +#if 1 + // This was the original transform done when building the XML command + nat[0] = left.mV[VX]; + nat[1] = up.mV[VX]; + nat[2] = at.mV[VX]; + + nup[0] = left.mV[VZ]; + nup[1] = up.mV[VY]; + nup[2] = at.mV[VZ]; + + nl[0] = left.mV[VY]; + nl[1] = up.mV[VZ]; + nl[2] = at.mV[VY]; + + npos[0] = pos.mdV[VX]; + npos[1] = pos.mdV[VZ]; + npos[2] = pos.mdV[VY]; + + nvel[0] = vel.mV[VX]; + nvel[1] = vel.mV[VZ]; + nvel[2] = vel.mV[VY]; + + for(int i=0;i<3;++i) { + at.mV[i] = nat[i]; + up.mV[i] = nup[i]; + left.mV[i] = nl[i]; + pos.mdV[i] = npos[i]; + } + + // This was the original transform done in the SDK + nat[0] = at.mV[2]; + nat[1] = 0; // y component of at vector is always 0, this was up[2] + nat[2] = -1 * left.mV[2]; + + // We override whatever the application gives us + nup[0] = 0; // x component of up vector is always 0 + nup[1] = 1; // y component of up vector is always 1 + nup[2] = 0; // z component of up vector is always 0 + + nl[0] = at.mV[0]; + nl[1] = 0; // y component of left vector is always zero, this was up[0] + nl[2] = -1 * left.mV[0]; + + npos[2] = pos.mdV[2] * -1.0; + npos[1] = pos.mdV[1]; + npos[0] = pos.mdV[0]; + + for(int i=0;i<3;++i) { + at.mV[i] = nat[i]; + up.mV[i] = nup[i]; + left.mV[i] = nl[i]; + pos.mdV[i] = npos[i]; + } +#else + // This is the compose of the two transforms (at least, that's what I'm trying for) + nat[0] = at.mV[VX]; + nat[1] = 0; // y component of at vector is always 0, this was up[2] + nat[2] = -1 * up.mV[VZ]; + + // We override whatever the application gives us + nup[0] = 0; // x component of up vector is always 0 + nup[1] = 1; // y component of up vector is always 1 + nup[2] = 0; // z component of up vector is always 0 + + nl[0] = left.mV[VX]; + nl[1] = 0; // y component of left vector is always zero, this was up[0] + nl[2] = -1 * left.mV[VY]; + + npos[0] = pos.mdV[VX]; + npos[1] = pos.mdV[VZ]; + npos[2] = pos.mdV[VY] * -1.0; + + nvel[0] = vel.mV[VX]; + nvel[1] = vel.mV[VZ]; + nvel[2] = vel.mV[VY]; + + for(int i=0;i<3;++i) { + at.mV[i] = nat[i]; + up.mV[i] = nup[i]; + left.mV[i] = nl[i]; + pos.mdV[i] = npos[i]; + } + +#endif +} + +void LLVivoxVoiceClient::sendPositionalUpdate(void) +{ + std::ostringstream stream; + + if(mSpatialCoordsDirty) + { + LLVector3 l, u, a, vel; + LLVector3d pos; + + mSpatialCoordsDirty = false; + + // Always send both speaker and listener positions together. + stream << "" + << "" << getAudioSessionHandle() << ""; + + stream << ""; + +// LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; + l = mAvatarRot.getLeftRow(); + u = mAvatarRot.getUpRow(); + a = mAvatarRot.getFwdRow(); + pos = mAvatarPosition; + vel = mAvatarVelocity; + + // SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore. + // The old transform is replicated by this function. + oldSDKTransform(l, u, a, pos, vel); + + stream + << "" + << "" << pos.mdV[VX] << "" + << "" << pos.mdV[VY] << "" + << "" << pos.mdV[VZ] << "" + << "" + << "" + << "" << vel.mV[VX] << "" + << "" << vel.mV[VY] << "" + << "" << vel.mV[VZ] << "" + << "" + << "" + << "" << a.mV[VX] << "" + << "" << a.mV[VY] << "" + << "" << a.mV[VZ] << "" + << "" + << "" + << "" << u.mV[VX] << "" + << "" << u.mV[VY] << "" + << "" << u.mV[VZ] << "" + << "" + << "" + << "" << l.mV [VX] << "" + << "" << l.mV [VY] << "" + << "" << l.mV [VZ] << "" + << ""; + + stream << ""; + + stream << ""; + + LLVector3d earPosition; + LLVector3 earVelocity; + LLMatrix3 earRot; + + switch(mEarLocation) + { + case earLocCamera: + default: + earPosition = mCameraPosition; + earVelocity = mCameraVelocity; + earRot = mCameraRot; + break; + + case earLocAvatar: + earPosition = mAvatarPosition; + earVelocity = mAvatarVelocity; + earRot = mAvatarRot; + break; + + case earLocMixed: + earPosition = mAvatarPosition; + earVelocity = mAvatarVelocity; + earRot = mCameraRot; + break; + } + + l = earRot.getLeftRow(); + u = earRot.getUpRow(); + a = earRot.getFwdRow(); + pos = earPosition; + vel = earVelocity; + +// LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL; + + oldSDKTransform(l, u, a, pos, vel); + + stream + << "" + << "" << pos.mdV[VX] << "" + << "" << pos.mdV[VY] << "" + << "" << pos.mdV[VZ] << "" + << "" + << "" + << "" << vel.mV[VX] << "" + << "" << vel.mV[VY] << "" + << "" << vel.mV[VZ] << "" + << "" + << "" + << "" << a.mV[VX] << "" + << "" << a.mV[VY] << "" + << "" << a.mV[VZ] << "" + << "" + << "" + << "" << u.mV[VX] << "" + << "" << u.mV[VY] << "" + << "" << u.mV[VZ] << "" + << "" + << "" + << "" << l.mV [VX] << "" + << "" << l.mV [VY] << "" + << "" << l.mV [VZ] << "" + << ""; + + + stream << ""; + + stream << "\n\n\n"; + } + + if(mAudioSession && mAudioSession->mVolumeDirty) + { + participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); + + mAudioSession->mVolumeDirty = false; + + for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) + { + participantState *p = iter->second; + + if(p->mVolumeDirty) + { + // Can't set volume/mute for yourself + if(!p->mIsSelf) + { + int volume = 56; // nominal default value + bool mute = p->mOnMuteList; + + if(p->mUserVolume != -1) + { + // scale from user volume in the range 0-400 (with 100 as "normal") to vivox volume in the range 0-100 (with 56 as "normal") + if(p->mUserVolume < 100) + volume = (p->mUserVolume * 56) / 100; + else + volume = (((p->mUserVolume - 100) * (100 - 56)) / 300) + 56; + } + else if(p->mVolume != -1) + { + // Use the previously reported internal volume (comes in with a ParticipantUpdatedEvent) + volume = p->mVolume; + } + + + if(mute) + { + // SetParticipantMuteForMe doesn't work in p2p sessions. + // If we want the user to be muted, set their volume to 0 as well. + // This isn't perfect, but it will at least reduce their volume to a minimum. + volume = 0; + } + + if(volume == 0) + mute = true; + + LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL; + + // SLIM SDK: Send both volume and mute commands. + + // Send a "volume for me" command for the user. + stream << "" + << "" << getAudioSessionHandle() << "" + << "" << p->mURI << "" + << "" << volume << "" + << "\n\n\n"; + + if(!mAudioSession->mIsP2P) + { + // Send a "mute for me" command for the user + // Doesn't work in P2P sessions + stream << "" + << "" << getAudioSessionHandle() << "" + << "" << p->mURI << "" + << "" << (mute?"1":"0") << "" + << "Audio" + << "\n\n\n"; + } + } + + p->mVolumeDirty = false; + } + } + } + + buildLocalAudioUpdates(stream); + + if(!stream.str().empty()) + { + writeString(stream.str()); + } + + // Friends list updates can be huge, especially on the first voice login of an account with lots of friends. + // Batching them all together can choke SLVoice, so send them in separate writes. + sendFriendsListUpdates(); +} + +void LLVivoxVoiceClient::buildSetCaptureDevice(std::ostringstream &stream) +{ + if(mCaptureDeviceDirty) + { + LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL; + + stream + << "" + << "" << mCaptureDevice << "" + << "" + << "\n\n\n"; + + mCaptureDeviceDirty = false; + } +} + +void LLVivoxVoiceClient::buildSetRenderDevice(std::ostringstream &stream) +{ + if(mRenderDeviceDirty) + { + LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL; + + stream + << "" + << "" << mRenderDevice << "" + << "" + << "\n\n\n"; + mRenderDeviceDirty = false; + } +} + +void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream) +{ + buildSetCaptureDevice(stream); + + buildSetRenderDevice(stream); + + if(mPTTDirty) + { + mPTTDirty = false; + + // Send a local mute command. + // NOTE that the state of "PTT" is the inverse of "local mute". + // (i.e. when PTT is true, we send a mute command with "false", and vice versa) + + LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << LL_ENDL; + + stream << "" + << "" << mConnectorHandle << "" + << "" << (mPTT?"false":"true") << "" + << "\n\n\n"; + + } + + if(mSpeakerMuteDirty) + { + const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0))?"true":"false"); + + mSpeakerMuteDirty = false; + + LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL; + + stream << "" + << "" << mConnectorHandle << "" + << "" << muteval << "" + << "\n\n\n"; + + } + + if(mSpeakerVolumeDirty) + { + mSpeakerVolumeDirty = false; + + LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL; + + stream << "" + << "" << mConnectorHandle << "" + << "" << mSpeakerVolume << "" + << "\n\n\n"; + + } + + if(mMicVolumeDirty) + { + mMicVolumeDirty = false; + + LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL; + + stream << "" + << "" << mConnectorHandle << "" + << "" << mMicVolume << "" + << "\n\n\n"; + } + + +} + +void LLVivoxVoiceClient::checkFriend(const LLUUID& id) +{ + std::string name; + buddyListEntry *buddy = findBuddy(id); + + // Make sure we don't add a name before it's been looked up. + if(gCacheName->getFullName(id, name)) + { + + const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id); + bool canSeeMeOnline = false; + if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)) + canSeeMeOnline = true; + + // When we get here, mNeedsSend is true and mInSLFriends is false. Change them as necessary. + + if(buddy) + { + // This buddy is already in both lists. + + if(name != buddy->mDisplayName) + { + // The buddy is in the list with the wrong name. Update it with the correct name. + LL_WARNS("Voice") << "Buddy " << id << " has wrong name (\"" << buddy->mDisplayName << "\" should be \"" << name << "\"), updating."<< LL_ENDL; + buddy->mDisplayName = name; + buddy->mNeedsNameUpdate = true; // This will cause the buddy to be resent. + } + } + else + { + // This buddy was not in the vivox list, needs to be added. + buddy = addBuddy(sipURIFromID(id), name); + buddy->mUUID = id; + } + + // In all the above cases, the buddy is in the SL friends list (which is how we got here). + buddy->mInSLFriends = true; + buddy->mCanSeeMeOnline = canSeeMeOnline; + buddy->mNameResolved = true; + + } + else + { + // This name hasn't been looked up yet. Don't do anything with this buddy list entry until it has. + if(buddy) + { + buddy->mNameResolved = false; + } + + // Initiate a lookup. + // The "lookup completed" callback will ensure that the friends list is rechecked after it completes. + lookupName(id); + } +} + +void LLVivoxVoiceClient::clearAllLists() +{ + // FOR TESTING ONLY + + // This will send the necessary commands to delete ALL buddies, autoaccept rules, and block rules SLVoice tells us about. + buddyListMap::iterator buddy_it; + for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();) + { + buddyListEntry *buddy = buddy_it->second; + buddy_it++; + + std::ostringstream stream; + + if(buddy->mInVivoxBuddies) + { + // delete this entry from the vivox buddy list + buddy->mInVivoxBuddies = false; + LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + } + + if(buddy->mHasBlockListEntry) + { + // Delete the associated block list entry (so the block list doesn't fill up with junk) + buddy->mHasBlockListEntry = false; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + } + if(buddy->mHasAutoAcceptListEntry) + { + // Delete the associated auto-accept list entry (so the auto-accept list doesn't fill up with junk) + buddy->mHasAutoAcceptListEntry = false; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + } + + writeString(stream.str()); + + } +} + +void LLVivoxVoiceClient::sendFriendsListUpdates() +{ + if(mBuddyListMapPopulated && mBlockRulesListReceived && mAutoAcceptRulesListReceived && mFriendsListDirty) + { + mFriendsListDirty = false; + + if(0) + { + // FOR TESTING ONLY -- clear all buddy list, block list, and auto-accept list entries. + clearAllLists(); + return; + } + + LL_INFOS("Voice") << "Checking vivox buddy list against friends list..." << LL_ENDL; + + buddyListMap::iterator buddy_it; + for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) + { + // reset the temp flags in the local buddy list + buddy_it->second->mInSLFriends = false; + } + + // correlate with the friends list + { + LLCollectAllBuddies collect; + LLAvatarTracker::instance().applyFunctor(collect); + LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin(); + LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end(); + + for ( ; it != end; ++it) + { + checkFriend(it->second); + } + it = collect.mOffline.begin(); + end = collect.mOffline.end(); + for ( ; it != end; ++it) + { + checkFriend(it->second); + } + } + + LL_INFOS("Voice") << "Sending friend list updates..." << LL_ENDL; + + for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();) + { + buddyListEntry *buddy = buddy_it->second; + buddy_it++; + + // Ignore entries that aren't resolved yet. + if(buddy->mNameResolved) + { + std::ostringstream stream; + + if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate)) + { + if(mNumberOfAliases > 0) + { + // Add (or update) this entry in the vivox buddy list + buddy->mInVivoxBuddies = true; + buddy->mNeedsNameUpdate = false; + LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; + stream + << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "" << buddy->mDisplayName << "" + << "" // Without this, SLVoice doesn't seem to parse the command. + << "0" + << "\n\n\n"; + } + } + else if(!buddy->mInSLFriends) + { + // This entry no longer exists in your SL friends list. Remove all traces of it from the Vivox buddy list. + if(buddy->mInVivoxBuddies) + { + // delete this entry from the vivox buddy list + buddy->mInVivoxBuddies = false; + LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + } + + if(buddy->mHasBlockListEntry) + { + // Delete the associated block list entry, if any + buddy->mHasBlockListEntry = false; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + } + if(buddy->mHasAutoAcceptListEntry) + { + // Delete the associated auto-accept list entry, if any + buddy->mHasAutoAcceptListEntry = false; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + } + } + + if(buddy->mInSLFriends) + { + + if(buddy->mCanSeeMeOnline) + { + // Buddy should not be blocked. + + // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent. + + // If the buddy has a block list entry, delete it. + if(buddy->mHasBlockListEntry) + { + buddy->mHasBlockListEntry = false; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + + + // If we just deleted a block list entry, add an auto-accept entry. + if(!buddy->mHasAutoAcceptListEntry) + { + buddy->mHasAutoAcceptListEntry = true; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "0" + << "\n\n\n"; + } + } + } + else + { + // Buddy should be blocked. + + // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent. + + // If this buddy has an autoaccept entry, delete it + if(buddy->mHasAutoAcceptListEntry) + { + buddy->mHasAutoAcceptListEntry = false; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + + // If we just deleted an auto-accept entry, add a block list entry. + if(!buddy->mHasBlockListEntry) + { + buddy->mHasBlockListEntry = true; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "1" + << "\n\n\n"; + } + } + } + + if(!buddy->mInSLFriends && !buddy->mInVivoxBuddies) + { + // Delete this entry from the local buddy list. This should NOT invalidate the iterator, + // since it has already been incremented to the next entry. + deleteBuddy(buddy->mURI); + } + + } + writeString(stream.str()); + } + } + } +} + +///////////////////////////// +// Response/Event handlers + +void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID) +{ + if(statusCode != 0) + { + LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL; + setState(stateConnectorFailed); + LLSD args; + std::stringstream errs; + errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000"; + args["HOSTID"] = errs.str(); + if (LLGridManager::getInstance()->isSystemGrid()) + { + LLNotificationsUtil::add("NoVoiceConnect", args); + } + else + { + LLNotificationsUtil::add("NoVoiceConnect-GIAB", args); + } + } + else + { + // Connector created, move forward. + LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL; + mVoiceVersion.serverVersion = versionID; + mConnectorHandle = connectorHandle; + if(getState() == stateConnectorStarting) + { + setState(stateConnectorStarted); + } + } +} + +void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases) +{ + LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL; + + // Status code of 20200 means "bad password". We may want to special-case that at some point. + + if ( statusCode == 401 ) + { + // Login failure which is probably caused by the delay after a user's password being updated. + LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; + setState(stateLoginRetry); + } + else if(statusCode != 0) + { + LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; + setState(stateLoginFailed); + } + else + { + // Login succeeded, move forward. + mAccountHandle = accountHandle; + mNumberOfAliases = numberOfAliases; + // This needs to wait until the AccountLoginStateChangeEvent is received. +// if(getState() == stateLoggingIn) +// { +// setState(stateLoggedIn); +// } + } +} + +void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) +{ + sessionState *session = findSessionBeingCreatedByURI(requestId); + + if(session) + { + session->mCreateInProgress = false; + } + + if(statusCode != 0) + { + LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL; + if(session) + { + session->mErrorStatusCode = statusCode; + session->mErrorStatusString = statusString; + if(session == mAudioSession) + { + setState(stateJoinSessionFailed); + } + else + { + reapSession(session); + } + } + } + else + { + LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL; + if(session) + { + setSessionHandle(session, sessionHandle); + } + } +} + +void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) +{ + sessionState *session = findSessionBeingCreatedByURI(requestId); + + if(session) + { + session->mCreateInProgress = false; + } + + if(statusCode != 0) + { + LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL; + if(session) + { + session->mErrorStatusCode = statusCode; + session->mErrorStatusString = statusString; + if(session == mAudioSession) + { + setState(stateJoinSessionFailed); + } + else + { + reapSession(session); + } + } + } + else + { + LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL; + if(session) + { + setSessionHandle(session, sessionHandle); + } + } +} + +void LLVivoxVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString) +{ + sessionState *session = findSession(requestId); + if(statusCode != 0) + { + LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; + if(session) + { + session->mMediaConnectInProgress = false; + session->mErrorStatusCode = statusCode; + session->mErrorStatusString = statusString; + if(session == mAudioSession) + setState(stateJoinSessionFailed); + } + } + else + { + LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL; + } +} + +void LLVivoxVoiceClient::logoutResponse(int statusCode, std::string &statusString) +{ + if(statusCode != 0) + { + LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; + // Should this ever fail? do we care if it does? + } +} + +void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString) +{ + if(statusCode != 0) + { + LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL; + // Should this ever fail? do we care if it does? + } + + mConnected = false; + + if(getState() == stateConnectorStopping) + { + setState(stateConnectorStopped); + } +} + +void LLVivoxVoiceClient::sessionAddedEvent( + std::string &uriString, + std::string &alias, + std::string &sessionHandle, + std::string &sessionGroupHandle, + bool isChannel, + bool incoming, + std::string &nameString, + std::string &applicationString) +{ + sessionState *session = NULL; + + LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL; + + session = addSession(uriString, sessionHandle); + if(session) + { + session->mGroupHandle = sessionGroupHandle; + session->mIsChannel = isChannel; + session->mIncoming = incoming; + session->mAlias = alias; + + // Generate a caller UUID -- don't need to do this for channels + if(!session->mIsChannel) + { + if(IDFromName(session->mSIPURI, session->mCallerID)) + { + // Normal URI(base64-encoded UUID) + } + else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID)) + { + // Wrong URI, but an alias is available. Stash the incoming URI as an alternate + session->mAlternateSIPURI = session->mSIPURI; + + // and generate a proper URI from the ID. + setSessionURI(session, sipURIFromID(session->mCallerID)); + } + else + { + LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL; + setUUIDFromStringHash(session->mCallerID, session->mSIPURI); + session->mSynthesizedCallerID = true; + + // Can't look up the name in this case -- we have to extract it from the URI. + std::string namePortion = nameFromsipURI(session->mSIPURI); + if(namePortion.empty()) + { + // Didn't seem to be a SIP URI, just use the whole provided name. + namePortion = nameString; + } + + // Some incoming names may be separated with an underscore instead of a space. Fix this. + LLStringUtil::replaceChar(namePortion, '_', ' '); + + // Act like we just finished resolving the name (this stores it in all the right places) + avatarNameResolved(session->mCallerID, namePortion); + } + + LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL; + + if(!session->mSynthesizedCallerID) + { + // If we got here, we don't have a proper name. Initiate a lookup. + lookupName(session->mCallerID); + } + } + } +} + +void LLVivoxVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) +{ + LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL; + +#if USE_SESSION_GROUPS + if(mMainSessionGroupHandle.empty()) + { + // This is the first (i.e. "main") session group. Save its handle. + mMainSessionGroupHandle = sessionGroupHandle; + } + else + { + LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL; + } +#endif +} + +void LLVivoxVoiceClient::joinedAudioSession(sessionState *session) +{ + LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; + if(mAudioSession != session) + { + sessionState *oldSession = mAudioSession; + + mAudioSession = session; + mAudioSessionChanged = true; + + // The old session may now need to be deleted. + reapSession(oldSession); + } + + // This is the session we're joining. + if(getState() == stateJoiningSession) + { + setState(stateSessionJoined); + + // SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now. + // Add the current user as a participant here. + participantState *participant = session->addParticipant(sipURIFromName(mAccountName)); + if(participant) + { + participant->mIsSelf = true; + lookupName(participant->mAvatarID); + + LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName + << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; + } + + if(!session->mIsChannel) + { + // this is a p2p session. Make sure the other end is added as a participant. + participantState *participant = session->addParticipant(session->mSIPURI); + if(participant) + { + if(participant->mAvatarIDValid) + { + lookupName(participant->mAvatarID); + } + else if(!session->mName.empty()) + { + participant->mDisplayName = session->mName; + avatarNameResolved(participant->mAvatarID, session->mName); + } + + // TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here? + LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName + << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; + } + } + } +} + +void LLVivoxVoiceClient::sessionRemovedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle) +{ + LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL; + + sessionState *session = findSession(sessionHandle); + if(session) + { + leftAudioSession(session); + + // This message invalidates the session's handle. Set it to empty. + setSessionHandle(session); + + // This also means that the session's session group is now empty. + // Terminate the session group so it doesn't leak. + sessionGroupTerminateSendMessage(session); + + // Reset the media state (we now have no info) + session->mMediaStreamState = streamStateUnknown; + session->mTextStreamState = streamStateUnknown; + + // Conditionally delete the session + reapSession(session); + } + else + { + LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL; + } +} + +void LLVivoxVoiceClient::reapSession(sessionState *session) +{ + if(session) + { + if(!session->mHandle.empty()) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL; + } + else if(session->mCreateInProgress) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL; + } + else if(session->mMediaConnectInProgress) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL; + } + else if(session == mAudioSession) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL; + } + else if(session == mNextAudioSession) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL; + } + else + { + // TODO: Question: Should we check for queued text messages here? + // We don't have a reason to keep tracking this session, so just delete it. + LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL; + deleteSession(session); + session = NULL; + } + } + else + { +// LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL; + } +} + +// Returns true if the session seems to indicate we've moved to a region on a different voice server +bool LLVivoxVoiceClient::sessionNeedsRelog(sessionState *session) +{ + bool result = false; + + if(session != NULL) + { + // Only make this check for spatial channels (so it won't happen for group or p2p calls) + if(session->mIsSpatial) + { + std::string::size_type atsign; + + atsign = session->mSIPURI.find("@"); + + if(atsign != std::string::npos) + { + std::string urihost = session->mSIPURI.substr(atsign + 1); + if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str())) + { + // The hostname in this URI is different from what we expect. This probably means we need to relog. + + // We could make a ProvisionVoiceAccountRequest and compare the result with the current values of + // mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator. + + result = true; + } + } + } + } + + return result; +} + +void LLVivoxVoiceClient::leftAudioSession( + sessionState *session) +{ + if(mAudioSession == session) + { + switch(getState()) + { + case stateJoiningSession: + case stateSessionJoined: + case stateRunning: + case stateLeavingSession: + case stateJoinSessionFailed: + case stateJoinSessionFailedWaiting: + // normal transition + LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; + setState(stateSessionTerminated); + break; + + case stateSessionTerminated: + // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state. + LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; + break; + + default: + LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL; + setState(stateSessionTerminated); + break; + } + } +} + +void LLVivoxVoiceClient::accountLoginStateChangeEvent( + std::string &accountHandle, + int statusCode, + std::string &statusString, + int state) +{ + /* + According to Mike S., status codes for this event are: + login_state_logged_out=0, + login_state_logged_in = 1, + login_state_logging_in = 2, + login_state_logging_out = 3, + login_state_resetting = 4, + login_state_error=100 + */ + + LL_DEBUGS("Voice") << "state change event: " << state << LL_ENDL; + switch(state) + { + case 1: + if(getState() == stateLoggingIn) + { + setState(stateLoggedIn); + } + break; + + case 3: + // The user is in the process of logging out. + setState(stateLoggingOut); + break; + + case 0: + // The user has been logged out. + setState(stateLoggedOut); + break; + + default: + //Used to be a commented out warning + LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL; + break; + } +} + +void LLVivoxVoiceClient::mediaStreamUpdatedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + int statusCode, + std::string &statusString, + int state, + bool incoming) +{ + sessionState *session = findSession(sessionHandle); + + LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL; + + if(session) + { + // We know about this session + + // Save the state for later use + session->mMediaStreamState = state; + + switch(statusCode) + { + case 0: + case 200: + // generic success + // Don't change the saved error code (it may have been set elsewhere) + break; + default: + // save the status code for later + session->mErrorStatusCode = statusCode; + break; + } + + switch(state) + { + case streamStateIdle: + // Standard "left audio session" + session->mVoiceEnabled = false; + session->mMediaConnectInProgress = false; + leftAudioSession(session); + break; + + case streamStateConnected: + session->mVoiceEnabled = true; + session->mMediaConnectInProgress = false; + joinedAudioSession(session); + break; + + case streamStateRinging: + if(incoming) + { + // Send the voice chat invite to the GUI layer + // TODO: Question: Should we correlate with the mute list here? + session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID); + session->mVoiceInvitePending = true; + if(session->mName.empty()) + { + lookupName(session->mCallerID); + } + else + { + // Act like we just finished resolving the name + avatarNameResolved(session->mCallerID, session->mName); + } + } + break; + + default: + LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; + break; + + } + + } + else + { + LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL; + } +} + +void LLVivoxVoiceClient::textStreamUpdatedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + bool enabled, + int state, + bool incoming) +{ + sessionState *session = findSession(sessionHandle); + + if(session) + { + // Save the state for later use + session->mTextStreamState = state; + + // We know about this session + switch(state) + { + case 0: // We see this when the text stream closes + LL_DEBUGS("Voice") << "stream closed" << LL_ENDL; + break; + + case 1: // We see this on an incoming call from the Connector + // Try to send any text messages queued for this session. + sendQueuedTextMessages(session); + + // Send the text chat invite to the GUI layer + // TODO: Question: Should we correlate with the mute list here? + session->mTextInvitePending = true; + if(session->mName.empty()) + { + lookupName(session->mCallerID); + } + else + { + // Act like we just finished resolving the name + avatarNameResolved(session->mCallerID, session->mName); + } + break; + + default: + LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; + break; + + } + } +} + +void LLVivoxVoiceClient::participantAddedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + std::string &uriString, + std::string &alias, + std::string &nameString, + std::string &displayNameString, + int participantType) +{ + sessionState *session = findSession(sessionHandle); + if(session) + { + participantState *participant = session->addParticipant(uriString); + if(participant) + { + participant->mAccountName = nameString; + + LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName + << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; + + if(participant->mAvatarIDValid) + { + // Initiate a lookup + lookupName(participant->mAvatarID); + } + else + { + // If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work. + std::string namePortion = nameFromsipURI(uriString); + if(namePortion.empty()) + { + // Problem with the SIP URI, fall back to the display name + namePortion = displayNameString; + } + if(namePortion.empty()) + { + // Problems with both of the above, fall back to the account name + namePortion = nameString; + } + + // Set the display name (which is a hint to the active speakers window not to do its own lookup) + participant->mDisplayName = namePortion; + avatarNameResolved(participant->mAvatarID, namePortion); + } + } + } +} + +void LLVivoxVoiceClient::participantRemovedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + std::string &uriString, + std::string &alias, + std::string &nameString) +{ + sessionState *session = findSession(sessionHandle); + if(session) + { + participantState *participant = session->findParticipant(uriString); + if(participant) + { + session->removeParticipant(participant); + } + else + { + LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL; + } + } + else + { + LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; + } +} + + +void LLVivoxVoiceClient::participantUpdatedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + std::string &uriString, + std::string &alias, + bool isModeratorMuted, + bool isSpeaking, + int volume, + F32 energy) +{ + sessionState *session = findSession(sessionHandle); + if(session) + { + participantState *participant = session->findParticipant(uriString); + + if(participant) + { + participant->mIsSpeaking = isSpeaking; + participant->mIsModeratorMuted = isModeratorMuted; + + // SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false + if (isSpeaking) + { + participant->mSpeakingTimeout.reset(); + participant->mPower = energy; + } + else + { + participant->mPower = 0.0f; + } + + // *HACK: Minimal hack to fix EXT-6508, ignore the incoming volume if it is zero. + // This happens because we send volume zero to Vivox when someone is muted, + // Vivox then send it back to us, overwriting the previous volume. + // Remove this hack once volume refactoring from EXT-6031 is applied. + if (volume != 0) + { + participant->mVolume = volume; + } + + + // *HACK: mantipov: added while working on EXT-3544 + /* + Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE + LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER. + + participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted + Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug. + Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates. + + But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post() + voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager + and event is not fired. + + So, we have to call LLSpeakerMgr::update() here. In any case it is better than call it + in LLCallFloater::draw() + */ + LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel(); + + // ignore session ID of local chat + if (voice_cnl && voice_cnl->getSessionID().notNull()) + { + LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID()); + if (speaker_manager) + { + speaker_manager->update(true); + } + } + + } + else + { + LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL; + } + } + else + { + LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL; + } +} + +void LLVivoxVoiceClient::buddyPresenceEvent( + std::string &uriString, + std::string &alias, + std::string &statusString, + std::string &applicationString) +{ + buddyListEntry *buddy = findBuddy(uriString); + + if(buddy) + { + LL_DEBUGS("Voice") << "Presence event for " << buddy->mDisplayName << " status \"" << statusString << "\", application \"" << applicationString << "\""<< LL_ENDL; + LL_DEBUGS("Voice") << "before: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL; + + if(applicationString.empty()) + { + // This presence event is from a client that doesn't set up the Application string. Do things the old-skool way. + // NOTE: this will be needed to support people who aren't on the 3010-class SDK yet. + + if ( stricmp("Unknown", statusString.c_str())== 0) + { + // User went offline with a non-SLim-enabled viewer. + buddy->mOnlineSL = false; + } + else if ( stricmp("Online", statusString.c_str())== 0) + { + // User came online with a non-SLim-enabled viewer. + buddy->mOnlineSL = true; + } + else + { + // If the user is online through SLim, their status will be "Online-slc", "Away", or something else. + // NOTE: we should never see this unless someone is running an OLD version of SLim -- the versions that should be in use now all set the application string. + buddy->mOnlineSLim = true; + } + } + else if(applicationString.find("SecondLifeViewer") != std::string::npos) + { + // This presence event is from a viewer that sets the application string + if ( stricmp("Unknown", statusString.c_str())== 0) + { + // Viewer says they're offline + buddy->mOnlineSL = false; + } + else + { + // Viewer says they're online + buddy->mOnlineSL = true; + } + } + else + { + // This presence event is from something which is NOT the SL viewer (assume it's SLim). + if ( stricmp("Unknown", statusString.c_str())== 0) + { + // SLim says they're offline + buddy->mOnlineSLim = false; + } + else + { + // SLim says they're online + buddy->mOnlineSLim = true; + } + } + + LL_DEBUGS("Voice") << "after: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL; + + // HACK -- increment the internal change serial number in the LLRelationship (without changing the actual status), so the UI notices the change. + LLAvatarTracker::instance().setBuddyOnline(buddy->mUUID,LLAvatarTracker::instance().isBuddyOnline(buddy->mUUID)); + + notifyFriendObservers(); + } + else + { + LL_DEBUGS("Voice") << "Presence for unknown buddy " << uriString << LL_ENDL; + } +} + +void LLVivoxVoiceClient::messageEvent( + std::string &sessionHandle, + std::string &uriString, + std::string &alias, + std::string &messageHeader, + std::string &messageBody, + std::string &applicationString) +{ + LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL; +// LL_DEBUGS("Voice") << " header " << messageHeader << ", body: \n" << messageBody << LL_ENDL; + + if(messageHeader.find("text/html") != std::string::npos) + { + std::string message; + + { + const std::string startMarker = ", try looking for a instead. + start = messageBody.find(startSpan); + start = messageBody.find(startMarker2, start); + end = messageBody.find(endSpan); + + if(start != std::string::npos) + { + start += startMarker2.size(); + + if(end != std::string::npos) + end -= start; + + message.assign(messageBody, start, end); + } + } + } + +// LL_DEBUGS("Voice") << " raw message = \n" << message << LL_ENDL; + + // strip formatting tags + { + std::string::size_type start; + std::string::size_type end; + + while((start = message.find('<')) != std::string::npos) + { + if((end = message.find('>', start + 1)) != std::string::npos) + { + // Strip out the tag + message.erase(start, (end + 1) - start); + } + else + { + // Avoid an infinite loop + break; + } + } + } + + // Decode ampersand-escaped chars + { + std::string::size_type mark = 0; + + // The text may contain text encoded with <, >, and & + mark = 0; + while((mark = message.find("<", mark)) != std::string::npos) + { + message.replace(mark, 4, "<"); + mark += 1; + } + + mark = 0; + while((mark = message.find(">", mark)) != std::string::npos) + { + message.replace(mark, 4, ">"); + mark += 1; + } + + mark = 0; + while((mark = message.find("&", mark)) != std::string::npos) + { + message.replace(mark, 5, "&"); + mark += 1; + } + } + + // strip leading/trailing whitespace (since we always seem to get a couple newlines) + LLStringUtil::trim(message); + +// LL_DEBUGS("Voice") << " stripped message = \n" << message << LL_ENDL; + + sessionState *session = findSession(sessionHandle); + if(session) + { + bool is_busy = gAgent.getBusy(); + bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat); + bool is_linden = LLMuteList::getInstance()->isLinden(session->mName); + bool quiet_chat = false; + LLChat chat; + + chat.mMuted = is_muted && !is_linden; + + if(!chat.mMuted) + { + chat.mFromID = session->mCallerID; + chat.mFromName = session->mName; + chat.mSourceType = CHAT_SOURCE_AGENT; + + if(is_busy && !is_linden) + { + quiet_chat = true; + // TODO: Question: Return busy mode response here? Or maybe when session is started instead? + } + + LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL; + gIMMgr->addMessage(session->mIMSessionID, + session->mCallerID, + session->mName.c_str(), + message.c_str(), + LLStringUtil::null, // default arg + IM_NOTHING_SPECIAL, // default arg + 0, // default arg + LLUUID::null, // default arg + LLVector3::zero, // default arg + true); // prepend name and make it a link to the user's profile + + } + } + } +} + +void LLVivoxVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType) +{ + sessionState *session = findSession(sessionHandle); + + if(session) + { + participantState *participant = session->findParticipant(uriString); + if(participant) + { + if (!stricmp(notificationType.c_str(), "Typing")) + { + // Other end started typing + // TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart(). + // It requires an LLIMInfo for the message, which we don't have here. + } + else if (!stricmp(notificationType.c_str(), "NotTyping")) + { + // Other end stopped typing + // TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop(). + // It requires an LLIMInfo for the message, which we don't have here. + } + else + { + LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; + } + } + else + { + LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; + } + } + else + { + LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL; + } +} + +void LLVivoxVoiceClient::subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType) +{ + buddyListEntry *buddy = findBuddy(buddyURI); + + if(!buddy) + { + // Couldn't find buddy by URI, try converting the alias... + if(!alias.empty()) + { + LLUUID id; + if(IDFromName(alias, id)) + { + buddy = findBuddy(id); + } + } + } + + if(buddy) + { + std::ostringstream stream; + + if(buddy->mCanSeeMeOnline) + { + // Sending the response will create an auto-accept rule + buddy->mHasAutoAcceptListEntry = true; + } + else + { + // Sending the response will create a block rule + buddy->mHasBlockListEntry = true; + } + + if(buddy->mInSLFriends) + { + buddy->mInVivoxBuddies = true; + } + + stream + << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "" << (buddy->mCanSeeMeOnline?"Allow":"Hide") << "" + << ""<< (buddy->mInSLFriends?"1":"0")<< "" + << "" << subscriptionHandle << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::auxAudioPropertiesEvent(F32 energy) +{ + LL_DEBUGS("Voice") << "got energy " << energy << LL_ENDL; + mTuningEnergy = energy; +} + +void LLVivoxVoiceClient::buddyListChanged() +{ + // This is called after we receive a BuddyAndGroupListChangedEvent. + mBuddyListMapPopulated = true; + mFriendsListDirty = true; +} + +void LLVivoxVoiceClient::muteListChanged() +{ + // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. + if(mAudioSession) + { + participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); + + for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) + { + participantState *p = iter->second; + + // Check to see if this participant is on the mute list already + if(p->updateMuteState()) + mAudioSession->mVolumeDirty = true; + } + } +} + +void LLVivoxVoiceClient::updateFriends(U32 mask) +{ + if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::POWERS)) + { + // Just resend the whole friend list to the daemon + mFriendsListDirty = true; + } +} + +///////////////////////////// +// Managing list of participants +LLVivoxVoiceClient::participantState::participantState(const std::string &uri) : + mURI(uri), + mPTT(false), + mIsSpeaking(false), + mIsModeratorMuted(false), + mLastSpokeTimestamp(0.f), + mPower(0.f), + mVolume(-1), + mOnMuteList(false), + mUserVolume(-1), + mVolumeDirty(false), + mAvatarIDValid(false), + mIsSelf(false) +{ +} + +LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParticipant(const std::string &uri) +{ + participantState *result = NULL; + bool useAlternateURI = false; + + // Note: this is mostly the body of LLVivoxVoiceClient::sessionState::findParticipant(), but since we need to know if it + // matched the alternate SIP URI (so we can add it properly), we need to reproduce it here. + { + participantMap::iterator iter = mParticipantsByURI.find(uri); + + if(iter == mParticipantsByURI.end()) + { + if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) + { + // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. + // Use mSIPURI instead, since it will be properly encoded. + iter = mParticipantsByURI.find(mSIPURI); + useAlternateURI = true; + } + } + + if(iter != mParticipantsByURI.end()) + { + result = iter->second; + } + } + + if(!result) + { + // participant isn't already in one list or the other. + result = new participantState(useAlternateURI?mSIPURI:uri); + mParticipantsByURI.insert(participantMap::value_type(result->mURI, result)); + mParticipantsChanged = true; + + // Try to do a reverse transform on the URI to get the GUID back. + { + LLUUID id; + if(LLVivoxVoiceClient::getInstance()->IDFromName(result->mURI, id)) + { + result->mAvatarIDValid = true; + result->mAvatarID = id; + + if(result->updateMuteState()) + mVolumeDirty = true; + } + else + { + // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. + // This tells both code in LLVivoxVoiceClient and code in llfloateractivespeakers.cpp that the ID will not be in the name cache. + setUUIDFromStringHash(result->mAvatarID, uri); + } + } + + mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result)); + + result->mUserVolume = LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID); + + LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; + } + + return result; +} + +bool LLVivoxVoiceClient::participantState::updateMuteState() +{ + bool result = false; + + if(mAvatarIDValid) + { + bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); + if(mOnMuteList != isMuted) + { + mOnMuteList = isMuted; + mVolumeDirty = true; + result = true; + } + } + return result; +} + +bool LLVivoxVoiceClient::participantState::isAvatar() +{ + return mAvatarIDValid; +} + +void LLVivoxVoiceClient::sessionState::removeParticipant(LLVivoxVoiceClient::participantState *participant) +{ + if(participant) + { + participantMap::iterator iter = mParticipantsByURI.find(participant->mURI); + participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(participant->mAvatarID); + + LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; + + if(iter == mParticipantsByURI.end()) + { + LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL; + } + else if(iter2 == mParticipantsByUUID.end()) + { + LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; + } + else if(iter->second != iter2->second) + { + LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; + } + else + { + mParticipantsByURI.erase(iter); + mParticipantsByUUID.erase(iter2); + + delete participant; + mParticipantsChanged = true; + } + } +} + +void LLVivoxVoiceClient::sessionState::removeAllParticipants() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + + while(!mParticipantsByURI.empty()) + { + removeParticipant(mParticipantsByURI.begin()->second); + } + + if(!mParticipantsByUUID.empty()) + { + LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; + } +} + +void LLVivoxVoiceClient::getParticipantList(std::set &participants) +{ + if(mAudioSession) + { + for(participantUUIDMap::iterator iter = mAudioSession->mParticipantsByUUID.begin(); + iter != mAudioSession->mParticipantsByUUID.end(); + iter++) + { + participants.insert(iter->first); + } + } +} + +bool LLVivoxVoiceClient::isParticipant(const LLUUID &speaker_id) +{ + if(mAudioSession) + { + return (mAudioSession->mParticipantsByUUID.find(speaker_id) != mAudioSession->mParticipantsByUUID.end()); + } + return false; +} + + +LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::findParticipant(const std::string &uri) +{ + participantState *result = NULL; + + participantMap::iterator iter = mParticipantsByURI.find(uri); + + if(iter == mParticipantsByURI.end()) + { + if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) + { + // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. + // Look up the other URI + iter = mParticipantsByURI.find(mSIPURI); + } + } + + if(iter != mParticipantsByURI.end()) + { + result = iter->second; + } + + return result; +} + +LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::sessionState::findParticipantByID(const LLUUID& id) +{ + participantState * result = NULL; + participantUUIDMap::iterator iter = mParticipantsByUUID.find(id); + + if(iter != mParticipantsByUUID.end()) + { + result = iter->second; + } + + return result; +} + +LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::findParticipantByID(const LLUUID& id) +{ + participantState * result = NULL; + + if(mAudioSession) + { + result = mAudioSession->findParticipantByID(id); + } + + return result; +} + + +void LLVivoxVoiceClient::parcelChanged() +{ + if(getState() >= stateNoChannel) + { + // If the user is logged in, start a channel lookup. + LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; + + std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); + LLSD data; + LLHTTPClient::post( + url, + data, + new LLVivoxVoiceClientCapResponder); + } + else + { + // The transition to stateNoChannel needs to kick this off again. + LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL; + } +} + +void LLVivoxVoiceClient::switchChannel( + std::string uri, + bool spatial, + bool no_reconnect, + bool is_p2p, + std::string hash) +{ + bool needsSwitch = false; + + LL_DEBUGS("Voice") + << "called in state " << state2string(getState()) + << " with uri \"" << uri << "\"" + << (spatial?", spatial is true":", spatial is false") + << LL_ENDL; + + switch(getState()) + { + case stateJoinSessionFailed: + case stateJoinSessionFailedWaiting: + case stateNoChannel: + // Always switch to the new URI from these states. + needsSwitch = true; + break; + + default: + if(mSessionTerminateRequested) + { + // If a terminate has been requested, we need to compare against where the URI we're already headed to. + if(mNextAudioSession) + { + if(mNextAudioSession->mSIPURI != uri) + needsSwitch = true; + } + else + { + // mNextAudioSession is null -- this probably means we're on our way back to spatial. + if(!uri.empty()) + { + // We do want to process a switch in this case. + needsSwitch = true; + } + } + } + else + { + // Otherwise, compare against the URI we're in now. + if(mAudioSession) + { + if(mAudioSession->mSIPURI != uri) + { + needsSwitch = true; + } + } + else + { + if(!uri.empty()) + { + // mAudioSession is null -- it's not clear what case would cause this. + // For now, log it as a warning and see if it ever crops up. + LL_WARNS("Voice") << "No current audio session." << LL_ENDL; + } + } + } + break; + } + + if(needsSwitch) + { + if(uri.empty()) + { + // Leave any channel we may be in + LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; + + sessionState *oldSession = mNextAudioSession; + mNextAudioSession = NULL; + + // The old session may now need to be deleted. + reapSession(oldSession); + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); + } + else + { + LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; + + mNextAudioSession = addSession(uri); + mNextAudioSession->mHash = hash; + mNextAudioSession->mIsSpatial = spatial; + mNextAudioSession->mReconnect = !no_reconnect; + mNextAudioSession->mIsP2P = is_p2p; + } + + if(getState() <= stateNoChannel) + { + // We're already set up to join a channel, just needed to fill in the session URI + } + else + { + // State machine will come around and rejoin if uri/handle is not empty. + sessionTerminate(); + } + } +} + +void LLVivoxVoiceClient::joinSession(sessionState *session) +{ + mNextAudioSession = session; + + if(getState() <= stateNoChannel) + { + // We're already set up to join a channel, just needed to fill in the session handle + } + else + { + // State machine will come around and rejoin if uri/handle is not empty. + sessionTerminate(); + } +} + +void LLVivoxVoiceClient::setNonSpatialChannel( + const std::string &uri, + const std::string &credentials) +{ + switchChannel(uri, false, false, false, credentials); +} + +void LLVivoxVoiceClient::setSpatialChannel( + const std::string &uri, + const std::string &credentials) +{ + mSpatialSessionURI = uri; + mSpatialSessionCredentials = credentials; + mAreaVoiceDisabled = mSpatialSessionURI.empty(); + + LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; + + if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) + { + // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels. + LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; + } + else + { + switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); + } +} + +void LLVivoxVoiceClient::callUser(const LLUUID &uuid) +{ + std::string userURI = sipURIFromID(uuid); + + switchChannel(userURI, false, true, true); +} + +LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const LLUUID &uuid) +{ + // Figure out if a session with the user already exists + sessionState *session = findSession(uuid); + if(!session) + { + // No session with user, need to start one. + std::string uri = sipURIFromID(uuid); + session = addSession(uri); + + llassert(session); + if (!session) return NULL; + + session->mIsSpatial = false; + session->mReconnect = false; + session->mIsP2P = true; + session->mCallerID = uuid; + } + + if(session->mHandle.empty()) + { + // Session isn't active -- start it up. + sessionCreateSendMessage(session, false, true); + } + else + { + // Session is already active -- start up text. + sessionTextConnectSendMessage(session); + } + + return session; +} + +BOOL LLVivoxVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message) +{ + bool result = false; + + // Attempt to locate the indicated session + sessionState *session = startUserIMSession(participant_id); + if(session) + { + // found the session, attempt to send the message + session->mTextMsgQueue.push(message); + + // Try to send queued messages (will do nothing if the session is not open yet) + sendQueuedTextMessages(session); + + // The message is queued, so we succeed. + result = true; + } + else + { + LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL; + } + + return result; +} + +void LLVivoxVoiceClient::sendQueuedTextMessages(sessionState *session) +{ + if(session->mTextStreamState == 1) + { + if(!session->mTextMsgQueue.empty()) + { + std::ostringstream stream; + + while(!session->mTextMsgQueue.empty()) + { + std::string message = session->mTextMsgQueue.front(); + session->mTextMsgQueue.pop(); + stream + << "" + << "" << session->mHandle << "" + << "text/HTML" + << "" << message << "" + << "" + << "\n\n\n"; + } + writeString(stream.str()); + } + } + else + { + // Session isn't connected yet, defer until later. + } +} + +void LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid) +{ + // Figure out if a session with the user exists + sessionState *session = findSession(uuid); + if(session) + { + // found the session + if(!session->mHandle.empty()) + { + sessionTextDisconnectSendMessage(session); + } + } + else + { + LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL; + } +} + +bool LLVivoxVoiceClient::answerInvite(std::string &sessionHandle) +{ + // this is only ever used to answer incoming p2p call invites. + + sessionState *session = findSession(sessionHandle); + if(session) + { + session->mIsSpatial = false; + session->mReconnect = false; + session->mIsP2P = true; + + joinSession(session); + return true; + } + + return false; +} + +BOOL LLVivoxVoiceClient::isOnlineSIP(const LLUUID &id) +{ + bool result = false; + buddyListEntry *buddy = findBuddy(id); + if(buddy) + { + result = buddy->mOnlineSLim; + LL_DEBUGS("Voice") << "Buddy " << buddy->mDisplayName << " is SIP " << (result?"online":"offline") << LL_ENDL; + } + + if(!result) + { + // This user isn't on the buddy list or doesn't show online status through the buddy list, but could be a participant in an existing session if they initiated a text IM. + sessionState *session = findSession(id); + if(session && !session->mHandle.empty()) + { + if((session->mTextStreamState != streamStateUnknown) || (session->mMediaStreamState > streamStateIdle)) + { + LL_DEBUGS("Voice") << "Open session with " << id << " found, returning SIP online state" << LL_ENDL; + // we have a p2p text session open with this user, so by definition they're online. + result = true; + } + } + } + + return result; +} + +bool LLVivoxVoiceClient::isVoiceWorking() +{ + //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) + // Condition with joining spatial num was added to take into account possible problems with connection to voice + // server(EXT-4313). See bug descriptions and comments for MAX_NORMAL_JOINING_SPATIAL_NUM for more info. + return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated); +} + +// Returns true if the indicated participant in the current audio session is really an SL avatar. +// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. +BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id) +{ + BOOL result = TRUE; + sessionState *session = findSession(id); + + if(session != NULL) + { + // this is a p2p session with the indicated caller, or the session with the specified UUID. + if(session->mSynthesizedCallerID) + result = FALSE; + } + else + { + // Didn't find a matching session -- check the current audio session for a matching participant + if(mAudioSession != NULL) + { + participantState *participant = findParticipantByID(id); + if(participant != NULL) + { + result = participant->isAvatar(); + } + } + } + + return result; +} + +// Returns true if calling back the session URI after the session has closed is possible. +// Currently this will be false only for PSTN P2P calls. +BOOL LLVivoxVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) +{ + BOOL result = TRUE; + sessionState *session = findSession(session_id); + + if(session != NULL) + { + result = session->isCallBackPossible(); + } + + return result; +} + +// Returns true if the session can accepte text IM's. +// Currently this will be false only for PSTN P2P calls. +BOOL LLVivoxVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) +{ + bool result = TRUE; + sessionState *session = findSession(session_id); + + if(session != NULL) + { + result = session->isTextIMPossible(); + } + + return result; +} + + +void LLVivoxVoiceClient::declineInvite(std::string &sessionHandle) +{ + sessionState *session = findSession(sessionHandle); + if(session) + { + sessionMediaDisconnectSendMessage(session); + } +} + +void LLVivoxVoiceClient::leaveNonSpatialChannel() +{ + LL_DEBUGS("Voice") + << "called in state " << state2string(getState()) + << LL_ENDL; + + // Make sure we don't rejoin the current session. + sessionState *oldNextSession = mNextAudioSession; + mNextAudioSession = NULL; + + // Most likely this will still be the current session at this point, but check it anyway. + reapSession(oldNextSession); + + verifySessionState(); + + sessionTerminate(); +} + +std::string LLVivoxVoiceClient::getCurrentChannel() +{ + std::string result; + + if((getState() == stateRunning) && !mSessionTerminateRequested) + { + result = getAudioSessionURI(); + } + + return result; +} + +bool LLVivoxVoiceClient::inProximalChannel() +{ + bool result = false; + + if((getState() == stateRunning) && !mSessionTerminateRequested) + { + result = inSpatialChannel(); + } + + return result; +} + +std::string LLVivoxVoiceClient::sipURIFromID(const LLUUID &id) +{ + std::string result; + result = "sip:"; + result += nameFromID(id); + result += "@"; + result += mVoiceSIPURIHostName; + + return result; +} + +std::string LLVivoxVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar) +{ + std::string result; + if(avatar) + { + result = "sip:"; + result += nameFromID(avatar->getID()); + result += "@"; + result += mVoiceSIPURIHostName; + } + + return result; +} + +std::string LLVivoxVoiceClient::nameFromAvatar(LLVOAvatar *avatar) +{ + std::string result; + if(avatar) + { + result = nameFromID(avatar->getID()); + } + return result; +} + +std::string LLVivoxVoiceClient::nameFromID(const LLUUID &uuid) +{ + std::string result; + + if (uuid.isNull()) { + //VIVOX, the uuid emtpy look for the mURIString and return that instead. + //result.assign(uuid.mURIStringName); + LLStringUtil::replaceChar(result, '_', ' '); + return result; + } + // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code. + result = "x"; + + // Base64 encode and replace the pieces of base64 that are less compatible + // with e-mail local-parts. + // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet" + result += LLBase64::encode(uuid.mData, UUID_BYTES); + LLStringUtil::replaceChar(result, '+', '-'); + LLStringUtil::replaceChar(result, '/', '_'); + + // If you need to transform a GUID to this form on the Mac OS X command line, this will do so: + // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-') + + // The reverse transform can be done with: + // echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p + + return result; +} + +bool LLVivoxVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) +{ + bool result = false; + + // SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com" + // If it is, convert to a bare name before doing the transform. + std::string name = nameFromsipURI(inName); + + // Doesn't look like a SIP URI, assume it's an actual name. + if(name.empty()) + name = inName; + + // This will only work if the name is of the proper form. + // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is: + // "xFnPP04IpREWNkuw1cOXlhw==" + + if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '=')) + { + // The name appears to have the right form. + + // Reverse the transforms done by nameFromID + std::string temp = name; + LLStringUtil::replaceChar(temp, '-', '+'); + LLStringUtil::replaceChar(temp, '_', '/'); + + U8 rawuuid[UUID_BYTES + 1]; + int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1); + if(len == UUID_BYTES) + { + // The decode succeeded. Stuff the bits into the result's UUID + memcpy(uuid.mData, rawuuid, UUID_BYTES); + result = true; + } + } + + if(!result) + { + // VIVOX: not a standard account name, just copy the URI name mURIString field + // and hope for the best. bpj + uuid.setNull(); // VIVOX, set the uuid field to nulls + } + + return result; +} + +std::string LLVivoxVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar) +{ + return avatar->getFullname(); +} + +std::string LLVivoxVoiceClient::sipURIFromName(std::string &name) +{ + std::string result; + result = "sip:"; + result += name; + result += "@"; + result += mVoiceSIPURIHostName; + +// LLStringUtil::toLower(result); + + return result; +} + +std::string LLVivoxVoiceClient::nameFromsipURI(const std::string &uri) +{ + std::string result; + + std::string::size_type sipOffset, atOffset; + sipOffset = uri.find("sip:"); + atOffset = uri.find("@"); + if((sipOffset != std::string::npos) && (atOffset != std::string::npos)) + { + result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4)); + } + + return result; +} + +bool LLVivoxVoiceClient::inSpatialChannel(void) +{ + bool result = false; + + if(mAudioSession) + result = mAudioSession->mIsSpatial; + + return result; +} + +std::string LLVivoxVoiceClient::getAudioSessionURI() +{ + std::string result; + + if(mAudioSession) + result = mAudioSession->mSIPURI; + + return result; +} + +std::string LLVivoxVoiceClient::getAudioSessionHandle() +{ + std::string result; + + if(mAudioSession) + result = mAudioSession->mHandle; + + return result; +} + + +///////////////////////////// +// Sending updates of current state + +void LLVivoxVoiceClient::enforceTether(void) +{ + LLVector3d tethered = mCameraRequestedPosition; + + // constrain 'tethered' to within 50m of mAvatarPosition. + { + F32 max_dist = 50.0f; + LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition; + F32 camera_distance = (F32)camera_offset.magVec(); + if(camera_distance > max_dist) + { + tethered = mAvatarPosition + + (max_dist / camera_distance) * camera_offset; + } + } + + if(dist_vec(mCameraPosition, tethered) > 0.1) + { + mCameraPosition = tethered; + mSpatialCoordsDirty = true; + } +} + +void LLVivoxVoiceClient::updatePosition(void) +{ + + LLViewerRegion *region = gAgent.getRegion(); + if(region && isAgentAvatarValid()) + { + LLMatrix3 rot; + LLVector3d pos; + + // TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here... + // They're currently always set to zero. + + // Send the current camera position to the voice code + rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis()); + pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); + + LLVivoxVoiceClient::getInstance()->setCameraPosition( + pos, // position + LLVector3::zero, // velocity + rot); // rotation matrix + + // Send the current avatar position to the voice code + rot = gAgentAvatarp->getRootJoint()->getWorldRotation().getMatrix3(); + pos = gAgentAvatarp->getPositionGlobal(); + + // TODO: Can we get the head offset from outside the LLVOAvatar? + // pos += LLVector3d(mHeadOffset); + pos += LLVector3d(0.f, 0.f, 1.f); + + LLVivoxVoiceClient::getInstance()->setAvatarPosition( + pos, // position + LLVector3::zero, // velocity + rot); // rotation matrix + } +} + +void LLVivoxVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) +{ + mCameraRequestedPosition = position; + + if(mCameraVelocity != velocity) + { + mCameraVelocity = velocity; + mSpatialCoordsDirty = true; + } + + if(mCameraRot != rot) + { + mCameraRot = rot; + mSpatialCoordsDirty = true; + } +} + +void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) +{ + if(dist_vec(mAvatarPosition, position) > 0.1) + { + mAvatarPosition = position; + mSpatialCoordsDirty = true; + } + + if(mAvatarVelocity != velocity) + { + mAvatarVelocity = velocity; + mSpatialCoordsDirty = true; + } + + if(mAvatarRot != rot) + { + mAvatarRot = rot; + mSpatialCoordsDirty = true; + } +} + +bool LLVivoxVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name) +{ + bool result = false; + + if(region) + { + name = region->getName(); + } + + if(!name.empty()) + result = true; + + return result; +} + +void LLVivoxVoiceClient::leaveChannel(void) +{ + if(getState() == stateRunning) + { + LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL; + mChannelName.clear(); + sessionTerminate(); + } +} + +void LLVivoxVoiceClient::setMuteMic(bool muted) +{ + mMuteMic = muted; +} + +void LLVivoxVoiceClient::setUserPTTState(bool ptt) +{ + mUserPTTState = ptt; +} + +bool LLVivoxVoiceClient::getUserPTTState() +{ + return mUserPTTState; +} + +void LLVivoxVoiceClient::inputUserControlState(bool down) +{ + if(mPTTIsToggle) + { + if(down) // toggle open-mic state on 'down' + { + toggleUserPTTState(); + } + } + else // set open-mic state as an absolute + { + setUserPTTState(down); + } +} + + +void LLVivoxVoiceClient::toggleUserPTTState(void) +{ + mUserPTTState = !mUserPTTState; +} + +void LLVivoxVoiceClient::setVoiceEnabled(bool enabled) +{ + if (enabled != mVoiceEnabled) + { + // TODO: Refactor this so we don't call into LLVoiceChannel, but simply + // use the status observer + mVoiceEnabled = enabled; + LLVoiceClientStatusObserver::EStatusType status; + + + if (enabled) + { + LLVoiceChannel::getCurrentVoiceChannel()->activate(); + status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; + } + else + { + // Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it. + LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); + status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED; + } + } +} + +bool LLVivoxVoiceClient::voiceEnabled() +{ + return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice"); +} + +void LLVivoxVoiceClient::setLipSyncEnabled(BOOL enabled) +{ + mLipSyncEnabled = enabled; +} + +BOOL LLVivoxVoiceClient::lipSyncEnabled() +{ + + if ( mVoiceEnabled && stateDisabled != getState() ) + { + return mLipSyncEnabled; + } + else + { + return FALSE; + } +} + +void LLVivoxVoiceClient::setUsePTT(bool usePTT) +{ + if(usePTT && !mUsePTT) + { + // When the user turns on PTT, reset the current state. + mUserPTTState = false; + } + mUsePTT = usePTT; +} + +void LLVivoxVoiceClient::setPTTIsToggle(bool PTTIsToggle) +{ + if(!PTTIsToggle && mPTTIsToggle) + { + // When the user turns off toggle, reset the current state. + mUserPTTState = false; + } + + mPTTIsToggle = PTTIsToggle; +} + +bool LLVivoxVoiceClient::getPTTIsToggle() +{ + return mPTTIsToggle; +} + +void LLVivoxVoiceClient::setPTTKey(std::string &key) +{ + if(key == "MiddleMouse") + { + mPTTIsMiddleMouse = true; + } + else + { + mPTTIsMiddleMouse = false; + if(!LLKeyboard::keyFromString(key, &mPTTKey)) + { + // If the call failed, don't match any key. + key = KEY_NONE; + } + } +} + +void LLVivoxVoiceClient::setEarLocation(S32 loc) +{ + if(mEarLocation != loc) + { + LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; + + mEarLocation = loc; + mSpatialCoordsDirty = true; + } +} + +void LLVivoxVoiceClient::setVoiceVolume(F32 volume) +{ + int scaled_volume = scale_speaker_volume(volume); + + if(scaled_volume != mSpeakerVolume) + { + int min_volume = scale_speaker_volume(0); + if((scaled_volume == min_volume) || (mSpeakerVolume == min_volume)) + { + mSpeakerMuteDirty = true; + } + + mSpeakerVolume = scaled_volume; + mSpeakerVolumeDirty = true; + } +} + +void LLVivoxVoiceClient::setMicGain(F32 volume) +{ + int scaled_volume = scale_mic_volume(volume); + + if(scaled_volume != mMicVolume) + { + mMicVolume = scaled_volume; + mMicVolumeDirty = true; + } +} + +void LLVivoxVoiceClient::keyDown(KEY key, MASK mask) +{ + if (gKeyboard->getKeyRepeated(key)) + { + // ignore auto-repeat keys + return; + } + + if(!mPTTIsMiddleMouse) + { + bool down = (mPTTKey != KEY_NONE) + && gKeyboard->getKeyDown(mPTTKey); + inputUserControlState(down); + } + + +} +void LLVivoxVoiceClient::keyUp(KEY key, MASK mask) +{ + if(!mPTTIsMiddleMouse) + { + bool down = (mPTTKey != KEY_NONE) + && gKeyboard->getKeyDown(mPTTKey); + inputUserControlState(down); + } + +} +void LLVivoxVoiceClient::middleMouseState(bool down) +{ + if(mPTTIsMiddleMouse) + { + if(mPTTIsMiddleMouse) + { + inputUserControlState(down); + } + } +} + +///////////////////////////// +// Accessors for data related to nearby speakers +BOOL LLVivoxVoiceClient::getVoiceEnabled(const LLUUID& id) +{ + BOOL result = FALSE; + participantState *participant = findParticipantByID(id); + if(participant) + { + // I'm not sure what the semantics of this should be. + // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled. + result = TRUE; + } + + return result; +} + +std::string LLVivoxVoiceClient::getDisplayName(const LLUUID& id) +{ + std::string result; + participantState *participant = findParticipantByID(id); + if(participant) + { + result = participant->mDisplayName; + } + + return result; +} + + + +BOOL LLVivoxVoiceClient::getIsSpeaking(const LLUUID& id) +{ + BOOL result = FALSE; + + participantState *participant = findParticipantByID(id); + if(participant) + { + if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) + { + participant->mIsSpeaking = FALSE; + } + result = participant->mIsSpeaking; + } + + return result; +} + +BOOL LLVivoxVoiceClient::getIsModeratorMuted(const LLUUID& id) +{ + BOOL result = FALSE; + + participantState *participant = findParticipantByID(id); + if(participant) + { + result = participant->mIsModeratorMuted; + } + + return result; +} + +F32 LLVivoxVoiceClient::getCurrentPower(const LLUUID& id) +{ + F32 result = 0; + participantState *participant = findParticipantByID(id); + if(participant) + { + result = participant->mPower; + } + + return result; +} + + + +BOOL LLVivoxVoiceClient::getUsingPTT(const LLUUID& id) +{ + BOOL result = FALSE; + + participantState *participant = findParticipantByID(id); + if(participant) + { + // I'm not sure what the semantics of this should be. + // Does "using PTT" mean they're configured with a push-to-talk button? + // For now, we know there's no PTT mechanism in place, so nobody is using it. + } + + return result; +} + +BOOL LLVivoxVoiceClient::getOnMuteList(const LLUUID& id) +{ + BOOL result = FALSE; + + participantState *participant = findParticipantByID(id); + if(participant) + { + result = participant->mOnMuteList; + } + + return result; +} + +// External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100 +// internal = 400 * external^2 +F32 LLVivoxVoiceClient::getUserVolume(const LLUUID& id) +{ + F32 result = 0.0f; + + participantState *participant = findParticipantByID(id); + if(participant) + { + S32 ires = 100; // nominal default volume + + if(participant->mIsSelf) + { + // Always make it look like the user's own volume is set at the default. + } + else if(participant->mUserVolume != -1) + { + // Use the internal volume + ires = participant->mUserVolume; + + // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. +// LL_DEBUGS("Voice") << "mapping from mUserVolume " << ires << LL_ENDL; + } + else if(participant->mVolume != -1) + { + // Map backwards from vivox volume + + // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. +// LL_DEBUGS("Voice") << "mapping from mVolume " << participant->mVolume << LL_ENDL; + + if(participant->mVolume < 56) + { + ires = (participant->mVolume * 100) / 56; + } + else + { + ires = (((participant->mVolume - 56) * 300) / (100 - 56)) + 100; + } + } + result = sqrtf(((F32)ires) / 400.f); + } + + // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. +// LL_DEBUGS("Voice") << "returning " << result << LL_ENDL; + + return result; +} + +void LLVivoxVoiceClient::setUserVolume(const LLUUID& id, F32 volume) +{ + if(mAudioSession) + { + participantState *participant = findParticipantByID(id); + if (participant) + { + // store this volume setting for future sessions + LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume); + // volume can amplify by as much as 4x! + S32 ivol = (S32)(400.f * volume * volume); + participant->mUserVolume = llclamp(ivol, 0, 400); + participant->mVolumeDirty = TRUE; + mAudioSession->mVolumeDirty = TRUE; + + } + } +} + +std::string LLVivoxVoiceClient::getGroupID(const LLUUID& id) +{ + std::string result; + + participantState *participant = findParticipantByID(id); + if(participant) + { + result = participant->mGroupID; + } + + return result; +} + +BOOL LLVivoxVoiceClient::getAreaVoiceDisabled() +{ + return mAreaVoiceDisabled; +} + +void LLVivoxVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame) +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; + + if(!mMainSessionGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Start" + << "" << deltaFramesPerControlFrame << "" + << "" << "" << "" + << "false" + << "" << seconds << "" + << "\n\n\n"; + + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::recordingLoopSave(const std::string& filename) +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Flush" + << "" << filename << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::recordingStop() +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Stop" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::filePlaybackStart(const std::string& filename) +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Start" + << "" << filename << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::filePlaybackStop() +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Stop" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::filePlaybackSetPaused(bool paused) +{ + // TODO: Implement once Vivox gives me a sample +} + +void LLVivoxVoiceClient::filePlaybackSetMode(bool vox, float speed) +{ + // TODO: Implement once Vivox gives me a sample +} + +LLVivoxVoiceClient::sessionState::sessionState() : + mErrorStatusCode(0), + mMediaStreamState(streamStateUnknown), + mTextStreamState(streamStateUnknown), + mCreateInProgress(false), + mMediaConnectInProgress(false), + mVoiceInvitePending(false), + mTextInvitePending(false), + mSynthesizedCallerID(false), + mIsChannel(false), + mIsSpatial(false), + mIsP2P(false), + mIncoming(false), + mVoiceEnabled(false), + mReconnect(false), + mVolumeDirty(false), + mParticipantsChanged(false) +{ +} + +LLVivoxVoiceClient::sessionState::~sessionState() +{ + removeAllParticipants(); +} + +bool LLVivoxVoiceClient::sessionState::isCallBackPossible() +{ + // This may change to be explicitly specified by vivox in the future... + // Currently, only PSTN P2P calls cannot be returned. + // Conveniently, this is also the only case where we synthesize a caller UUID. + return !mSynthesizedCallerID; +} + +bool LLVivoxVoiceClient::sessionState::isTextIMPossible() +{ + // This may change to be explicitly specified by vivox in the future... + return !mSynthesizedCallerID; +} + + +LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsBegin(void) +{ + return mSessions.begin(); +} + +LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsEnd(void) +{ + return mSessions.end(); +} + + +LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const std::string &handle) +{ + sessionState *result = NULL; + sessionMap::iterator iter = mSessionsByHandle.find(handle); + if(iter != mSessionsByHandle.end()) + { + result = iter->second; + } + + return result; +} + +LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) +{ + sessionState *result = NULL; + for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + { + sessionState *session = *iter; + if(session->mCreateInProgress && (session->mSIPURI == uri)) + { + result = session; + break; + } + } + + return result; +} + +LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const LLUUID &participant_id) +{ + sessionState *result = NULL; + + for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + { + sessionState *session = *iter; + if((session->mCallerID == participant_id) || (session->mIMSessionID == participant_id)) + { + result = session; + break; + } + } + + return result; +} + +LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::string &uri, const std::string &handle) +{ + sessionState *result = NULL; + + if(handle.empty()) + { + // No handle supplied. + // Check whether there's already a session with this URI + for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + { + sessionState *s = *iter; + if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri)) + { + // TODO: I need to think about this logic... it's possible that this case should raise an internal error. + result = s; + break; + } + } + } + else // (!handle.empty()) + { + // Check for an existing session with this handle + sessionMap::iterator iter = mSessionsByHandle.find(handle); + + if(iter != mSessionsByHandle.end()) + { + result = iter->second; + } + } + + if(!result) + { + // No existing session found. + + LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL; + result = new sessionState(); + result->mSIPURI = uri; + result->mHandle = handle; + + mSessions.insert(result); + + if(!result->mHandle.empty()) + { + mSessionsByHandle.insert(sessionMap::value_type(result->mHandle, result)); + } + } + else + { + // Found an existing session + + if(uri != result->mSIPURI) + { + // TODO: Should this be an internal error? + LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL; + setSessionURI(result, uri); + } + + if(handle != result->mHandle) + { + if(handle.empty()) + { + // There's at least one race condition where where addSession was clearing an existing session handle, which caused things to break. + LL_DEBUGS("Voice") << "NOT clearing handle " << result->mHandle << LL_ENDL; + } + else + { + // TODO: Should this be an internal error? + LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL; + setSessionHandle(result, handle); + } + } + + LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL; + } + + verifySessionState(); + + return result; +} + +void LLVivoxVoiceClient::setSessionHandle(sessionState *session, const std::string &handle) +{ + // Have to remove the session from the handle-indexed map before changing the handle, or things will break badly. + + if(!session->mHandle.empty()) + { + // Remove session from the map if it should have been there. + sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); + if(iter != mSessionsByHandle.end()) + { + if(iter->second != session) + { + LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL; + } + + mSessionsByHandle.erase(iter); + } + else + { + LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL; + } + } + + session->mHandle = handle; + + if(!handle.empty()) + { + mSessionsByHandle.insert(sessionMap::value_type(session->mHandle, session)); + } + + verifySessionState(); +} + +void LLVivoxVoiceClient::setSessionURI(sessionState *session, const std::string &uri) +{ + // There used to be a map of session URIs to sessions, which made this complex.... + session->mSIPURI = uri; + + verifySessionState(); +} + +void LLVivoxVoiceClient::deleteSession(sessionState *session) +{ + // Remove the session from the handle map + if(!session->mHandle.empty()) + { + sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); + if(iter != mSessionsByHandle.end()) + { + if(iter->second != session) + { + LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL; + } + mSessionsByHandle.erase(iter); + } + } + + // Remove the session from the URI map + mSessions.erase(session); + + // At this point, the session should be unhooked from all lists and all state should be consistent. + verifySessionState(); + + // If this is the current audio session, clean up the pointer which will soon be dangling. + if(mAudioSession == session) + { + mAudioSession = NULL; + mAudioSessionChanged = true; + } + + // ditto for the next audio session + if(mNextAudioSession == session) + { + mNextAudioSession = NULL; + } + + // delete the session + delete session; +} + +void LLVivoxVoiceClient::deleteAllSessions() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + + while(!mSessions.empty()) + { + deleteSession(*(sessionsBegin())); + } + + if(!mSessionsByHandle.empty()) + { + LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL; + } +} + +void LLVivoxVoiceClient::verifySessionState(void) +{ + // This is mostly intended for debugging problems with session state management. + LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL; + + for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + { + sessionState *session = *iter; + + LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL; + + if(!session->mHandle.empty()) + { + // every session with a non-empty handle needs to be in the handle map + sessionMap::iterator i2 = mSessionsByHandle.find(session->mHandle); + if(i2 == mSessionsByHandle.end()) + { + LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL; + } + else + { + if(i2->second != session) + { + LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL; + } + } + } + } + + // check that every entry in the handle map points to a valid session in the session set + for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++) + { + sessionState *session = iter->second; + sessionIterator i2 = mSessions.find(session); + if(i2 == mSessions.end()) + { + LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL; + } + else + { + if(session->mHandle != (*i2)->mHandle) + { + LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL; + } + } + } +} + +LLVivoxVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) : + mURI(uri) +{ + mOnlineSL = false; + mOnlineSLim = false; + mCanSeeMeOnline = true; + mHasBlockListEntry = false; + mHasAutoAcceptListEntry = false; + mNameResolved = false; + mInVivoxBuddies = false; + mInSLFriends = false; + mNeedsNameUpdate = false; +} + +void LLVivoxVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName) +{ + buddyListEntry *buddy = addBuddy(uri, displayName); + buddy->mInVivoxBuddies = true; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::addBuddy(const std::string &uri) +{ + std::string empty; + buddyListEntry *buddy = addBuddy(uri, empty); + if(buddy->mDisplayName.empty()) + { + buddy->mNameResolved = false; + } + return buddy; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::addBuddy(const std::string &uri, const std::string &displayName) +{ + buddyListEntry *result = NULL; + buddyListMap::iterator iter = mBuddyListMap.find(uri); + + if(iter != mBuddyListMap.end()) + { + // Found a matching buddy already in the map. + LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL; + result = iter->second; + } + + if(!result) + { + // participant isn't already in one list or the other. + LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL; + result = new buddyListEntry(uri); + result->mDisplayName = displayName; + + if(IDFromName(uri, result->mUUID)) + { + // Extracted UUID from name successfully. + } + else + { + LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " (\"" << displayName << "\")" << LL_ENDL; + } + + mBuddyListMap.insert(buddyListMap::value_type(result->mURI, result)); + } + + return result; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::findBuddy(const std::string &uri) +{ + buddyListEntry *result = NULL; + buddyListMap::iterator iter = mBuddyListMap.find(uri); + if(iter != mBuddyListMap.end()) + { + result = iter->second; + } + + return result; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::findBuddy(const LLUUID &id) +{ + buddyListEntry *result = NULL; + buddyListMap::iterator iter; + + for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++) + { + if(iter->second->mUUID == id) + { + result = iter->second; + break; + } + } + + return result; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::findBuddyByDisplayName(const std::string &name) +{ + buddyListEntry *result = NULL; + buddyListMap::iterator iter; + + for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++) + { + if(iter->second->mDisplayName == name) + { + result = iter->second; + break; + } + } + + return result; +} + +void LLVivoxVoiceClient::deleteBuddy(const std::string &uri) +{ + buddyListMap::iterator iter = mBuddyListMap.find(uri); + if(iter != mBuddyListMap.end()) + { + LL_DEBUGS("Voice") << "deleting buddy " << uri << LL_ENDL; + buddyListEntry *buddy = iter->second; + mBuddyListMap.erase(iter); + delete buddy; + } + else + { + LL_DEBUGS("Voice") << "attempt to delete nonexistent buddy " << uri << LL_ENDL; + } + +} + +void LLVivoxVoiceClient::deleteAllBuddies(void) +{ + while(!mBuddyListMap.empty()) + { + deleteBuddy(mBuddyListMap.begin()->first); + } + + // Don't want to correlate with friends list when we've emptied the buddy list. + mBuddyListMapPopulated = false; + + // Don't want to correlate with friends list when we've reset the block rules. + mBlockRulesListReceived = false; + mAutoAcceptRulesListReceived = false; +} + +void LLVivoxVoiceClient::deleteAllBlockRules(void) +{ + // Clear the block list entry flags from all local buddy list entries + buddyListMap::iterator buddy_it; + for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) + { + buddy_it->second->mHasBlockListEntry = false; + } +} + +void LLVivoxVoiceClient::deleteAllAutoAcceptRules(void) +{ + // Clear the auto-accept list entry flags from all local buddy list entries + buddyListMap::iterator buddy_it; + for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) + { + buddy_it->second->mHasAutoAcceptListEntry = false; + } +} + +void LLVivoxVoiceClient::addBlockRule(const std::string &blockMask, const std::string &presenceOnly) +{ + buddyListEntry *buddy = NULL; + + // blockMask is the SIP URI of a friends list entry + buddyListMap::iterator iter = mBuddyListMap.find(blockMask); + if(iter != mBuddyListMap.end()) + { + LL_DEBUGS("Voice") << "block list entry for " << blockMask << LL_ENDL; + buddy = iter->second; + } + + if(buddy == NULL) + { + LL_DEBUGS("Voice") << "block list entry for unknown buddy " << blockMask << LL_ENDL; + buddy = addBuddy(blockMask); + } + + if(buddy != NULL) + { + buddy->mHasBlockListEntry = true; + } +} + +void LLVivoxVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy) +{ + buddyListEntry *buddy = NULL; + + // blockMask is the SIP URI of a friends list entry + buddyListMap::iterator iter = mBuddyListMap.find(autoAcceptMask); + if(iter != mBuddyListMap.end()) + { + LL_DEBUGS("Voice") << "auto-accept list entry for " << autoAcceptMask << LL_ENDL; + buddy = iter->second; + } + + if(buddy == NULL) + { + LL_DEBUGS("Voice") << "auto-accept list entry for unknown buddy " << autoAcceptMask << LL_ENDL; + buddy = addBuddy(autoAcceptMask); + } + + if(buddy != NULL) + { + buddy->mHasAutoAcceptListEntry = true; + } +} + +void LLVivoxVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString) +{ + // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done. + mBlockRulesListReceived = true; +} + +void LLVivoxVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString) +{ + // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done. + mAutoAcceptRulesListReceived = true; +} + +void LLVivoxVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) +{ + mParticipantObservers.insert(observer); +} + +void LLVivoxVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) +{ + mParticipantObservers.erase(observer); +} + +void LLVivoxVoiceClient::notifyParticipantObservers() +{ + for (observer_set_t::iterator it = mParticipantObservers.begin(); + it != mParticipantObservers.end(); + ) + { + LLVoiceClientParticipantObserver* observer = *it; + observer->onChange(); + // In case onChange() deleted an entry. + it = mParticipantObservers.upper_bound(observer); + } +} + +void LLVivoxVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) +{ + mStatusObservers.insert(observer); +} + +void LLVivoxVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) +{ + mStatusObservers.erase(observer); +} + +void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) +{ + if(mAudioSession) + { + if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) + { + switch(mAudioSession->mErrorStatusCode) + { + case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; + case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; + case 20715: + //invalid channel, we may be using a set of poorly cached + //info + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + break; + case 1009: + //invalid username and password + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + break; + } + + // Reset the error code to make sure it won't be reused later by accident. + mAudioSession->mErrorStatusCode = 0; + } + else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) + { + switch(mAudioSession->mErrorStatusCode) + { + case 404: // NOT_FOUND + case 480: // TEMPORARILY_UNAVAILABLE + case 408: // REQUEST_TIMEOUT + // call failed because other user was not available + // treat this as an error case + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + + // Reset the error code to make sure it won't be reused later by accident. + mAudioSession->mErrorStatusCode = 0; + break; + } + } + } + + LL_DEBUGS("Voice") + << " " << LLVoiceClientStatusObserver::status2string(status) + << ", session URI " << getAudioSessionURI() + << (inSpatialChannel()?", proximal is true":", proximal is false") + << LL_ENDL; + + for (status_observer_set_t::iterator it = mStatusObservers.begin(); + it != mStatusObservers.end(); + ) + { + LLVoiceClientStatusObserver* observer = *it; + observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); + // In case onError() deleted an entry. + it = mStatusObservers.upper_bound(observer); + } + +} + +void LLVivoxVoiceClient::addObserver(LLFriendObserver* observer) +{ + mFriendObservers.insert(observer); +} + +void LLVivoxVoiceClient::removeObserver(LLFriendObserver* observer) +{ + mFriendObservers.erase(observer); +} + +void LLVivoxVoiceClient::notifyFriendObservers() +{ + for (friend_observer_set_t::iterator it = mFriendObservers.begin(); + it != mFriendObservers.end(); + ) + { + LLFriendObserver* observer = *it; + it++; + // The only friend-related thing we notify on is online/offline transitions. + observer->changed(LLFriendObserver::ONLINE); + } +} + +void LLVivoxVoiceClient::lookupName(const LLUUID &id) +{ + BOOL is_group = FALSE; + gCacheName->get(id, is_group, &LLVivoxVoiceClient::onAvatarNameLookup); +} + +//static +void LLVivoxVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group) +{ + std::string name = llformat("%s %s", first.c_str(), last.c_str()); + LLVivoxVoiceClient::getInstance()->avatarNameResolved(id, name); + +} + +void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) +{ + // If the avatar whose name just resolved is on our friends list, resync the friends list. + if(LLAvatarTracker::instance().getBuddyInfo(id) != NULL) + { + mFriendsListDirty = true; + } + + // Iterate over all sessions. + for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + { + sessionState *session = *iter; + + // Check for this user as a participant in this session + participantState *participant = session->findParticipantByID(id); + if(participant) + { + // Found -- fill in the name + participant->mAccountName = name; + // and post a "participants updated" message to listeners later. + session->mParticipantsChanged = true; + } + + // Check whether this is a p2p session whose caller name just resolved + if(session->mCallerID == id) + { + // this session's "caller ID" just resolved. Fill in the name. + session->mName = name; + if(session->mTextInvitePending) + { + session->mTextInvitePending = false; + + // We don't need to call gIMMgr->addP2PSession() here. The first incoming message will create the panel. + } + if(session->mVoiceInvitePending) + { + session->mVoiceInvitePending = false; + + gIMMgr->inviteToSession( + LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID), + session->mName, + session->mCallerID, + session->mName, + IM_SESSION_P2P_INVITE, + LLIMMgr::INVITATION_TYPE_VOICE, + session->mHandle); + } + + } + } +} + + +LLVivoxProtocolParser::LLVivoxProtocolParser() +{ + parser = NULL; + parser = XML_ParserCreate(NULL); + + reset(); +} + +void LLVivoxProtocolParser::reset() +{ + responseDepth = 0; + ignoringTags = false; + accumulateText = false; + energy = 0.f; + hasText = false; + hasAudio = false; + hasVideo = false; + terminated = false; + ignoreDepth = 0; + isChannel = false; + incoming = false; + enabled = false; + isEvent = false; + isLocallyMuted = false; + isModeratorMuted = false; + isSpeaking = false; + participantType = 0; + squelchDebugOutput = false; + returnCode = -1; + state = 0; + statusCode = 0; + volume = 0; + textBuffer.clear(); + alias.clear(); + numberOfAliases = 0; + applicationString.clear(); +} + +//virtual +LLVivoxProtocolParser::~LLVivoxProtocolParser() +{ + if (parser) + XML_ParserFree(parser); +} + +// virtual +LLIOPipe::EStatus LLVivoxProtocolParser::process_impl( + const LLChannelDescriptors& channels, + buffer_ptr_t& buffer, + bool& eos, + LLSD& context, + LLPumpIO* pump) +{ + LLBufferStream istr(channels, buffer.get()); + std::ostringstream ostr; + while (istr.good()) + { + char buf[1024]; + istr.read(buf, sizeof(buf)); + mInput.append(buf, istr.gcount()); + } + + // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser. + int start = 0; + int delim; + while((delim = mInput.find("\n\n\n", start)) != std::string::npos) + { + + // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser) + reset(); + + XML_ParserReset(parser, NULL); + XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag); + XML_SetCharacterDataHandler(parser, ExpatCharHandler); + XML_SetUserData(parser, this); + XML_Parse(parser, mInput.data() + start, delim - start, false); + + // If this message isn't set to be squelched, output the raw XML received. + if(!squelchDebugOutput) + { + LL_DEBUGS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL; + } + + start = delim + 3; + } + + if(start != 0) + mInput = mInput.substr(start); + + LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL; + + if(!LLVivoxVoiceClient::getInstance()->mConnected) + { + // If voice has been disabled, we just want to close the socket. This does so. + LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL; + return STATUS_STOP; + } + + return STATUS_OK; +} + +void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr) +{ + if (data) + { + LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data; + object->StartTag(el, attr); + } +} + +// -------------------------------------------------------------------------------- + +void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void *data, const char *el) +{ + if (data) + { + LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data; + object->EndTag(el); + } +} + +// -------------------------------------------------------------------------------- + +void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len) +{ + if (data) + { + LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data; + object->CharData(s, len); + } +} + +// -------------------------------------------------------------------------------- + + +void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) +{ + // Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags + textBuffer.clear(); + // only accumulate text if we're not ignoring tags. + accumulateText = !ignoringTags; + + if (responseDepth == 0) + { + isEvent = !stricmp("Event", tag); + + if (!stricmp("Response", tag) || isEvent) + { + // Grab the attributes + while (*attr) + { + const char *key = *attr++; + const char *value = *attr++; + + if (!stricmp("requestId", key)) + { + requestId = value; + } + else if (!stricmp("action", key)) + { + actionString = value; + } + else if (!stricmp("type", key)) + { + eventTypeString = value; + } + } + } + LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; + } + else + { + if (ignoringTags) + { + LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; + } + else + { + LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; + + // Ignore the InputXml stuff so we don't get confused + if (!stricmp("InputXml", tag)) + { + ignoringTags = true; + ignoreDepth = responseDepth; + accumulateText = false; + + LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; + } + else if (!stricmp("CaptureDevices", tag)) + { + LLVivoxVoiceClient::getInstance()->clearCaptureDevices(); + } + else if (!stricmp("RenderDevices", tag)) + { + LLVivoxVoiceClient::getInstance()->clearRenderDevices(); + } + else if (!stricmp("CaptureDevice", tag)) + { + deviceString.clear(); + } + else if (!stricmp("RenderDevice", tag)) + { + deviceString.clear(); + } + else if (!stricmp("Buddies", tag)) + { + LLVivoxVoiceClient::getInstance()->deleteAllBuddies(); + } + else if (!stricmp("BlockRules", tag)) + { + LLVivoxVoiceClient::getInstance()->deleteAllBlockRules(); + } + else if (!stricmp("AutoAcceptRules", tag)) + { + LLVivoxVoiceClient::getInstance()->deleteAllAutoAcceptRules(); + } + + } + } + responseDepth++; +} + +// -------------------------------------------------------------------------------- + +void LLVivoxProtocolParser::EndTag(const char *tag) +{ + const std::string& string = textBuffer; + + responseDepth--; + + if (ignoringTags) + { + if (ignoreDepth == responseDepth) + { + LL_DEBUGS("VivoxProtocolParser") << "end of ignore" << LL_ENDL; + ignoringTags = false; + } + else + { + LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; + } + } + + if (!ignoringTags) + { + LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; + + // Closing a tag. Finalize the text we've accumulated and reset + if (!stricmp("ReturnCode", tag)) + returnCode = strtol(string.c_str(), NULL, 10); + else if (!stricmp("SessionHandle", tag)) + sessionHandle = string; + else if (!stricmp("SessionGroupHandle", tag)) + sessionGroupHandle = string; + else if (!stricmp("StatusCode", tag)) + statusCode = strtol(string.c_str(), NULL, 10); + else if (!stricmp("StatusString", tag)) + statusString = string; + else if (!stricmp("ParticipantURI", tag)) + uriString = string; + else if (!stricmp("Volume", tag)) + volume = strtol(string.c_str(), NULL, 10); + else if (!stricmp("Energy", tag)) + energy = (F32)strtod(string.c_str(), NULL); + else if (!stricmp("IsModeratorMuted", tag)) + isModeratorMuted = !stricmp(string.c_str(), "true"); + else if (!stricmp("IsSpeaking", tag)) + isSpeaking = !stricmp(string.c_str(), "true"); + else if (!stricmp("Alias", tag)) + alias = string; + else if (!stricmp("NumberOfAliases", tag)) + numberOfAliases = strtol(string.c_str(), NULL, 10); + else if (!stricmp("Application", tag)) + applicationString = string; + else if (!stricmp("ConnectorHandle", tag)) + connectorHandle = string; + else if (!stricmp("VersionID", tag)) + versionID = string; + else if (!stricmp("AccountHandle", tag)) + accountHandle = string; + else if (!stricmp("State", tag)) + state = strtol(string.c_str(), NULL, 10); + else if (!stricmp("URI", tag)) + uriString = string; + else if (!stricmp("IsChannel", tag)) + isChannel = !stricmp(string.c_str(), "true"); + else if (!stricmp("Incoming", tag)) + incoming = !stricmp(string.c_str(), "true"); + else if (!stricmp("Enabled", tag)) + enabled = !stricmp(string.c_str(), "true"); + else if (!stricmp("Name", tag)) + nameString = string; + else if (!stricmp("AudioMedia", tag)) + audioMediaString = string; + else if (!stricmp("ChannelName", tag)) + nameString = string; + else if (!stricmp("DisplayName", tag)) + displayNameString = string; + else if (!stricmp("Device", tag)) + deviceString = string; + else if (!stricmp("AccountName", tag)) + nameString = string; + else if (!stricmp("ParticipantType", tag)) + participantType = strtol(string.c_str(), NULL, 10); + else if (!stricmp("IsLocallyMuted", tag)) + isLocallyMuted = !stricmp(string.c_str(), "true"); + else if (!stricmp("MicEnergy", tag)) + energy = (F32)strtod(string.c_str(), NULL); + else if (!stricmp("ChannelName", tag)) + nameString = string; + else if (!stricmp("ChannelURI", tag)) + uriString = string; + else if (!stricmp("BuddyURI", tag)) + uriString = string; + else if (!stricmp("Presence", tag)) + statusString = string; + else if (!stricmp("CaptureDevice", tag)) + { + LLVivoxVoiceClient::getInstance()->addCaptureDevice(deviceString); + } + else if (!stricmp("RenderDevice", tag)) + { + LLVivoxVoiceClient::getInstance()->addRenderDevice(deviceString); + } + else if (!stricmp("Buddy", tag)) + { + LLVivoxVoiceClient::getInstance()->processBuddyListEntry(uriString, displayNameString); + } + else if (!stricmp("BlockRule", tag)) + { + LLVivoxVoiceClient::getInstance()->addBlockRule(blockMask, presenceOnly); + } + else if (!stricmp("BlockMask", tag)) + blockMask = string; + else if (!stricmp("PresenceOnly", tag)) + presenceOnly = string; + else if (!stricmp("AutoAcceptRule", tag)) + { + LLVivoxVoiceClient::getInstance()->addAutoAcceptRule(autoAcceptMask, autoAddAsBuddy); + } + else if (!stricmp("AutoAcceptMask", tag)) + autoAcceptMask = string; + else if (!stricmp("AutoAddAsBuddy", tag)) + autoAddAsBuddy = string; + else if (!stricmp("MessageHeader", tag)) + messageHeader = string; + else if (!stricmp("MessageBody", tag)) + messageBody = string; + else if (!stricmp("NotificationType", tag)) + notificationType = string; + else if (!stricmp("HasText", tag)) + hasText = !stricmp(string.c_str(), "true"); + else if (!stricmp("HasAudio", tag)) + hasAudio = !stricmp(string.c_str(), "true"); + else if (!stricmp("HasVideo", tag)) + hasVideo = !stricmp(string.c_str(), "true"); + else if (!stricmp("Terminated", tag)) + terminated = !stricmp(string.c_str(), "true"); + else if (!stricmp("SubscriptionHandle", tag)) + subscriptionHandle = string; + else if (!stricmp("SubscriptionType", tag)) + subscriptionType = string; + + + textBuffer.clear(); + accumulateText= false; + + if (responseDepth == 0) + { + // We finished all of the XML, process the data + processResponse(tag); + } + } +} + +// -------------------------------------------------------------------------------- + +void LLVivoxProtocolParser::CharData(const char *buffer, int length) +{ + /* + This method is called for anything that isn't a tag, which can be text you + want that lies between tags, and a lot of stuff you don't want like file formatting + (tabs, spaces, CR/LF, etc). + + Only copy text if we are in accumulate mode... + */ + if (accumulateText) + textBuffer.append(buffer, length); +} + +// -------------------------------------------------------------------------------- + +void LLVivoxProtocolParser::processResponse(std::string tag) +{ + LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL; + + // SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success. This is a change vs. previous SDKs. + // According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned", + // so I believe this will give correct behavior. + + if(returnCode == 0) + statusCode = 0; + + if (isEvent) + { + const char *eventTypeCstr = eventTypeString.c_str(); + if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent")) + { + LLVivoxVoiceClient::getInstance()->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state); + } + else if (!stricmp(eventTypeCstr, "SessionAddedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + sip:confctl-1408789@bhr.vivox.com + true + false + + + */ + LLVivoxVoiceClient::getInstance()->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString); + } + else if (!stricmp(eventTypeCstr, "SessionRemovedEvent")) + { + LLVivoxVoiceClient::getInstance()->sessionRemovedEvent(sessionHandle, sessionGroupHandle); + } + else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent")) + { + LLVivoxVoiceClient::getInstance()->sessionGroupAddedEvent(sessionGroupHandle); + } + else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + 200 + OK + 2 + false + + */ + LLVivoxVoiceClient::getInstance()->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); + } + else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==1 + true + 1 + true + + */ + LLVivoxVoiceClient::getInstance()->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming); + } + else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 + sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.vivox.com + xI5auBZ60SJWIk606-1JGRQ== + + 0 + + */ + LLVivoxVoiceClient::getInstance()->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType); + } + else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 + sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.vivox.com + xtx7YNV-3SGiG7rA1fo5Ndw== + + */ + LLVivoxVoiceClient::getInstance()->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString); + } + else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com + false + true + 44 + 0.0879437 + + */ + + // These happen so often that logging them is pretty useless. + squelchDebugOutput = true; + + LLVivoxVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy); + } + else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) + { + LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy); + } + else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent")) + { + LLVivoxVoiceClient::getInstance()->buddyPresenceEvent(uriString, alias, statusString, applicationString); + } + else if (!stricmp(eventTypeCstr, "BuddyAndGroupListChangedEvent")) + { + // The buddy list was updated during parsing. + // Need to recheck against the friends list. + LLVivoxVoiceClient::getInstance()->buddyListChanged(); + } + else if (!stricmp(eventTypeCstr, "BuddyChangedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw== + sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com + Monroe Tester + + 0 + Set + + */ + // TODO: Question: Do we need to process this at all? + } + else if (!stricmp(eventTypeCstr, "MessageEvent")) + { + LLVivoxVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString); + } + else if (!stricmp(eventTypeCstr, "SessionNotificationEvent")) + { + LLVivoxVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType); + } + else if (!stricmp(eventTypeCstr, "SubscriptionEvent")) + { + LLVivoxVoiceClient::getInstance()->subscriptionEvent(uriString, subscriptionHandle, alias, displayNameString, applicationString, subscriptionType); + } + else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + sip:confctl-9@bhd.vivox.com + 0 + 50 + 1 + 0 + 000 + 0 + + */ + // We don't need to process this, but we also shouldn't warn on it, since that confuses people. + } + + else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + + */ + // We don't need to process this, but we also shouldn't warn on it, since that confuses people. + } + else + { + LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL; + } + } + else + { + const char *actionCstr = actionString.c_str(); + if (!stricmp(actionCstr, "Connector.Create.1")) + { + LLVivoxVoiceClient::getInstance()->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID); + } + else if (!stricmp(actionCstr, "Account.Login.1")) + { + LLVivoxVoiceClient::getInstance()->loginResponse(statusCode, statusString, accountHandle, numberOfAliases); + } + else if (!stricmp(actionCstr, "Session.Create.1")) + { + LLVivoxVoiceClient::getInstance()->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle); + } + else if (!stricmp(actionCstr, "SessionGroup.AddSession.1")) + { + LLVivoxVoiceClient::getInstance()->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle); + } + else if (!stricmp(actionCstr, "Session.Connect.1")) + { + LLVivoxVoiceClient::getInstance()->sessionConnectResponse(requestId, statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.Logout.1")) + { + LLVivoxVoiceClient::getInstance()->logoutResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1")) + { + LLVivoxVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.ListBlockRules.1")) + { + LLVivoxVoiceClient::getInstance()->accountListBlockRulesResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.ListAutoAcceptRules.1")) + { + LLVivoxVoiceClient::getInstance()->accountListAutoAcceptRulesResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Session.Set3DPosition.1")) + { + // We don't need to process these, but they're so spammy we don't want to log them. + squelchDebugOutput = true; + } + /* + else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) + { + LLVoiceClient::getInstance()->channelGetListResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Connector.AccountCreate.1")) + { + + } + else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1")) + { + + } + else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1")) + { + + } + else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1")) + { + + } + else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1")) + { + + } + else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1")) + { + + } + else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1")) + { + + } + else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1")) + { + + } + else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelCreate.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelUpdate.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelDelete.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1")) + { + + } + */ + } +} + diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h new file mode 100644 index 0000000000..10577254e8 --- /dev/null +++ b/indra/newview/llvoicevivox.h @@ -0,0 +1,914 @@ +/** + * @file llvoicevivox.h + * @brief Declaration of LLDiamondwareVoiceClient class which is the interface to the voice client process. + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#ifndef LL_VOICE_VIVOX_H +#define LL_VOICE_VIVOX_H + +class LLVOAvatar; +class LLVivoxProtocolParser; + +#include "lliopipe.h" +#include "llpumpio.h" +#include "llchainio.h" +#include "lliosocket.h" +#include "v3math.h" +#include "llframetimer.h" +#include "llviewerregion.h" +#include "llcallingcard.h" // for LLFriendObserver + +#ifdef LL_STANDALONE +# include "expat.h" +#else +# include "expat/expat.h" +#endif +#include "llvoiceclient.h" + + +class LLVivoxVoiceAccountProvisionResponder; +class LLVivoxVoiceClientMuteListObserver; +class LLVivoxVoiceClientFriendsObserver; + + +class LLVivoxVoiceClientParticipantObserver +{ +public: + virtual ~LLVivoxVoiceClientParticipantObserver() { } + virtual void onChange() = 0; +}; + + +class LLVivoxVoiceClient: public LLSingleton, virtual public LLVoiceModuleInterface +{ + LOG_CLASS(LLVivoxVoiceClient); +public: + LLVivoxVoiceClient(); + virtual ~LLVivoxVoiceClient(); + + + /// @name LLVoiceModuleInterface virtual implementations + /// @see LLVoiceModuleInterface + //@{ + virtual void init(LLPumpIO *pump); // Call this once at application startup (creates connector) + virtual void terminate(); // Call this to clean up during shutdown + + virtual const LLVoiceVersionInfo& getVersion(); + + virtual void updateSettings(); // call after loading settings and whenever they change + + // Returns true if vivox has successfully logged in and is not in error state + virtual bool isVoiceWorking(); + + ///////////////////// + /// @name Tuning + //@{ + virtual void tuningStart(); + virtual void tuningStop(); + virtual bool inTuningMode(); + + virtual void tuningSetMicVolume(float volume); + virtual void tuningSetSpeakerVolume(float volume); + virtual float tuningGetEnergy(void); + //@} + + ///////////////////// + /// @name Devices + //@{ + // This returns true when it's safe to bring up the "device settings" dialog in the prefs. + // i.e. when the daemon is running and connected, and the device lists are populated. + virtual bool deviceSettingsAvailable(); + + // Requery the vivox daemon for the current list of input/output devices. + // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed + // (use this if you want to know when it's done). + // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. + virtual void refreshDeviceLists(bool clearCurrentList = true); + + virtual void setCaptureDevice(const std::string& name); + virtual void setRenderDevice(const std::string& name); + + virtual LLVoiceDeviceList& getCaptureDevices(); + virtual LLVoiceDeviceList& getRenderDevices(); + //@} + + virtual void getParticipantList(std::set &participants); + virtual bool isParticipant(const LLUUID& speaker_id); + + // Send a text message to the specified user, initiating the session if necessary. + virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message); + + // close any existing text IM session with the specified user + virtual void endUserIMSession(const LLUUID &uuid); + + // Returns true if calling back the session URI after the session has closed is possible. + // Currently this will be false only for PSTN P2P calls. + // NOTE: this will return true if the session can't be found. + virtual BOOL isSessionCallBackPossible(const LLUUID &session_id); + + // Returns true if the session can accepte text IM's. + // Currently this will be false only for PSTN P2P calls. + // NOTE: this will return true if the session can't be found. + virtual BOOL isSessionTextIMPossible(const LLUUID &session_id); + + + //////////////////////////// + /// @name Channel stuff + //@{ + // returns true iff the user is currently in a proximal (local spatial) channel. + // Note that gestures should only fire if this returns true. + virtual bool inProximalChannel(); + + virtual void setNonSpatialChannel(const std::string &uri, + const std::string &credentials); + + virtual void setSpatialChannel(const std::string &uri, + const std::string &credentials); + + virtual void leaveNonSpatialChannel(); + + virtual void leaveChannel(void); + + // Returns the URI of the current channel, or an empty string if not currently in a channel. + // NOTE that it will return an empty string if it's in the process of joining a channel. + virtual std::string getCurrentChannel(); + //@} + + + ////////////////////////// + /// @name invitations + //@{ + // start a voice channel with the specified user + virtual void callUser(const LLUUID &uuid); + virtual bool answerInvite(std::string &channelHandle); + virtual void declineInvite(std::string &channelHandle); + //@} + + ///////////////////////// + /// @name Volume/gain + //@{ + virtual void setVoiceVolume(F32 volume); + virtual void setMicGain(F32 volume); + //@} + + ///////////////////////// + /// @name enable disable voice and features + //@{ + virtual bool voiceEnabled(); + virtual void setVoiceEnabled(bool enabled); + virtual BOOL lipSyncEnabled(); + virtual void setLipSyncEnabled(BOOL enabled); + virtual void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. + //@} + + //////////////////////// + /// @name PTT + //@{ + virtual void setUserPTTState(bool ptt); + virtual bool getUserPTTState(); + virtual void setUsePTT(bool usePTT); + virtual void setPTTIsToggle(bool PTTIsToggle); + virtual bool getPTTIsToggle(); + virtual void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs + virtual void toggleUserPTTState(void); + + virtual void keyDown(KEY key, MASK mask); + virtual void keyUp(KEY key, MASK mask); + virtual void middleMouseState(bool down); + //@} + + ////////////////////////// + /// @name nearby speaker accessors + //@{ + virtual BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar + virtual std::string getDisplayName(const LLUUID& id); + virtual BOOL isOnlineSIP(const LLUUID &id); + virtual BOOL isParticipantAvatar(const LLUUID &id); + virtual BOOL getIsSpeaking(const LLUUID& id); + virtual BOOL getIsModeratorMuted(const LLUUID& id); + virtual F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + virtual BOOL getOnMuteList(const LLUUID& id); + virtual F32 getUserVolume(const LLUUID& id); + virtual void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) + //@} + + // authorize the user + virtual void userAuthorized(const std::string& user_id, + const LLUUID &agentID); + + ////////////////////////////// + /// @name Status notification + //@{ + virtual void addObserver(LLVoiceClientStatusObserver* observer); + virtual void removeObserver(LLVoiceClientStatusObserver* observer); + virtual void addObserver(LLFriendObserver* observer); + virtual void removeObserver(LLFriendObserver* observer); + virtual void addObserver(LLVoiceClientParticipantObserver* observer); + virtual void removeObserver(LLVoiceClientParticipantObserver* observer); + + + + //@} + + virtual std::string sipURIFromID(const LLUUID &id); + //@} + + +protected: + ////////////////////// + // Vivox Specific definitions + + friend class LLVivoxVoiceAccountProvisionResponder; + friend class LLVivoxVoiceClientMuteListObserver; + friend class LLVivoxVoiceClientFriendsObserver; + + enum streamState + { + streamStateUnknown = 0, + streamStateIdle = 1, + streamStateConnected = 2, + streamStateRinging = 3, + }; + struct participantState + { + public: + participantState(const std::string &uri); + + bool updateMuteState(); + bool isAvatar(); + + std::string mURI; + LLUUID mAvatarID; + std::string mAccountName; + std::string mDisplayName; + LLFrameTimer mSpeakingTimeout; + F32 mLastSpokeTimestamp; + F32 mPower; + int mVolume; + std::string mGroupID; + int mUserVolume; + bool mPTT; + bool mIsSpeaking; + bool mIsModeratorMuted; + bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted) + bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed) + bool mAvatarIDValid; + bool mIsSelf; + }; + + typedef std::map participantMap; + + typedef std::map participantUUIDMap; + + struct sessionState + { + public: + sessionState(); + ~sessionState(); + + participantState *addParticipant(const std::string &uri); + // Note: after removeParticipant returns, the participant* that was passed to it will have been deleted. + // Take care not to use the pointer again after that. + void removeParticipant(participantState *participant); + void removeAllParticipants(); + + participantState *findParticipant(const std::string &uri); + participantState *findParticipantByID(const LLUUID& id); + + bool isCallBackPossible(); + bool isTextIMPossible(); + + std::string mHandle; + std::string mGroupHandle; + std::string mSIPURI; + std::string mAlias; + std::string mName; + std::string mAlternateSIPURI; + std::string mHash; // Channel password + std::string mErrorStatusString; + std::queue mTextMsgQueue; + + LLUUID mIMSessionID; + LLUUID mCallerID; + int mErrorStatusCode; + int mMediaStreamState; + int mTextStreamState; + bool mCreateInProgress; // True if a Session.Create has been sent for this session and no response has been received yet. + bool mMediaConnectInProgress; // True if a Session.MediaConnect has been sent for this session and no response has been received yet. + bool mVoiceInvitePending; // True if a voice invite is pending for this session (usually waiting on a name lookup) + bool mTextInvitePending; // True if a text invite is pending for this session (usually waiting on a name lookup) + bool mSynthesizedCallerID; // True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup. + bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) + bool mIsSpatial; // True for spatial channels + bool mIsP2P; + bool mIncoming; + bool mVoiceEnabled; + bool mReconnect; // Whether we should try to reconnect to this session if it's dropped + // Set to true when the mute state of someone in the participant list changes. + // The code will have to walk the list to find the changed participant(s). + bool mVolumeDirty; + + bool mParticipantsChanged; + participantMap mParticipantsByURI; + participantUUIDMap mParticipantsByUUID; + }; + + // internal state for a simple state machine. This is used to deal with the asynchronous nature of some of the messages. + // Note: if you change this list, please make corresponding changes to LLVivoxVoiceClient::state2string(). + enum state + { + stateDisableCleanup, + stateDisabled, // Voice is turned off. + stateStart, // Class is initialized, socket is created + stateDaemonLaunched, // Daemon has been launched + stateConnecting, // connect() call has been issued + stateConnected, // connection to the daemon has been made, send some initial setup commands. + stateIdle, // socket is connected, ready for messaging + stateMicTuningStart, + stateMicTuningRunning, + stateMicTuningStop, + stateConnectorStart, // connector needs to be started + stateConnectorStarting, // waiting for connector handle + stateConnectorStarted, // connector handle received + stateLoginRetry, // need to retry login (failed due to changing password) + stateLoginRetryWait, // waiting for retry timer + stateNeedsLogin, // send login request + stateLoggingIn, // waiting for account handle + stateLoggedIn, // account handle received + stateCreatingSessionGroup, // Creating the main session group + stateNoChannel, // + stateJoiningSession, // waiting for session handle + stateSessionJoined, // session handle received + stateRunning, // in session, steady state + stateLeavingSession, // waiting for terminate session response + stateSessionTerminated, // waiting for terminate session response + + stateLoggingOut, // waiting for logout response + stateLoggedOut, // logout response received + stateConnectorStopping, // waiting for connector stop + stateConnectorStopped, // connector stop received + + // We go to this state if the login fails because the account needs to be provisioned. + + // error states. No way to recover from these yet. + stateConnectorFailed, + stateConnectorFailedWaiting, + stateLoginFailed, + stateLoginFailedWaiting, + stateJoinSessionFailed, + stateJoinSessionFailedWaiting, + + stateJail // Go here when all else has failed. Nothing will be retried, we're done. + }; + + typedef std::map sessionMap; + + + + /////////////////////////////////////////////////////// + // Private Member Functions + ////////////////////////////////////////////////////// + + ////////////////////////////// + /// @name TVC/Server management and communication + //@{ + // Call this if the connection to the daemon terminates unexpectedly. It will attempt to reset everything and relaunch. + void daemonDied(); + + // Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away. + void giveUp(); + + // write to the tvc + bool writeString(const std::string &str); + + void connectorCreate(); + void connectorShutdown(); + void closeSocket(void); + + void requestVoiceAccountProvision(S32 retries = 3); + void login( + const std::string& account_name, + const std::string& password, + const std::string& voice_sip_uri_hostname, + const std::string& voice_account_server_uri); + void loginSendMessage(); + void logout(); + void logoutSendMessage(); + + + //@} + + //------------------------------------ + // tuning + + void tuningRenderStartSendMessage(const std::string& name, bool loop); + void tuningRenderStopSendMessage(); + + void tuningCaptureStartSendMessage(int duration); + void tuningCaptureStopSendMessage(); + + bool inTuningStates(); + + //---------------------------------- + // devices + void clearCaptureDevices(); + void addCaptureDevice(const std::string& name); + void clearRenderDevices(); + void addRenderDevice(const std::string& name); + void buildSetAudioDevices(std::ostringstream &stream); + + void getCaptureDevicesSendMessage(); + void getRenderDevicesSendMessage(); + + // local audio updates + void buildLocalAudioUpdates(std::ostringstream &stream); + + + ///////////////////////////// + // Response/Event handlers + void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID); + void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases); + void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); + void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); + void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString); + void logoutResponse(int statusCode, std::string &statusString); + void connectorShutdownResponse(int statusCode, std::string &statusString); + + void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); + void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming); + void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming); + void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString); + void sessionGroupAddedEvent(std::string &sessionGroupHandle); + void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); + void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); + void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); + void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy); + void auxAudioPropertiesEvent(F32 energy); + void buddyPresenceEvent(std::string &uriString, std::string &alias, std::string &statusString, std::string &applicationString); + void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); + void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType); + void subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType); + + void buddyListChanged(); + void muteListChanged(); + void updateFriends(U32 mask); + + ///////////////////////////// + // Sending updates of current state + void updatePosition(void); + void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); + void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); + bool channelFromRegion(LLViewerRegion *region, std::string &name); + + void setEarLocation(S32 loc); + + + ///////////////////////////// + // Accessors for data related to nearby speakers + + // MBW -- XXX -- Not sure how to get this data out of the TVC + BOOL getUsingPTT(const LLUUID& id); + std::string getGroupID(const LLUUID& id); // group ID if the user is in group chat (empty string if not applicable) + + ///////////////////////////// + BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. + // Use this to determine whether to show a "no speech" icon in the menu bar. + + + // PTT + void setPTTKey(std::string &key); + + ///////////////////////////// + // Recording controls + void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200); + void recordingLoopSave(const std::string& filename); + void recordingStop(); + + // Playback controls + void filePlaybackStart(const std::string& filename); + void filePlaybackStop(); + void filePlaybackSetPaused(bool paused); + void filePlaybackSetMode(bool vox = false, float speed = 1.0f); + + participantState *findParticipantByID(const LLUUID& id); + + + //////////////////////////////////////// + // voice sessions. + typedef std::set sessionSet; + + typedef sessionSet::iterator sessionIterator; + sessionIterator sessionsBegin(void); + sessionIterator sessionsEnd(void); + + sessionState *findSession(const std::string &handle); + sessionState *findSessionBeingCreatedByURI(const std::string &uri); + sessionState *findSession(const LLUUID &participant_id); + sessionState *findSessionByCreateID(const std::string &create_id); + + sessionState *addSession(const std::string &uri, const std::string &handle = LLStringUtil::null); + void setSessionHandle(sessionState *session, const std::string &handle = LLStringUtil::null); + void setSessionURI(sessionState *session, const std::string &uri); + void deleteSession(sessionState *session); + void deleteAllSessions(void); + + void verifySessionState(void); + + void joinedAudioSession(sessionState *session); + void leftAudioSession(sessionState *session); + + // This is called in several places where the session _may_ need to be deleted. + // It contains logic for whether to delete the session or keep it around. + void reapSession(sessionState *session); + + // Returns true if the session seems to indicate we've moved to a region on a different voice server + bool sessionNeedsRelog(sessionState *session); + + + ////////////////////////////////////// + // buddy list stuff, needed for SLIM later + struct buddyListEntry + { + buddyListEntry(const std::string &uri); + std::string mURI; + std::string mDisplayName; + LLUUID mUUID; + bool mOnlineSL; + bool mOnlineSLim; + bool mCanSeeMeOnline; + bool mHasBlockListEntry; + bool mHasAutoAcceptListEntry; + bool mNameResolved; + bool mInSLFriends; + bool mInVivoxBuddies; + bool mNeedsNameUpdate; + }; + + typedef std::map buddyListMap; + + // This should be called when parsing a buddy list entry sent by SLVoice. + void processBuddyListEntry(const std::string &uri, const std::string &displayName); + + buddyListEntry *addBuddy(const std::string &uri); + buddyListEntry *addBuddy(const std::string &uri, const std::string &displayName); + buddyListEntry *findBuddy(const std::string &uri); + buddyListEntry *findBuddy(const LLUUID &id); + buddyListEntry *findBuddyByDisplayName(const std::string &name); + void deleteBuddy(const std::string &uri); + void deleteAllBuddies(void); + + void deleteAllBlockRules(void); + void addBlockRule(const std::string &blockMask, const std::string &presenceOnly); + void deleteAllAutoAcceptRules(void); + void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy); + void accountListBlockRulesResponse(int statusCode, const std::string &statusString); + void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString); + + ///////////////////////////// + // session control messages + + void accountListBlockRulesSendMessage(); + void accountListAutoAcceptRulesSendMessage(); + + void sessionGroupCreateSendMessage(); + void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false); + void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false); + void sessionMediaConnectSendMessage(sessionState *session); // just joins the audio session + void sessionTextConnectSendMessage(sessionState *session); // just joins the text session + void sessionTerminateSendMessage(sessionState *session); + void sessionGroupTerminateSendMessage(sessionState *session); + void sessionMediaDisconnectSendMessage(sessionState *session); + void sessionTextDisconnectSendMessage(sessionState *session); + + // Pokes the state machine to leave the audio session next time around. + void sessionTerminate(); + + // Pokes the state machine to shut down the connector and restart it. + void requestRelog(); + + // Does the actual work to get out of the audio session + void leaveAudioSession(); + + void lookupName(const LLUUID &id); + static void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); + void avatarNameResolved(const LLUUID &id, const std::string &name); + +private: + LLVoiceVersionInfo mVoiceVersion; + + state mState; + bool mSessionTerminateRequested; + bool mRelogRequested; + // Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). + // The larger it is the greater is possibility there is a problem with connection to voice server. + // Introduced while fixing EXT-4313. + int mSpatialJoiningNum; + + void setState(state inState); + state getState(void) { return mState; }; + std::string state2string(state inState); + + void stateMachine(); + static void idle(void *user_data); + + LLHost mDaemonHost; + LLSocket::ptr_t mSocket; + bool mConnected; + + + LLPumpIO *mPump; + friend class LLVivoxProtocolParser; + + std::string mAccountName; + std::string mAccountPassword; + std::string mAccountDisplayName; + + bool mTuningMode; + float mTuningEnergy; + std::string mTuningAudioFile; + int mTuningMicVolume; + bool mTuningMicVolumeDirty; + int mTuningSpeakerVolume; + bool mTuningSpeakerVolumeDirty; + state mTuningExitState; // state to return to when we leave tuning mode. + + std::string mSpatialSessionURI; + std::string mSpatialSessionCredentials; + + std::string mMainSessionGroupHandle; // handle of the "main" session group. + + std::string mChannelName; // Name of the channel to be looked up + bool mAreaVoiceDisabled; + sessionState *mAudioSession; // Session state for the current audio session + bool mAudioSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. + + sessionState *mNextAudioSession; // Session state for the audio session we're trying to join + +// std::string mSessionURI; // URI of the session we're in. +// std::string mSessionHandle; // returned by ? + + S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings + std::string mCurrentRegionName; // Used to detect parcel boundary crossings + + std::string mConnectorHandle; // returned by "Create Connector" message + std::string mAccountHandle; // returned by login message + int mNumberOfAliases; + U32 mCommandCookie; + + std::string mVoiceAccountServerURI; + std::string mVoiceSIPURIHostName; + + int mLoginRetryCount; + + sessionMap mSessionsByHandle; // Active sessions, indexed by session handle. Sessions which are being initiated may not be in this map. + sessionSet mSessions; // All sessions, not indexed. This is the canonical session list. + + bool mBuddyListMapPopulated; + bool mBlockRulesListReceived; + bool mAutoAcceptRulesListReceived; + buddyListMap mBuddyListMap; + + LLVoiceDeviceList mCaptureDevices; + LLVoiceDeviceList mRenderDevices; + + std::string mCaptureDevice; + std::string mRenderDevice; + bool mCaptureDeviceDirty; + bool mRenderDeviceDirty; + + // This should be called when the code detects we have changed parcels. + // It initiates the call to the server that gets the parcel channel. + void parcelChanged(); + + void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); + void joinSession(sessionState *session); + + std::string nameFromAvatar(LLVOAvatar *avatar); + std::string nameFromID(const LLUUID &id); + bool IDFromName(const std::string name, LLUUID &uuid); + std::string displayNameFromAvatar(LLVOAvatar *avatar); + std::string sipURIFromAvatar(LLVOAvatar *avatar); + std::string sipURIFromName(std::string &name); + + // Returns the name portion of the SIP URI if the string looks vaguely like a SIP URI, or an empty string if not. + std::string nameFromsipURI(const std::string &uri); + + bool inSpatialChannel(void); + std::string getAudioSessionURI(); + std::string getAudioSessionHandle(); + + void sendPositionalUpdate(void); + + void buildSetCaptureDevice(std::ostringstream &stream); + void buildSetRenderDevice(std::ostringstream &stream); + + void clearAllLists(); + void checkFriend(const LLUUID& id); + void sendFriendsListUpdates(); + + // start a text IM session with the specified user + // This will be asynchronous, the session may be established at a future time. + sessionState* startUserIMSession(const LLUUID& uuid); + void sendQueuedTextMessages(sessionState *session); + + void enforceTether(void); + + bool mSpatialCoordsDirty; + + LLVector3d mCameraPosition; + LLVector3d mCameraRequestedPosition; + LLVector3 mCameraVelocity; + LLMatrix3 mCameraRot; + + LLVector3d mAvatarPosition; + LLVector3 mAvatarVelocity; + LLMatrix3 mAvatarRot; + + bool mPTTDirty; + bool mPTT; + + bool mUsePTT; + bool mPTTIsMiddleMouse; + KEY mPTTKey; + bool mPTTIsToggle; + bool mUserPTTState; + bool mMuteMic; + + // Set to true when the friends list is known to have changed. + bool mFriendsListDirty; + + enum + { + earLocCamera = 0, // ear at camera + earLocAvatar, // ear at avatar + earLocMixed // ear at avatar location/camera direction + }; + + S32 mEarLocation; + + bool mSpeakerVolumeDirty; + bool mSpeakerMuteDirty; + int mSpeakerVolume; + + int mMicVolume; + bool mMicVolumeDirty; + + bool mVoiceEnabled; + bool mWriteInProgress; + std::string mWriteString; + size_t mWriteOffset; + + LLTimer mUpdateTimer; + + BOOL mLipSyncEnabled; + + typedef std::set observer_set_t; + observer_set_t mParticipantObservers; + + void notifyParticipantObservers(); + + typedef std::set status_observer_set_t; + status_observer_set_t mStatusObservers; + + void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); + + typedef std::set friend_observer_set_t; + friend_observer_set_t mFriendObservers; + void notifyFriendObservers(); +}; + +/** + * @class LLVivoxProtocolParser + * @brief This class helps construct new LLIOPipe specializations + * @see LLIOPipe + * + * THOROUGH_DESCRIPTION + */ +class LLVivoxProtocolParser : public LLIOPipe +{ + LOG_CLASS(LLVivoxProtocolParser); +public: + LLVivoxProtocolParser(); + virtual ~LLVivoxProtocolParser(); + +protected: + /* @name LLIOPipe virtual implementations + */ + //@{ + /** + * @brief Process the data in buffer + */ + virtual EStatus process_impl( + const LLChannelDescriptors& channels, + buffer_ptr_t& buffer, + bool& eos, + LLSD& context, + LLPumpIO* pump); + //@} + + std::string mInput; + + // Expat control members + XML_Parser parser; + int responseDepth; + bool ignoringTags; + bool isEvent; + int ignoreDepth; + + // Members for processing responses. The values are transient and only valid within a call to processResponse(). + bool squelchDebugOutput; + int returnCode; + int statusCode; + std::string statusString; + std::string requestId; + std::string actionString; + std::string connectorHandle; + std::string versionID; + std::string accountHandle; + std::string sessionHandle; + std::string sessionGroupHandle; + std::string alias; + std::string applicationString; + + // Members for processing events. The values are transient and only valid within a call to processResponse(). + std::string eventTypeString; + int state; + std::string uriString; + bool isChannel; + bool incoming; + bool enabled; + std::string nameString; + std::string audioMediaString; + std::string deviceString; + std::string displayNameString; + int participantType; + bool isLocallyMuted; + bool isModeratorMuted; + bool isSpeaking; + int volume; + F32 energy; + std::string messageHeader; + std::string messageBody; + std::string notificationType; + bool hasText; + bool hasAudio; + bool hasVideo; + bool terminated; + std::string blockMask; + std::string presenceOnly; + std::string autoAcceptMask; + std::string autoAddAsBuddy; + int numberOfAliases; + std::string subscriptionHandle; + std::string subscriptionType; + + + // Members for processing text between tags + std::string textBuffer; + bool accumulateText; + + void reset(); + + void processResponse(std::string tag); + + static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr); + static void XMLCALL ExpatEndTag(void *data, const char *el); + static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len); + + void StartTag(const char *tag, const char **attr); + void EndTag(const char *tag); + void CharData(const char *buffer, int length); + +}; + + +#endif //LL_VIVOX_VOICE_CLIENT_H + + + diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp index 1a64f9d881..aa03b1afd1 100644 --- a/indra/newview/llweb.cpp +++ b/indra/newview/llweb.cpp @@ -155,7 +155,7 @@ std::string LLWeb::expandURLSubstitutions(const std::string &url, substitution["VERSION_PATCH"] = LLVersionInfo::getPatch(); substitution["VERSION_BUILD"] = LLVersionInfo::getBuild(); substitution["CHANNEL"] = LLVersionInfo::getChannel(); - substitution["GRID"] = LLViewerLogin::getInstance()->getGridLabel(); + substitution["GRID"] = LLGridManager::getInstance()->getGridLabel(); substitution["OS"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); substitution["SESSION_ID"] = gAgent.getSessionID(); substitution["FIRST_LOGIN"] = gAgent.isFirstLogin(); diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 0b63f5efbd..58b9f5ce18 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -133,10 +133,11 @@ void LLWorld::destroyClass() LLViewerRegion* LLWorld::addRegion(const U64 ®ion_handle, const LLHost &host) { LLMemType mt(LLMemType::MTYPE_REGIONS); - + llinfos << "Add region with handle: " << region_handle << " on host " << host << llendl; LLViewerRegion *regionp = getRegionFromHandle(region_handle); if (regionp) { + llinfos << "Region exists, removing it " << llendl; LLHost old_host = regionp->getHost(); // region already exists! if (host == old_host && regionp->isAlive()) diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp index 15417614af..8237132ac5 100644 --- a/indra/newview/llxmlrpclistener.cpp +++ b/indra/newview/llxmlrpclistener.cpp @@ -28,6 +28,7 @@ #include "llerror.h" #include "stringize.h" #include "llxmlrpctransaction.h" +#include "llsecapi.h" #if LL_WINDOWS #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally @@ -356,7 +357,22 @@ public: << data["errorcode"].asString() << " (" << data["error"].asString() << ")" << LL_ENDL; - // In addition to CURLE_OK, LLUserAuth distinguishes different error + + switch (curlcode) + { + case CURLE_SSL_PEER_CERTIFICATE: + case CURLE_SSL_CACERT: + { + LLPointer error_cert(mTransaction->getErrorCert()); + if(error_cert) + { + data["certificate"] = error_cert->getPem(); + } + break; + } + default: + break; + } // values of 'curlcode': // CURLE_COULDNT_RESOLVE_HOST, // CURLE_SSL_PEER_CERTIFICATE, diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index c19be37e75..da61840761 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -31,6 +31,9 @@ */ #include "llviewerprecompiledheaders.h" +#include +#include +#include "llsecapi.h" #include "llxmlrpctransaction.h" #include "llxmlrpclistener.h" @@ -176,6 +179,8 @@ public: std::string mResponseText; XMLRPC_REQUEST mResponse; + std::string mCertStore; + LLPointer mErrorCert; Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip); Impl(const std::string& uri, @@ -190,7 +195,8 @@ public: private: void init(XMLRPC_REQUEST request, bool useGzip); - + static int _sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param); + static CURLcode _sslCtxFunction(CURL * curl, void *sslctx, void *param); static size_t curlDownloadCallback( char* data, size_t size, size_t nmemb, void* user_data); }; @@ -228,8 +234,74 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, XMLRPC_RequestFree(request, 1); } +// _sslCertVerifyCallback +// callback called when a cert verification is requested. +// calls SECAPI to validate the context +int LLXMLRPCTransaction::Impl::_sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param) +{ + LLXMLRPCTransaction::Impl *transaction = (LLXMLRPCTransaction::Impl *)param; + LLPointer store = gSecAPIHandler->getCertificateStore(transaction->mCertStore); + LLPointer chain = gSecAPIHandler->getCertificateChain(ctx); + LLSD validation_params = LLSD::emptyMap(); + LLURI uri(transaction->mURI); + validation_params[CERT_HOSTNAME] = uri.hostName(); + try + { + chain->validate(VALIDATION_POLICY_SSL, store, validation_params); + } + catch (LLCertValidationTrustException& cert_exception) + { + // this exception is is handled differently than the general cert + // exceptions, as we allow the user to actually add the certificate + // for trust. + // therefore we pass back a different error code + // NOTE: We're currently 'wired' to pass around CURL error codes. This is + // somewhat clumsy, as we may run into errors that do not map directly to curl + // error codes. Should be refactored with login refactoring, perhaps. + transaction->mCurlCode = CURLE_SSL_CACERT; + // set the status directly. set curl status generates error messages and we want + // to use the fixed ones from the exceptions + transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string()); + // We should probably have a more generic way of passing information + // back to the error handlers. + transaction->mErrorCert = cert_exception.getCert(); + return 0; + } + catch (LLCertException& cert_exception) + { + transaction->mCurlCode = CURLE_SSL_PEER_CERTIFICATE; + // set the status directly. set curl status generates error messages and we want + // to use the fixed ones from the exceptions + transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string()); + transaction->mErrorCert = cert_exception.getCert(); + return 0; + } + catch (...) + { + // any other odd error, we just handle as a connect error. + transaction->mCurlCode = CURLE_SSL_CONNECT_ERROR; + transaction->setCurlStatus(CURLE_SSL_CONNECT_ERROR); + return 0; + } + return 1; +} +// _sslCtxFunction +// Callback function called when an SSL Context is created via CURL +// used to configure the context for custom cert validate(<, <#const & xs#>, <#T * #>, <#long #>)tion +// based on SECAPI +CURLcode LLXMLRPCTransaction::Impl::_sslCtxFunction(CURL * curl, void *sslctx, void *param) +{ + SSL_CTX * ctx = (SSL_CTX *) sslctx; + // disable any default verification for server certs + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + // set the verification callback. + SSL_CTX_set_cert_verify_callback(ctx, _sslCertVerifyCallback, param); + // the calls are void + return CURLE_OK; + +} void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) { @@ -237,6 +309,7 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) { mCurlRequest = new LLCurlEasyRequest(); } + mErrorCert = NULL; if (gSavedSettings.getBOOL("BrowserProxyEnabled")) { @@ -252,10 +325,13 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) // mCurlRequest->setopt(CURLOPT_VERBOSE, 1); // usefull for debugging mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); mCurlRequest->setWriteCallback(&curlDownloadCallback, (void*)this); - mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, LLCurl::getSSLVerify()); - mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, LLCurl::getSSLVerify() ? 2 : 0); + BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); + mCertStore = gSavedSettings.getString("CertStore"); + mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, vefifySSLCert); + mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, vefifySSLCert ? 2 : 0); // Be a little impatient about establishing connections. mCurlRequest->setopt(CURLOPT_CONNECTTIMEOUT, 40L); + mCurlRequest->setSSLCtxCallback(_sslCtxFunction, (void *)this); /* Setting the DNS cache timeout to -1 disables it completely. This might help with bug #503 */ @@ -341,11 +417,19 @@ bool LLXMLRPCTransaction::Impl::process() { if (result != CURLE_OK) { - setCurlStatus(result); - llwarns << "LLXMLRPCTransaction CURL error " - << mCurlCode << ": " << mCurlRequest->getErrorString() << llendl; - llwarns << "LLXMLRPCTransaction request URI: " - << mURI << llendl; + if ((result != CURLE_SSL_PEER_CERTIFICATE) && + (result != CURLE_SSL_CACERT)) + { + // if we have a curl error that's not already been handled + // (a non cert error), then generate the error message as + // appropriate + setCurlStatus(result); + + llwarns << "LLXMLRPCTransaction CURL error " + << mCurlCode << ": " << mCurlRequest->getErrorString() << llendl; + llwarns << "LLXMLRPCTransaction request URI: " + << mURI << llendl; + } return true; } @@ -423,7 +507,6 @@ void LLXMLRPCTransaction::Impl::setStatus(EStatus status, case StatusComplete: mStatusMessage = "(done)"; break; - default: // Usually this means that there's a problem with the login server, // not with the client. Direct user to status page. @@ -539,6 +622,11 @@ std::string LLXMLRPCTransaction::statusMessage() return impl.mStatusMessage; } +LLPointer LLXMLRPCTransaction::getErrorCert() +{ + return impl.mErrorCert; +} + std::string LLXMLRPCTransaction::statusURI() { return impl.mStatusURI; diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h index c835423d67..8beb2e2623 100644 --- a/indra/newview/llxmlrpctransaction.h +++ b/indra/newview/llxmlrpctransaction.h @@ -38,6 +38,7 @@ typedef struct _xmlrpc_request* XMLRPC_REQUEST; typedef struct _xmlrpc_value* XMLRPC_VALUE; // foward decl of types from xmlrpc.h (this usage is type safe) +class LLCertificate; class LLXMLRPCValue // a c++ wrapper around XMLRPC_VALUE @@ -115,6 +116,8 @@ public: EStatus status(int* curlCode); // return status, and extended CURL code, if code isn't null + + LLPointer getErrorCert(); std::string statusMessage(); // return a message string, suitable for showing the user std::string statusURI(); diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml index d03231a3fa..a55201fd53 100644 --- a/indra/newview/skins/default/xui/en/floater_about.xml +++ b/indra/newview/skins/default/xui/en/floater_about.xml @@ -49,7 +49,7 @@ libcurl Version: [LIBCURL_VERSION] J2C Decoder Version: [J2C_VERSION] Audio Driver Version: [AUDIO_DRIVER_VERSION] Qt Webkit Version: [QT_WEBKIT_VERSION] -Vivox Version: [VIVOX_VERSION] +Voice Server Version: [VOICE_VERSION] diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index e8ba8c683d..c42b846dbb 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -1382,6 +1382,18 @@ Unknown Vorbis encode failure on: [FILE] Unable to encode file: [FILE] + + We are unable to read your protected data so it is being reset. + This may happen when you change network setup. + + + + + +Could not teleport to [SLURL] as it's on a different grid ([GRID]) than the current grid ([CURRENT_GRID]). Please close your viewer and try again. + + + + +Could not connect to the server. +[REASON] + +SubjectName: [SUBJECT_NAME_STRING] +IssuerName: [ISSUER_NAME_STRING] +Valid From: [VALID_FROM] +Valid To: [VALID_TO] +MD5 Fingerprint: [SHA1_DIGEST] +SHA1 Fingerprint: [MD5_DIGEST] +Key Usage: [KEYUSAGE] +Extended Key Usage: [EXTENDEDKEYUSAGE] +Subject Key Identifier: [SUBJECTKEYIDENTIFIER] + + + + +The certification authority for this server is not known. + +Certificate Information: +SubjectName: [SUBJECT_NAME_STRING] +IssuerName: [ISSUER_NAME_STRING] +Valid From: [VALID_FROM] +Valid To: [VALID_TO] +MD5 Fingerprint: [SHA1_DIGEST] +SHA1 Fingerprint: [MD5_DIGEST] +Key Usage: [KEYUSAGE] +Extended Key Usage: [EXTENDEDKEYUSAGE] +Subject Key Identifier: [SUBJECTKEYIDENTIFIER] + +Would you like to trust this authority? + + -First name: +Username: - - Last name: - +width="150" /> + width="135"> Password: -
- + width="83"> - Date: Wed, 12 May 2010 17:38:07 +0300 Subject: EXT-7203 FIXED Re-enabled inventory search for wearables panel - Insert filter editor and inventory panels into layout_stack - restored filter functionality for a list view - added proper switching between flat list view and folder list view - made a little optimization: got rid of redundant findChild calls because of childSetVisible(..., childGetValue(...)); Reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/362/ --HG-- branch : product-engine --- indra/newview/llpaneloutfitedit.cpp | 49 +++++- indra/newview/llpaneloutfitedit.h | 16 +- .../skins/default/xui/en/panel_outfit_edit.xml | 192 ++++++++++----------- 3 files changed, 154 insertions(+), 103 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 008661221a..1c74c8f26a 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -210,7 +210,11 @@ BOOL LLPanelOutfitEdit::postBuild() mCurrentOutfitName = getChild("curr_outfit_name"); mStatus = getChild("status"); + mFolderViewBtn = getChild("folder_view_btn"); + mListViewBtn = getChild("list_view_btn"); + childSetCommitCallback("filter_button", boost::bind(&LLPanelOutfitEdit::showWearablesFilter, this), NULL); + childSetCommitCallback("folder_view_btn", boost::bind(&LLPanelOutfitEdit::showFilteredFolderWearablesPanel, this), NULL); childSetCommitCallback("list_view_btn", boost::bind(&LLPanelOutfitEdit::showFilteredWearablesPanel, this), NULL); mCOFWearables = getChild("cof_wearables_list"); @@ -267,8 +271,9 @@ BOOL LLPanelOutfitEdit::postBuild() save_registar.add("Outfit.SaveAsNew.Action", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, true)); mSaveMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_save_outfit.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - mWearableListManager = new LLFilteredWearableListManager( - getChild("filtered_wearables_list"), ALL_ITEMS_MASK); + mWearableItemsPanel = getChild("filtered_wearables_panel"); + mWearableItemsList = getChild("filtered_wearables_list"); + mWearableListManager = new LLFilteredWearableListManager(mWearableItemsList, ALL_ITEMS_MASK); return TRUE; } @@ -289,12 +294,33 @@ void LLPanelOutfitEdit::toggleAddWearablesPanel() void LLPanelOutfitEdit::showWearablesFilter() { - childSetVisible("filter_combobox_panel", childGetValue("filter_button")); + bool filter_visible = childGetValue("filter_button"); + + childSetVisible("filter_panel", filter_visible); + + if(!filter_visible) + { + mSearchFilter->clear(); + onSearchEdit(LLStringUtil::null); + } } void LLPanelOutfitEdit::showFilteredWearablesPanel() { - childSetVisible("filtered_wearables_panel", !childIsVisible("filtered_wearables_panel")); + if(switchPanels(mInventoryItemsPanel, mWearableItemsPanel)) + { + mFolderViewBtn->setToggleState(FALSE); + } + mListViewBtn->setToggleState(TRUE); +} + +void LLPanelOutfitEdit::showFilteredFolderWearablesPanel() +{ + if(switchPanels(mWearableItemsPanel, mInventoryItemsPanel)) + { + mListViewBtn->setToggleState(FALSE); + } + mFolderViewBtn->setToggleState(TRUE); } void LLPanelOutfitEdit::saveOutfit(bool as_new) @@ -359,7 +385,7 @@ void LLPanelOutfitEdit::onSearchEdit(const std::string& string) if (mSearchString == "") { mInventoryItemsPanel->setFilterSubString(LLStringUtil::null); - + mWearableItemsList->setFilterSubString(LLStringUtil::null); // re-open folders that were initially open mSavedFolderState->setApply(TRUE); mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); @@ -385,6 +411,8 @@ void LLPanelOutfitEdit::onSearchEdit(const std::string& string) // set new filter string mInventoryItemsPanel->setFilterSubString(mSearchString); + mWearableItemsList->setFilterSubString(mSearchString); + } void LLPanelOutfitEdit::onAddToOutfitClicked(void) @@ -540,4 +568,15 @@ void LLPanelOutfitEdit::updateVerbs() } +bool LLPanelOutfitEdit::switchPanels(LLPanel* switch_from_panel, LLPanel* switch_to_panel) +{ + if(switch_from_panel && switch_to_panel && !switch_to_panel->getVisible()) + { + switch_from_panel->setVisible(FALSE); + switch_to_panel->setVisible(TRUE); + return true; + } + return false; +} + // EOF diff --git a/indra/newview/llpaneloutfitedit.h b/indra/newview/llpaneloutfitedit.h index 04c94e553f..cb8283fca3 100644 --- a/indra/newview/llpaneloutfitedit.h +++ b/indra/newview/llpaneloutfitedit.h @@ -42,6 +42,7 @@ #include "llremoteparcelrequest.h" #include "llinventory.h" +#include "llinventoryitemslist.h" #include "llinventorymodel.h" class LLButton; @@ -87,6 +88,7 @@ public: void toggleAddWearablesPanel(); void showWearablesFilter(); void showFilteredWearablesPanel(); + void showFilteredFolderWearablesPanel(); void saveOutfit(bool as_new = false); void showSaveMenu(); @@ -103,6 +105,14 @@ public: void update(); void updateVerbs(); + /** + * @brief Helper function. Shows one panel instead of another. + * If panels already switched does nothing and returns false. + * @param switch_from_panel panel to hide + * @param switch_to_panel panel to show + * @retun returns true if switching happened, false if not. + */ + bool switchPanels(LLPanel* switch_from_panel, LLPanel* switch_to_panel); private: @@ -115,9 +125,13 @@ private: LLSaveFolderState* mSavedFolderState; std::string mSearchString; LLButton* mEditWearableBtn; + LLButton* mFolderViewBtn; + LLButton* mListViewBtn; LLToggleableMenu* mSaveMenu; - LLFilteredWearableListManager* mWearableListManager; + LLFilteredWearableListManager* mWearableListManager; + LLInventoryItemsList* mWearableItemsList; + LLPanel* mWearableItemsPanel; LLCOFObserver* mCOFObserver; std::vector mLookItemTypes; diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml index 3200951253..7961664516 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml @@ -215,124 +215,120 @@ tab_group="2" user_resize="true" visible="false"> - - - + - + width="36"> + + + + width="189"> @@ -341,11 +336,11 @@ image_pressed_selected "Lit" + "Selected" - there are new messages and the Well height="28" layout="topleft" min_height="28" - top="0" - name="notification_well_panel" - width="37" min_width="37" - user_resize="false"> + name="notification_well_panel" + top="0" + user_resize="false" + width="37"> - - + label_color="Black" + left="0" + name="Unread" + tool_tip="Notifications" + width="34"> + + + -- cgit v1.3 From f1c775b7be55731475796b6b3b63eef1b2e290ef Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Wed, 12 May 2010 11:40:38 -0400 Subject: EXT-7319 : FIXED : INFRASTRUCTURE : LLInventoryBridge cleanup Superficial header file cleanup --- indra/newview/llinventorybridge.h | 168 +++++++++++++++----------------------- 1 file changed, 64 insertions(+), 104 deletions(-) (limited to 'indra') diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index d77062ba84..1c85dc8d7e 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -79,16 +79,15 @@ public: virtual void restoreItem() {} virtual void restoreToWorld() {} + //-------------------------------------------------------------------- // LLFolderViewEventListener functions + //-------------------------------------------------------------------- virtual const std::string& getName() const; virtual const std::string& getDisplayName() const; virtual PermissionMask getPermissionMask() const; virtual LLFolderType::EType getPreferredType() const; virtual time_t getCreationDate() const; - virtual LLFontGL::StyleFlags getLabelStyle() const - { - return LLFontGL::NORMAL; - } + virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } virtual std::string getLabelSuffix() const { return LLStringUtil::null; } virtual void openItem() {} virtual void closeItem() {} @@ -120,11 +119,12 @@ public: void* cargo_data) { return FALSE; } virtual LLInventoryType::EType getInventoryType() const { return mInvType; } + //-------------------------------------------------------------------- // LLInvFVBridge functionality + //-------------------------------------------------------------------- virtual void clearDisplayName() {} - // Allow context menus to be customized for side panel. - bool isInOutfitsSidePanel() const; + bool isInOutfitsSidePanel() const; // allow context menus to be customized for side panel bool canShare(); @@ -147,7 +147,7 @@ protected: BOOL isLinkedObjectMissing() const; // Is this a linked obj whose baseobj is not in inventory? BOOL isAgentInventory() const; // false if lost or in the inventory library - BOOL isCOFFolder() const; // true if COF or descendent of. + BOOL isCOFFolder() const; // true if COF or descendent of virtual BOOL isItemPermissive() const; static void changeItemParent(LLInventoryModel* model, LLViewerInventoryItem* item, @@ -168,7 +168,7 @@ protected: }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInvFVBridge +// Class LLInvFVBridgeBuilder // // This class intended to build Folder View Bridge via LLInvFVBridge::createBridge. // It can be overridden with another way of creation necessary Inventory-Folder-View-Bridge. @@ -214,7 +214,7 @@ public: virtual BOOL hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } - // override for LLInvFVBridge + // Override for LLInvFVBridge virtual void clearDisplayName() { mDisplayName.clear(); } LLViewerInventoryItem* getItem() const; @@ -232,8 +232,14 @@ protected: class LLFolderBridge : public LLInvFVBridge { - friend class LLInvFVBridge; public: + LLFolderBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : + LLInvFVBridge(inventory, root, uuid), + mCallingCards(FALSE), + mWearables(FALSE), + mMenu(NULL) {} BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop); BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, @@ -278,18 +284,11 @@ public: LLViewerInventoryCategory* getCategory() const; protected: - LLFolderBridge(LLInventoryPanel* inventory, - LLFolderView* root, - const LLUUID& uuid) : - LLInvFVBridge(inventory, root, uuid), - mCallingCards(FALSE), - mWearables(FALSE), - mMenu(NULL) {} - - // menu callbacks + //-------------------------------------------------------------------- + // Menu callbacks + //-------------------------------------------------------------------- static void pasteClipboard(void* user_data); static void createNewCategory(void* user_data); - static void createNewShirt(void* user_data); static void createNewPants(void* user_data); static void createNewShoes(void* user_data); @@ -310,10 +309,7 @@ protected: void modifyOutfit(BOOL append); void determineFolderType(); - /** - * Returns a copy of current menu items. - */ - menuentry_vec_t getMenuItems() { return mItems; } + menuentry_vec_t getMenuItems() { return mItems; } // returns a copy of current menu items public: static LLFolderBridge* sSelf; @@ -329,63 +325,57 @@ private: class LLTextureBridge : public LLItemBridge { - friend class LLInvFVBridge; public: - virtual LLUIImagePtr getIcon() const; - virtual void openItem(); - virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - virtual void performAction(LLInventoryModel* model, std::string action); - -protected: LLTextureBridge(LLInventoryPanel* inventory, LLFolderView* root, const LLUUID& uuid, LLInventoryType::EType type) : LLItemBridge(inventory, root, uuid), - mInvType(type) - {} + mInvType(type) {} + virtual LLUIImagePtr getIcon() const; + virtual void openItem(); + virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + virtual void performAction(LLInventoryModel* model, std::string action); + bool canSaveTexture(void); LLInventoryType::EType mInvType; }; class LLSoundBridge : public LLItemBridge { - friend class LLInvFVBridge; public: + LLSoundBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : + LLItemBridge(inventory, root, uuid) {} virtual void openItem(); virtual void previewItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); static void openSoundPreview(void*); - -protected: - LLSoundBridge(LLInventoryPanel* inventory, - LLFolderView* root, - const LLUUID& uuid) : - LLItemBridge(inventory, root, uuid) {} }; class LLLandmarkBridge : public LLItemBridge { - friend class LLInvFVBridge; public: + LLLandmarkBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid, + U32 flags = 0x00); virtual void performAction(LLInventoryModel* model, std::string action); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual LLUIImagePtr getIcon() const; virtual void openItem(); - -protected: - LLLandmarkBridge(LLInventoryPanel* inventory, - LLFolderView* root, - const LLUUID& uuid, - U32 flags = 0x00); protected: BOOL mVisited; }; class LLCallingCardBridge : public LLItemBridge { - friend class LLInvFVBridge; public: + LLCallingCardBridge(LLInventoryPanel* inventory, + LLFolderView* folder, + const LLUUID& uuid ); + ~LLCallingCardBridge(); virtual std::string getLabelSuffix() const; //virtual const std::string& getDisplayName() const; virtual LLUIImagePtr getIcon() const; @@ -396,72 +386,58 @@ public: EDragAndDropType cargo_type, void* cargo_data); void refreshFolderViewItem(); -protected: - LLCallingCardBridge(LLInventoryPanel* inventory, - LLFolderView* folder, - const LLUUID& uuid ); - ~LLCallingCardBridge(); protected: LLCallingCardObserver* mObserver; }; - class LLNotecardBridge : public LLItemBridge { - friend class LLInvFVBridge; public: - virtual void openItem(); -protected: LLNotecardBridge(LLInventoryPanel* inventory, LLFolderView* root, const LLUUID& uuid) : LLItemBridge(inventory, root, uuid) {} + virtual void openItem(); }; class LLGestureBridge : public LLItemBridge { - friend class LLInvFVBridge; public: + LLGestureBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : + LLItemBridge(inventory, root, uuid) {} // Only suffix for gesture items, not task items, because only // gestures in your inventory can be active. virtual LLFontGL::StyleFlags getLabelStyle() const; virtual std::string getLabelSuffix() const; - virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); virtual BOOL removeItem(); - virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - static void playGesture(const LLUUID& item_id); - -protected: - LLGestureBridge(LLInventoryPanel* inventory, - LLFolderView* root, - const LLUUID& uuid) - : LLItemBridge(inventory, root, uuid) {} }; class LLAnimationBridge : public LLItemBridge { - friend class LLInvFVBridge; public: - virtual void performAction(LLInventoryModel* model, std::string action); - virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - - virtual void openItem(); - -protected: LLAnimationBridge(LLInventoryPanel* inventory, LLFolderView* root, const LLUUID& uuid) : LLItemBridge(inventory, root, uuid) {} + virtual void performAction(LLInventoryModel* model, std::string action); + virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + virtual void openItem(); }; class LLObjectBridge : public LLItemBridge { - friend class LLInvFVBridge; public: + LLObjectBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid, + LLInventoryType::EType type, + U32 flags); virtual LLUIImagePtr getIcon() const; virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); @@ -469,14 +445,7 @@ public: virtual std::string getLabelSuffix() const; virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual BOOL renameItem(const std::string& new_name); - LLInventoryObject* getObject() const; -protected: - LLObjectBridge(LLInventoryPanel* inventory, - LLFolderView* root, - const LLUUID& uuid, - LLInventoryType::EType type, - U32 flags); protected: static LLUUID sContextMenuItemID; // Only valid while the context menu is open. LLInventoryType::EType mInvType; @@ -486,20 +455,23 @@ protected: class LLLSLTextBridge : public LLItemBridge { - friend class LLInvFVBridge; public: - virtual void openItem(); -protected: LLLSLTextBridge(LLInventoryPanel* inventory, LLFolderView* root, const LLUUID& uuid ) : LLItemBridge(inventory, root, uuid) {} + virtual void openItem(); }; class LLWearableBridge : public LLItemBridge { - friend class LLInvFVBridge; public: + LLWearableBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid, + LLAssetType::EType asset_type, + LLInventoryType::EType inv_type, + LLWearableType::EType wearable_type); virtual LLUIImagePtr getIcon() const; virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); @@ -525,14 +497,6 @@ public: static void removeItemFromAvatar(LLViewerInventoryItem *item); static void removeAllClothesFromAvatar(); void removeFromAvatar(); - -protected: - LLWearableBridge(LLInventoryPanel* inventory, - LLFolderView* root, - const LLUUID& uuid, - LLAssetType::EType asset_type, - LLInventoryType::EType inv_type, - LLWearableType::EType wearable_type); protected: LLAssetType::EType mAssetType; LLInventoryType::EType mInvType; @@ -541,35 +505,31 @@ protected: class LLLinkItemBridge : public LLItemBridge { - friend class LLInvFVBridge; public: - virtual const std::string& getPrefix() { return sPrefix; } - virtual void buildContextMenu(LLMenuGL& menu, U32 flags); -protected: LLLinkItemBridge(LLInventoryPanel* inventory, LLFolderView* root, const LLUUID& uuid) : LLItemBridge(inventory, root, uuid) {} + virtual const std::string& getPrefix() { return sPrefix; } + virtual void buildContextMenu(LLMenuGL& menu, U32 flags); protected: static std::string sPrefix; }; class LLLinkFolderBridge : public LLItemBridge { - friend class LLInvFVBridge; public: + LLLinkFolderBridge(LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : + LLItemBridge(inventory, root, uuid) {} virtual const std::string& getPrefix() { return sPrefix; } virtual LLUIImagePtr getIcon() const; virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual void performAction(LLInventoryModel* model, std::string action); virtual void gotoItem(); protected: - LLLinkFolderBridge(LLInventoryPanel* inventory, - LLFolderView* root, - const LLUUID& uuid) : - LLItemBridge(inventory, root, uuid) {} const LLUUID &getFolderID() const; -protected: static std::string sPrefix; }; -- cgit v1.3 From dbaaebfe5b2847a258019cd82a2abe044e3d3140 Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Wed, 12 May 2010 13:46:21 -0400 Subject: EXT-7319 : FIXED : INFRASTRUCTURE : LLInventoryBridge cleanup Const correctness, logic cleanup, took out subclasses redefining same variable as base class (!!!). --- indra/newview/llinventorybridge.cpp | 45 ++++++---------- indra/newview/llinventorybridge.h | 99 +++++++++++++--------------------- indra/newview/llinventoryfunctions.cpp | 21 +++----- indra/newview/llinventoryfunctions.h | 2 +- 4 files changed, 62 insertions(+), 105 deletions(-) (limited to 'indra') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 64379f4ce7..973257b19c 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -997,46 +997,36 @@ void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid) } } -bool LLInvFVBridge::isInOutfitsSidePanel() const +BOOL LLInvFVBridge::isInOutfitsSidePanel() const { LLInventoryPanel *my_panel = dynamic_cast(mInventoryPanel.get()); LLPanelOutfitsInventory *outfit_panel = dynamic_cast(LLSideTray::getInstance()->getPanel("panel_outfits_inventory")); if (!outfit_panel) - return false; + return FALSE; return outfit_panel->isTabPanel(my_panel); } -bool LLInvFVBridge::canShare() +BOOL LLInvFVBridge::canShare() const { const LLInventoryModel* model = getInventoryModel(); - if(!model) - { - return false; - } + if (!model) return FALSE; - LLViewerInventoryItem *item = model->getItem(mUUID); + const LLViewerInventoryItem *item = model->getItem(mUUID); if (item) { - bool allowed = false; - allowed = LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item); - if (allowed && - !item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) - { - allowed = false; - } - if (allowed && - !item->getPermissions().allowCopyBy(gAgent.getID())) - { - allowed = false; - } - return allowed; + if (!LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item)) + return FALSE; + if (!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) + return FALSE; + if (!item->getPermissions().allowCopyBy(gAgent.getID())) + return FALSE; + return TRUE; } - LLViewerInventoryCategory* cat = model->getCategory(mUUID); - // All categories can be given. - return cat != NULL; + const LLViewerInventoryCategory* cat = model->getCategory(mUUID); + return (cat != NULL); } // +=================================================+ @@ -4095,12 +4085,11 @@ LLObjectBridge::LLObjectBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type, U32 flags) : - LLItemBridge(inventory, root, uuid), - mInvType(type) + LLItemBridge(inventory, root, uuid) { mAttachPt = (flags & 0xff); // low bye of inventory flags - mIsMultiObject = ( flags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS ) ? TRUE: FALSE; + mInvType = type; } LLUIImagePtr LLObjectBridge::getIcon() const @@ -4447,9 +4436,9 @@ LLWearableBridge::LLWearableBridge(LLInventoryPanel* inventory, LLWearableType::EType wearable_type) : LLItemBridge(inventory, root, uuid), mAssetType( asset_type ), - mInvType(inv_type), mWearableType(wearable_type) { + mInvType = inv_type; } // *NOTE: hack to get from avatar inventory to avatar diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 1c85dc8d7e..c5efefac7e 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -74,13 +74,19 @@ public: U32 flags = 0x00); virtual ~LLInvFVBridge() {} - virtual const LLUUID& getUUID() const { return mUUID; } + BOOL isInOutfitsSidePanel() const; // allow context menus to be customized for side panel + BOOL canShare() const; + //-------------------------------------------------------------------- + // LLInvFVBridge functionality + //-------------------------------------------------------------------- + virtual const LLUUID& getUUID() const { return mUUID; } + virtual void clearDisplayName() {} virtual void restoreItem() {} virtual void restoreToWorld() {} //-------------------------------------------------------------------- - // LLFolderViewEventListener functions + // Inherited LLFolderViewEventListener functions //-------------------------------------------------------------------- virtual const std::string& getName() const; virtual const std::string& getDisplayName() const; @@ -99,7 +105,6 @@ public: virtual BOOL isItemMovable() const; virtual BOOL isItemInTrash() const; virtual BOOL isLink() const; - //virtual BOOL removeItem() = 0; virtual void removeBatch(LLDynamicArray& batch); virtual void move(LLFolderViewEventListener* new_parent_bridge) {} @@ -119,15 +124,6 @@ public: void* cargo_data) { return FALSE; } virtual LLInventoryType::EType getInventoryType() const { return mInvType; } - //-------------------------------------------------------------------- - // LLInvFVBridge functionality - //-------------------------------------------------------------------- - virtual void clearDisplayName() {} - - bool isInOutfitsSidePanel() const; // allow context menus to be customized for side panel - - bool canShare(); - //-------------------------------------------------------------------- // Convenience functions for adding various common menu options. //-------------------------------------------------------------------- @@ -195,7 +191,6 @@ public: LLInvFVBridge(inventory, root, uuid) {} virtual void performAction(LLInventoryModel* model, std::string action); - virtual void selectItem(); virtual void restoreItem(); virtual void restoreToWorld(); @@ -214,20 +209,17 @@ public: virtual BOOL hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } - // Override for LLInvFVBridge - virtual void clearDisplayName() { mDisplayName.clear(); } + /*virtual*/ void clearDisplayName() { mDisplayName.clear(); } LLViewerInventoryItem* getItem() const; - bool isAddAction(std::string action) const; bool isRemoveAction(std::string action) const; - protected: + BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response); virtual BOOL isItemPermissive() const; static void buildDisplayName(LLInventoryItem* item, std::string& name); - mutable std::string mDisplayName; - BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response); + mutable std::string mDisplayName; }; class LLFolderBridge : public LLInvFVBridge @@ -240,10 +232,9 @@ public: mCallingCards(FALSE), mWearables(FALSE), mMenu(NULL) {} - BOOL dragItemIntoFolder(LLInventoryItem* inv_item, - BOOL drop); - BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, - BOOL drop); + BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop); + BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop); + virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); virtual void closeItem(); @@ -311,10 +302,15 @@ protected: menuentry_vec_t getMenuItems() { return mItems; } // returns a copy of current menu items + + //-------------------------------------------------------------------- + // Messy hacks for handling folder options + //-------------------------------------------------------------------- public: static LLFolderBridge* sSelf; static void staticFolderOptionsMenu(); void folderOptionsMenu(); + private: BOOL mCallingCards; BOOL mWearables; @@ -330,15 +326,15 @@ public: LLFolderView* root, const LLUUID& uuid, LLInventoryType::EType type) : - LLItemBridge(inventory, root, uuid), - mInvType(type) {} + LLItemBridge(inventory, root, uuid) + { + mInvType = type; + } virtual LLUIImagePtr getIcon() const; virtual void openItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual void performAction(LLInventoryModel* model, std::string action); - bool canSaveTexture(void); - LLInventoryType::EType mInvType; }; class LLSoundBridge : public LLItemBridge @@ -447,8 +443,7 @@ public: virtual BOOL renameItem(const std::string& new_name); LLInventoryObject* getObject() const; protected: - static LLUUID sContextMenuItemID; // Only valid while the context menu is open. - LLInventoryType::EType mInvType; + static LLUUID sContextMenuItemID; // Only valid while the context menu is open. U32 mAttachPt; BOOL mIsMultiObject; }; @@ -499,7 +494,6 @@ public: void removeFromAvatar(); protected: LLAssetType::EType mAssetType; - LLInventoryType::EType mInvType; LLWearableType::EType mWearableType; }; @@ -563,47 +557,33 @@ protected: LLInventoryModel* mModel; }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Recent Inventory Panel related classes +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -/************************************************************************/ -/* Recent Inventory Panel related classes */ -/************************************************************************/ -class LLRecentInventoryBridgeBuilder; -/** - * Overridden version of the Inventory-Folder-View-Bridge for Folders - */ +// Overridden version of the Inventory-Folder-View-Bridge for Folders class LLRecentItemsFolderBridge : public LLFolderBridge { - friend class LLRecentInventoryBridgeBuilder; - public: - /** - * Creates context menu for Folders related to Recent Inventory Panel. - * - * It uses base logic and than removes from visible items "New..." menu items. - */ - /*virtual*/ void buildContextMenu(LLMenuGL& menu, U32 flags); - -protected: + // Creates context menu for Folders related to Recent Inventory Panel. + // Uses base logic and than removes from visible items "New..." menu items. LLRecentItemsFolderBridge(LLInventoryType::EType type, - LLInventoryPanel* inventory, - LLFolderView* root, - const LLUUID& uuid) : + LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : LLFolderBridge(inventory, root, uuid) { mInvType = type; } + /*virtual*/ void buildContextMenu(LLMenuGL& menu, U32 flags); }; -/** - * Bridge builder to create Inventory-Folder-View-Bridge for Recent Inventory Panel - */ +// Bridge builder to create Inventory-Folder-View-Bridge for Recent Inventory Panel class LLRecentInventoryBridgeBuilder : public LLInventoryFVBridgeBuilder { - /** - * Overrides FolderBridge for Recent Inventory Panel. - * - * It use base functionality for bridges other than FolderBridge. - */ +public: + // Overrides FolderBridge for Recent Inventory Panel. + // It use base functionality for bridges other than FolderBridge. virtual LLInvFVBridge* createBridge(LLAssetType::EType asset_type, LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, @@ -611,11 +591,8 @@ class LLRecentInventoryBridgeBuilder : public LLInventoryFVBridgeBuilder LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00) const; - }; - - void wear_inventory_item_on_avatar(LLInventoryItem* item); void rez_attachment(LLViewerInventoryItem* item, diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 35ad8b64da..f1520d34df 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -92,39 +92,30 @@ LLUUID LLInventoryState::sWearNewClothingTransactionID; ///---------------------------------------------------------------------------- // static -bool LLInventoryCollectFunctor::itemTransferCommonlyAllowed(LLInventoryItem* item) +bool LLInventoryCollectFunctor::itemTransferCommonlyAllowed(const LLInventoryItem* item) { if (!item) return false; - bool allowed = false; - switch(item->getType()) { case LLAssetType::AT_CALLINGCARD: - // not allowed + return false; break; - case LLAssetType::AT_OBJECT: if (isAgentAvatarValid() && !gAgentAvatarp->isWearingAttachment(item->getUUID())) - { - allowed = true; - } + return true; break; - case LLAssetType::AT_BODYPART: case LLAssetType::AT_CLOTHING: if(!gAgentWearables.isWearingItem(item->getUUID())) - { - allowed = true; - } + return true; break; default: - allowed = true; + return true; break; } - - return allowed; + return false; } bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 1e2b4ff09c..02a5fc9224 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -58,7 +58,7 @@ public: virtual ~LLInventoryCollectFunctor(){}; virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) = 0; - static bool itemTransferCommonlyAllowed(LLInventoryItem* item); + static bool itemTransferCommonlyAllowed(const LLInventoryItem* item); }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- cgit v1.3 From 491d0f1ae4b3557f36a9c1ba7d0033fdcb7d22e5 Mon Sep 17 00:00:00 2001 From: Eli Linden Date: Wed, 12 May 2010 11:27:09 -0700 Subject: ND-46735 WIP IT linguistic --- indra/newview/skins/default/xui/it/floater_animation_preview.xml | 2 +- indra/newview/skins/default/xui/it/floater_tools.xml | 8 ++++---- indra/newview/skins/default/xui/it/menu_viewer.xml | 4 ++-- indra/newview/skins/default/xui/it/panel_group_general.xml | 2 +- indra/newview/skins/default/xui/it/panel_region_general.xml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/it/floater_animation_preview.xml b/indra/newview/skins/default/xui/it/floater_animation_preview.xml index 5472e32544..77341cad63 100644 --- a/indra/newview/skins/default/xui/it/floater_animation_preview.xml +++ b/indra/newview/skins/default/xui/it/floater_animation_preview.xml @@ -153,7 +153,7 @@ La lunghezza massima è [MAX_LENGTH] secondi. - + diff --git a/indra/newview/skins/default/xui/it/floater_tools.xml b/indra/newview/skins/default/xui/it/floater_tools.xml index 1c611915dc..cd16246f0f 100644 --- a/indra/newview/skins/default/xui/it/floater_tools.xml +++ b/indra/newview/skins/default/xui/it/floater_tools.xml @@ -39,11 +39,11 @@ Accessorio - + + + + + + + + + + + + + Date: Fri, 14 May 2010 11:27:29 -0400 Subject: EXT-4088 FIXED (INFRASTRUCTURE) Change LLFolderView::getSelectionList to return a selection Fixed issue where mCurrentSelectedList was being used before NULL check. --- indra/newview/llpanellandmarks.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 6634bc948d..4bf4f9eac1 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -993,10 +993,13 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const } else if("create_pick" == command_name) { - std::set selection = mCurrentSelectedList->getRootFolder()->getSelectionList(); - if ( mCurrentSelectedList && !selection.empty() ) + if (mCurrentSelectedList) { - return ( 1 == selection.size() && !LLAgentPicksInfo::getInstance()->isPickLimitReached() ); + std::set selection = mCurrentSelectedList->getRootFolder()->getSelectionList(); + if (!selection.empty()) + { + return ( 1 == selection.size() && !LLAgentPicksInfo::getInstance()->isPickLimitReached() ); + } } return false; } -- cgit v1.3 From f4cb22f33d03998e41e8cc6f4bbf5e46b39b2aa0 Mon Sep 17 00:00:00 2001 From: Paul Guslisty Date: Fri, 14 May 2010 18:52:20 +0300 Subject: EXT-7203 ADDITIONAL FIX Deleted autoresize of filter panel for proper scaling while resizing --HG-- branch : product-engine --- indra/newview/skins/default/xui/en/panel_outfit_edit.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml index 7961664516..f661d7e237 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml @@ -260,7 +260,7 @@ top_pad="5" left="0"> Date: Fri, 14 May 2010 17:31:56 -0400 Subject: EXT-7353 FIXED short cuts (links) to textures should show up in the texture picker Links in general will now show up for all filtered inventory views of their associated types. --- indra/newview/llinventorybridge.cpp | 14 +------------- indra/newview/llinventoryfilter.h | 7 ++++++- indra/newview/llinventorymodel.h | 2 +- indra/newview/llinventorypanel.cpp | 5 +++++ indra/newview/llinventorypanel.h | 3 ++- indra/newview/llviewerinventory.cpp | 14 ++++++++++++++ indra/newview/llviewerinventory.h | 1 + 7 files changed, 30 insertions(+), 16 deletions(-) (limited to 'indra') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 973257b19c..b9b4fa8b03 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -143,7 +143,6 @@ const std::string& LLInvFVBridge::getDisplayName() const // Folders have full perms PermissionMask LLInvFVBridge::getPermissionMask() const { - return PERM_ALL; } @@ -1219,18 +1218,7 @@ PermissionMask LLItemBridge::getPermissionMask() const { LLViewerInventoryItem* item = getItem(); PermissionMask perm_mask = 0; - if(item) - { - BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID()); - BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID()); - BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, - gAgent.getID()); - - if (copy) perm_mask |= PERM_COPY; - if (mod) perm_mask |= PERM_MODIFY; - if (xfer) perm_mask |= PERM_TRANSFER; - - } + if (item) perm_mask = item->getPermissionMask(); return perm_mask; } diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 2376ba5d22..3ef51baefc 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -98,11 +98,15 @@ public: void setHoursAgo(U32 hours); U32 getHoursAgo() const; + void setIncludeLinks(BOOL include_links); + BOOL getIncludeLinks() const; + // +-------------------------------------------------------------------+ // + Execution And Results // +-------------------------------------------------------------------+ BOOL check(const LLFolderViewItem* item); - BOOL checkAgainstFilterType(const LLFolderViewItem* item); + BOOL checkAgainstFilterType(const LLFolderViewItem* item) const; + BOOL checkAgainstPermissions(const LLFolderViewItem* item) const; std::string::size_type getStringMatchOffset() const; // +-------------------------------------------------------------------+ @@ -172,6 +176,7 @@ private: U32 mHoursAgo; EFolderShow mShowFolderState; PermissionMask mPermissions; + BOOL mIncludeLinks; }; U32 mOrder; diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 5f5d4d6118..1f7bd50328 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -256,7 +256,7 @@ public: // updateCategory() method to actually modify values. LLViewerInventoryCategory* getCategory(const LLUUID& id) const; - // Get the inventoryID that this item points to, else just return item_id. + // Get the inventoryID or item that this item points to, else just return object_id const LLUUID& getLinkedItemID(const LLUUID& object_id) const; private: mutable LLPointer mLastItem; // cache recent lookups diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 000bcdd265..4766c1c227 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -248,6 +248,11 @@ void LLInventoryPanel::setHoursAgo(U32 hours) getFilter()->setHoursAgo(hours); } +void LLInventoryPanel::setIncludeLinks(BOOL include_links) +{ + getFilter()->setIncludeLinks(include_links); +} + void LLInventoryPanel::setShowFolderState(LLInventoryFilter::EFolderShow show) { getFilter()->setShowFolderState(show); diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 160a3d6f23..4373cedf66 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -141,7 +141,8 @@ public: void setSinceLogoff(BOOL sl); void setHoursAgo(U32 hours); BOOL getSinceLogoff(); - + void setIncludeLinks(BOOL include_links); + void setShowFolderState(LLInventoryFilter::EFolderShow show); LLInventoryFilter::EFolderShow getShowFolderState(); void setAllowMultiSelect(BOOL allow) { mFolderRoot->setAllowMultiSelect(allow); } diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 49748c59e8..ac70be029a 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1653,6 +1653,20 @@ bool LLViewerInventoryItem::checkPermissionsSet(PermissionMask mask) const return ((curr_mask & mask) == mask); } +PermissionMask LLViewerInventoryItem::getPermissionMask() const +{ + const LLPermissions& permissions = getPermissions(); + + BOOL copy = permissions.allowCopyBy(gAgent.getID()); + BOOL mod = permissions.allowModifyBy(gAgent.getID()); + BOOL xfer = permissions.allowOperationBy(PERM_TRANSFER, gAgent.getID()); + PermissionMask perm_mask = 0; + if (copy) perm_mask |= PERM_COPY; + if (mod) perm_mask |= PERM_MODIFY; + if (xfer) perm_mask |= PERM_TRANSFER; + return perm_mask; +} + //---------- void LLViewerInventoryItem::onCallingCardNameLookup(const LLUUID& id, const std::string& first_name, const std::string& last_name) diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index f0dc7bcb67..8ab7c9710d 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -159,6 +159,7 @@ public: // Checks the items permissions (for owner, group, or everyone) and returns true if all mask bits are set. bool checkPermissionsSet(PermissionMask mask) const; + PermissionMask getPermissionMask() const; // callback void onCallingCardNameLookup(const LLUUID& id, const std::string& first_name, const std::string& last_name); -- cgit v1.3 From 53a5b74af12ec772a2a69cff49b2e52da51c5c8f Mon Sep 17 00:00:00 2001 From: Vadim Savchuk Date: Sat, 15 May 2010 14:40:15 +0300 Subject: EXT-7241 FIXED Changed all "Edit Appearance" links to "Change Outfit" and pointed them to the My Outfits tab. Renamed the following menu items to "Change Outfit" and made them open My Outfits tab: - Avatar in-world context menu -> My Appearance. - Avatar inspector menu -> My Appearance. - Me -> My Appearance. To enable the Appearance SP switch to My outfits I cleaned up the logic of switching between my outfits <-> edit outfit <-> edit wearable panels. Also done the following cleanups and minor improvements: - Removed unused LLSidepanelAppearance::mEditBtn and mLookInfoType members. - Removed empty LLSidepanelAppearance::updateVerbs() method. - Made the "params" argument of LLSideTray::showPanel() and togglePanel() optional. Reviewed by Nyx at https://codereview.productengine.com/secondlife/r/389/ --HG-- branch : product-engine --- indra/newview/llpaneloutfitsinventory.cpp | 5 - indra/newview/llsidepanelappearance.cpp | 102 +++++++++------------ indra/newview/llsidepanelappearance.h | 11 +-- indra/newview/llsidetray.h | 4 +- indra/newview/llviewermenu.cpp | 5 +- .../skins/default/xui/en/menu_attachment_self.xml | 4 +- .../skins/default/xui/en/menu_avatar_self.xml | 4 +- .../default/xui/en/menu_inspect_self_gear.xml | 4 +- indra/newview/skins/default/xui/en/menu_viewer.xml | 4 +- 9 files changed, 55 insertions(+), 88 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 660615df5a..ea75c16c56 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -134,11 +134,6 @@ void LLPanelOutfitsInventory::onOpen(const LLSD& key) void LLPanelOutfitsInventory::updateVerbs() { - if (mParent) - { - mParent->updateVerbs(); - } - if (mListCommands) { updateListCommands(); diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp index 0086c6aec4..658a7b52e3 100644 --- a/indra/newview/llsidepanelappearance.cpp +++ b/indra/newview/llsidepanelappearance.cpp @@ -118,9 +118,6 @@ BOOL LLSidepanelAppearance::postBuild() childSetAction("edit_outfit_btn", boost::bind(&LLSidepanelAppearance::onEditOutfitButtonClicked, this)); - mEditBtn = getChild("edit_btn"); - mEditBtn->setClickedCallback(boost::bind(&LLSidepanelAppearance::onEditButtonClicked, this)); - mNewOutfitBtn = getChild("newlook_btn"); mNewOutfitBtn->setClickedCallback(boost::bind(&LLSidepanelAppearance::onNewOutfitButtonClicked, this)); mNewOutfitBtn->setEnabled(false); @@ -172,24 +169,24 @@ void LLSidepanelAppearance::onOpen(const LLSD& key) { fetchInventory(); refreshCurrentOutfitName(); - updateVerbs(); if (mPanelOutfitsInventory) { mPanelOutfitsInventory->onOpen(key); } - if(key.size() == 0) + if (!key.has("type")) return; - - toggleOutfitEditPanel(TRUE); - updateVerbs(); - - mLookInfoType = key["type"].asString(); - if (mLookInfoType == "edit_outfit") + // Switch to the requested panel. + std::string type = key["type"].asString(); + if (type == "my_outfits") { - mOutfitEdit->displayCurrentOutfit(); + showOutfitsInventoryPanel(); + } + else if (type == "edit_outfit") + { + showOutfitEditPanel(/*update = */ true); } } @@ -233,6 +230,7 @@ void LLSidepanelAppearance::onOpenOutfitButtonClicked() } } +// *TODO: obsolete? void LLSidepanelAppearance::onEditAppearanceButtonClicked() { if (gAgentWearables.areWearablesLoaded()) @@ -248,19 +246,6 @@ void LLSidepanelAppearance::onEditOutfitButtonClicked() LLSideTray::getInstance()->showPanel("sidepanel_appearance", key); } -void LLSidepanelAppearance::onEditButtonClicked() -{ - toggleOutfitEditPanel(FALSE); - toggleWearableEditPanel(TRUE, NULL); - /*if (mOutfitEdit->getVisible()) - { - } - else - { - mPanelOutfitsInventory->onEdit(); - }*/ -} - void LLSidepanelAppearance::onNewOutfitButtonClicked() { if (!mOutfitEdit->getVisible()) @@ -271,33 +256,27 @@ void LLSidepanelAppearance::onNewOutfitButtonClicked() void LLSidepanelAppearance::onEditWearBackClicked() { - mEditWearable->saveChanges(); - toggleWearableEditPanel(FALSE, NULL); - toggleOutfitEditPanel(TRUE); + showOutfitEditPanel(/* update = */ false); } void LLSidepanelAppearance::showOutfitsInventoryPanel() { - mOutfitEdit->setVisible(FALSE); - - mPanelOutfitsInventory->setVisible(TRUE); - - mFilterEditor->setVisible(TRUE); - mEditBtn->setVisible(TRUE); - mNewOutfitBtn->setVisible(TRUE); - mCurrOutfitPanel->setVisible(TRUE); + toggleWearableEditPanel(FALSE); + toggleOutfitEditPanel(FALSE); } -void LLSidepanelAppearance::showOutfitEditPanel() +void LLSidepanelAppearance::showOutfitEditPanel(bool update) { - mOutfitEdit->setVisible(TRUE); - - mPanelOutfitsInventory->setVisible(FALSE); + if (!mOutfitEdit) + return; + + toggleWearableEditPanel(FALSE); + toggleOutfitEditPanel(TRUE); - mFilterEditor->setVisible(FALSE); - mEditBtn->setVisible(FALSE); - mNewOutfitBtn->setVisible(FALSE); - mCurrOutfitPanel->setVisible(FALSE); + if (update) + { + mOutfitEdit->displayCurrentOutfit(); + } } void LLSidepanelAppearance::toggleOutfitEditPanel(BOOL visible) @@ -305,16 +284,27 @@ void LLSidepanelAppearance::toggleOutfitEditPanel(BOOL visible) if (!mOutfitEdit) return; + if (mOutfitEdit->getVisible() == visible) + { + // visibility isn't changing, hence nothing to do + return; + } + mOutfitEdit->setVisible(visible); if (mPanelOutfitsInventory) mPanelOutfitsInventory->setVisible(!visible); mFilterEditor->setVisible(!visible); - mEditBtn->setVisible(!visible); mNewOutfitBtn->setVisible(!visible); mCurrOutfitPanel->setVisible(!visible); } void LLSidepanelAppearance::toggleWearableEditPanel(BOOL visible, LLWearable *wearable) { + if (mEditWearable->getVisible() == visible) + { + // visibility isn't changing, hence nothing to do + return; + } + if (!wearable) { wearable = gAgentWearables.getWearable(LLWearableType::WT_SHAPE, 0); @@ -324,6 +314,13 @@ void LLSidepanelAppearance::toggleWearableEditPanel(BOOL visible, LLWearable *we return; } + // Save changes if closing. + if (!visible) + { + mEditWearable->saveChanges(); + } + + // Toggle panel visibility. mCurrOutfitPanel->setVisible(!visible); mEditWearable->setVisible(visible); @@ -332,21 +329,6 @@ void LLSidepanelAppearance::toggleWearableEditPanel(BOOL visible, LLWearable *we mPanelOutfitsInventory->setVisible(!visible); } -void LLSidepanelAppearance::updateVerbs() -{ - bool is_look_info_visible = mOutfitEdit->getVisible(); - - if (mPanelOutfitsInventory && !is_look_info_visible) - { -// const bool is_correct_type = (mPanelOutfitsInventory->getCorrectListenerForAction() != NULL); -// mEditBtn->setEnabled(is_correct_type); - } - else - { - mEditBtn->setEnabled(FALSE); - } -} - void LLSidepanelAppearance::refreshCurrentOutfitName(const std::string& name) { // Set current outfit status (wearing/unsaved). diff --git a/indra/newview/llsidepanelappearance.h b/indra/newview/llsidepanelappearance.h index 2900831099..a919b07ed6 100644 --- a/indra/newview/llsidepanelappearance.h +++ b/indra/newview/llsidepanelappearance.h @@ -60,11 +60,10 @@ public: void fetchInventory(); void inventoryFetched(); - void updateVerbs(); void onNewOutfitButtonClicked(); void showOutfitsInventoryPanel(); - void showOutfitEditPanel(); + void showOutfitEditPanel(bool update); void setWearablesLoading(bool val); private: @@ -73,12 +72,11 @@ private: void onOpenOutfitButtonClicked(); void onEditAppearanceButtonClicked(); void onEditOutfitButtonClicked(); - void onEditButtonClicked(); void onEditWearBackClicked(); //@deprecated use showXXX() methods instead void toggleOutfitEditPanel(BOOL visible); - void toggleWearableEditPanel(BOOL visible, LLWearable* wearable); + void toggleWearableEditPanel(BOOL visible, LLWearable* wearable = NULL); LLFilterEditor* mFilterEditor; LLPanelOutfitsInventory* mPanelOutfitsInventory; @@ -87,7 +85,6 @@ private: LLButton* mOpenOutfitBtn; LLButton* mEditAppearanceBtn; - LLButton* mEditBtn; LLButton* mNewOutfitBtn; LLPanel* mCurrOutfitPanel; @@ -103,10 +100,6 @@ private: // Search string for filtering landmarks and teleport // history locations std::string mFilterSubString; - - // Information type currently shown in Look Information panel - std::string mLookInfoType; - }; #endif //LL_LLSIDEPANELAPPEARANCE_H diff --git a/indra/newview/llsidetray.h b/indra/newview/llsidetray.h index 140a9c818a..e8fdee9430 100644 --- a/indra/newview/llsidetray.h +++ b/indra/newview/llsidetray.h @@ -94,7 +94,7 @@ public: * if no such tab - return NULL, otherwise a pointer to the panel * Pass params as array, or they may be overwritten(example - params["name"]="nearby") */ - LLPanel* showPanel (const std::string& panel_name, const LLSD& params); + LLPanel* showPanel (const std::string& panel_name, const LLSD& params = LLSD()); /** * Toggling Side Tray tab which contains "sub_panel" child of "panel_name" panel. @@ -102,7 +102,7 @@ public: * otherwise Side Tray is collapsed. * params are passed to "panel_name" panel onOpen(). */ - void togglePanel (LLPanel* &sub_panel, const std::string& panel_name, const LLSD& params); + void togglePanel (LLPanel* &sub_panel, const std::string& panel_name, const LLSD& params = LLSD()); /* * get the panel (don't show it or do anything else with it) diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 42428bab03..c245650d51 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -5597,10 +5597,7 @@ void handle_viewer_disable_message_log(void*) void handle_customize_avatar() { - if (gAgentWearables.areWearablesLoaded()) - { - gAgentCamera.changeCameraToCustomizeAvatar(); - } + LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "my_outfits")); } void handle_report_abuse() diff --git a/indra/newview/skins/default/xui/en/menu_attachment_self.xml b/indra/newview/skins/default/xui/en/menu_attachment_self.xml index 5c30b9ee94..dc51ae8b92 100644 --- a/indra/newview/skins/default/xui/en/menu_attachment_self.xml +++ b/indra/newview/skins/default/xui/en/menu_attachment_self.xml @@ -68,8 +68,8 @@ function="Self.EnableStandUp" /> + label="Change Outfit" + name="Change Outfit"> + name="Chenge Outfit"> + label="Change Outfit" + name="change_outfit"> + label="Change Outfit" + name="ChangeOutfit"> Date: Mon, 17 May 2010 09:20:57 +0300 Subject: EXT-6941 FIX setup global EditMenu handle setup global EditMenu handle when pressing mouse on FolderView. --HG-- branch : product-engine --- indra/newview/llfolderview.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra') diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 2ae11aa2b5..a87f7288fa 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -1744,6 +1744,8 @@ BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) mParentPanel->setFocus(TRUE); + LLEditMenuHandler::gEditMenuHandler = this; + return LLView::handleMouseDown( x, y, mask ); } -- cgit v1.3 From e462c90db6063ee67b95ea376c65089dd0061f6d Mon Sep 17 00:00:00 2001 From: Yuri Chebotarev Date: Mon, 17 May 2010 09:22:08 +0300 Subject: EXT-7051 FIX Add more server hardcoded strings to localization dict. --HG-- branch : product-engine --- indra/newview/llviewerinventory.cpp | 35 ++++++++++++++++++++++++++ indra/newview/skins/default/xui/en/strings.xml | 30 ++++++++++++++++++++++ 2 files changed, 65 insertions(+) (limited to 'indra') diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 49748c59e8..da8832b1a2 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -95,6 +95,41 @@ public: mInventoryItemsDict["New Script"] = LLTrans::getString("New Script"); mInventoryItemsDict["New Folder"] = LLTrans::getString("New Folder"); mInventoryItemsDict["Contents"] = LLTrans::getString("Contents"); + + mInventoryItemsDict["Gesture"] = LLTrans::getString("Gesture"); + mInventoryItemsDict["Male Gestures"] = LLTrans::getString("Male Gestures"); + mInventoryItemsDict["Female Gestures"] = LLTrans::getString("Female Gestures"); + mInventoryItemsDict["Other Gestures"] = LLTrans::getString("Other Gestures"); + mInventoryItemsDict["Speech Gestures"] = LLTrans::getString("Speech Gestures"); + + //predefined gestures + + //male + mInventoryItemsDict["Male - Excuse me"] = LLTrans::getString("Male - Excuse me"); + mInventoryItemsDict["Male - Get lost"] = LLTrans::getString("Male - Get lost"); + mInventoryItemsDict["Male - Blow kiss"] = LLTrans::getString("Male - Blow kiss"); + mInventoryItemsDict["Male - Boo"] = LLTrans::getString("Male - Boo"); + mInventoryItemsDict["Male - Bored"] = LLTrans::getString("Male - Bored"); + mInventoryItemsDict["Male - Hey"] = LLTrans::getString("Male - Hey"); + mInventoryItemsDict["Male - Laugh"] = LLTrans::getString("Male - Laugh"); + mInventoryItemsDict["Male - Repulsed"] = LLTrans::getString("Male - Repulsed"); + mInventoryItemsDict["Male - Shrug"] = LLTrans::getString("Male - Shrug"); + mInventoryItemsDict["Male - Stick tougue out"] = LLTrans::getString("Male - Stick tougue out"); + mInventoryItemsDict["Male - Wow"] = LLTrans::getString("Male - Wow"); + + //female + mInventoryItemsDict["FeMale - Excuse me"] = LLTrans::getString("FeMale - Excuse me"); + mInventoryItemsDict["FeMale - Get lost"] = LLTrans::getString("FeMale - Get lost"); + mInventoryItemsDict["FeMale - Blow kiss"] = LLTrans::getString("FeMale - Blow kiss"); + mInventoryItemsDict["FeMale - Boo"] = LLTrans::getString("FeMale - Boo"); + mInventoryItemsDict["Female - Bored"] = LLTrans::getString("Female - Bored"); + mInventoryItemsDict["Female - Hey"] = LLTrans::getString("Female - Hey"); + mInventoryItemsDict["Female - Laugh"] = LLTrans::getString("Female - Laugh"); + mInventoryItemsDict["Female - Repulsed"] = LLTrans::getString("Female - Repulsed"); + mInventoryItemsDict["Female - Shrug"] = LLTrans::getString("Female - Shrug"); + mInventoryItemsDict["Female - Stick tougue out"]= LLTrans::getString("Female - Stick tougue out"); + mInventoryItemsDict["Female - Wow"] = LLTrans::getString("Female - Wow"); + } }; diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index e028ef2077..bf28e78cf6 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -3137,6 +3137,36 @@ Abuse Report New Script New Folder Contents + Gesture + Male Gestures + Female Gestures + Other Gestures + Speech Gestures + + + Male - Excuse me + Male - Get lost + Male - Blow kiss + Male - Boo + Male - Bored + Male - Hey + Male - Laugh + Male - Repulsed + Male - Shrug + Male - Stick tougue out + Male - Wow + + FeMale - Excuse me + FeMale - Get lost + FeMale - Blow kiss + FeMale - Boo + Female - Bored + Female - Hey + Female - Laugh + Female - Repulsed + Female - Shrug + Female - Stick tougue out + Female - Wow [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] -- cgit v1.3 From f9dff766a8fbd6e8ee8db486c1d76ededdac7ed4 Mon Sep 17 00:00:00 2001 From: Yuri Chebotarev Date: Mon, 17 May 2010 09:49:33 +0300 Subject: EXT-7000 FIX "eat" TAB key while menu is active Two problems: 1. AvatarList didn't hide popup menu when goes invisible. 2. (Real problem) MenuHolder View didn't process TAB key. Which leads to changing active ("focused") control. Which leads to the ability (for example) to switch tabs and hide popup menu parent view. --HG-- branch : product-engine --- indra/llui/llmenugl.cpp | 6 ++++++ indra/newview/llavatarlist.cpp | 9 +++++++++ indra/newview/llavatarlist.h | 2 ++ indra/newview/llavatarlistitem.h | 1 + 4 files changed, 18 insertions(+) (limited to 'indra') diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index e0e86ae228..70c144a832 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -3420,6 +3420,12 @@ BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) if (pMenu) { + //eat TAB key - EXT-7000 + if (key == KEY_TAB && mask == MASK_NONE) + { + return TRUE; + } + //handle ESCAPE and RETURN key handled = LLPanel::handleKey(key, mask, called_from_parent); if (!handled) diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 47ec5270c3..72ee289c91 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -399,6 +399,15 @@ BOOL LLAvatarList::handleRightMouseDown(S32 x, S32 y, MASK mask) return handled; } +void LLAvatarList::setVisible(BOOL visible) +{ + if ( visible == FALSE && mContextMenu ) + { + mContextMenu->hide(); + } + LLFlatListViewEx::setVisible(visible); +} + void LLAvatarList::computeDifference( const uuid_vec_t& vnew_unsorted, uuid_vec_t& vadded, diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index ff090f3a34..a9320055ca 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -70,6 +70,8 @@ public: virtual void clear(); + virtual void setVisible(BOOL visible); + void setNameFilter(const std::string& filter); void setDirty(bool val = true, bool force_refresh = false); uuid_vec_t& getIDs() { return mIDs; } diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index 3ba2c7a3e3..c6fac7a9f1 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -71,6 +71,7 @@ public: { public: virtual void show(LLView* spawning_view, const uuid_vec_t& selected_uuids, S32 x, S32 y) = 0; + virtual void hide() = 0; }; /** -- cgit v1.3 From 8bd31fa35190bdd99a4167a2bfdd7d3d1a6e5d3f Mon Sep 17 00:00:00 2001 From: Alexei Arabadji Date: Mon, 17 May 2010 09:53:56 +0300 Subject: EXT-7085 FIXED Added check to avoid showing invisible menu items. reviewed by Vadim Savchuk at https://codereview.productengine.com/secondlife/r/388/ --HG-- branch : product-engine --- indra/llui/llmenugl.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index e0e86ae228..779779c7bc 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -3726,10 +3726,14 @@ void LLContextMenuBranch::buildDrawLabel( void ) void LLContextMenuBranch::showSubMenu() { - S32 center_x; - S32 center_y; - localPointToScreen(getRect().getWidth(), getRect().getHeight() , ¢er_x, ¢er_y); - mBranch->show( center_x, center_y); + LLMenuItemGL* menu_item = mBranch->getParentMenuItem(); + if (menu_item != NULL && menu_item->getVisible()) + { + S32 center_x; + S32 center_y; + localPointToScreen(getRect().getWidth(), getRect().getHeight() , ¢er_x, ¢er_y); + mBranch->show(center_x, center_y); + } } // onCommit() - do the primary funcationality of the menu item. -- cgit v1.3 From 8739d0469ba5a766693664ca6dae0421d217ee38 Mon Sep 17 00:00:00 2001 From: Yuri Chebotarev Date: Mon, 17 May 2010 09:57:20 +0300 Subject: EXT-7106 FIX Change from commit callback to validate callback, remove some cyclic tab panel switches. Problem was caused by incorrect use on Commit callback (which didn't actually prevent from tab switching) which leads to cyclic tab switching. Validate callback actually prevents tab switching. Reviwed by Vadim Savchuk at https://codereview.productengine.com/secondlife/r/385/ --HG-- branch : product-engine --- indra/newview/llpanelgrouproles.cpp | 63 +++++++++++++------------------------ indra/newview/llpanelgrouproles.h | 3 +- 2 files changed, 22 insertions(+), 44 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index a77ba0c69c..93fbecfd3f 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -118,8 +118,7 @@ LLPanelGroupRoles::LLPanelGroupRoles() mCurrentTab(NULL), mRequestedTab( NULL ), mSubTabContainer( NULL ), - mFirstUse( TRUE ), - mIgnoreTransition( FALSE ) + mFirstUse( TRUE ) { } @@ -153,8 +152,8 @@ BOOL LLPanelGroupRoles::postBuild() //subtabp->addObserver(this); } - // Add click callbacks to all the tabs. - mSubTabContainer->setCommitCallback(boost::bind(&LLPanelGroupRoles::handleClickSubTab, this)); + // Add click callbacks to tab switching. + mSubTabContainer->setValidateBeforeCommit(boost::bind(&LLPanelGroupRoles::handleSubTabSwitch, this, _1)); // Set the current tab to whatever is currently being shown. mCurrentTab = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel(); @@ -196,30 +195,20 @@ BOOL LLPanelGroupRoles::isVisibleByAgent(LLAgent* agentp) } -void LLPanelGroupRoles::handleClickSubTab() +bool LLPanelGroupRoles::handleSubTabSwitch(const LLSD& data) { - // If we are already handling a transition, - // ignore this. - if (mIgnoreTransition) - { - return; - } - - mRequestedTab = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel(); + std::string panel_name = data.asString(); + + LLPanelGroupTab* activating_tab = static_cast(mSubTabContainer->getPanelByName(panel_name)); - // Make sure they aren't just clicking the same tab... - if (mRequestedTab == mCurrentTab) + if(activating_tab == mCurrentTab + || activating_tab == mRequestedTab) { - return; + return true; } - // Try to switch from the current panel to the panel the user selected. - attemptTransition(); -} - -BOOL LLPanelGroupRoles::attemptTransition() -{ - // Check if the current tab needs to be applied. + mRequestedTab = activating_tab; + std::string mesg; if (mCurrentTab && mCurrentTab->needsApply(mesg)) { @@ -235,16 +224,10 @@ BOOL LLPanelGroupRoles::attemptTransition() LLNotificationsUtil::add("PanelGroupApply", args, LLSD(), boost::bind(&LLPanelGroupRoles::handleNotifyCallback, this, _1, _2)); mHasModal = TRUE; - // We need to reselect the current tab, since it isn't finished. - if (mSubTabContainer) - { - mIgnoreTransition = TRUE; - mSubTabContainer->selectTabPanel( mCurrentTab ); - mIgnoreTransition = FALSE; - } + // Returning FALSE will block a close action from finishing until // we get a response back from the user. - return FALSE; + return false; } else { @@ -253,7 +236,7 @@ BOOL LLPanelGroupRoles::attemptTransition() { transitionToTab(); } - return TRUE; + return true; } } @@ -271,6 +254,7 @@ void LLPanelGroupRoles::transitionToTab() // This is now the current tab; mCurrentTab = mRequestedTab; mCurrentTab->activate(); + mRequestedTab = 0; } } @@ -278,6 +262,7 @@ bool LLPanelGroupRoles::handleNotifyCallback(const LLSD& notification, const LLS { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); mHasModal = FALSE; + LLPanelGroupTab* transition_tab = mRequestedTab; switch (option) { case 0: // "Apply Changes" @@ -297,26 +282,20 @@ bool LLPanelGroupRoles::handleNotifyCallback(const LLSD& notification, const LLS // Skip switching tabs. break; } - - // This panel's info successfully applied. - // Switch to the next panel. - // No break! Continue into 'Ignore Changes' which just switches tabs. - mIgnoreTransition = TRUE; - mSubTabContainer->selectTabPanel( mRequestedTab ); - mIgnoreTransition = FALSE; transitionToTab(); + mSubTabContainer->selectTabPanel( transition_tab ); + break; } case 1: // "Ignore Changes" // Switch to the requested panel without applying changes cancel(); - mIgnoreTransition = TRUE; - mSubTabContainer->selectTabPanel( mRequestedTab ); - mIgnoreTransition = FALSE; transitionToTab(); + mSubTabContainer->selectTabPanel( transition_tab ); break; case 2: // "Cancel" default: + mRequestedTab = NULL; // Do nothing. The user is canceling the action. break; } diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h index 98cebe9882..a877402041 100644 --- a/indra/newview/llpanelgrouproles.h +++ b/indra/newview/llpanelgrouproles.h @@ -65,7 +65,7 @@ public: virtual BOOL isVisibleByAgent(LLAgent* agentp); - void handleClickSubTab(); + bool handleSubTabSwitch(const LLSD& data); // Checks if the current tab needs to be applied, and tries to switch to the requested tab. BOOL attemptTransition(); @@ -93,7 +93,6 @@ protected: LLPanelGroupTab* mRequestedTab; LLTabContainer* mSubTabContainer; BOOL mFirstUse; - BOOL mIgnoreTransition; std::string mDefaultNeedsApplyMesg; std::string mWantApplyMesg; -- cgit v1.3 From 3d4cb72f327eca1bf6b5af366e9e403392e8abc8 Mon Sep 17 00:00:00 2001 From: Alexei Arabadji Date: Mon, 17 May 2010 12:04:22 +0300 Subject: EXT-7085 FIXED Corrected visibility of "Moderator Options Separator" reviewed by Mike Antipov --HG-- branch : product-engine --- indra/newview/llparticipantlist.cpp | 1 + indra/newview/skins/default/xui/en/menu_participant_list.xml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index daf116d255..1117ae05d7 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -655,6 +655,7 @@ LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() bool is_sort_visible = (mParent.mAvatarList && mParent.mAvatarList->size() > 1); main_menu->setItemVisible("SortByName", is_sort_visible); main_menu->setItemVisible("SortByRecentSpeakers", is_sort_visible); + main_menu->setItemVisible("Moderator Options Separator", isGroupModerator()); main_menu->setItemVisible("Moderator Options", isGroupModerator()); main_menu->setItemVisible("View Icons Separator", mParent.mAvatarListToggleIconsConnection.connected()); main_menu->setItemVisible("View Icons", mParent.mAvatarListToggleIconsConnection.connected()); diff --git a/indra/newview/skins/default/xui/en/menu_participant_list.xml b/indra/newview/skins/default/xui/en/menu_participant_list.xml index f126431263..4ed5807808 100644 --- a/indra/newview/skins/default/xui/en/menu_participant_list.xml +++ b/indra/newview/skins/default/xui/en/menu_participant_list.xml @@ -127,7 +127,8 @@ parameter="can_mute_text" /> + layout="topleft" + name="Moderator Options Separator"/> Date: Mon, 17 May 2010 16:40:08 +0300 Subject: EXT-7363 FIXED Added missing conversion of SLURL parameters string to query parameters map. reviewed by Vadim Savchuk at https://codereview.productengine.com/secondlife/r/397/ --HG-- branch : product-engine --- indra/newview/llurldispatcher.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index a31c3a0f1b..9efa6c4108 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -151,8 +151,9 @@ bool LLURLDispatcherImpl::dispatchApp(const LLSLURL& slurl, bool trusted_browser) { llinfos << "cmd: " << slurl.getAppCmd() << " path: " << slurl.getAppPath() << " query: " << slurl.getAppQuery() << llendl; + const LLSD& query_map = LLURI::queryMap(slurl.getAppQuery()); bool handled = LLCommandDispatcher::dispatch( - slurl.getAppCmd(), slurl.getAppPath(), slurl.getAppQuery(), web, trusted_browser); + slurl.getAppCmd(), slurl.getAppPath(), query_map, web, trusted_browser); // alert if we didn't handle this secondlife:///app/ SLURL // (but still return true because it is a valid app SLURL) -- cgit v1.3 From bf197d4f410b686a80bd66ab7a3fcc761e8591ca Mon Sep 17 00:00:00 2001 From: Andrew Polunin Date: Mon, 17 May 2010 16:52:44 +0300 Subject: EXT-7199 FIXED (Enable dragging items from inventory view to current outfit in Outfit Editor) - HandleDragAndDrop() method was overridden in the LLPanelOutfitEdit class to handle drag and drop operations of the Clothing, Body Parts and Attachments. - In panel_outfit_edit.xml parameter allow_multi_select was set to true to allow multiple selections. - Added class LLCOFDragAndDropObserver to implement the drag and drop. - Class LLInventoryMoveFromWorldObserver renamed to LLInventoryAddItemByAssetObserver. Drag and drop reimplemented using functionality of the LLInventoryAddItemByAssetObserver. Reviewed by Neal Orman and Mike Antipov at https://codereview.productengine.com/secondlife/r/373/ --HG-- branch : product-engine --- indra/newview/llinventoryobserver.cpp | 6 +- indra/newview/llinventoryobserver.h | 4 +- indra/newview/llpaneloutfitedit.cpp | 89 +++++++++++++++++++++- indra/newview/llpaneloutfitedit.h | 9 +++ indra/newview/llviewermessage.cpp | 4 +- .../skins/default/xui/en/panel_outfit_edit.xml | 2 +- 6 files changed, 105 insertions(+), 9 deletions(-) (limited to 'indra') diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp index c24d2ee0ea..8557548887 100644 --- a/indra/newview/llinventoryobserver.cpp +++ b/indra/newview/llinventoryobserver.cpp @@ -493,7 +493,7 @@ void LLInventoryExistenceObserver::changed(U32 mask) } } -void LLInventoryMoveFromWorldObserver::changed(U32 mask) +void LLInventoryAddItemByAssetObserver::changed(U32 mask) { if(!(mask & LLInventoryObserver::ADD)) { @@ -535,7 +535,7 @@ void LLInventoryMoveFromWorldObserver::changed(U32 mask) } } -void LLInventoryMoveFromWorldObserver::watchAsset(const LLUUID& asset_id) +void LLInventoryAddItemByAssetObserver::watchAsset(const LLUUID& asset_id) { if(asset_id.notNull()) { @@ -551,7 +551,7 @@ void LLInventoryMoveFromWorldObserver::watchAsset(const LLUUID& asset_id) } } -bool LLInventoryMoveFromWorldObserver::isAssetWatched( const LLUUID& asset_id ) +bool LLInventoryAddItemByAssetObserver::isAssetWatched( const LLUUID& asset_id ) { return std::find(mWatchedAssets.begin(), mWatchedAssets.end(), asset_id) != mWatchedAssets.end(); } diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h index 036e6ca40d..6d5a86a6fc 100644 --- a/indra/newview/llinventoryobserver.h +++ b/indra/newview/llinventoryobserver.h @@ -180,10 +180,10 @@ protected: // something useful. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInventoryMoveFromWorldObserver : public LLInventoryObserver +class LLInventoryAddItemByAssetObserver : public LLInventoryObserver { public: - LLInventoryMoveFromWorldObserver() : mIsDirty(false) {} + LLInventoryAddItemByAssetObserver() : mIsDirty(false) {} virtual void changed(U32 mask); void watchAsset(const LLUUID& asset_id); diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index c04be85174..44832ac496 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -169,14 +169,48 @@ protected: S32 mBaseOutfitLastVersion; }; +class LLCOFDragAndDropObserver : public LLInventoryAddItemByAssetObserver +{ +public: + LLCOFDragAndDropObserver(LLInventoryModel* model); + + virtual ~LLCOFDragAndDropObserver(); + + virtual void done(); + +private: + LLInventoryModel* mModel; +}; + +inline LLCOFDragAndDropObserver::LLCOFDragAndDropObserver(LLInventoryModel* model): + mModel(model) +{ + if (model != NULL) + { + model->addObserver(this); + } +} + +inline LLCOFDragAndDropObserver::~LLCOFDragAndDropObserver() +{ + if (mModel != NULL && mModel->containsObserver(this)) + { + mModel->removeObserver(this); + } +} +void LLCOFDragAndDropObserver::done() +{ + LLAppearanceMgr::instance().updateAppearanceFromCOF(); +} LLPanelOutfitEdit::LLPanelOutfitEdit() : LLPanel(), mSearchFilter(NULL), mCOFWearables(NULL), mInventoryItemsPanel(NULL), - mCOFObserver(NULL) + mCOFObserver(NULL), + mCOFDragAndDropObserver(NULL) { mSavedFolderState = new LLSaveFolderState(); mSavedFolderState->setApply(FALSE); @@ -197,6 +231,7 @@ LLPanelOutfitEdit::~LLPanelOutfitEdit() delete mSavedFolderState; delete mCOFObserver; + delete mCOFDragAndDropObserver; } BOOL LLPanelOutfitEdit::postBuild() @@ -234,6 +269,8 @@ BOOL LLPanelOutfitEdit::postBuild() mInventoryItemsPanel->setSelectCallback(boost::bind(&LLPanelOutfitEdit::onInventorySelectionChange, this, _1, _2)); mInventoryItemsPanel->getRootFolder()->setReshapeCallback(boost::bind(&LLPanelOutfitEdit::onInventorySelectionChange, this, _1, _2)); + mCOFDragAndDropObserver = new LLCOFDragAndDropObserver(mInventoryItemsPanel->getModel()); + LLComboBox* type_filter = getChild("filter_wearables_combobox"); type_filter->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onTypeFilterChanged, this, _1)); type_filter->removeall(); @@ -522,6 +559,56 @@ void LLPanelOutfitEdit::update() updateVerbs(); } +BOOL LLPanelOutfitEdit::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + if (cargo_data == NULL) + { + llwarns << "cargo_data is NULL" << llendl; + return TRUE; + } + + switch (cargo_type) + { + case DAD_BODYPART: + case DAD_CLOTHING: + case DAD_OBJECT: + case DAD_LINK: + *accept = ACCEPT_YES_MULTI; + break; + default: + *accept = ACCEPT_NO; + } + + if (drop) + { + LLInventoryItem* item = static_cast(cargo_data); + + if (LLAssetType::lookupIsAssetIDKnowable(item->getType())) + { + mCOFDragAndDropObserver->watchAsset(item->getAssetUUID()); + + /* + * Adding request to wear item. If the item is a link, then getLinkedUUID() will + * return the ID of the linked item. Otherwise it will return the item's ID. The + * second argument is used to delay the appearance update until all dragged items + * are added to optimize user experience. + */ + LLAppearanceMgr::instance().addCOFItemLink(item->getLinkedUUID(), false); + } + else + { + // if asset id is not available for the item we must wear it immediately (attachments only) + LLAppearanceMgr::instance().addCOFItemLink(item->getLinkedUUID(), true); + } + } + + return TRUE; +} + void LLPanelOutfitEdit::displayCurrentOutfit() { if (!getVisible()) diff --git a/indra/newview/llpaneloutfitedit.h b/indra/newview/llpaneloutfitedit.h index cb8283fca3..953a70785c 100644 --- a/indra/newview/llpaneloutfitedit.h +++ b/indra/newview/llpaneloutfitedit.h @@ -50,6 +50,7 @@ class LLCOFWearables; class LLTextBox; class LLInventoryCategory; class LLCOFObserver; +class LLCOFDragAndDropObserver; class LLInventoryPanel; class LLSaveFolderState; class LLFolderViewItem; @@ -114,6 +115,12 @@ public: */ bool switchPanels(LLPanel* switch_from_panel, LLPanel* switch_to_panel); + virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); + private: @@ -134,6 +141,8 @@ private: LLPanel* mWearableItemsPanel; LLCOFObserver* mCOFObserver; + LLCOFDragAndDropObserver* mCOFDragAndDropObserver; + std::vector mLookItemTypes; LLCOFWearables* mCOFWearables; diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 86e040692c..fb87e2d3b9 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -774,11 +774,11 @@ private: * We can't create it each time items are moved because "drop" event is sent separately for each * element even while multi-dragging. We have to have the only instance of the observer. See EXT-4347. */ -class LLViewerInventoryMoveFromWorldObserver : public LLInventoryMoveFromWorldObserver +class LLViewerInventoryMoveFromWorldObserver : public LLInventoryAddItemByAssetObserver { public: LLViewerInventoryMoveFromWorldObserver() - : LLInventoryMoveFromWorldObserver() + : LLInventoryAddItemByAssetObserver() , mActivePanel(NULL) { diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml index f661d7e237..9072418329 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml @@ -292,7 +292,7 @@ width="311" user_resize="true"> Date: Mon, 17 May 2010 15:02:34 +0100 Subject: EXT-6964 FIXED Added back the secondlife:///app/appearance SLapp handler. This was removed inadvertantly in change 1fc92fc19c98. --- indra/newview/llappearancemgr.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'indra') diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index bcaba34921..be840cc348 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -55,6 +55,25 @@ char ORDER_NUMBER_SEPARATOR('@'); +// support for secondlife:///app/appearance SLapps +class LLAppearanceHandler : public LLCommandHandler +{ +public: + // requests will be throttled from a non-trusted browser + LLAppearanceHandler() : LLCommandHandler("appearance", UNTRUSTED_THROTTLE) {} + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + // support secondlife:///app/appearance/show, but for now we just + // make all secondlife:///app/appearance SLapps behave this way + LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD()); + return true; + } +}; + +LLAppearanceHandler gAppearanceHandler; + + LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id, const std::string& name) { LLInventoryModel::cat_array_t cat_array; -- cgit v1.3 From 4683283a666e3c34b6df52951b96c342a44679a8 Mon Sep 17 00:00:00 2001 From: Yuri Chebotarev Date: Mon, 17 May 2010 18:36:53 +0300 Subject: EXT-7045 FIX miss one more texture setup place. Also remove redundant variable and do some refactoring. Reviwed by Vadim Savchuk at https://codereview.productengine.com/secondlife/r/391/ --HG-- branch : product-engine --- indra/newview/llpanelgroupgeneral.cpp | 14 ++------------ indra/newview/llpanelgroupgeneral.h | 1 - indra/newview/lltexturectrl.cpp | 14 ++++++++++++++ indra/newview/lltexturectrl.h | 4 +++- 4 files changed, 19 insertions(+), 14 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index ddd41a1791..555248e31a 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -212,7 +212,6 @@ void LLPanelGroupGeneral::setupCtrls(LLPanel* panel_group) if (mInsignia) { mInsignia->setCommitCallback(onCommitAny, this); - mDefaultIconID = mInsignia->getImageAssetID(); } mFounderName = getChild("founder_name"); @@ -656,7 +655,7 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) } else { - mInsignia->setImageAssetID(mDefaultIconID); + mInsignia->setImageAssetName(mInsignia->getDefaultImageName()); } } @@ -846,16 +845,7 @@ void LLPanelGroupGeneral::reset() mInsignia->setEnabled(true); - LLPointer imagep = LLUI::getUIImage(mInsignia->getDefaultImageName()); - if(imagep) - { - LLViewerFetchedTexture* pTexture = dynamic_cast(imagep->getImage().get()); - if(pTexture) - { - LLUUID id = pTexture->getID(); - mInsignia->setImageAssetID(id); - } - } + mInsignia->setImageAssetName(mInsignia->getDefaultImageName()); { std::string empty_str = ""; diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h index 6245018871..6f4fa994da 100644 --- a/indra/newview/llpanelgroupgeneral.h +++ b/indra/newview/llpanelgroupgeneral.h @@ -95,7 +95,6 @@ private: BOOL mChanged; BOOL mFirstUse; std::string mIncompleteMemberDataStr; - LLUUID mDefaultIconID; // Group information (include any updates in updateChanged) LLLineEditor *mGroupNameEditor; diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index a1b3c8dabd..6165d309c3 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -1137,6 +1137,20 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op) } } +void LLTextureCtrl::setImageAssetName(const std::string& name) +{ + LLPointer imagep = LLUI::getUIImage(name); + if(imagep) + { + LLViewerFetchedTexture* pTexture = dynamic_cast(imagep->getImage().get()); + if(pTexture) + { + LLUUID id = pTexture->getID(); + setImageAssetID(id); + } + } +} + void LLTextureCtrl::setImageAssetID( const LLUUID& asset_id ) { if( mImageAssetID != asset_id ) diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index 837f837430..bcd0a083f2 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -128,7 +128,7 @@ public: virtual void clear(); // Takes a UUID, wraps get/setImageAssetID - virtual void setValue(const LLSD& value ); + virtual void setValue(const LLSD& value); virtual LLSD getValue() const; // LLTextureCtrl interface @@ -142,6 +142,8 @@ public: const LLUUID& getImageItemID() { return mImageItemID; } + virtual void setImageAssetName(const std::string& name); + void setImageAssetID(const LLUUID &image_asset_id); const LLUUID& getImageAssetID() const { return mImageAssetID; } -- cgit v1.3 From fcea61df76ce69b438d26037df4bafc3380f129d Mon Sep 17 00:00:00 2001 From: Yuri Chebotarev Date: Mon, 17 May 2010 18:39:11 +0300 Subject: EXT-7266 FIX change default icon id tofrom object icon to id that comes with message Reviwed by Igor Borovkov at https://codereview.productengine.com/secondlife/r/371/ --HG-- branch : product-engine --- indra/newview/llchatitemscontainerctrl.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index 5b6a99e793..35a244c461 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -326,12 +326,14 @@ void LLNearbyChatToastPanel::draw() if(icon) { icon->setDrawTooltip(mSourceType == CHAT_SOURCE_AGENT); - if(mSourceType == CHAT_SOURCE_AGENT) - icon->setValue(mFromID); + if(mSourceType == CHAT_SOURCE_OBJECT) + icon->setValue(LLSD("OBJECT_Icon")); else if(mSourceType == CHAT_SOURCE_SYSTEM) icon->setValue(LLSD("SL_Logo")); - else - icon->setValue(LLSD("OBJECT_Icon")); + else if(mSourceType == CHAT_SOURCE_AGENT) + icon->setValue(mFromID); + else if(!mFromID.isNull()) + icon->setValue(mFromID); } mIsDirty = false; } -- cgit v1.3 From d8b989a87b94fd851404b42094e88c0a0c213ac8 Mon Sep 17 00:00:00 2001 From: Sergei Litovchuk Date: Mon, 17 May 2010 18:57:03 +0300 Subject: EXT-7158 ADDITIONAL FIX Optimization and implementation fix - Fixed issue with filter not applied to outfit newly added while filtering is active. - Optimization of refreshing outfit list when filter is applied - list is not refreshed on every list visibility change. Reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/394/ --HG-- branch : product-engine --- indra/newview/llinventoryitemslist.cpp | 10 +++++----- indra/newview/llinventoryitemslist.h | 8 +++++++- indra/newview/lloutfitslist.cpp | 35 +++++++++++++++++++++++++++++----- indra/newview/lloutfitslist.h | 2 +- 4 files changed, 43 insertions(+), 12 deletions(-) (limited to 'indra') diff --git a/indra/newview/llinventoryitemslist.cpp b/indra/newview/llinventoryitemslist.cpp index 2d1d401cd4..9b592e79af 100644 --- a/indra/newview/llinventoryitemslist.cpp +++ b/indra/newview/llinventoryitemslist.cpp @@ -322,7 +322,7 @@ LLInventoryItemsList::Params::Params() LLInventoryItemsList::LLInventoryItemsList(const LLInventoryItemsList::Params& p) : LLFlatListViewEx(p) , mNeedsRefresh(false) -, mPrevVisibility(false) +, mForceRefresh(false) { // TODO: mCommitOnSelectionChange is set to "false" in LLFlatListView // but reset to true in all derived classes. This settings might need to @@ -356,14 +356,13 @@ boost::signals2::connection LLInventoryItemsList::setRefreshCompleteCallback(con void LLInventoryItemsList::doIdle() { - bool cur_visibility = getVisible(); - if(cur_visibility != mPrevVisibility || mNeedsRefresh) + if (!mNeedsRefresh) return; + + if (isInVisibleChain() || mForceRefresh) { refresh(); mRefreshCompleteSignal(this, LLSD()); - - mPrevVisibility = getVisible(); } } @@ -414,6 +413,7 @@ void LLInventoryItemsList::refresh() bool needs_refresh = add_limit_exceeded; setNeedsRefresh(needs_refresh); + setForceRefresh(needs_refresh); } void LLInventoryItemsList::computeDifference( diff --git a/indra/newview/llinventoryitemslist.h b/indra/newview/llinventoryitemslist.h index 60cccc0f4f..a3863b511c 100644 --- a/indra/newview/llinventoryitemslist.h +++ b/indra/newview/llinventoryitemslist.h @@ -221,6 +221,12 @@ public: bool getNeedsRefresh(){ return mNeedsRefresh; } + /** + * Sets the flag indicating that the list needs to be refreshed even if it is + * not currently visible. + */ + void setForceRefresh(bool force_refresh){ mForceRefresh = force_refresh; } + /** * Idle routine used to refresh the list regardless of the current list * visibility, unlike draw() which is called only for the visible list. @@ -259,7 +265,7 @@ private: // Will be used in refresh() to determine added and removed ids bool mNeedsRefresh; - bool mPrevVisibility; + bool mForceRefresh; commit_signal_t mRefreshCompleteSignal; }; diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 12d5203429..43b7e15977 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -170,7 +170,7 @@ void LLOutfitsList::refreshList(const LLUUID& category_id) list->setCommitCallback(boost::bind(&LLOutfitsList::onSelectionChange, this, _1)); // Setting list refresh callback to apply filter on list change. - list->setRefreshCompleteCallback(boost::bind(&LLOutfitsList::onWearableItemsListRefresh, this, _1)); + list->setRefreshCompleteCallback(boost::bind(&LLOutfitsList::onFilteredWearableItemsListRefresh, this, _1)); // Fetch the new outfit contents. cat->fetch(); @@ -178,6 +178,21 @@ void LLOutfitsList::refreshList(const LLUUID& category_id) // Refresh the list of outfit items after fetch(). // Further list updates will be triggered by the category observer. list->updateList(cat_id); + + // If filter is currently applied we store the initial tab state and + // open it to show matched items if any. + if (!mFilterSubString.empty()) + { + tab->notifyChildren(LLSD().with("action","store_state")); + tab->setDisplayChildren(true); + + // Setting mForceRefresh flag will make the list refresh its contents + // even if it is not currently visible. This is required to apply the + // filter to the newly added list. + list->setForceRefresh(true); + + list->setFilterSubString(mFilterSubString); + } } // Handle removed tabs. @@ -327,7 +342,7 @@ void LLOutfitsList::changeOutfitSelection(LLWearableItemsList* list, const LLUUI mSelectedOutfitUUID = category_id; } -void LLOutfitsList::onWearableItemsListRefresh(LLUICtrl* ctrl) +void LLOutfitsList::onFilteredWearableItemsListRefresh(LLUICtrl* ctrl) { if (!ctrl || mFilterSubString.empty()) return; @@ -338,7 +353,7 @@ void LLOutfitsList::onWearableItemsListRefresh(LLUICtrl* ctrl) iter != iter_end; ++iter) { LLAccordionCtrlTab* tab = iter->second; - if (tab) continue; + if (!tab) continue; LLWearableItemsList* list = dynamic_cast(tab->getAccordionView()); if (list != ctrl) continue; @@ -395,8 +410,6 @@ void LLOutfitsList::applyFilter(const std::string& new_filter_substring) if (!new_filter_substring.empty()) { - tab->setDisplayChildren(true); - std::string title = tab->getTitle(); LLStringUtil::toUpper(title); @@ -413,6 +426,18 @@ void LLOutfitsList::applyFilter(const std::string& new_filter_substring) { tab->setTitle(tab->getTitle(), cur_filter); } + + if (tab->getVisible()) + { + // Open tab if it has passed the filter. + tab->setDisplayChildren(true); + } + else + { + // Set force refresh flag to refresh not visible list + // when some changes occur in it. + list->setForceRefresh(true); + } } else { diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h index bcb393b12a..8eaa39e6f1 100644 --- a/indra/newview/lloutfitslist.h +++ b/indra/newview/lloutfitslist.h @@ -98,7 +98,7 @@ private: * Called upon list refresh event to update tab visibility depending on * the results of applying filter to the title and list items of the tab. */ - void onWearableItemsListRefresh(LLUICtrl* ctrl); + void onFilteredWearableItemsListRefresh(LLUICtrl* ctrl); /** * Highlights filtered items and hides tabs which haven't passed filter. -- cgit v1.3 From 05a44d78ff6f83388945999c4f84954a0a900879 Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Mon, 17 May 2010 12:13:02 -0400 Subject: EXT-7353 FIXED short cuts (links) to textures should show up in the texture picker Added flag for controlling whether links show up in filtered lists. --- indra/newview/llinventoryfilter.cpp | 50 +++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 7 deletions(-) (limited to 'indra') diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 901a570487..c90919e8fd 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -41,6 +41,7 @@ #include "llinventorymodelbackgroundfetch.h" #include "llviewercontrol.h" #include "llfolderview.h" +#include "llinventorybridge.h" // linden library includes #include "lltrans.h" @@ -54,7 +55,8 @@ LLInventoryFilter::FilterOps::FilterOps() : mShowFolderState(SHOW_NON_EMPTY_FOLDERS), mPermissions(PERM_NONE), mFilterTypes(FILTERTYPE_OBJECT), - mFilterUUID(LLUUID::null) + mFilterUUID(LLUUID::null), + mIncludeLinks(TRUE) { } @@ -97,23 +99,23 @@ BOOL LLInventoryFilter::check(const LLFolderViewItem* item) return TRUE; } - const LLFolderViewEventListener* listener = item->getListener(); mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : std::string::npos; const BOOL passed_filtertype = checkAgainstFilterType(item); - const BOOL passed = passed_filtertype && - (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos) && - ((listener->getPermissionMask() & mFilterOps.mPermissions) == mFilterOps.mPermissions); + const BOOL passed_permissions = checkAgainstPermissions(item); + const BOOL passed = (passed_filtertype && + passed_permissions && + (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos)); return passed; } -BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) +BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) const { const LLFolderViewEventListener* listener = item->getListener(); if (!listener) return FALSE; - const LLInventoryType::EType object_type = listener->getInventoryType(); + LLInventoryType::EType object_type = listener->getInventoryType(); const LLUUID object_id = listener->getUUID(); const LLInventoryObject *object = gInventory.getObject(object_id); @@ -128,7 +130,9 @@ BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) if (object_type == LLInventoryType::IT_NONE) { if (object && object->getIsLinkType()) + { return FALSE; + } } else if ((1LL << object_type & mFilterOps.mFilterObjectTypes) == U64(0)) { @@ -203,6 +207,22 @@ BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) return TRUE; } +BOOL LLInventoryFilter::checkAgainstPermissions(const LLFolderViewItem* item) const +{ + const LLFolderViewEventListener* listener = item->getListener(); + if (!listener) return FALSE; + + PermissionMask perm = listener->getPermissionMask(); + const LLInvFVBridge *bridge = dynamic_cast(item->getListener()); + if (bridge && bridge->isLink()) + { + const LLUUID& linked_uuid = gInventory.getLinkedItemID(bridge->getUUID()); + const LLViewerInventoryItem *linked_item = gInventory.getItem(linked_uuid); + if (linked_item) + perm = linked_item->getPermissionMask(); + } + return (perm & mFilterOps.mPermissions) == mFilterOps.mPermissions; +} const std::string& LLInventoryFilter::getFilterSubString(BOOL trim) const { @@ -451,6 +471,18 @@ void LLInventoryFilter::setHoursAgo(U32 hours) mFilterOps.mFilterTypes |= FILTERTYPE_DATE; } +void LLInventoryFilter::setIncludeLinks(BOOL include_links) +{ + if (mFilterOps.mIncludeLinks != include_links) + { + if (!mFilterOps.mIncludeLinks) + setModified(FILTER_LESS_RESTRICTIVE); + else + setModified(FILTER_MORE_RESTRICTIVE); + } + mFilterOps.mIncludeLinks = include_links; +} + void LLInventoryFilter::setShowFolderState(EFolderShow state) { if (mFilterOps.mShowFolderState != state) @@ -826,6 +858,10 @@ U32 LLInventoryFilter::getHoursAgo() const { return mFilterOps.mHoursAgo; } +BOOL LLInventoryFilter::getIncludeLinks() const +{ + return mFilterOps.mIncludeLinks; +} LLInventoryFilter::EFolderShow LLInventoryFilter::getShowFolderState() const { return mFilterOps.mShowFolderState; -- cgit v1.3 From 01eca6f45ee3abf599e669cdf90f4316899f8a22 Mon Sep 17 00:00:00 2001 From: Eli Linden Date: Mon, 17 May 2010 16:33:56 -0700 Subject: ND-46735 WIP JA linguistic --- indra/newview/skins/default/xui/ja/strings.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index 560465f808..261c49931e 100644 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -1630,25 +1630,25 @@ ãŒæ¸¡ã—ã¾ã—㟠- You decline + æ‹’å¦ï¼š - from + é€ä¿¡å…ƒï¼š åˆè¨ˆ - ã¯è³¼å…¥ã—ã¾ã—㟠+ 購入: - ãŒã‚ãªãŸã«æ”¯æ‰•ã„ã¾ã—㟠+ ã‚ãªãŸã«æ”¯æ‰•ã„: paid into - bought pass to + 入場許å¯ã‚’購入: ãŒã‚¤ãƒ™ãƒ³ãƒˆç”¨ã®è²»ç”¨ã‚’支払ã„ã¾ã—㟠-- cgit v1.3 From f8e01d512af0a05cf5006bc974f1725c7151f785 Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Mon, 17 May 2010 20:40:46 -0400 Subject: EXT-7384 FIXED Aesthetic changes to debug textures floater Texture floater now scrolls appropriately. --- .../default/xui/en/floater_avatar_textures.xml | 47 ++++++++++++++-------- 1 file changed, 30 insertions(+), 17 deletions(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/floater_avatar_textures.xml b/indra/newview/skins/default/xui/en/floater_avatar_textures.xml index e30e958543..6690e9748a 100644 --- a/indra/newview/skins/default/xui/en/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/en/floater_avatar_textures.xml @@ -1,16 +1,39 @@ + width="1240" + can_resize="true"> INVALID AVATAR + + + Baked Textures @@ -43,27 +66,17 @@ top_delta="0" width="150" /> - + width="1230"> + Date: Mon, 17 May 2010 20:41:30 -0400 Subject: EXT-7385 FIXED Show load and discard level for loading textures More debugging information is now shown for loading textures --- indra/newview/lltexturectrl.cpp | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) (limited to 'indra') diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index a1b3c8dabd..f6479bd004 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -1248,25 +1248,49 @@ void LLTextureCtrl::draw() mTentativeLabel->setVisible( !mTexturep.isNull() && getTentative() ); - // Show "Loading..." string on the top left corner while this texture is loading. // Using the discard level, do not show the string if the texture is almost but not // fully loaded. - if ( mTexturep.notNull() && - (!mTexturep->isFullyLoaded()) && - (mShowLoadingPlaceholder == TRUE) && - (mTexturep->getDiscardLevel() != 1) && - (mTexturep->getDiscardLevel() != 0)) + if (mTexturep.notNull() && + (!mTexturep->isFullyLoaded()) && + (mShowLoadingPlaceholder == TRUE) && + (mTexturep->getDiscardLevel() != 1) && + (mTexturep->getDiscardLevel() != 0)) { + U32 v_offset = 25; LLFontGL* font = LLFontGL::getFontSansSerif(); font->renderUTF8( - mLoadingPlaceholderString, 0, + mLoadingPlaceholderString, + 0, llfloor(interior.mLeft+3), - llfloor(interior.mTop-25), + llfloor(interior.mTop-v_offset), LLColor4::white, LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::DROP_SHADOW); + + // Show more detailed information if this agent is god. + if (gAgent.isGodlike()) + { + LLFontGL* font = LLFontGL::getFontSansSerif(); + std::string tdesc; + // Show what % the texture has loaded (0 to 100%, 100 is highest), and what level of detail (5 to 0, 0 is best). + + v_offset += 12; + tdesc = llformat(" PK : %d%%", U32(mTexturep->getDownloadProgress()*100.0)); + font->renderUTF8(tdesc, 0, llfloor(interior.mLeft+3), llfloor(interior.mTop-v_offset), + LLColor4::white, LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::DROP_SHADOW); + + v_offset += 12; + tdesc = llformat(" LVL: %d", mTexturep->getDiscardLevel()); + font->renderUTF8(tdesc, 0, llfloor(interior.mLeft+3), llfloor(interior.mTop-v_offset), + LLColor4::white, LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::DROP_SHADOW); + + v_offset += 12; + tdesc = llformat(" ID : %s...", (mImageAssetID.asString().substr(0,10)).c_str()); + font->renderUTF8(tdesc, 0, llfloor(interior.mLeft+3), llfloor(interior.mTop-v_offset), + LLColor4::white, LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::DROP_SHADOW); + } } LLUICtrl::draw(); -- cgit v1.3 From e9d3fcbbb9c99ba2711980f4d70dc76ed05fb65c Mon Sep 17 00:00:00 2001 From: Alexei Arabadji Date: Tue, 18 May 2010 11:47:16 +0300 Subject: EXT-7365 FIXED Added alert toast panel to GLOBAL group of exclude list of transient floater manager(the same as sidebar); reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/396/ --HG-- branch : product-engine --- indra/newview/lltoastalertpanel.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'indra') diff --git a/indra/newview/lltoastalertpanel.cpp b/indra/newview/lltoastalertpanel.cpp index 2b529a4e50..6c6eda2e68 100644 --- a/indra/newview/lltoastalertpanel.cpp +++ b/indra/newview/lltoastalertpanel.cpp @@ -50,6 +50,7 @@ #include "llnotifications.h" #include "llfunctorregistry.h" #include "llrootview.h" +#include "lltransientfloatermgr.h" #include "llviewercontrol.h" // for gSavedSettings const S32 MAX_ALLOWED_MSG_WIDTH = 400; @@ -324,6 +325,9 @@ LLToastAlertPanel::LLToastAlertPanel( LLNotificationPtr notification, bool modal mDefaultBtnTimer.start(); mDefaultBtnTimer.setTimerExpirySec(DEFAULT_BUTTON_DELAY); } + + LLTransientFloaterMgr::instance().addControlView( + LLTransientFloaterMgr::GLOBAL, this); } bool LLToastAlertPanel::setCheckBox( const std::string& check_title, const std::string& check_control ) @@ -377,6 +381,8 @@ void LLToastAlertPanel::setVisible( BOOL visible ) LLToastAlertPanel::~LLToastAlertPanel() { + LLTransientFloaterMgr::instance().removeControlView( + LLTransientFloaterMgr::GLOBAL, this); } BOOL LLToastAlertPanel::hasTitleBar() const -- cgit v1.3 From 8ea6cc7dc235184267f265234228981d97e835f9 Mon Sep 17 00:00:00 2001 From: Andrew Polunin Date: Tue, 18 May 2010 14:15:52 +0300 Subject: EXT-7362 FIXED (World Map - make teleport button green) Default value for button's attribute 'image_unselected' was overriden: image_unselected=\"PushButton_On\". Reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/399/ --HG-- branch : product-engine --- indra/newview/skins/default/xui/en/floater_world_map.xml | 1 + 1 file changed, 1 insertion(+) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml index 233ab2c1b2..fa5a0b77f9 100644 --- a/indra/newview/skins/default/xui/en/floater_world_map.xml +++ b/indra/newview/skins/default/xui/en/floater_world_map.xml @@ -538,6 +538,7 @@ + + + + -- cgit v1.3 From b416dcb73b1f405ebf5260cc588b07fde7ad1604 Mon Sep 17 00:00:00 2001 From: "Nyx (Neal Orman)" Date: Tue, 18 May 2010 10:20:44 -0400 Subject: adding verbose logging around your own avatar rez time. Documents load times for each component texture at each discard level, as well as baked texture upload times, and reports the timing of each texture to the console when your avatar sends out its appearance data to the server. Reviewed by Seraph, will be removed before release. -Nyx --- indra/newview/llappearancemgr.cpp | 2 ++ indra/newview/lltexlayer.cpp | 1 + indra/newview/llvoavatar.cpp | 4 +-- indra/newview/llvoavatarself.cpp | 70 +++++++++++++++++++++++++++++++++------ indra/newview/llvoavatarself.h | 23 +++++++++++++ indra/newview/llwearable.cpp | 3 ++ 6 files changed, 91 insertions(+), 12 deletions(-) (limited to 'indra') diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index be840cc348..4aef72ab0b 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -524,6 +524,7 @@ bool LLWearableHoldingPattern::pollMissingWearables() if (done) { + gAgentAvatarp->wearablesLoaded(); clearCOFLinksForMissingWearables(); onAllComplete(); } @@ -1649,6 +1650,7 @@ void LLAppearanceMgr::autopopulateOutfits() // Handler for anything that's deferred until avatar de-clouds. void LLAppearanceMgr::onFirstFullyVisible() { + gAgentAvatarp->avatarVisible(); autopopulateOutfits(); } diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp index 82fa672342..337627cc79 100644 --- a/indra/newview/lltexlayer.cpp +++ b/indra/newview/lltexlayer.cpp @@ -237,6 +237,7 @@ BOOL LLTexLayerSetBuffer::render() { if (mTexLayerSet->isVisible()) { + mTexLayerSet->getAvatar()->bakedTextureUpload(mTexLayerSet->getBakedTexIndex(), FALSE); // FALSE for start of upload, TRUE for finish. readBackAndUpload(); } else diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 2ec8ced543..c64f7b5a3e 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -5869,14 +5869,14 @@ void LLVOAvatar::updateRuthTimer(bool loading) const F32 LOADING_TIMEOUT = 120.f; if (mRuthTimer.getElapsedTimeF32() > LOADING_TIMEOUT) { - /* + llinfos << "Ruth Timer timeout: Missing texture data for '" << getFullname() << "' " << "( Params loaded : " << !visualParamWeightsAreDefault() << " ) " << "( Lower : " << isTextureDefined(TEX_LOWER_BAKED) << " ) " << "( Upper : " << isTextureDefined(TEX_UPPER_BAKED) << " ) " << "( Head : " << isTextureDefined(TEX_HEAD_BAKED) << " )." << llendl; - */ + LLAvatarPropertiesProcessor::getInstance()->sendAvatarTexturesRequest(getID()); mRuthTimer.reset(); } diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index dec2e7efdf..739c021311 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -103,15 +103,7 @@ struct LocalTextureData //----------------------------------------------------------------------------- // Callback data //----------------------------------------------------------------------------- -struct LLAvatarTexData -{ - LLAvatarTexData(const LLUUID& id, ETextureIndex index) : - mAvatarID(id), - mIndex(index) - {} - LLUUID mAvatarID; - ETextureIndex mIndex; -}; + /** ** @@ -156,6 +148,23 @@ void LLVOAvatarSelf::initInstance() // adds attachment points to mScreen among other things LLVOAvatar::initInstance(); + llinfos << "Self avatar object created. Starting timer." << llendl; + mSelfLoadTimer.reset(); + // clear all times to -1 for debugging + for (U32 i =0; i < LLVOAvatarDefines::TEX_NUM_INDICES; ++i) + { + for (U32 j = 0; j <= MAX_DISCARD_LEVEL; ++j) + { + mTextureLoadTimes[i][j] = -1.0f; + } + } + + for (U32 i =0; i < LLVOAvatarDefines::BAKED_NUM_INDICES; ++i) + { + mBakedTextureTimes[i][0] = -1.0f; + mBakedTextureTimes[i][1] = -1.0f; + } + status &= buildMenus(); if (!status) { @@ -1117,12 +1126,12 @@ U32 LLVOAvatarSelf::getNumWearables(LLVOAvatarDefines::ETextureIndex i) const // virtual void LLVOAvatarSelf::localTextureLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src_raw, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata) { - //llinfos << "onLocalTextureLoaded: " << src_vi->getID() << llendl; const LLUUID& src_id = src_vi->getID(); LLAvatarTexData *data = (LLAvatarTexData *)userdata; ETextureIndex index = data->mIndex; if (!isIndexLocalTexture(index)) return; + LLLocalTextureObject *local_tex_obj = getLocalTextureObject(index, 0); // fix for EXT-268. Preventing using of NULL pointer @@ -1748,6 +1757,32 @@ BOOL LLVOAvatarSelf::getIsCloud() return FALSE; } +/*static*/ +void LLVOAvatarSelf::onTimingLocalTexLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata) +{ + gAgentAvatarp->timingLocalTexLoaded(success, src_vi, src, aux_src, discard_level, final, userdata); +} + +void LLVOAvatarSelf::timingLocalTexLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata) +{ + LLAvatarTexData *data = (LLAvatarTexData *)userdata; + ETextureIndex index = data->mIndex; + + if (discard_level >=0 && discard_level <= MAX_DISCARD_LEVEL) // ignore discard level -1, as it means we have no data. + { + mTextureLoadTimes[(U32)index][(U32)discard_level] = mSelfLoadTimer.getElapsedTimeF32(); + } +} + +void LLVOAvatarSelf::bakedTextureUpload(EBakedTextureIndex index, BOOL finished) +{ + U32 done = 0; + if (finished) + { + done = 1; + } + mBakedTextureTimes[index][done] = mSelfLoadTimer.getElapsedTimeF32(); +} const LLUUID& LLVOAvatarSelf::grabLocalTexture(ETextureIndex type, U32 index) const { @@ -1907,6 +1942,7 @@ void LLVOAvatarSelf::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid ) const LLVOAvatarDictionary::TextureEntry *texture_dict = LLVOAvatarDictionary::getInstance()->getTexture(te); if (texture_dict->mIsBakedTexture) { + bakedTextureUpload(texture_dict->mBakedTextureIndex, TRUE); // FALSE for start of upload, TRUE for finish. llinfos << "New baked texture: " << texture_dict->mName << " UUID: " << uuid <setLoadedCallback(LLVOAvatarSelf::onTimingLocalTexLoaded,0,TRUE,FALSE, new LLVOAvatarSelf::LLAvatarTexData(id, (LLVOAvatarDefines::ETextureIndex)te)); + + LLUUID textureid(text_buffer); mTEMap[te] = new LLLocalTextureObject(image, textureid); mSavedTEMap[te] = new LLLocalTextureObject(image, textureid); -- cgit v1.3 From 3e0c0b86c0fb1a1c26d020ca44a30365487edf3b Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Tue, 18 May 2010 10:24:21 -0400 Subject: EXT-7384 FIXED Aesthetic changes to debug textures floater Fixed missing closing XML tag. --- indra/newview/skins/default/xui/en/floater_avatar_textures.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/floater_avatar_textures.xml b/indra/newview/skins/default/xui/en/floater_avatar_textures.xml index 6690e9748a..119dd63a17 100644 --- a/indra/newview/skins/default/xui/en/floater_avatar_textures.xml +++ b/indra/newview/skins/default/xui/en/floater_avatar_textures.xml @@ -315,5 +315,6 @@ top_delta="0" width="128" /> + - + \ No newline at end of file -- cgit v1.3 From 04ef90ceb0829e660d9c89a13ef526b3d30c732c Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Tue, 18 May 2010 15:55:22 +0100 Subject: EXT-7390 WIP Created SLapp secondlife:///app/voice/effects/refresh to update the voice effect list with new effects. --- indra/newview/llvoiceclient.cpp | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 52a7de61a0..d44b47707e 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -35,6 +35,7 @@ #include "llviewerwindow.h" #include "llvoicevivox.h" #include "llviewernetwork.h" +#include "llcommandhandler.h" #include "llhttpnode.h" #include "llnotificationsutil.h" #include "llsdserialize.h" @@ -46,6 +47,39 @@ const F32 LLVoiceClient::VOLUME_MIN = 0.f; const F32 LLVoiceClient::VOLUME_DEFAULT = 0.5f; const F32 LLVoiceClient::VOLUME_MAX = 1.0f; + +// Support for secondlife:///app/voice SLapps +class LLVoiceHandler : public LLCommandHandler +{ +public: + // requests will be throttled from a non-trusted browser + LLVoiceHandler() : LLCommandHandler("voice", UNTRUSTED_THROTTLE) {} + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + if (params[0].asString() == "effects") + { + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + // If the voice client doesn't support voice effects, we can't handle effects SLapps + if (!effect_interface) + { + return false; + } + + // Support secondlife:///app/voice/effects/refresh to update the voice effect list with new effects + if (params[1].asString() == "refresh") + { + effect_interface->refreshVoiceEffectLists(false); + return true; + } + } + return false; + } +}; +LLVoiceHandler gVoiceHandler; + + + std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus) { std::string result = "UNKNOWN"; @@ -77,7 +111,6 @@ std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserv - /////////////////////////////////////////////////////////////////////////////////////////////// LLVoiceClient::LLVoiceClient() : -- cgit v1.3 From d3cfb6ca36d2e4624fff626fbf8ab0ed7bc8ec59 Mon Sep 17 00:00:00 2001 From: Yuri Chebotarev Date: Tue, 18 May 2010 17:57:04 +0300 Subject: EXT-7112 fix Set map pos,bgnd and size in code to match mockup --HG-- branch : product-engine --- indra/newview/llfloatermap.cpp | 11 +++++++++++ indra/newview/skins/default/colors.xml | 2 +- indra/newview/skins/default/xui/en/floater_map.xml | 8 ++++---- 3 files changed, 16 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloatermap.cpp b/indra/newview/llfloatermap.cpp index 5677899dd9..725fc17136 100644 --- a/indra/newview/llfloatermap.cpp +++ b/indra/newview/llfloatermap.cpp @@ -232,6 +232,17 @@ void LLFloaterMap::onFocusLost() void LLFloaterMap::reshape(S32 width, S32 height, BOOL called_from_parent) { LLFloater::reshape(width, height, called_from_parent); + + //fix for ext-7112 + //by default ctrl can't overlap caption area + if(mMap) + { + LLRect map_rect; + map_rect.setLeftTopAndSize( 0, getRect().getHeight(), width, height); + mMap->reshape( width, height, 1); + mMap->setRect(map_rect); + } + updateMinorDirections(); } diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 0540a03e25..33668f5ed6 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -530,7 +530,7 @@ reference="Unused?" /> + value="0 0 0 1" /> diff --git a/indra/newview/skins/default/xui/en/floater_map.xml b/indra/newview/skins/default/xui/en/floater_map.xml index e21e44204d..249b091800 100644 --- a/indra/newview/skins/default/xui/en/floater_map.xml +++ b/indra/newview/skins/default/xui/en/floater_map.xml @@ -1,7 +1,7 @@ + height="218" + top="0"/> Date: Tue, 18 May 2010 17:59:15 +0300 Subject: ext-7106 FIX Always prevent tab switching while processing tab switch request reviwed by Mike Antipov at https://codereview.productengine.com/secondlife/r/402/ --HG-- branch : product-engine --- indra/newview/llpanelgrouproles.cpp | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index 93fbecfd3f..95ec8170f2 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -199,16 +199,13 @@ bool LLPanelGroupRoles::handleSubTabSwitch(const LLSD& data) { std::string panel_name = data.asString(); - LLPanelGroupTab* activating_tab = static_cast(mSubTabContainer->getPanelByName(panel_name)); - - if(activating_tab == mCurrentTab - || activating_tab == mRequestedTab) + if(mRequestedTab != NULL)//we already have tab change request { - return true; + return false; } - mRequestedTab = activating_tab; - + mRequestedTab = static_cast(mSubTabContainer->getPanelByName(panel_name)); + std::string mesg; if (mCurrentTab && mCurrentTab->needsApply(mesg)) { @@ -229,15 +226,9 @@ bool LLPanelGroupRoles::handleSubTabSwitch(const LLSD& data) // we get a response back from the user. return false; } - else - { - // The current panel didn't have anything it needed to apply. - if (mRequestedTab) - { - transitionToTab(); - } - return true; - } + + transitionToTab(); + return true; } void LLPanelGroupRoles::transitionToTab() -- cgit v1.3 From 5689f2884a69a20a950abe4e38052fc9a91a5dfd Mon Sep 17 00:00:00 2001 From: Yuri Chebotarev Date: Tue, 18 May 2010 18:01:08 +0300 Subject: ext-5600 partitial fix Add missing message to notification.xml Reviwed by Mike Antipov at https://codereview.productengine.com/secondlife/r/400/ --HG-- branch : product-engine --- indra/newview/skins/default/xui/en/notifications.xml | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index a2acb8100f..2d73cc904b 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -6118,6 +6118,12 @@ The button will be shown when there is enough space for it. type="notifytip"> Drag items from inventory onto a person in the resident picker + +Deed to group failed. + Date: Tue, 18 May 2010 15:15:08 -0400 Subject: AVP-72 FIXED Debug settings for gathering Welcome Island Rez time statistics Fixed memory leak. Prettied up formatting for debug readouts. Added more notifications for rez and cloud time. --- indra/newview/lltexturectrl.cpp | 29 +++++++------ indra/newview/llvoavatar.cpp | 49 +++++++++++++++++++++- indra/newview/llvoavatarself.cpp | 37 ++++++++++++---- .../newview/skins/default/xui/en/notifications.xml | 28 +++++++++++++ 4 files changed, 121 insertions(+), 22 deletions(-) (limited to 'indra') diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 07db3e0cef..9aebc264a2 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -1267,22 +1267,25 @@ void LLTextureCtrl::draw() // fully loaded. if (mTexturep.notNull() && (!mTexturep->isFullyLoaded()) && - (mShowLoadingPlaceholder == TRUE) && - (mTexturep->getDiscardLevel() != 1) && - (mTexturep->getDiscardLevel() != 0)) + (mShowLoadingPlaceholder == TRUE)) { U32 v_offset = 25; LLFontGL* font = LLFontGL::getFontSansSerif(); - font->renderUTF8( - mLoadingPlaceholderString, - 0, - llfloor(interior.mLeft+3), - llfloor(interior.mTop-v_offset), - LLColor4::white, - LLFontGL::LEFT, - LLFontGL::BASELINE, - LLFontGL::DROP_SHADOW); - + + // Don't show as loaded if the texture is almost fully loaded (i.e. discard1) unless god + if ((mTexturep->getDiscardLevel() > 1) || gAgent.isGodlike()) + { + font->renderUTF8( + mLoadingPlaceholderString, + 0, + llfloor(interior.mLeft+3), + llfloor(interior.mTop-v_offset), + LLColor4::white, + LLFontGL::LEFT, + LLFontGL::BASELINE, + LLFontGL::DROP_SHADOW); + } + // Show more detailed information if this agent is god. if (gAgent.isGodlike()) { diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index c64f7b5a3e..f95017b658 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -667,6 +667,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mTexEyeColor( NULL ), mNeedsSkin(FALSE), mUpdatePeriod(1), + mFullyLoaded(FALSE), + mPreviousFullyLoaded(FALSE), mFullyLoadedInitialized(FALSE), mSupportsAlphaLayers(FALSE) { @@ -746,8 +748,10 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mOohMorph = NULL; mAahMorph = NULL; - mCurrentGesticulationLevel = 0; + mCurrentGesticulationLevel = 0; + mRuthTimer.reset(); + mRuthDebugTimer.reset(); } //------------------------------------------------------------------------ @@ -755,6 +759,25 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, //------------------------------------------------------------------------ LLVOAvatar::~LLVOAvatar() { + if (gSavedSettings.getBOOL("DebugAvatarRezTime")) + { + if (!mFullyLoaded) + { + llinfos << "AVATARREZTIME: Avatar '" << getFullname() << " left after " << (U32)mRuthDebugTimer.getElapsedTimeF32() << " seconds as cloud." << llendl; + LLSD args; + args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32()); + args["NAME"] = getFullname(); + LLNotificationsUtil::add("AvatarRezLeftCloudNotification",args); + } + else + { + llinfos << "AVATARREZTIME: Avatar '" << getFullname() << " left." << llendl; + LLSD args; + args["NAME"] = getFullname(); + LLNotificationsUtil::add("AvatarRezLeftNotification",args); + } + + } lldebugs << "LLVOAvatar Destructor (0x" << this << ") id:" << mID << llendl; mRoot.removeAllChildren(); @@ -1269,6 +1292,7 @@ void LLVOAvatar::initInstance(void) //VTPause(); // VTune mVoiceVisualizer->setVoiceEnabled( LLVoiceClient::getInstance()->getVoiceEnabled( mID ) ); + } const LLVector3 LLVOAvatar::getRenderPosition() const @@ -2076,9 +2100,22 @@ U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys, LLMemType mt(LLMemType::MTYPE_AVATAR); LLVector3 old_vel = getVelocity(); + const BOOL has_name = !getNVPair("FirstName"); + // Do base class updates... U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp); + // Print out arrival information once we have name of avatar. + if (gSavedSettings.getBOOL("DebugAvatarRezTime")) + { + if (has_name && getNVPair("FirstName")) + { + LLSD args; + args["NAME"] = getFullname(); + LLNotificationsUtil::add("AvatarRezArrivedNotification",args); + llinfos << "AVATARREZTIME: Avatar '" << getFullname() << "' arrived." << llendl; + } + } if(retval & LLViewerObject::INVALID_UPDATE) { if (isSelf()) @@ -5863,6 +5900,14 @@ void LLVOAvatar::updateRuthTimer(bool loading) if (mPreviousFullyLoaded) { mRuthTimer.reset(); + if (gSavedSettings.getBOOL("DebugAvatarRezTime")) + { + llinfos << "AVATARREZTIME: Avatar '" << getFullname() << "' became cloud." << llendl; + LLSD args; + args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32()); + args["NAME"] = getFullname(); + LLNotificationsUtil::add("AvatarRezCloudNotification",args); + } mRuthDebugTimer.reset(); } @@ -5896,7 +5941,7 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading) { if (!mPreviousFullyLoaded && !loading && mFullyLoaded) { - llinfos << "Avatar '" << getFullname() << "' resolved in " << mRuthDebugTimer.getElapsedTimeF32() << " seconds." << llendl; + llinfos << "AVATARREZTIME: Avatar '" << getFullname() << "' resolved in " << (U32)mRuthDebugTimer.getElapsedTimeF32() << " seconds." << llendl; LLSD args; args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32()); args["NAME"] = getFullname(); diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 739c021311..e8f42178a1 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -1772,6 +1772,7 @@ void LLVOAvatarSelf::timingLocalTexLoaded(BOOL success, LLViewerFetchedTexture * { mTextureLoadTimes[(U32)index][(U32)discard_level] = mSelfLoadTimer.getElapsedTimeF32(); } + delete data; } void LLVOAvatarSelf::bakedTextureUpload(EBakedTextureIndex index, BOOL finished) @@ -1956,18 +1957,40 @@ void LLVOAvatarSelf::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid ) { gAgent.sendAgentSetAppearance(); F32 final_time = mSelfLoadTimer.getElapsedTimeF32(); - llinfos << "time from avatar creation to load wearables: " << mTimeWearablesLoaded << llendl; - llinfos << "time from avatar creation to de-cloud: " << mTimeAvatarVisible << llendl; - llinfos << "time from avatar creation to de-cloud for others: " << final_time << llendl; - llinfos << "load time for each texture: " << llendl; + llinfos << "AVATARREZTIME: Myself rez stats:" << llendl; + llinfos << "\t Time from avatar creation to load wearables: " << (S32)mTimeWearablesLoaded << llendl; + llinfos << "\t Time from avatar creation to de-cloud: " << (S32)mTimeAvatarVisible << llendl; + llinfos << "\t Time from avatar creation to de-cloud for others: " << (S32)final_time << llendl; + llinfos << "\t Load time for each texture: " << llendl; for (U32 i = 0; i < LLVOAvatarDefines::TEX_NUM_INDICES; ++i) { - llinfos << "(" << i << "): " << (S32)mTextureLoadTimes[i][0] << "\t" << (S32)mTextureLoadTimes[i][1] << "\t" << (S32)mTextureLoadTimes[i][2] << "\t" << (S32)mTextureLoadTimes[i][3] << "\t" << (S32)mTextureLoadTimes[i][4] << "\t" << (S32)mTextureLoadTimes[i][5] << llendl; + std::stringstream out; + out << "\t\t (" << i << ") "; + U32 j=0; + for (j=0; j <= MAX_DISCARD_LEVEL; j++) + { + out << "\t"; + S32 load_time = (S32)mTextureLoadTimes[i][j]; + if (load_time == -1) + { + out << "*"; + if (j == 0) + break; + } + else + { + out << load_time; + } + } + + // Don't print out non-existent textures. + if (j != 0) + llinfos << out.str() << llendl; } - llinfos << "Time points for each upload (start / finish)" << llendl; + llinfos << "\t Time points for each upload (start / finish)" << llendl; for (U32 i = 0; i < LLVOAvatarDefines::BAKED_NUM_INDICES; ++i) { - llinfos << "(" << i << "): " << (S32)mBakedTextureTimes[i][0] << " / " << (S32)mBakedTextureTimes[i][1] << llendl; + llinfos << "\t\t (" << i << ") \t" << (S32)mBakedTextureTimes[i][0] << " / " << (S32)mBakedTextureTimes[i][1] << llendl; } } } diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index a2acb8100f..6b486a91c5 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -6126,6 +6126,34 @@ Drag items from inventory onto a person in the resident picker Avatar '[NAME]' rezzed in [TIME] seconds. + +Avatar '[NAME]' became cloud. + + + +Avatar '[NAME]' appeared. + + + +Avatar '[NAME]' left after [TIME] seconds as cloud. + + + +Avatar '[NAME]' left as fully loaded. + + Date: Tue, 18 May 2010 14:14:50 -0600 Subject: EXT-7145: FIXED: make viewer to handle "URL redirect " for http texture. --- indra/llmessage/llcurl.cpp | 7 +++++++ indra/newview/lltexturefetch.cpp | 20 ++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) (limited to 'indra') diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 91e11b8c0d..36874a5d48 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -365,6 +365,13 @@ U32 LLCurl::Easy::report(CURLcode code) responseReason = strerror(code) + " : " + mErrorBuffer; } + if(responseCode >= 300 && responseCode < 400) //redirect + { + char new_url[512] ; + curl_easy_getinfo(mCurlEasyHandle, CURLINFO_REDIRECT_URL, new_url); + responseReason = new_url ; //get the new URL. + } + if (mResponder) { mResponder->completedRaw(responseCode, responseReason, mChannels, mOutput); diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 7fa04ce574..913a0b92c2 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -328,11 +328,7 @@ public: partial = true; } } - else - { - worker->setGetStatus(status, reason); -// llwarns << status << ": " << reason << llendl; - } + if (!success) { worker->setGetStatus(status, reason); @@ -904,7 +900,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mGetStatus == HTTP_NOT_FOUND) { mHTTPFailCount = max_attempts = 1; // Don't retry - //llinfos << "Texture missing from server (404): " << mUrl << llendl; + llwarns << "Texture missing from server (404): " << mUrl << llendl; //roll back to try UDP mState = INIT ; @@ -921,6 +917,17 @@ bool LLTextureFetchWorker::doWork(S32 param) max_attempts = mHTTPFailCount+1; // Keep retrying LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; } + else if(mGetStatus >= HTTP_MULTIPLE_CHOICES && mGetStatus < HTTP_BAD_REQUEST) //http re-direct + { + ++mHTTPFailCount; + max_attempts = 5 ; //try at most 5 times to avoid infinite redirection loop. + + llwarns << "HTTP GET failed because of redirection: " << mUrl + << " Status: " << mGetStatus << " Reason: '" << mGetReason << llendl ; + + //assign to the new url + mUrl = mGetReason ; + } else { const S32 HTTP_MAX_RETRY_COUNT = 3; @@ -930,6 +937,7 @@ bool LLTextureFetchWorker::doWork(S32 param) << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'" << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; } + if (mHTTPFailCount >= max_attempts) { if (cur_size > 0) -- cgit v1.3 From dd4c0fa41701a53a11987b3adf837d49119cfbc2 Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Tue, 18 May 2010 16:16:10 -0400 Subject: AVP-72 FIXED Debug settings for gathering Welcome Island Rez time statistics Fix for conditional deletion. --- indra/newview/llvoavatarself.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index e8f42178a1..6eaec0e7a1 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -1772,7 +1772,10 @@ void LLVOAvatarSelf::timingLocalTexLoaded(BOOL success, LLViewerFetchedTexture * { mTextureLoadTimes[(U32)index][(U32)discard_level] = mSelfLoadTimer.getElapsedTimeF32(); } - delete data; + if (final) + { + delete data; + } } void LLVOAvatarSelf::bakedTextureUpload(EBakedTextureIndex index, BOOL finished) @@ -1957,7 +1960,7 @@ void LLVOAvatarSelf::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid ) { gAgent.sendAgentSetAppearance(); F32 final_time = mSelfLoadTimer.getElapsedTimeF32(); - llinfos << "AVATARREZTIME: Myself rez stats:" << llendl; + llinfos << "REZTIME: Myself rez stats:" << llendl; llinfos << "\t Time from avatar creation to load wearables: " << (S32)mTimeWearablesLoaded << llendl; llinfos << "\t Time from avatar creation to de-cloud: " << (S32)mTimeAvatarVisible << llendl; llinfos << "\t Time from avatar creation to de-cloud for others: " << (S32)final_time << llendl; -- cgit v1.3 From 09a58b804bfcc055ce7722bf5aa8a05bfd6ed890 Mon Sep 17 00:00:00 2001 From: Sergei Litovchuk Date: Wed, 19 May 2010 01:20:39 +0300 Subject: EXT-7158 FIXED Moved lltextutil from newview to llui library. Reviewed by Richard Nelson and Mike Antipov at https://codereview.productengine.com/secondlife/r/363/ --HG-- branch : product-engine --- indra/llui/CMakeLists.txt | 4 +- indra/llui/llaccordionctrltab.cpp | 2 +- indra/llui/lltextutil.cpp | 79 ++++++++++++++++++++++++++++++++ indra/llui/lltextutil.h | 72 +++++++++++++++++++++++++++++ indra/newview/CMakeLists.txt | 2 - indra/newview/llavatarlist.cpp | 8 ++-- indra/newview/llavatarlistitem.cpp | 2 +- indra/newview/llgrouplist.cpp | 2 +- indra/newview/llimview.cpp | 2 +- indra/newview/llinventoryitemslist.cpp | 2 +- indra/newview/llpanelteleporthistory.cpp | 2 +- 11 files changed, 164 insertions(+), 13 deletions(-) create mode 100644 indra/llui/lltextutil.cpp create mode 100644 indra/llui/lltextutil.h (limited to 'indra') diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 88a1652671..12df9ccae4 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -91,6 +91,7 @@ set(llui_SOURCE_FILES lltextbox.cpp lltexteditor.cpp lltextparser.cpp + lltextutil.cpp lltextvalidate.cpp lltransutil.cpp lltoggleablemenu.cpp @@ -101,7 +102,6 @@ set(llui_SOURCE_FILES lluictrlfactory.cpp lluiimage.cpp lluistring.cpp - lluitextutil.cpp llundo.cpp llurlaction.cpp llurlentry.cpp @@ -186,6 +186,7 @@ set(llui_HEADER_FILES lltextbox.h lltexteditor.h lltextparser.h + lltextutil.h lltextvalidate.h lltoggleablemenu.h lltooltip.h @@ -198,7 +199,6 @@ set(llui_HEADER_FILES llui.h lluiimage.h lluistring.h - lluitextutil.h llundo.h llurlaction.h llurlentry.h diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index 0db2dca615..b09c108ec3 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -37,8 +37,8 @@ #include "lllocalcliprect.h" #include "llscrollbar.h" #include "lltextbox.h" +#include "lltextutil.h" #include "lluictrl.h" -#include "lluitextutil.h" static const std::string DD_BUTTON_NAME = "dd_button"; static const std::string DD_TEXTBOX_NAME = "dd_textbox"; diff --git a/indra/llui/lltextutil.cpp b/indra/llui/lltextutil.cpp new file mode 100644 index 0000000000..c5f3929fb1 --- /dev/null +++ b/indra/llui/lltextutil.cpp @@ -0,0 +1,79 @@ +/** + * @file lltextutil.cpp + * @brief Misc text-related auxiliary methods + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "lltextutil.h" + +#include "lluicolor.h" +#include "lltextbox.h" + + +void LLTextUtil::textboxSetHighlightedVal(LLTextBox *txtbox, const LLStyle::Params& normal_style, const std::string& text, const std::string& hl) +{ + static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", LLColor4::green); + + std::string text_uc = text; + LLStringUtil::toUpper(text_uc); + + size_t hl_begin = 0, hl_len = hl.size(); + + if (hl_len == 0 || (hl_begin = text_uc.find(hl)) == std::string::npos) + { + txtbox->setText(text, normal_style); + return; + } + + LLStyle::Params hl_style = normal_style; + hl_style.color = sFilterTextColor; + + txtbox->setText(LLStringUtil::null); // clear text + txtbox->appendText(text.substr(0, hl_begin), false, normal_style); + txtbox->appendText(text.substr(hl_begin, hl_len), false, hl_style); + txtbox->appendText(text.substr(hl_begin + hl_len), false, normal_style); +} + +const std::string& LLTextUtil::formatPhoneNumber(const std::string& phone_str) +{ + static const std::string PHONE_SEPARATOR = LLUI::sSettingGroups["config"]->getString("AvalinePhoneSeparator"); + static const S32 PHONE_PART_LEN = 2; + + static std::string formatted_phone_str; + formatted_phone_str = phone_str; + S32 separator_pos = (S32)(formatted_phone_str.size()) - PHONE_PART_LEN; + for (; separator_pos >= PHONE_PART_LEN; separator_pos -= PHONE_PART_LEN) + { + formatted_phone_str.insert(separator_pos, PHONE_SEPARATOR); + } + + return formatted_phone_str; +} + +// EOF diff --git a/indra/llui/lltextutil.h b/indra/llui/lltextutil.h new file mode 100644 index 0000000000..325c3c5b7c --- /dev/null +++ b/indra/llui/lltextutil.h @@ -0,0 +1,72 @@ +/** + * @file lltextutil.h + * @brief Misc text-related auxiliary methods + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLTEXTUTIL_H +#define LL_LLTEXTUTIL_H + +#include "llstyle.h" + +class LLTextBox; + +namespace LLTextUtil +{ + + /** + * Set value for text box, highlighting substring hl_uc. + * + * Used to highlight filter matches. + * + * @param txtbox Text box to set value for + * @param normal_style Style to use for non-highlighted text + * @param text Text to set + * @param hl Upper-cased string to highlight + */ + void textboxSetHighlightedVal( + LLTextBox *txtbox, + const LLStyle::Params& normal_style, + const std::string& text, + const std::string& hl); + + /** + * Formats passed phone number to be more human readable. + * + * It just divides the number on parts by two digits from right to left. The first left part + * can have 2 or 3 digits, i.e. +44-33-33-44-55-66 or 12-34-56-78-90. Separator is set in + * application settings (AvalinePhoneSeparator) + * + * @param[in] phone_str string with original phone number + * @return reference to string with formatted phone number + */ + const std::string& formatPhoneNumber(const std::string& phone_str); +} + +#endif // LL_LLTEXTUTIL_H diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index f2bed843c9..d77d53f6af 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -421,7 +421,6 @@ set(viewer_SOURCE_FILES lltexturestats.cpp lltexturestatsuploader.cpp lltextureview.cpp - lltextutil.cpp lltoast.cpp lltoastalertpanel.cpp lltoastgroupnotifypanel.cpp @@ -935,7 +934,6 @@ set(viewer_HEADER_FILES lltexturestats.h lltexturestatsuploader.h lltextureview.h - lltextutil.h lltoast.h lltoastalertpanel.h lltoastgroupnotifypanel.h diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 72ee289c91..24290ac089 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -32,19 +32,21 @@ #include "llviewerprecompiledheaders.h" +#include "llavatarlist.h" + // common #include "lltrans.h" #include "llcommonutils.h" -#include "llavatarlist.h" -#include "llagentdata.h" // for comparator +// llui +#include "lltextutil.h" // newview +#include "llagentdata.h" // for comparator #include "llavatariconctrl.h" #include "llcallingcard.h" // for LLAvatarTracker #include "llcachename.h" #include "llrecentpeople.h" -#include "lltextutil.h" #include "lluuid.h" #include "llvoiceclient.h" #include "llviewercontrol.h" // for gSavedSettings diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 656274cb7a..5a8ad73c83 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -38,7 +38,7 @@ #include "llbutton.h" #include "llfloaterreg.h" -#include "lluitextutil.h" +#include "lltextutil.h" #include "llagent.h" #include "llavatariconctrl.h" diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 3224ac6d9b..da5196df45 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -39,8 +39,8 @@ #include "lliconctrl.h" #include "llmenugl.h" #include "lltextbox.h" +#include "lltextutil.h" #include "lltrans.h" -#include "lluitextutil.h" // newview #include "llagent.h" diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index c143ac9dea..039df69454 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -43,6 +43,7 @@ #include "llhttpclient.h" #include "llsdutil_math.h" #include "llstring.h" +#include "lltextutil.h" #include "lltrans.h" #include "lluictrlfactory.h" @@ -65,7 +66,6 @@ #include "llnearbychat.h" #include "llspeakers.h" //for LLIMSpeakerMgr #include "lltextbox.h" -#include "lltextutil.h" #include "llviewercontrol.h" #include "llviewerparcelmgr.h" diff --git a/indra/newview/llinventoryitemslist.cpp b/indra/newview/llinventoryitemslist.cpp index 9b592e79af..f94515d242 100644 --- a/indra/newview/llinventoryitemslist.cpp +++ b/indra/newview/llinventoryitemslist.cpp @@ -42,7 +42,7 @@ // llui #include "lliconctrl.h" -#include "lluitextutil.h" +#include "lltextutil.h" #include "llcallbacklist.h" #include "llinventoryfunctions.h" diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 4dd03e04a9..e8b6c6bfe5 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -38,7 +38,7 @@ #include "llsidetray.h" #include "llworldmap.h" #include "llteleporthistorystorage.h" -#include "lluitextutil.h" +#include "lltextutil.h" #include "llaccordionctrl.h" #include "llaccordionctrltab.h" -- cgit v1.3 From e041b67501ef2c24991ee25dd759e6d69e36ed75 Mon Sep 17 00:00:00 2001 From: Sergei Litovchuk Date: Tue, 18 May 2010 23:37:37 +0300 Subject: Removed duplicated include. --HG-- branch : product-engine --- indra/newview/lloutfitslist.cpp | 3 --- 1 file changed, 3 deletions(-) (limited to 'indra') diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 43b7e15977..7ebbddca25 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -36,9 +36,6 @@ // llcommon #include "llcommonutils.h" -// llcommon -#include "llcommonutils.h" - #include "llaccordionctrl.h" #include "llaccordionctrltab.h" #include "llappearancemgr.h" -- cgit v1.3 From 3c25cf6750b5cdb554d661a0280135c0e94a2248 Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Tue, 18 May 2010 17:10:44 -0400 Subject: AVP-72 FIXED Debug settings for gathering Welcome Island Rez time statistics Added timestamp for avatar rez messages. Added messaging when entering/leaving Edit Appearance Mode. --- indra/newview/llvoavatar.cpp | 40 +++++++++++++++++++--- indra/newview/llvoavatar.h | 1 + .../newview/skins/default/xui/en/notifications.xml | 21 ++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index f95017b658..46d10b0fe6 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -752,6 +752,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mRuthTimer.reset(); mRuthDebugTimer.reset(); + mDebugExistenceTimer.reset(); } //------------------------------------------------------------------------ @@ -763,16 +764,18 @@ LLVOAvatar::~LLVOAvatar() { if (!mFullyLoaded) { - llinfos << "AVATARREZTIME: Avatar '" << getFullname() << " left after " << (U32)mRuthDebugTimer.getElapsedTimeF32() << " seconds as cloud." << llendl; + llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << " left after " << (U32)mRuthDebugTimer.getElapsedTimeF32() << " seconds as cloud." << llendl; LLSD args; + args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32()); args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32()); args["NAME"] = getFullname(); LLNotificationsUtil::add("AvatarRezLeftCloudNotification",args); } else { - llinfos << "AVATARREZTIME: Avatar '" << getFullname() << " left." << llendl; + llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << " left." << llendl; LLSD args; + args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32()); args["NAME"] = getFullname(); LLNotificationsUtil::add("AvatarRezLeftNotification",args); } @@ -2110,10 +2113,12 @@ U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys, { if (has_name && getNVPair("FirstName")) { + mDebugExistenceTimer.reset(); LLSD args; + args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32()); args["NAME"] = getFullname(); LLNotificationsUtil::add("AvatarRezArrivedNotification",args); - llinfos << "AVATARREZTIME: Avatar '" << getFullname() << "' arrived." << llendl; + llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' arrived." << llendl; } } if(retval & LLViewerObject::INVALID_UPDATE) @@ -2809,6 +2814,29 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) const BOOL is_muted = isSelf() ? FALSE : LLMuteList::getInstance()->isMuted(getID()); const BOOL is_cloud = getIsCloud(); + if (gSavedSettings.getBOOL("DebugAvatarRezTime")) + { + if (is_appearance != mNameAppearance) + { + if (is_appearance) + { + LLSD args; + args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32()); + args["NAME"] = getFullname(); + LLNotificationsUtil::add("AvatarRezEnteredAppearanceNotification",args); + llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << " entered appearance mode." << llendl; + } + else + { + LLSD args; + args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32()); + args["NAME"] = getFullname(); + LLNotificationsUtil::add("AvatarRezLeftAppearanceNotification",args); + llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << " left appearance mode." << llendl; + } + } + } + if (mNameString.empty() || new_name || (!title && !mTitle.empty()) || @@ -5902,8 +5930,9 @@ void LLVOAvatar::updateRuthTimer(bool loading) mRuthTimer.reset(); if (gSavedSettings.getBOOL("DebugAvatarRezTime")) { - llinfos << "AVATARREZTIME: Avatar '" << getFullname() << "' became cloud." << llendl; + llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' became cloud." << llendl; LLSD args; + args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32()); args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32()); args["NAME"] = getFullname(); LLNotificationsUtil::add("AvatarRezCloudNotification",args); @@ -5941,8 +5970,9 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading) { if (!mPreviousFullyLoaded && !loading && mFullyLoaded) { - llinfos << "AVATARREZTIME: Avatar '" << getFullname() << "' resolved in " << (U32)mRuthDebugTimer.getElapsedTimeF32() << " seconds." << llendl; + llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' resolved in " << (U32)mRuthDebugTimer.getElapsedTimeF32() << " seconds." << llendl; LLSD args; + args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32()); args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32()); args["NAME"] = getFullname(); LLNotificationsUtil::add("AvatarRezNotification",args); diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 24bd2739f7..d74b4fe7b2 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -261,6 +261,7 @@ private: LLFrameTimer mFullyLoadedTimer; LLFrameTimer mRuthTimer; LLFrameTimer mRuthDebugTimer; // For tracking how long it takes for av to rez + LLFrameTimer mDebugExistenceTimer; // Debugging for how long the avatar has been in memory. /** State ** ** diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 6b486a91c5..4b31a1a0ad 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -6123,6 +6123,7 @@ Drag items from inventory onto a person in the resident picker icon="notifytip.tga" name="AvatarRezNotification" type="notifytip"> +( [EXISTENCE] seconds ) Avatar '[NAME]' rezzed in [TIME] seconds. @@ -6130,6 +6131,7 @@ Avatar '[NAME]' rezzed in [TIME] seconds. icon="notifytip.tga" name="AvatarRezCloudNotification" type="notifytip"> +( [EXISTENCE] seconds ) Avatar '[NAME]' became cloud. @@ -6137,6 +6139,7 @@ Avatar '[NAME]' became cloud. icon="notifytip.tga" name="AvatarRezArrivedNotification" type="notifytip"> +( [EXISTENCE] seconds ) Avatar '[NAME]' appeared. @@ -6144,13 +6147,31 @@ Avatar '[NAME]' appeared. icon="notifytip.tga" name="AvatarRezLeftCloudNotification" type="notifytip"> +( [EXISTENCE] seconds ) Avatar '[NAME]' left after [TIME] seconds as cloud. + +( [EXISTENCE] seconds ) +Avatar '[NAME]' entered appearance mode. + + + +( [EXISTENCE] seconds ) +Avatar '[NAME]' left appearance mode. + + +( [EXISTENCE] seconds ) Avatar '[NAME]' left as fully loaded. -- cgit v1.3 From 529a88458ac774932a61e2e41bded3079520a73b Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Tue, 18 May 2010 17:30:31 -0400 Subject: AVP-72 FIXED Debug settings for gathering Welcome Island Rez time statistics Superficial improvements to llinfos messaging. --- indra/newview/llvoavatar.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 46d10b0fe6..0f4623c678 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -764,7 +764,7 @@ LLVOAvatar::~LLVOAvatar() { if (!mFullyLoaded) { - llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << " left after " << (U32)mRuthDebugTimer.getElapsedTimeF32() << " seconds as cloud." << llendl; + llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' left after " << (U32)mRuthDebugTimer.getElapsedTimeF32() << " seconds as cloud." << llendl; LLSD args; args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32()); args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32()); @@ -773,7 +773,7 @@ LLVOAvatar::~LLVOAvatar() } else { - llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << " left." << llendl; + llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' left." << llendl; LLSD args; args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32()); args["NAME"] = getFullname(); @@ -2824,7 +2824,7 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32()); args["NAME"] = getFullname(); LLNotificationsUtil::add("AvatarRezEnteredAppearanceNotification",args); - llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << " entered appearance mode." << llendl; + llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' entered appearance mode." << llendl; } else { @@ -2832,7 +2832,7 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32()); args["NAME"] = getFullname(); LLNotificationsUtil::add("AvatarRezLeftAppearanceNotification",args); - llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << " left appearance mode." << llendl; + llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' left appearance mode." << llendl; } } } -- cgit v1.3 From b9a8e7f4282f521fc382a8ac083eb2375183cfa9 Mon Sep 17 00:00:00 2001 From: "Nyx (Neal Orman)" Date: Tue, 18 May 2010 17:44:24 -0400 Subject: crash prevention for viewer debug logging. Will be code reviewed before push, will be removed before release. -Nyx --- indra/newview/llvoavatarself.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'indra') diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 739c021311..61cae3b64f 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -1766,7 +1766,16 @@ void LLVOAvatarSelf::onTimingLocalTexLoaded(BOOL success, LLViewerFetchedTexture void LLVOAvatarSelf::timingLocalTexLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata) { LLAvatarTexData *data = (LLAvatarTexData *)userdata; + if (!data) + { + return; + } + ETextureIndex index = data->mIndex; + if (index < 0 || index >= TEX_NUM_INDICES) + { + return; + } if (discard_level >=0 && discard_level <= MAX_DISCARD_LEVEL) // ignore discard level -1, as it means we have no data. { -- cgit v1.3 From 263877fec111f77d3116913c440e454cba216c61 Mon Sep 17 00:00:00 2001 From: "Nyx (Neal Orman)" Date: Tue, 18 May 2010 18:03:01 -0400 Subject: FIX: crash on texture load callback timer. After reviewing with bao, re-initialize memory to be NULL after delete to ensure that stray callbacks don't try to use it. --- indra/newview/llvoavatarself.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 79b235afae..695ea68ae8 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -1772,7 +1772,8 @@ void LLVOAvatarSelf::timingLocalTexLoaded(BOOL success, LLViewerFetchedTexture * } ETextureIndex index = data->mIndex; - if (index < 0 || index >= TEX_NUM_INDICES) + +if (index < 0 || index >= TEX_NUM_INDICES) { return; } @@ -1784,6 +1785,8 @@ void LLVOAvatarSelf::timingLocalTexLoaded(BOOL success, LLViewerFetchedTexture * if (final) { delete data; + // for debugging, apparently there is a case in which we are keeping old de-allocated structures around in callbacks + *data = NULL; } } -- cgit v1.3 From c70be25eca32ff3c8bab9af060b8e5c8cd684344 Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Tue, 18 May 2010 18:28:23 -0400 Subject: Fix for build error (not from my code ;-) ). --- indra/newview/llvoavatarself.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 695ea68ae8..8bf082ad45 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -1785,8 +1785,6 @@ if (index < 0 || index >= TEX_NUM_INDICES) if (final) { delete data; - // for debugging, apparently there is a case in which we are keeping old de-allocated structures around in callbacks - *data = NULL; } } -- cgit v1.3 From 75755a675120002fd7cbc3dc324bd8b50a9dba23 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Tue, 18 May 2010 15:56:42 -0700 Subject: Add missing debug grids --- indra/newview/llviewernetwork.cpp | 26 +++++++++++++++++++++++++- indra/newview/tests/llviewernetwork_test.cpp | 8 ++++---- 2 files changed, 29 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index a160572f7a..7812867667 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -108,6 +108,26 @@ void LLGridManager::initialize(const std::string& grid_file) "https://login.aruna.lindenlab.com/cgi-bin/login.cgi", "http://aruna-secondlife.webdev.lindenlab.com/helpers/", DEFAULT_LOGIN_PAGE); + addSystemGrid("Bharati", + "util.bharati.lindenlab.com", + "https://login.bharati.lindenlab.com/cgi-bin/login.cgi", + "http://bharati-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Chandra", + "util.chandra.lindenlab.com", + "https://login.chandra.lindenlab.com/cgi-bin/login.cgi", + "http://chandra-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Damballah", + "util.damballah.lindenlab.com", + "https://login.damballah.lindenlab.com/cgi-bin/login.cgi", + "http://damballah-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); + addSystemGrid("Danu", + "util.danu.lindenlab.com", + "https://login.danu.lindenlab.com/cgi-bin/login.cgi", + "http://danu-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); addSystemGrid("Durga", "util.durga.lindenlab.com", "https://login.durga.lindenlab.com/cgi-bin/login.cgi", @@ -133,6 +153,11 @@ void LLGridManager::initialize(const std::string& grid_file) "https://login.nandi.lindenlab.com/cgi-bin/login.cgi", "http://nandi-secondlife.webdev.lindenlab.com/helpers/", DEFAULT_LOGIN_PAGE); + addSystemGrid("Parvati", + "util.parvati.lindenlab.com", + "https://login.parvati.lindenlab.com/cgi-bin/login.cgi", + "http://parvati-secondlife.webdev.lindenlab.com/helpers/", + DEFAULT_LOGIN_PAGE); addSystemGrid("Radha", "util.radha.lindenlab.com", "https://login.radha.lindenlab.com/cgi-bin/login.cgi", @@ -158,7 +183,6 @@ void LLGridManager::initialize(const std::string& grid_file) "https://login.soma.lindenlab.com/cgi-bin/login.cgi", "http://soma-secondlife.webdev.lindenlab.com/helpers/", DEFAULT_LOGIN_PAGE); - addSystemGrid("Uma", "util.uma.lindenlab.com", "https://login.uma.lindenlab.com/cgi-bin/login.cgi", diff --git a/indra/newview/tests/llviewernetwork_test.cpp b/indra/newview/tests/llviewernetwork_test.cpp index 025b570be2..d819b44564 100644 --- a/indra/newview/tests/llviewernetwork_test.cpp +++ b/indra/newview/tests/llviewernetwork_test.cpp @@ -143,7 +143,7 @@ namespace tut manager->initialize("grid_test.xml"); // validate that some of the defaults are available. std::map known_grids = manager->getKnownGrids(); - ensure_equals("Known grids is a string-string map of size 18", known_grids.size(), 18); + ensure_equals("Known grids is a string-string map of size 23", known_grids.size(), 23); ensure_equals("Agni has the right name and label", known_grids[std::string("util.agni.lindenlab.com")], std::string("Agni")); ensure_equals("None exists", known_grids[""], "None"); @@ -184,7 +184,7 @@ namespace tut LLGridManager::getInstance()->initialize("grid_test.xml"); std::map known_grids = LLGridManager::getInstance()->getKnownGrids(); ensure_equals("adding a grid via a grid file increases known grid size", - known_grids.size(), 19); + known_grids.size(), 24); ensure_equals("Agni is still there after we've added a grid via a grid file", known_grids["util.agni.lindenlab.com"], std::string("Agni")); @@ -250,7 +250,7 @@ namespace tut // with single login uri specified. std::map known_grids = LLGridManager::getInstance()->getKnownGrids(); ensure_equals("adding a command line grid increases known grid size", - known_grids.size(), 19); + known_grids.size(), 24); ensure_equals("Command line grid is added to the list of grids", known_grids["my.login.uri"], std::string("my.login.uri")); LLSD grid = LLGridManager::getInstance()->getGridInfo("my.login.uri"); @@ -279,7 +279,7 @@ namespace tut LLGridManager::getInstance()->initialize("grid_test.xml"); known_grids = LLGridManager::getInstance()->getKnownGrids(); ensure_equals("adding a command line grid with custom name increases known grid size", - known_grids.size(), 19); + known_grids.size(), 24); ensure_equals("Custom Command line grid is added to the list of grids", known_grids["mycustomgridchoice"], std::string("mycustomgridchoice")); grid = LLGridManager::getInstance()->getGridInfo("mycustomgridchoice"); -- cgit v1.3 From 5921f2c14b57bbc4c5c327713a521e270b250f52 Mon Sep 17 00:00:00 2001 From: Richard Linden Date: Tue, 18 May 2010 16:26:18 -0700 Subject: EXT-7398 FIXED negative top/bottom coordinates do the wrong thing with topleft layout reviewed by Leyla --- indra/llui/llfloater.cpp | 9 ++++ indra/llui/llfloater.h | 4 +- indra/llui/llview.cpp | 58 +++------------------- indra/llui/llview.h | 3 -- .../skins/default/xui/en/floater_camera.xml | 1 - .../skins/default/xui/en/floater_moveview.xml | 1 - .../skins/default/xui/en/floater_preferences.xml | 3 +- .../default/xui/en/floater_preview_gesture.xml | 2 +- .../skins/default/xui/en/floater_test_widgets.xml | 3 -- .../newview/skins/default/xui/en/floater_tools.xml | 2 +- .../skins/default/xui/en/floater_world_map.xml | 3 +- .../skins/default/xui/en/panel_bottomtray.xml | 4 +- .../default/xui/en/panel_notifications_channel.xml | 4 +- 13 files changed, 28 insertions(+), 69 deletions(-) (limited to 'indra') diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 79c47a1136..6babaf936b 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -169,6 +169,7 @@ LLFloater::Params::Params() save_rect("save_rect", false), save_visibility("save_visibility", false), can_dock("can_dock", false), + open_centered("open_centered", false), header_height("header_height", 0), legacy_header_height("legacy_header_height", 0), close_image("close_image"), @@ -763,6 +764,13 @@ void LLFloater::applySavedVariables() void LLFloater::applyRectControl() { + // first, center on screen if requested + if (mOpenCentered) + { + center(); + } + + // override center if we have saved rect control if (mRectControl.size() > 1) { const LLRect& rect = LLUI::sSettingGroups["floater"]->getRect(mRectControl); @@ -2711,6 +2719,7 @@ void LLFloater::initFromParams(const LLFloater::Params& p) mLegacyHeaderHeight = p.legacy_header_height; mSingleInstance = p.single_instance; mAutoTile = p.auto_tile; + mOpenCentered = p.open_centered; if (p.save_rect) { diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 444711de81..654164ddc0 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -110,7 +110,8 @@ public: save_rect, save_visibility, save_dock_state, - can_dock; + can_dock, + open_centered; Optional header_height, legacy_header_height; // HACK see initFromXML() @@ -373,6 +374,7 @@ private: BOOL mCanClose; BOOL mDragOnLeft; BOOL mResizable; + bool mOpenCentered; S32 mMinWidth; S32 mMinHeight; diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index e67f0ec3fc..c091686ffb 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -107,8 +107,6 @@ LLView::Params::Params() top_delta("top_delta", S32_MAX), left_pad("left_pad"), left_delta("left_delta", S32_MAX), - center_horiz("center_horiz", false), - center_vert("center_vert", false), from_xui("from_xui", false), user_resize("user_resize"), auto_resize("auto_resize"), @@ -2501,7 +2499,6 @@ void LLView::applyXUILayout(LLView::Params& p, LLView* parent) p.layout = parent->getLayout(); } - if (parent) { LLRect parent_rect = parent->getLocalRect(); @@ -2509,60 +2506,21 @@ void LLView::applyXUILayout(LLView::Params& p, LLView* parent) LLRect last_rect = parent->getLocalRect(); bool layout_topleft = (p.layout() == "topleft"); - if (layout_topleft) - { - //invert top to bottom - if (p.rect.top.isProvided()) p.rect.top = parent_rect.getHeight() - p.rect.top; - if (p.rect.bottom.isProvided()) p.rect.bottom = parent_rect.getHeight() - p.rect.bottom; - } // convert negative or centered coordinates to parent relative values // Note: some of this logic matches the logic in TypedParam::setValueFromBlock() + if (p.rect.left.isProvided() && p.rect.left < 0) p.rect.left = p.rect.left + parent_rect.getWidth(); + if (p.rect.right.isProvided() && p.rect.right < 0) p.rect.right = p.rect.right + parent_rect.getWidth(); + if (p.rect.bottom.isProvided() && p.rect.bottom < 0) p.rect.bottom = p.rect.bottom + parent_rect.getHeight(); + if (p.rect.top.isProvided() && p.rect.top < 0) p.rect.top = p.rect.top + parent_rect.getHeight(); - if (p.center_horiz) - { - if (p.rect.left.isProvided() && p.rect.right.isProvided()) - { - S32 width = p.rect.right - p.rect.left; - width = llmax(width, 0); - S32 offset = parent_rect.getWidth()/2 - width/2; - p.rect.left = p.rect.left + offset; - p.rect.right = p.rect.right + offset; - } - else - { - p.rect.left = p.rect.left + parent_rect.getWidth()/2 - p.rect.width/2; - p.rect.right.setProvided(false); // recalculate the right - } - } - else - { - if (p.rect.left.isProvided() && p.rect.left < 0) p.rect.left = p.rect.left + parent_rect.getWidth(); - if (p.rect.right.isProvided() && p.rect.right < 0) p.rect.right = p.rect.right + parent_rect.getWidth(); - } - if (p.center_vert) - { - if (p.rect.bottom.isProvided() && p.rect.top.isProvided()) - { - S32 height = p.rect.top - p.rect.bottom; - height = llmax(height, 0); - S32 offset = parent_rect.getHeight()/2 - height/2; - p.rect.bottom = p.rect.bottom + offset; - p.rect.top = p.rect.top + offset; - } - else - { - p.rect.bottom = p.rect.bottom + parent_rect.getHeight()/2 - p.rect.height/2; - p.rect.top.setProvided(false); // recalculate the top - } - } - else + if (layout_topleft) { - if (p.rect.bottom.isProvided() && p.rect.bottom < 0) p.rect.bottom = p.rect.bottom + parent_rect.getHeight(); - if (p.rect.top.isProvided() && p.rect.top < 0) p.rect.top = p.rect.top + parent_rect.getHeight(); + //invert top to bottom + if (p.rect.top.isProvided()) p.rect.top = parent_rect.getHeight() - p.rect.top; + if (p.rect.bottom.isProvided()) p.rect.bottom = parent_rect.getHeight() - p.rect.bottom; } - // DEPRECATE: automatically fall back to height of MIN_WIDGET_HEIGHT pixels if (!p.rect.height.isProvided() && !p.rect.top.isProvided() && p.rect.height == 0) { diff --git a/indra/llui/llview.h b/indra/llui/llview.h index efae00f0e5..3779fedf34 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -143,9 +143,6 @@ public: left_pad, // from last right to my left left_delta; // from last left to my left - Optional center_horiz, - center_vert; - // these are nested attributes for LLLayoutPanel //FIXME: get parent context involved in parsing traversal Ignored user_resize, diff --git a/indra/newview/skins/default/xui/en/floater_camera.xml b/indra/newview/skins/default/xui/en/floater_camera.xml index b45e39a111..999cb9ed97 100644 --- a/indra/newview/skins/default/xui/en/floater_camera.xml +++ b/indra/newview/skins/default/xui/en/floater_camera.xml @@ -4,7 +4,6 @@ can_dock="true" can_minimize="true" can_close="false" - center_horiz="true" follows="bottom" height="152" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/floater_moveview.xml b/indra/newview/skins/default/xui/en/floater_moveview.xml index a685094190..7d9a17548e 100644 --- a/indra/newview/skins/default/xui/en/floater_moveview.xml +++ b/indra/newview/skins/default/xui/en/floater_moveview.xml @@ -4,7 +4,6 @@ can_dock="true" can_minimize="true" can_close="false" - center_horiz="true" follows="bottom" height="110" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/floater_preferences.xml b/indra/newview/skins/default/xui/en/floater_preferences.xml index 6e82bb09ee..50d0011338 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences.xml @@ -1,8 +1,7 @@ + + + + Date: Wed, 19 May 2010 15:26:05 +0300 Subject: EXT-7320 FIXED Moved date time stamp template to localized xml. reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/406/ --HG-- branch : product-engine --- indra/newview/llfloaterland.cpp | 7 +++++-- indra/newview/skins/default/xui/en/floater_about_land.xml | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 256796aa80..abdb55ec17 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -646,9 +646,12 @@ void LLPanelLandGeneral::refresh() } // Display claim date - // *TODO:Localize (Time format may need Translating) time_t claim_date = parcel->getClaimDate(); - mTextClaimDate->setText(formatted_time(claim_date)); + std::string claim_date_str = getString("time_stamp_template"); + LLSD substitution; + substitution["datetime"] = (S32) claim_date; + LLStringUtil::format (claim_date_str, substitution); + mTextClaimDate->setText(claim_date_str); mTextClaimDate->setEnabled(is_leased); BOOL enable_auction = (gAgent.getGodLevel() >= GOD_LIAISON) diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 40c6b14a4a..20e7c28db0 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -108,6 +108,9 @@ name="no_selection_text"> No parcel selected. + + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + Date: Wed, 19 May 2010 15:32:25 +0300 Subject: EXT-7104 WIP Implemented reaction on Sidebar button click Implementation details: * Added new setting SidebarWithButtonsVisibility * Sidebar bottom tray button changes its value on click (toggle state - value is TRUE) * SideTray listens changes of this setting's value and change its and its buttons visibility * expand/collapse Sidebar state is stored between changes of the setting. Reviewed by Yuri Chebotarev at https://codereview.productengine.com/secondlife/r/403/ --HG-- branch : product-engine --- indra/newview/app_settings/settings.xml | 11 ++++ indra/newview/llsidetray.cpp | 59 ++++++++++++++++++++-- indra/newview/llsidetray.h | 10 ++++ .../skins/default/xui/en/panel_bottomtray.xml | 5 +- 4 files changed, 76 insertions(+), 9 deletions(-) (limited to 'indra') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index cae4a14eed..7156af57ec 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8682,6 +8682,17 @@ Value 0 + SidebarWithButtonsVisibility + + Comment + Sets visibility of sidebar with its tabs' buttons + Persist + 1 + Type + Boolean + Value + 1 + SkinCurrent Comment diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp index 3ec1855484..9159f42968 100644 --- a/indra/newview/llsidetray.cpp +++ b/indra/newview/llsidetray.cpp @@ -66,6 +66,21 @@ static const std::string TAB_PANEL_CAPTION_TITLE_BOX = "sidetray_tab_title"; LLSideTray* LLSideTray::sInstance = 0; +/** + * Updates visibility of sidetray tabs buttons according to "SidebarWithButtonsVisibility" setting + * + * @param force_set_visible if true method ignores setting value and set buttons visible. + */ +static void update_tabs_buttons_visibility(bool force_set_visible = false) +{ + LLView* side_bar_tabs = gViewerWindow->getRootView()->getChildView("side_bar_tabs"); + if (side_bar_tabs) + { + BOOL visible = LLUI::sSettingGroups["config"]->getBOOL("SidebarWithButtonsVisibility"); + side_bar_tabs->setVisible(force_set_visible || visible); + } +} + LLSideTray* LLSideTray::getInstance() { if (!sInstance) @@ -258,6 +273,8 @@ LLSideTray::LLSideTray(Params& params) p.name = "buttons_panel"; p.mouse_opaque = false; mButtonsPanel = LLUICtrlFactory::create(p); + + initControlSettings(); } @@ -547,6 +564,7 @@ void LLSideTray::collapseSideBar() reflectCollapseChange(); setFocus( FALSE ); + update_tabs_buttons_visibility(); } void LLSideTray::expandSideBar() @@ -572,6 +590,7 @@ void LLSideTray::expandSideBar() btn->setImageOverlay( mActiveTab->mImageSelected ); } + update_tabs_buttons_visibility(true); } void LLSideTray::highlightFocused() @@ -638,6 +657,9 @@ LLPanel* LLSideTray::showPanel (const std::string& panel_name, const LLSD& para { panel->onOpen(params); } + + update_tabs_buttons_visibility(true); + return panel; } } @@ -720,11 +742,6 @@ bool LLSideTray::isPanelActive(const std::string& panel_name) return (panel->getName() == panel_name); } - -// *TODO: Eliminate magic constants. -static const S32 fake_offset = 132; -static const S32 fake_top_offset = 18; - void LLSideTray::updateSidetrayVisibility() { // set visibility of parent container based on collapsed state @@ -734,3 +751,35 @@ void LLSideTray::updateSidetrayVisibility() } } +void LLSideTray::initControlSettings() +{ + // set listeners to process runtime setting changes + LLUI::sSettingGroups["config"]->getControl("SidebarWithButtonsVisibility")->getSignal()->connect(boost::bind(&LLSideTray::toggleSidetrayAndTabButtonsVisibility, this, _2)); + + // update visibility according to current value + toggleSidetrayAndTabButtonsVisibility(LLUI::sSettingGroups["config"]->getBOOL("SidebarWithButtonsVisibility")); +} + +// sidebar visibility is implemented via its expanding/collapsing +void LLSideTray::toggleSidetrayAndTabButtonsVisibility(const LLSD::Boolean& new_visibility) +{ + // If new_visibility==FALSE it gets invisible but still can be expanded in other ways (Ctrl+I to see My Inventory) + + // store collapsed state to restore it properly on next call + static bool was_collapsed = false; + + if (!new_visibility && !mCollapsed) + { + collapseSideBar(); + was_collapsed = true; + } + // should be visible: expand only if it was expanded when has been collapsed on previous call + else if (new_visibility && was_collapsed) + { + if (mCollapsed) expandSideBar(); + was_collapsed = false; + } + + update_tabs_buttons_visibility(new_visibility); +} + diff --git a/indra/newview/llsidetray.h b/indra/newview/llsidetray.h index e8fdee9430..ed6b376d5c 100644 --- a/indra/newview/llsidetray.h +++ b/indra/newview/llsidetray.h @@ -179,6 +179,16 @@ private: LLSideTray::getInstance()->setEnabled(FALSE); } + /** + * Initializes listener of SidebarWithButtonsVisibility setting and updates state according to it. + */ + void initControlSettings(); + + /** + * Updates Sidebar and its Tab Buttons visibility according to passed value. + */ + void toggleSidetrayAndTabButtonsVisibility(const LLSD::Boolean& new_visibility); + private: LLPanel* mButtonsPanel; diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index dd813f5813..2ba7bef502 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -228,7 +228,7 @@ Disabled for now. --> Date: Wed, 19 May 2010 17:55:58 +0300 Subject: EXT-7157 FIXED Implemented outfit items sorting in "My Outfits" tab. - Moved wearable item name comparator from llcofwearables to llwearableitemslist. - Added wearable item type comparator. - Added item type and description getters to wearable item base class LLPanelInventoryListItemBase. Reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/407/ --HG-- branch : product-engine --- indra/llui/llflatlistview.cpp | 2 +- indra/newview/llcofwearables.cpp | 12 ----- indra/newview/llcofwearables.h | 57 +++------------------- indra/newview/llinventoryitemslist.h | 9 ++++ indra/newview/llwearableitemslist.cpp | 82 ++++++++++++++++++++++++++++++- indra/newview/llwearableitemslist.h | 92 +++++++++++++++++++++++++++++++++++ 6 files changed, 190 insertions(+), 64 deletions(-) (limited to 'indra') diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index 6bd16c9ee6..2433c14315 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -1285,7 +1285,7 @@ void LLFlatListViewEx::filterItems() } } - rearrangeItems(); + sort(); notifyParentItemsRectChanged(); } diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp index 8d4430a9ea..7c4ceb3458 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -49,18 +49,6 @@ const LLSD REARRANGE = LLSD().with("rearrange", LLSD()); static const LLWearableItemNameComparator WEARABLE_NAME_COMPARATOR; -bool LLWearableItemNameComparator::doCompare(const LLPanelWearableListItem* wearable_item1, const LLPanelWearableListItem* wearable_item2) const -{ - std::string name1 = wearable_item1->getItemName(); - std::string name2 = wearable_item2->getItemName(); - - LLStringUtil::toUpper(name1); - LLStringUtil::toUpper(name2); - - return name1 < name2; -} - - LLCOFWearables::LLCOFWearables() : LLPanel(), mAttachments(NULL), mClothing(NULL), diff --git a/indra/newview/llcofwearables.h b/indra/newview/llcofwearables.h index 612bb103d2..583ee96247 100644 --- a/indra/newview/llcofwearables.h +++ b/indra/newview/llcofwearables.h @@ -33,59 +33,16 @@ #ifndef LL_LLCOFWEARABLES_H #define LL_LLCOFWEARABLES_H +// llui +#include "llflatlistview.h" #include "llpanel.h" -#include "llinventorymodel.h" -#include "llappearancemgr.h" -#include "llwearableitemslist.h" - -class LLFlatListView; - - -/** Abstract comparator of wearable list items */ -class LLWearableListItemComparator : public LLFlatListView::ItemComparator -{ - LOG_CLASS(LLWearableListItemComparator); - -public: - LLWearableListItemComparator() {}; - virtual ~LLWearableListItemComparator() {}; - - virtual bool compare(const LLPanel* item1, const LLPanel* item2) const - { - const LLPanelWearableListItem* wearable_item1 = dynamic_cast(item1); - const LLPanelWearableListItem* wearable_item2 = dynamic_cast(item2); - - if (!wearable_item1 || !wearable_item2) - { - llwarning("item1 and item2 cannot be null", 0); - return true; - } - - return doCompare(wearable_item1, wearable_item2); - } -protected: - - /** - * Returns true if wearable_item1 < wearable_item2, false otherwise - * Implement this method in your particular comparator. - */ - virtual bool doCompare(const LLPanelWearableListItem* wearable_item1, const LLPanelWearableListItem* wearable_item2) const = 0; -}; - - -class LLWearableItemNameComparator : public LLWearableListItemComparator -{ - LOG_CLASS(LLWearableItemNameComparator); - -public: - LLWearableItemNameComparator() {}; - virtual ~LLWearableItemNameComparator() {}; - -protected: - virtual bool doCompare(const LLPanelWearableListItem* wearable_item1, const LLPanelWearableListItem* wearable_item2) const; -}; +#include "llappearancemgr.h" +#include "llinventorymodel.h" +class LLPanelClothingListItem; +class LLPanelBodyPartsListItem; +class LLPanelDeletableWearableListItem; /** * Adaptor between LLAccordionCtrlTab and LLFlatListView to facilitate communication between them diff --git a/indra/newview/llinventoryitemslist.h b/indra/newview/llinventoryitemslist.h index a3863b511c..807952948b 100644 --- a/indra/newview/llinventoryitemslist.h +++ b/indra/newview/llinventoryitemslist.h @@ -124,6 +124,15 @@ public: /** Get the name of a corresponding inventory item */ const std::string& getItemName() const { return mItem->getName(); } + /** Get the asset type of a corresponding inventory item */ + LLAssetType::EType getType() const { return mItem->getType(); } + + /** Get the wearable type of a corresponding inventory item */ + LLWearableType::EType getWearableType() const { return mItem->getWearableType(); } + + /** Get the description of a corresponding inventory item */ + const std::string& getDescription() const { return mItem->getDescription(); } + virtual ~LLPanelInventoryListItemBase(){} protected: diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index bac66d966a..b209dfecce 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -296,6 +296,84 @@ std::string LLPanelDummyClothingListItem::wearableTypeToString(LLWearableType::E ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// +/*virtual*/ +bool LLWearableItemNameComparator::doCompare(const LLPanelInventoryListItemBase* wearable_item1, const LLPanelInventoryListItemBase* wearable_item2) const +{ + std::string name1 = wearable_item1->getItemName(); + std::string name2 = wearable_item2->getItemName(); + + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); + + return name1 < name2; +} + +/*virtual*/ +bool LLWearableItemTypeNameComparator::doCompare(const LLPanelInventoryListItemBase* wearable_item1, const LLPanelInventoryListItemBase* wearable_item2) const +{ + const LLAssetType::EType item_type1 = wearable_item1->getType(); + const LLAssetType::EType item_type2 = wearable_item2->getType(); + + LLWearableItemTypeNameComparator::ETypeListOrder item_type_order1 = getTypeListOrder(item_type1); + LLWearableItemTypeNameComparator::ETypeListOrder item_type_order2 = getTypeListOrder(item_type2); + + if (item_type_order1 != item_type_order2) + { + // If items are of different asset types we can compare them + // by types order in the list. + return item_type_order1 < item_type_order2; + } + + if (item_type_order1 & TLO_NOT_CLOTHING) + { + // If both items are of the same asset type except AT_CLOTHING + // we can compare them by name. + return LLWearableItemNameComparator::doCompare(wearable_item1, wearable_item2); + } + + const LLWearableType::EType item_wearable_type1 = wearable_item1->getWearableType(); + const LLWearableType::EType item_wearable_type2 = wearable_item2->getWearableType(); + + if (item_wearable_type1 != item_wearable_type2) + { + // If items are of different clothing types they are compared + // by clothing types order determined in LLWearableType::EType. + return item_wearable_type1 < item_wearable_type2; + } + else + { + // If both items are of the same clothing type they are compared + // by description and place in reverse order i.e. outer layer item + // on top. + return wearable_item1->getDescription() > wearable_item2->getDescription(); + } +} + +// static +LLWearableItemTypeNameComparator::ETypeListOrder LLWearableItemTypeNameComparator::getTypeListOrder(LLAssetType::EType item_type) +{ + switch (item_type) + { + case LLAssetType::AT_OBJECT: + return TLO_ATTACHMENT; + + case LLAssetType::AT_CLOTHING: + return TLO_CLOTHING; + + case LLAssetType::AT_BODYPART: + return TLO_BODYPART; + + default: + return TLO_UNKNOWN; + } +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +static const LLWearableItemTypeNameComparator WEARABLE_TYPE_NAME_COMPARATOR; + static const LLDefaultChildRegistry::Register r("wearable_items_list"); LLWearableItemsList::Params::Params() @@ -303,7 +381,9 @@ LLWearableItemsList::Params::Params() LLWearableItemsList::LLWearableItemsList(const LLWearableItemsList::Params& p) : LLInventoryItemsList(p) -{} +{ + setComparator(&WEARABLE_TYPE_NAME_COMPARATOR); +} // virtual LLWearableItemsList::~LLWearableItemsList() diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index 5e3202c687..2cab5a07a2 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -171,6 +171,98 @@ private: LLWearableType::EType mWearableType; }; +/** + * @class LLWearableListItemComparator + * + * Abstract comparator of wearable list items. + */ +class LLWearableListItemComparator : public LLFlatListView::ItemComparator +{ + LOG_CLASS(LLWearableListItemComparator); + +public: + LLWearableListItemComparator() {}; + virtual ~LLWearableListItemComparator() {}; + + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const + { + const LLPanelInventoryListItemBase* wearable_item1 = dynamic_cast(item1); + const LLPanelInventoryListItemBase* wearable_item2 = dynamic_cast(item2); + + if (!wearable_item1 || !wearable_item2) + { + llwarning("item1 and item2 cannot be null", 0); + return true; + } + + return doCompare(wearable_item1, wearable_item2); + } + +protected: + + /** + * Returns true if wearable_item1 < wearable_item2, false otherwise + * Implement this method in your particular comparator. + */ + virtual bool doCompare(const LLPanelInventoryListItemBase* wearable_item1, const LLPanelInventoryListItemBase* wearable_item2) const = 0; +}; + +/** + * @class LLWearableItemNameComparator + * + * Comparator for sorting wearable list items by name. + */ +class LLWearableItemNameComparator : public LLWearableListItemComparator +{ + LOG_CLASS(LLWearableItemNameComparator); + +public: + LLWearableItemNameComparator() {}; + virtual ~LLWearableItemNameComparator() {}; + +protected: + /*virtual*/ bool doCompare(const LLPanelInventoryListItemBase* wearable_item1, const LLPanelInventoryListItemBase* wearable_item2) const; +}; + +/** + * @class LLWearableItemTypeNameComparator + * + * Comparator for sorting wearable list items by type and name. + */ +class LLWearableItemTypeNameComparator : public LLWearableItemNameComparator +{ + LOG_CLASS(LLWearableItemTypeNameComparator); + +public: + LLWearableItemTypeNameComparator() {}; + virtual ~LLWearableItemTypeNameComparator() {}; + +protected: + /** + * Returns "true" if wearable_item1 is placed before wearable_item2 sorted by the following: + * - Attachments (abc order) + * - Clothing + * - by type (types order determined in LLWearableType::EType) + * - outer layer on top + * - Body Parts (abc order), + * "false" otherwise. + */ + /*virtual*/ bool doCompare(const LLPanelInventoryListItemBase* wearable_item1, const LLPanelInventoryListItemBase* wearable_item2) const; + +private: + enum ETypeListOrder + { + TLO_ATTACHMENT = 0x01, + TLO_CLOTHING = 0x02, + TLO_BODYPART = 0x04, + TLO_UNKNOWN = 0x08, + + TLO_NOT_CLOTHING = TLO_ATTACHMENT | TLO_BODYPART | TLO_UNKNOWN + }; + + static LLWearableItemTypeNameComparator::ETypeListOrder getTypeListOrder(LLAssetType::EType item_type); +}; + /** * @class LLWearableItemsList * -- cgit v1.3 From be0662234565f772fc4c89e47400dc9df8277800 Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Wed, 19 May 2010 16:25:52 +0100 Subject: EXT-7337 WIP Voice font previewing Added LLVoiceEffectInterface::getVoiceEffectProperties Added LLVivoxVoiceClient::getVoiceFontTemplateIndex --- indra/newview/llvoicevivox.cpp | 50 +++++++++++++++++++++++++++++++++++++++++- indra/newview/llvoicevivox.h | 2 ++ 2 files changed, 51 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index ee67879d3d..2df9152a17 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -6378,6 +6378,36 @@ const LLUUID LLVivoxVoiceClient::getVoiceEffect() return mAudioSession ? mAudioSession->mVoiceFontID : LLUUID::null; } +LLSD LLVivoxVoiceClient::getVoiceEffectProperties(const LLUUID& id) +{ + LLSD sd; + + voice_font_map_t::iterator iter = mVoiceFontMap.find(id); + if (iter != mVoiceFontMap.end()) + { + sd["template_only"] = false; + } + else + { + // Voice effect is not in the voice font map, see if there is a template + iter = mVoiceFontTemplateMap.find(id); + if (iter == mVoiceFontTemplateMap.end()) + { + LL_WARNS("Voice") << "Voice effect " << id << "not found." << LL_ENDL; + return sd; + } + sd["template_only"] = true; + } + + voiceFontEntry *font = iter->second; + sd["name"] = font->mName; + sd["expired"] = font->mHasExpired; + sd["expiry_date"] = font->mExpirationDate; + sd["is_new"] = font->mIsNew; + + return sd; +} + LLVivoxVoiceClient::voiceFontEntry::voiceFontEntry(LLUUID& id) : mID(id), mFontIndex(0), @@ -6536,6 +6566,24 @@ S32 LLVivoxVoiceClient::getVoiceFontIndex(const LLUUID& id) const return result; } +S32 LLVivoxVoiceClient::getVoiceFontTemplateIndex(const LLUUID& id) const +{ + S32 result = 0; + if (!id.isNull()) + { + voice_font_map_t::const_iterator it = mVoiceFontTemplateMap.find(id); + if (it != mVoiceFontTemplateMap.end()) + { + result = it->second->mFontIndex; + } + else + { + LL_DEBUGS("Voice") << "Selected voice font template " << id << " is not available." << LL_ENDL; + } + } + return result; +} + void LLVivoxVoiceClient::accountGetSessionFontsSendMessage() { if(!mAccountHandle.empty()) @@ -6753,7 +6801,7 @@ void LLVivoxVoiceClient::captureBufferPlayStartSendMessage(const LLUUID& voice_f LL_DEBUGS("Voice") << "Starting audio buffer playback." << LL_ENDL; - S32 font_index = getVoiceFontIndex(voice_font_id); + S32 font_index = getVoiceFontTemplateIndex(voice_font_id); LL_DEBUGS("Voice") << "With voice font: " << voice_font_id << " (" << font_index << ")" << LL_ENDL; stream diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 6c302962a3..1047d2e5ed 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -240,6 +240,7 @@ public: //@{ virtual bool setVoiceEffect(const LLUUID& id); virtual const LLUUID getVoiceEffect(); + virtual LLSD getVoiceEffectProperties(const LLUUID& id); virtual void refreshVoiceEffectLists(bool clear_lists); virtual const voice_effect_list_t& getVoiceEffectList() const; @@ -859,6 +860,7 @@ private: void deleteVoiceFontTemplates(); S32 getVoiceFontIndex(const LLUUID& id) const; + S32 getVoiceFontTemplateIndex(const LLUUID& id) const; void accountGetSessionFontsSendMessage(); void accountGetTemplateFontsSendMessage(); -- cgit v1.3 From 95ed59a9dec737ffe8d566595e1a74f662fe1e43 Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Wed, 19 May 2010 16:28:03 +0100 Subject: EXT-7337 WIP Added template voice fonts to the voice font list for previewing --- indra/newview/llfloatervoiceeffect.cpp | 91 ++++++++++++++++++---- indra/newview/llvoiceclient.h | 1 + .../skins/default/xui/en/floater_voice_effect.xml | 89 ++++++++++++++++----- 3 files changed, 150 insertions(+), 31 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloatervoiceeffect.cpp b/indra/newview/llfloatervoiceeffect.cpp index 5a579f5aeb..de12e8d12a 100644 --- a/indra/newview/llfloatervoiceeffect.cpp +++ b/indra/newview/llfloatervoiceeffect.cpp @@ -99,29 +99,94 @@ void LLFloaterVoiceEffect::update() return; } + LL_DEBUGS("Voice")<< "Rebuilding voice effect list."<< LL_ENDL; + + // Preserve selected items and scroll position + S32 scroll_pos = mVoiceEffectList->getScrollPos(); + uuid_vec_t selected_items; + std::vector items = mVoiceEffectList->getAllSelected(); + for(std::vector::const_iterator it = items.begin(); it != items.end(); it++) + { + selected_items.push_back((*it)->getUUID()); + } + mVoiceEffectList->deleteAllItems(); - LLSD element; - element["id"] = LLUUID::null; - element["columns"][1]["column"] = "name"; - element["columns"][1]["value"] = getString("no_voice_effect"); - mVoiceEffectList->addElement(element, ADD_BOTTOM); - - const voice_effect_list_t& effect_list = effect_interface->getVoiceEffectList(); - if (!effect_list.empty()) + { - for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it) + // Add the "No Voice Effect" entry + LLSD element; + + element["id"] = LLUUID::null; + element["columns"][0]["column"] = "name"; + element["columns"][0]["value"] = getString("no_voice_effect"); + element["columns"][0]["font"]["name"] = "SANSSERIF"; + element["columns"][0]["font"]["style"] = "BOLD"; + + LLScrollListItem* sl_item = mVoiceEffectList->addElement(element, ADD_BOTTOM); + // *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :( + if(sl_item) { + ((LLScrollListText*)sl_item->getColumn(0))->setFontStyle(LLFontGL::BOLD); + } + } + + const voice_effect_list_t& template_list = effect_interface->getVoiceEffectTemplateList(); + if (!template_list.empty()) + { + for (voice_effect_list_t::const_iterator it = template_list.begin(); it != template_list.end(); ++it) + { + const LLUUID& effect_id = it->second; + std::string effect_name = it->first; + + LLSD effect_properties = effect_interface->getVoiceEffectProperties(effect_id); + bool is_template_only = effect_properties["template_only"].asBoolean(); + bool is_new = effect_properties["is_new"].asBoolean(); + std::string expiry_date = effect_properties["expiry_date"].asString(); + + std::string font_style = "NORMAL"; + if (!is_template_only) + { + font_style = "BOLD"; + } LLSD element; - element["id"] = it->second; - element["columns"][1]["column"] = "name"; - element["columns"][1]["value"] = it->first; - mVoiceEffectList->addElement(element, ADD_BOTTOM); + element["id"] = effect_id; + + element["columns"][0]["column"] = "name"; + element["columns"][0]["value"] = effect_name; + element["columns"][0]["font"]["name"] = "SANSSERIF"; + element["columns"][0]["font"]["style"] = font_style; + element["columns"][1]["column"] = "new"; + element["columns"][1]["value"] = is_new ? getString("new_voice_effect") : ""; + element["columns"][1]["font"]["name"] = "SANSSERIF"; + element["columns"][1]["font"]["style"] = font_style; + + element["columns"][2]["column"] = "expires"; + element["columns"][2]["value"] = !is_template_only ? expiry_date : ""; + element["columns"][2]["font"]["name"] = "SANSSERIF"; + element["columns"][2]["font"]["style"] = font_style; + + LLScrollListItem* sl_item = mVoiceEffectList->addElement(element, ADD_BOTTOM); + // *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :( + if(sl_item) + { + LLFontGL::StyleFlags style = is_template_only ? LLFontGL::NORMAL : LLFontGL::BOLD; + ((LLScrollListText*)sl_item->getColumn(0))->setFontStyle(style); + } } } + // Re-select items that were selected before, and restore the scroll position + for(uuid_vec_t::iterator it = selected_items.begin(); it != selected_items.end(); it++) + { + mVoiceEffectList->selectByID(*it); + } + mVoiceEffectList->setScrollPos(scroll_pos); + mVoiceEffectList->setValue(effect_interface->getVoiceEffect()); mVoiceEffectList->setEnabled(true); + // Update button states + // *TODO: Should separate this from rebuilding the effects list, to avoid rebuilding it unnecessarily bool recording = effect_interface->isPreviewRecording(); getChild("record_btn")->setVisible(!recording); getChild("record_stop_btn")->setVisible(recording); diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 8d898378d6..5caf26c492 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -289,6 +289,7 @@ public: //@{ virtual bool setVoiceEffect(const LLUUID& id) = 0; virtual const LLUUID getVoiceEffect() = 0; + virtual LLSD getVoiceEffectProperties(const LLUUID& id) = 0; virtual void refreshVoiceEffectLists(bool clear_lists) = 0; virtual const voice_effect_list_t &getVoiceEffectList() const = 0; diff --git a/indra/newview/skins/default/xui/en/floater_voice_effect.xml b/indra/newview/skins/default/xui/en/floater_voice_effect.xml index 5627eb3aff..8f2f13dffe 100644 --- a/indra/newview/skins/default/xui/en/floater_voice_effect.xml +++ b/indra/newview/skins/default/xui/en/floater_voice_effect.xml @@ -16,6 +16,9 @@ No Voice Effect + + New! + - + - - + + + + - + --> @@ -92,19 +145,19 @@ left_delta="0" name="record_stop_btn" top_delta="0" - width="83"> + width="135"> @@ -116,7 +169,7 @@ left_delta="0" name="play_stop_btn" top_delta="0" - width="83"> + width="135"> -- cgit v1.3 From 46fc55fca546ee8ac0db8cf69b136bc3e344eed1 Mon Sep 17 00:00:00 2001 From: Nyx Linden Date: Wed, 19 May 2010 12:16:20 -0400 Subject: EXT-7208 FIX add color / tint to tattoos Adds a color swatch to tattoo wearable that allows users to color their tattoos. Defaults to white so should not affect existing tattoo wearables. Code reviewed by Vir. --- indra/newview/character/avatar_lad.xml | 264 +++++++++++++++++++++ indra/newview/llpaneleditwearable.cpp | 3 +- indra/newview/llvoavatar.cpp | 12 +- .../skins/default/xui/en/panel_edit_tattoo.xml | 14 ++ 4 files changed, 290 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/newview/character/avatar_lad.xml b/indra/newview/character/avatar_lad.xml index 448e20b382..5efd997ab7 100644 --- a/indra/newview/character/avatar_lad.xml +++ b/indra/newview/character/avatar_lad.xml @@ -6623,6 +6623,60 @@ render_pass="bump"> name="head_tattoo"> + + + + + + + + + + + + + + + + + + + + + + + + @@ -6745,6 +6799,61 @@ render_pass="bump"> name="upper_tattoo"> + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7942,6 +8051,61 @@ render_pass="bump"> name="lower_tattoo"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index 5ad68ea4db..f8dbc91036 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -227,7 +227,7 @@ LLEditWearableDictionary::Wearables::Wearables() addEntry(LLWearableType::WT_UNDERPANTS, new WearableEntry(LLWearableType::WT_UNDERPANTS,"edit_underpants_title","underpants_desc_text",1,1,1, TEX_LOWER_UNDERPANTS, TEX_LOWER_UNDERPANTS, SUBPART_UNDERPANTS)); addEntry(LLWearableType::WT_SKIRT, new WearableEntry(LLWearableType::WT_SKIRT,"edit_skirt_title","skirt_desc_text",1,1,1, TEX_SKIRT, TEX_SKIRT, SUBPART_SKIRT)); addEntry(LLWearableType::WT_ALPHA, new WearableEntry(LLWearableType::WT_ALPHA,"edit_alpha_title","alpha_desc_text",0,5,1, TEX_LOWER_ALPHA, TEX_UPPER_ALPHA, TEX_HEAD_ALPHA, TEX_EYES_ALPHA, TEX_HAIR_ALPHA, SUBPART_ALPHA)); - addEntry(LLWearableType::WT_TATTOO, new WearableEntry(LLWearableType::WT_TATTOO,"edit_tattoo_title","tattoo_desc_text",0,3,1, TEX_LOWER_TATTOO, TEX_UPPER_TATTOO, TEX_HEAD_TATTOO, SUBPART_TATTOO)); + addEntry(LLWearableType::WT_TATTOO, new WearableEntry(LLWearableType::WT_TATTOO,"edit_tattoo_title","tattoo_desc_text",1,3,1, TEX_HEAD_TATTOO, TEX_LOWER_TATTOO, TEX_UPPER_TATTOO, TEX_HEAD_TATTOO, SUBPART_TATTOO)); } LLEditWearableDictionary::WearableEntry::WearableEntry(LLWearableType::EType type, @@ -331,6 +331,7 @@ LLEditWearableDictionary::ColorSwatchCtrls::ColorSwatchCtrls() addEntry ( TEX_UPPER_GLOVES, new PickerControlEntry (TEX_UPPER_GLOVES, "Color/Tint" )); addEntry ( TEX_UPPER_UNDERSHIRT, new PickerControlEntry (TEX_UPPER_UNDERSHIRT, "Color/Tint" )); addEntry ( TEX_LOWER_UNDERPANTS, new PickerControlEntry (TEX_LOWER_UNDERPANTS, "Color/Tint" )); + addEntry ( TEX_HEAD_TATTOO, new PickerControlEntry(TEX_HEAD_TATTOO, "Color/Tint" )); } LLEditWearableDictionary::TextureCtrls::TextureCtrls() diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 0f4623c678..4371396629 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -4886,7 +4886,7 @@ BOOL LLVOAvatar::loadAvatar() } // Uncomment to enable avatar_lad.xml debugging. -/* std::ofstream file; + std::ofstream file; file.open("avatar_lad.log"); for( LLViewerVisualParam* param = (LLViewerVisualParam*) getFirstVisualParam(); param; @@ -4896,7 +4896,7 @@ BOOL LLVOAvatar::loadAvatar() file << std::endl; } - file.close();*/ + file.close(); return TRUE; } @@ -6355,6 +6355,14 @@ BOOL LLVOAvatar::teToColorParams( ETextureIndex te, U32 *param_name ) param_name[2] = 923; //"skirt_blue"; break; + case TEX_HEAD_TATTOO: + case TEX_LOWER_TATTOO: + case TEX_UPPER_TATTOO: + param_name[0] = 1071; //"tattoo_red"; + param_name[1] = 1072; //"tattoo_green"; + param_name[2] = 1073; //"tattoo_blue"; + break; + default: llassert(0); return FALSE; diff --git a/indra/newview/skins/default/xui/en/panel_edit_tattoo.xml b/indra/newview/skins/default/xui/en/panel_edit_tattoo.xml index ed990eb095..6d02dd41de 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_tattoo.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_tattoo.xml @@ -57,6 +57,20 @@ tool_tip="Click to choose a picture" top_pad="10" width="94" /> + + + -- cgit v1.3 From 2930b805d3a5aa88efdbe9eadfec210c461edfa9 Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Wed, 19 May 2010 17:35:53 +0100 Subject: EXT-7337 WIP Fix formatting in floater_voice_effect.xml (no functional change) --- .../skins/default/xui/en/floater_voice_effect.xml | 48 +++++++++++----------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/floater_voice_effect.xml b/indra/newview/skins/default/xui/en/floater_voice_effect.xml index 8f2f13dffe..8452fb55b7 100644 --- a/indra/newview/skins/default/xui/en/floater_voice_effect.xml +++ b/indra/newview/skins/default/xui/en/floater_voice_effect.xml @@ -41,31 +41,31 @@ name="expires" width="100" /> - + -- cgit v1.3 From 265f77747f9ae7719c338bd9b3e76ba59b130b68 Mon Sep 17 00:00:00 2001 From: Yuri Chebotarev Date: Thu, 20 May 2010 16:55:06 +0300 Subject: EXT-6598 FIX Handle tooltip message for separator reviewed by Vadim Savchuk at https://codereview.productengine.com/secondlife/r/417/ --HG-- branch : product-engine --- indra/newview/llnearbychatbar.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp index 7a525596a0..680ed35fa2 100644 --- a/indra/newview/llnearbychatbar.cpp +++ b/indra/newview/llnearbychatbar.cpp @@ -70,7 +70,7 @@ static LLChatTypeTrigger sChatTypeTriggers[] = { }; //ext-7367 -//Problem: gesture list control (actually LLScrollListCtrl) didn't actually process mouse wheel message. +//Problem: gesture list control (actually LLScrollListCtrl) didn't actually process mouse wheel message. // introduce new gesture list subclass to "eat" mouse wheel messages (and probably some other messages) class LLGestureScrollListCtrl: public LLScrollListCtrl { @@ -86,6 +86,14 @@ public: LLScrollListCtrl::handleScrollWheel( x, y, clicks ); return TRUE; } + //See EXT-6598 + //Mouse hover over separator will result in not processing tooltip message + //So eat this message + BOOL handleToolTip(S32 x, S32 y, MASK mask) + { + LLScrollListCtrl::handleToolTip( x, y, mask ); + return TRUE; + } }; LLGestureComboList::Params::Params() -- cgit v1.3 From 55e26cf05604a4dc71fa6683f1ec576eac779cad Mon Sep 17 00:00:00 2001 From: Yuri Chebotarev Date: Thu, 20 May 2010 16:58:20 +0300 Subject: ext-7403 fix - reset all data before setting new group id reviewed by Vadim Savchuk https://codereview.productengine.com/secondlife/r/419/ --HG-- branch : product-engine --- indra/newview/llpanelgrouproles.cpp | 29 +++++++++++++++++++++++++++++ indra/newview/llpanelgrouproles.h | 4 ++++ 2 files changed, 33 insertions(+) (limited to 'indra') diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index 95ec8170f2..7dec2251e8 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -797,8 +797,37 @@ BOOL LLPanelGroupMembersSubTab::postBuildSubTab(LLView* root) void LLPanelGroupMembersSubTab::setGroupID(const LLUUID& id) { + //clear members list + if(mMembersList) mMembersList->deleteAllItems(); + if(mAssignedRolesList) mAssignedRolesList->deleteAllItems(); + if(mAllowedActionsList) mAllowedActionsList->deleteAllItems(); + LLPanelGroupSubTab::setGroupID(id); +} + +void LLPanelGroupRolesSubTab::setGroupID(const LLUUID& id) +{ + if(mRolesList) mRolesList->deleteAllItems(); + if(mAssignedMembersList) mAssignedMembersList->deleteAllItems(); + if(mAllowedActionsList) mAllowedActionsList->deleteAllItems(); + + if(mRoleName) mRoleName->clear(); + if(mRoleDescription) mRoleDescription->clear(); + if(mRoleTitle) mRoleTitle->clear(); + + setFooterEnabled(FALSE); + LLPanelGroupSubTab::setGroupID(id); +} +void LLPanelGroupActionsSubTab::setGroupID(const LLUUID& id) +{ + if(mActionList) mActionList->deleteAllItems(); + if(mActionRoles) mActionRoles->deleteAllItems(); + if(mActionMembers) mActionMembers->deleteAllItems(); + + if(mActionDescription) mActionDescription->clear(); + + LLPanelGroupSubTab::setGroupID(id); } diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h index a877402041..44aa7cea38 100644 --- a/indra/newview/llpanelgrouproles.h +++ b/indra/newview/llpanelgrouproles.h @@ -257,6 +257,8 @@ public: void handleDeleteRole(); void saveRoleChanges(); + + virtual void setGroupID(const LLUUID& id); protected: void handleActionCheck(LLUICtrl* ctrl, bool force); LLSD createRoleItem(const LLUUID& role_id, std::string name, std::string title, S32 members); @@ -294,6 +296,8 @@ public: virtual void update(LLGroupChange gc); void handleActionSelect(); + + virtual void setGroupID(const LLUUID& id); protected: LLScrollListCtrl* mActionList; LLScrollListCtrl* mActionRoles; -- cgit v1.3 From c3a352b219380faa722be6f1ac916a7b95eb836c Mon Sep 17 00:00:00 2001 From: Mike Antipov Date: Thu, 20 May 2010 17:33:26 +0300 Subject: EXT-7371 FIXED Improved new state handling in p2p Voice channel to process session leave event correctly. Reason: * when user leave a p2p voice session himself the session's voice channel is marked as "ignore next leave session event" * but when he re-starts the voice session the channel stays in "ignore next leave session event" state. So, when an other p2p participant ends call this voice channel is not deactivated because of this flag. Fix: reset "ignore next leave session event" flag to FALSE when viewer is joining p2p voice session. Reviewed by Aimee Walton at https://codereview.productengine.com/secondlife/r/418/ --HG-- branch : product-engine --- indra/newview/llvoicechannel.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'indra') diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 25b46f8e55..1b4471a9fe 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -791,6 +791,12 @@ void LLVoiceChannelP2P::handleStatusChange(EStatusType type) } mIgnoreNextSessionLeave = FALSE; return; + case STATUS_JOINING: + // because we join session we expect to process session leave event in the future. EXT-7371 + // may be this should be done in the LLVoiceChannel::handleStatusChange. + mIgnoreNextSessionLeave = FALSE; + break; + default: break; } -- cgit v1.3 From ae6c2de1e33e15f7a3766007a2b20b16f99c1114 Mon Sep 17 00:00:00 2001 From: Sergei Litovchuk Date: Thu, 20 May 2010 18:57:29 +0300 Subject: EXT-5666 FIXED Changed clicked attachment color. --HG-- branch : product-engine --- indra/newview/skins/default/colors.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 33668f5ed6..e8a893e31b 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -359,7 +359,7 @@ reference="White"/> + reference="LtGray" /> -- cgit v1.3 From 53505bd225608d1f048ca80e0bc475d2c125db3a Mon Sep 17 00:00:00 2001 From: Andrew Polunin Date: Thu, 20 May 2010 21:16:16 +0300 Subject: EXT-7223 FIXED (Add \"Shop\" Button in bottom of Inventory panel) - Added 'MarketplaceURL' setting in settings.xml to represent URL which points to the Marketplace. - Added member mShopBtn in the class LLSidepanelInventory. - Added handler LLSidepanelInventory::onShopButtonClicked() which handles a click on the 'Shop' button - Added corresponding widget in the sidepanel_inventory.xml Reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/408/ --HG-- branch : product-engine --- indra/newview/app_settings/settings.xml | 11 +++++++++++ indra/newview/llsidepanelinventory.cpp | 9 +++++++++ indra/newview/llsidepanelinventory.h | 1 + indra/newview/skins/default/xui/en/sidepanel_inventory.xml | 10 ++++++++++ 4 files changed, 31 insertions(+) (limited to 'indra') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 7156af57ec..418032c554 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4591,6 +4591,17 @@ Value + MarketplaceURL + + Comment + URL to the Marketplace + Persist + 0 + Type + String + Value + https://www.xstreetsl.com/modules.php?name=Marketplace + MaxDragDistance Comment diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index 65b9184fe5..fc5143d33b 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -42,6 +42,7 @@ #include "llsidepaneltaskinfo.h" #include "lltabcontainer.h" #include "llselectmgr.h" +#include "llweb.h" static LLRegisterPanelClassWrapper t_inventory("sidepanel_inventory"); @@ -70,6 +71,9 @@ BOOL LLSidepanelInventory::postBuild() mShareBtn = mInventoryPanel->getChild("share_btn"); mShareBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShareButtonClicked, this)); + LLButton* shop_btn = mInventoryPanel->getChild("shop_btn"); + shop_btn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShopButtonClicked, this)); + mWearBtn = mInventoryPanel->getChild("wear_btn"); mWearBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onWearButtonClicked, this)); @@ -155,6 +159,11 @@ void LLSidepanelInventory::onShareButtonClicked() LLAvatarActions::shareWithAvatars(); } +void LLSidepanelInventory::onShopButtonClicked() +{ + LLWeb::loadURLExternal(gSavedSettings.getString("MarketplaceURL")); +} + void LLSidepanelInventory::performActionOnSelection(const std::string &action) { LLPanelMainInventory *panel_main_inventory = mInventoryPanel->getChild("panel_main_inventory"); diff --git a/indra/newview/llsidepanelinventory.h b/indra/newview/llsidepanelinventory.h index 95eab3571c..2dc17e741d 100644 --- a/indra/newview/llsidepanelinventory.h +++ b/indra/newview/llsidepanelinventory.h @@ -79,6 +79,7 @@ private: protected: void onInfoButtonClicked(); void onShareButtonClicked(); + void onShopButtonClicked(); void onWearButtonClicked(); void onPlayButtonClicked(); void onTeleportButtonClicked(); diff --git a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml index 812d94c55f..4c42d1f750 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml @@ -57,6 +57,16 @@ name="share_btn" top="0" width="102" /> + -- cgit v1.3 From 49d8b3f5195e854f8b87fd08098b54eeda51b0ae Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Sun, 23 May 2010 02:31:46 +0100 Subject: EXT-7337 WIP Voice morph previewing - Remove redundant play button now single-click preview is possible. --- indra/newview/skins/default/xui/en/floater_voice_effect.xml | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/floater_voice_effect.xml b/indra/newview/skins/default/xui/en/floater_voice_effect.xml index 9a6c21c7f2..d0c503f7b8 100644 --- a/indra/newview/skins/default/xui/en/floater_voice_effect.xml +++ b/indra/newview/skins/default/xui/en/floater_voice_effect.xml @@ -155,6 +155,7 @@ + -- cgit v1.3 From 6e98ca08300fad1fa8a4a5ab3996e1a6f8318564 Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Sun, 23 May 2010 04:35:31 +0100 Subject: EXT-7337 WIP Voice morph previewing. Remove the activate button in the preview floater for now, it doesn't work in capture mode. Remove the remains of the play button. --- indra/newview/llfloatervoiceeffect.cpp | 45 +++++++-------- indra/newview/llfloatervoiceeffect.h | 2 +- .../skins/default/xui/en/floater_voice_effect.xml | 65 +++------------------- 3 files changed, 30 insertions(+), 82 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloatervoiceeffect.cpp b/indra/newview/llfloatervoiceeffect.cpp index 0fa6135da2..1afea66d91 100644 --- a/indra/newview/llfloatervoiceeffect.cpp +++ b/indra/newview/llfloatervoiceeffect.cpp @@ -45,7 +45,7 @@ LLFloaterVoiceEffect::LLFloaterVoiceEffect(const LLSD& key) mCommitCallbackRegistrar.add("VoiceEffect.Play", boost::bind(&LLFloaterVoiceEffect::onClickPlay, this)); mCommitCallbackRegistrar.add("VoiceEffect.Stop", boost::bind(&LLFloaterVoiceEffect::onClickStop, this)); mCommitCallbackRegistrar.add("VoiceEffect.Add", boost::bind(&LLFloaterVoiceEffect::onClickAdd, this)); - mCommitCallbackRegistrar.add("VoiceEffect.Activate", boost::bind(&LLFloaterVoiceEffect::onClickActivate, this)); +// mCommitCallbackRegistrar.add("VoiceEffect.Activate", boost::bind(&LLFloaterVoiceEffect::onClickActivate, this)); } // virtual @@ -70,7 +70,7 @@ BOOL LLFloaterVoiceEffect::postBuild() if (mVoiceEffectList) { mVoiceEffectList->setCommitCallback(boost::bind(&LLFloaterVoiceEffect::onClickPlay, this)); - mVoiceEffectList->setDoubleClickCallback(boost::bind(&LLFloaterVoiceEffect::onClickActivate, this)); +// mVoiceEffectList->setDoubleClickCallback(boost::bind(&LLFloaterVoiceEffect::onClickActivate, this)); } LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); @@ -133,7 +133,7 @@ void LLFloaterVoiceEffect::refreshEffectList() element["columns"][0]["column"] = "name"; element["columns"][0]["value"] = getString("no_voice_effect"); element["columns"][0]["font"]["name"] = "SANSSERIF"; - element["columns"][0]["font"]["style"] = "BOLD"; + element["columns"][0]["font"]["style"] = "ITALIC"; LLScrollListItem* sl_item = mVoiceEffectList->addElement(element, ADD_BOTTOM); // *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :( @@ -152,9 +152,16 @@ void LLFloaterVoiceEffect::refreshEffectList() std::string effect_name = it->first; LLSD effect_properties = effect_interface->getVoiceEffectProperties(effect_id); + + // Tag the active effect. + if (effect_id == LLVoiceClient::instance().getVoiceEffectDefault()) + { + effect_name += " " + getString("active_voice_effect"); + } + + std::string expiry_date = effect_properties["expiry_date"].asString(); bool is_template_only = effect_properties["template_only"].asBoolean(); bool is_new = effect_properties["is_new"].asBoolean(); - std::string expiry_date = effect_properties["expiry_date"].asString(); std::string font_style = "NORMAL"; if (!is_template_only) @@ -168,6 +175,7 @@ void LLFloaterVoiceEffect::refreshEffectList() element["columns"][0]["value"] = effect_name; element["columns"][0]["font"]["name"] = "SANSSERIF"; element["columns"][0]["font"]["style"] = font_style; + element["columns"][1]["column"] = "new"; element["columns"][1]["value"] = is_new ? getString("new_voice_effect") : ""; element["columns"][1]["font"]["name"] = "SANSSERIF"; @@ -194,8 +202,6 @@ void LLFloaterVoiceEffect::refreshEffectList() mVoiceEffectList->selectByID(*it); } mVoiceEffectList->setScrollPos(scroll_pos); - - mVoiceEffectList->setValue(effect_interface->getVoiceEffect()); mVoiceEffectList->setEnabled(true); } @@ -213,16 +219,6 @@ void LLFloaterVoiceEffect::updateControls() getChild("record_btn")->setVisible(!recording); getChild("record_stop_btn")->setVisible(recording); - - getChild("play_btn")->setVisible(!playing); - getChild("play_stop_btn")->setVisible(playing); - - getChild("play_btn")->setEnabled(effect_interface->isPreviewReady()); - - if (!mVoiceEffectList) - { - mVoiceEffectList->setValue(effect_interface->getVoiceEffect()); - } } // virtual @@ -281,11 +277,12 @@ void LLFloaterVoiceEffect::onClickAdd() LLWeb::loadURL(getString("get_voice_effects_url")); } -void LLFloaterVoiceEffect::onClickActivate() -{ - LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); - if (effect_interface && mVoiceEffectList) - { - effect_interface->setVoiceEffect(mVoiceEffectList->getCurrentID()); - } -} +//void LLFloaterVoiceEffect::onClickActivate() +//{ +// LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); +// if (effect_interface && mVoiceEffectList) +// { +// effect_interface->setVoiceEffect(mVoiceEffectList->getCurrentID()); +// } +//} + diff --git a/indra/newview/llfloatervoiceeffect.h b/indra/newview/llfloatervoiceeffect.h index 060ffc99e9..ed38cd6925 100644 --- a/indra/newview/llfloatervoiceeffect.h +++ b/indra/newview/llfloatervoiceeffect.h @@ -63,7 +63,7 @@ private: void onClickPlay(); void onClickStop(); void onClickAdd(); - void onClickActivate(); +// void onClickActivate(); LLUUID mSelectedID; LLScrollListCtrl* mVoiceEffectList; diff --git a/indra/newview/skins/default/xui/en/floater_voice_effect.xml b/indra/newview/skins/default/xui/en/floater_voice_effect.xml index d0c503f7b8..728beece6b 100644 --- a/indra/newview/skins/default/xui/en/floater_voice_effect.xml +++ b/indra/newview/skins/default/xui/en/floater_voice_effect.xml @@ -14,11 +14,14 @@ min_width="240" width="313"> - No Voice Effect + (No Voice Effect) http://secondlife.com/landing/v0icem0rphingt3st + + (Active) + New! @@ -38,8 +41,8 @@ - + @@ -86,6 +89,7 @@ + - - -- cgit v1.3 From 234b3edc26db7cf87a11fc16d9720bf4a9c20177 Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Sun, 23 May 2010 14:11:48 +0100 Subject: EXT-7337 WIP Added explanatory note to the voice morph preview floater --- indra/newview/llfloatervoiceeffect.cpp | 2 ++ .../skins/default/xui/en/floater_voice_effect.xml | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloatervoiceeffect.cpp b/indra/newview/llfloatervoiceeffect.cpp index 1afea66d91..49dd6fa4d9 100644 --- a/indra/newview/llfloatervoiceeffect.cpp +++ b/indra/newview/llfloatervoiceeffect.cpp @@ -65,6 +65,8 @@ LLFloaterVoiceEffect::~LLFloaterVoiceEffect() BOOL LLFloaterVoiceEffect::postBuild() { setDefaultBtn("record_btn"); + getChild("record_btn")->setFocus(true); + mVoiceEffectList = getChild("voice_effect_list"); if (mVoiceEffectList) diff --git a/indra/newview/skins/default/xui/en/floater_voice_effect.xml b/indra/newview/skins/default/xui/en/floater_voice_effect.xml index 728beece6b..acd70bce8e 100644 --- a/indra/newview/skins/default/xui/en/floater_voice_effect.xml +++ b/indra/newview/skins/default/xui/en/floater_voice_effect.xml @@ -2,7 +2,7 @@ --> + + Record a sample of your voice, and then select an effect to preview. Close this window to return to in-world voice. + - - + right="-10" + top="25"> Record a sample of your voice, and then select an effect to preview. Close this window to return to in-world voice. + + + + + + + [[URL] Get Voice Morphing] + -- cgit v1.3 From 358d23a3a0296c132bcdf2b1bc6e904134feac18 Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Mon, 24 May 2010 16:39:34 -0400 Subject: EXT-7463 FIXED Create instrumentation for tracking COF actions on login Added more llinfos for tracking COF and InventoryModel changes. --- indra/newview/llappearancemgr.cpp | 12 ++++++------ indra/newview/llinventorymodel.cpp | 4 +++- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'indra') diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index e9253278eb..57720382df 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -910,7 +910,7 @@ void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid, const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid); const std::string cat_name = cat ? cat->getName() : "CAT NOT FOUND"; - llinfos << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << llendl; + llinfos << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << llendl; // Seraph remove for 2.1 } } @@ -979,13 +979,13 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) llinfos << "creating LLUpdateAppearanceOnDestroy" << llendl; LLPointer link_waiter = new LLUpdateAppearanceOnDestroy; - llinfos << "Linking body items" << llendl; + llinfos << "Linking body items" << llendl; // Seraph remove for 2.1 linkAll(cof, body_items, link_waiter); - llinfos << "Linking wear items" << llendl; + llinfos << "Linking wear items" << llendl; // Seraph remove for 2.1 linkAll(cof, wear_items, link_waiter); - llinfos << "Linking obj items" << llendl; + llinfos << "Linking obj items" << llendl; // Seraph remove for 2.1 linkAll(cof, obj_items, link_waiter); - llinfos << "Linking gesture items" << llendl; + llinfos << "Linking gesture items" << llendl; // Seraph remove for 2.1 linkAll(cof, gest_items, link_waiter); // Add link to outfit if category is an outfit. @@ -1495,7 +1495,7 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update // TODO: MULTI-WEARABLE: check for wearable limit for clothing types else if (is_body_part) { - if (inv_item->getIsLinkType()) + if (inv_item->getIsLinkType() && (vitem->getWearableType() == wearable_type)) { gInventory.purgeObject(inv_item->getUUID()); } diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index a527694d25..c373512ace 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1244,7 +1244,9 @@ void LLInventoryModel::addCategory(LLViewerInventoryCategory* category) void LLInventoryModel::addItem(LLViewerInventoryItem* item) { - //llinfos << "LLInventoryModel::addItem()" << llendl; + const LLViewerInventoryCategory* cat = gInventory.getCategory(item->getParentUUID()); // Seraph remove for 2.1 + const std::string cat_name = cat ? cat->getName() : "CAT NOT FOUND"; // Seraph remove for 2.1 + llinfos << "Added item [ name:" << item->getName() << " UUID:" << item->getUUID() << " type:" << item->getActualType() << " ] to folder [ name:" << cat_name << " uuid:" << item->getParentUUID() << " ]" << llendl; // Seraph remove for 2.1 llassert(item); if(item) -- cgit v1.3 From e2f6ec6294609ef57572d414859727b57734e9ff Mon Sep 17 00:00:00 2001 From: Xiaohong Bao Date: Mon, 24 May 2010 14:41:24 -0600 Subject: EXT-7467: FIXED: [DUPLICATED XUI ID] panel_preferences_graphics1.xml has duplicated string id. --- indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml index 10beb89940..266fd6cb5e 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml @@ -390,7 +390,7 @@ height="12" layout="topleft" left_delta="291" - name="DrawDistanceMeterText2" + name="DrawDistanceMeterText3" top_delta="0" width="128"> m -- cgit v1.3 From 1ad46b5cd0fcac0d3224d37d555092258593eabd Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 24 May 2010 13:59:10 -0700 Subject: DEV-50173 - investigate certificate code performance DEV-50166 - LLBasicCertificateChain::validate calls in log Added caching of certificates that have been validated. The sha1 hash for the certificate is stored and is associated with the from and to times. When the certificate is validated, the code determines whether the certificate has successfully been validated before by looking for it in the cache, and then checks the date of the cert. If that is successful, the validation calls with success. Otherwise, it proceeds to do a full validation of the certificate. --- indra/newview/llsecapi.cpp | 2 +- indra/newview/llsecapi.h | 31 ++++--- indra/newview/llsechandler_basic.cpp | 114 +++++++++++++++++------- indra/newview/llsechandler_basic.h | 23 +++-- indra/newview/llstartup.cpp | 3 +- indra/newview/llxmlrpctransaction.cpp | 3 +- indra/newview/tests/llsechandler_basic_test.cpp | 34 +++---- 7 files changed, 134 insertions(+), 76 deletions(-) (limited to 'indra') diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp index 1caeec5b04..9e636f38c0 100644 --- a/indra/newview/llsecapi.cpp +++ b/indra/newview/llsecapi.cpp @@ -124,7 +124,7 @@ int secapiSSLCertVerifyCallback(X509_STORE_CTX *ctx, void *param) // we rely on libcurl to validate the hostname, as libcurl does more extensive validation // leaving our hostname validation call mechanism for future additions with respect to // OS native (Mac keyring, windows CAPI) validation. - chain->validate(VALIDATION_POLICY_SSL & (~VALIDATION_POLICY_HOSTNAME), store, validation_params); + store->validate(VALIDATION_POLICY_SSL & (~VALIDATION_POLICY_HOSTNAME), chain, validation_params); } catch (LLCertValidationTrustException& cert_exception) { diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h index 59a1e1eff0..5a1a3879d4 100644 --- a/indra/newview/llsecapi.h +++ b/indra/newview/llsecapi.h @@ -154,7 +154,7 @@ public: // return an LLSD object containing information about the certificate // such as its name, signature, expiry time, serial number - virtual LLSD getLLSD() const=0; + virtual void getLLSD(LLSD& llsd)=0; // return an openSSL X509 struct for the certificate virtual X509* getOpenSSLX509() const=0; @@ -231,6 +231,18 @@ public: virtual LLPointer erase(iterator cert)=0; }; +// class LLCertificateChain +// Class representing a chain of certificates in order, with the +// first element being the child cert. +class LLCertificateChain : virtual public LLCertificateVector +{ + +public: + LLCertificateChain() {} + + virtual ~LLCertificateChain() {} + +}; // class LLCertificateStore // represents a store of certificates, typically a store of root CA @@ -250,30 +262,17 @@ public: // return the store id virtual std::string storeId() const=0; -}; - -// class LLCertificateChain -// Class representing a chain of certificates in order, with the -// first element being the child cert. -class LLCertificateChain : virtual public LLCertificateVector -{ - -public: - LLCertificateChain() {} - virtual ~LLCertificateChain() {} - // validate a certificate chain given the params. // Will throw exceptions on error virtual void validate(int validation_policy, - LLPointer ca_store, + LLPointer cert_chain, const LLSD& validation_params) =0; + }; - - inline bool operator==(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs) { diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index edf5ce9b60..84ab9b9175 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -85,7 +85,6 @@ LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert) { throw LLInvalidCertificate(this); } - _initLLSD(); } @@ -96,7 +95,6 @@ LLBasicCertificate::LLBasicCertificate(X509* pCert) throw LLInvalidCertificate(this); } mCert = X509_dup(pCert); - _initLLSD(); } LLBasicCertificate::~LLBasicCertificate() @@ -150,9 +148,13 @@ std::vector LLBasicCertificate::getBinary() const } -LLSD LLBasicCertificate::getLLSD() const +void LLBasicCertificate::getLLSD(LLSD &llsd) { - return mLLSDInfo; + if (mLLSDInfo.isUndefined()) + { + _initLLSD(); + } + llsd = mLLSDInfo; } // Initialize the LLSD info for the certificate @@ -516,8 +518,9 @@ LLBasicCertificateVector::iterator LLBasicCertificateVector::find(const LLSD& pa cert++) { - found= TRUE; - LLSD cert_info = (*cert)->getLLSD(); + found= TRUE; + LLSD cert_info; + (*cert)->getLLSD(cert_info); for (LLSD::map_const_iterator param = params.beginMap(); param != params.endMap(); param++) @@ -543,7 +546,8 @@ LLBasicCertificateVector::iterator LLBasicCertificateVector::find(const LLSD& pa void LLBasicCertificateVector::insert(iterator _iter, LLPointer cert) { - LLSD cert_info = cert->getLLSD(); + LLSD cert_info; + cert->getLLSD(cert_info); if (cert_info.isMap() && cert_info.has(CERT_SHA1_DIGEST)) { LLSD existing_cert_info = LLSD::emptyMap(); @@ -691,7 +695,8 @@ LLBasicCertificateChain::LLBasicCertificateChain(const X509_STORE_CTX* store) while(untrusted_certs.size() > 0) { LLSD find_data = LLSD::emptyMap(); - LLSD cert_data = current->getLLSD(); + LLSD cert_data; + current->getLLSD(cert_data); // we simply build the chain via subject/issuer name as the // client should not have passed in multiple CA's with the same // subject name. If they did, it'll come out in the wash during @@ -850,12 +855,13 @@ bool _LLSDArrayIncludesValue(const LLSD& llsd_set, LLSD llsd_value) } void _validateCert(int validation_policy, - const LLPointer cert, + LLPointer cert, const LLSD& validation_params, int depth) { - LLSD current_cert_info = cert->getLLSD(); + LLSD current_cert_info; + cert->getLLSD(current_cert_info); // check basic properties exist in the cert if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info.has(CERT_SUBJECT_NAME_STRING)) { @@ -943,8 +949,9 @@ bool _verify_signature(LLPointer parent, LLPointer child) { bool verify_result = FALSE; - LLSD cert1 = parent->getLLSD(); - LLSD cert2 = child->getLLSD(); + LLSD cert1, cert2; + parent->getLLSD(cert1); + child->getLLSD(cert2); X509 *signing_cert = parent->getOpenSSLX509(); X509 *child_cert = child->getOpenSSLX509(); if((signing_cert != NULL) && (child_cert != NULL)) @@ -979,6 +986,7 @@ bool _verify_signature(LLPointer parent, return verify_result; } + // validate the certificate chain against a store. // There are many aspects of cert validatioin policy involved in // trust validation. The policies in this validation algorithm include @@ -993,8 +1001,8 @@ bool _verify_signature(LLPointer parent, // and verify the last cert is in the certificate store, or points // to a cert in the store. It validates whether any cert in the chain // is trusted in the store, even if it's not the last one. -void LLBasicCertificateChain::validate(int validation_policy, - LLPointer ca_store, +void LLBasicCertificateStore::validate(int validation_policy, + LLPointer cert_chain, const LLSD& validation_params) { @@ -1002,8 +1010,8 @@ void LLBasicCertificateChain::validate(int validation_policy, { throw LLCertException(NULL, "No certs in chain"); } - iterator current_cert = begin(); - LLSD current_cert_info = (*current_cert)->getLLSD(); + iterator current_cert = cert_chain->begin(); + LLSD current_cert_info; LLSD validation_date; if (validation_params.has(CERT_VALIDATION_DATE)) { @@ -1012,6 +1020,7 @@ void LLBasicCertificateChain::validate(int validation_policy, if (validation_policy & VALIDATION_POLICY_HOSTNAME) { + (*current_cert)->getLLSD(current_cert_info); if(!validation_params.has(CERT_HOSTNAME)) { throw LLCertException((*current_cert), "No hostname passed in for validation"); @@ -1021,7 +1030,7 @@ void LLBasicCertificateChain::validate(int validation_policy, throw LLInvalidCertificate((*current_cert)); } - LL_INFOS("SECAPI") << "Validating the hostname " << validation_params[CERT_HOSTNAME].asString() << + LL_DEBUGS("SECAPI") << "Validating the hostname " << validation_params[CERT_HOSTNAME].asString() << "against the cert CN " << current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString() << LL_ENDL; if(!_cert_hostname_wildcard_match(validation_params[CERT_HOSTNAME].asString(), current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString())) @@ -1030,16 +1039,50 @@ void LLBasicCertificateChain::validate(int validation_policy, (*current_cert)); } } - + // check the cache of already validated certs + X509* cert_x509 = (*current_cert)->getOpenSSLX509(); + if(!cert_x509) + { + throw LLInvalidCertificate((*current_cert)); + } + std::string sha1_hash((const char *)cert_x509->sha1_hash, SHA_DIGEST_LENGTH); + t_cert_cache::iterator cache_entry = mTrustedCertCache.find(sha1_hash); + if(cache_entry != mTrustedCertCache.end()) + { + LL_DEBUGS("SECAPI") << "Found cert in cache" << LL_ENDL; + // this cert is in the cache, so validate the time. + if (validation_policy & VALIDATION_POLICY_TIME) + { + LLDate validation_date(time(NULL)); + if(validation_params.has(CERT_VALIDATION_DATE)) + { + validation_date = validation_params[CERT_VALIDATION_DATE]; + } + + if((validation_date < cache_entry->second.first) || + (validation_date > cache_entry->second.second)) + { + throw LLCertValidationExpirationException((*current_cert), validation_date); + } + } + // successfully found in cache + return; + } + if(current_cert_info.isUndefined()) + { + (*current_cert)->getLLSD(current_cert_info); + } + LLDate from_time = current_cert_info[CERT_VALID_FROM].asDate(); + LLDate to_time = current_cert_info[CERT_VALID_TO].asDate(); int depth = 0; LLPointer previous_cert; // loop through the cert chain, validating the current cert against the next one. - while(current_cert != end()) + while(current_cert != cert_chain->end()) { int local_validation_policy = validation_policy; - if(current_cert == begin()) + if(current_cert == cert_chain->begin()) { // for the child cert, we don't validate CA stuff local_validation_policy &= ~(VALIDATION_POLICY_CA_KU | @@ -1061,23 +1104,23 @@ void LLBasicCertificateChain::validate(int validation_policy, depth); // look for a CA in the CA store that may belong to this chain. - LLSD cert_llsd = (*current_cert)->getLLSD(); LLSD cert_search_params = LLSD::emptyMap(); // is the cert itself in the store? - cert_search_params[CERT_SHA1_DIGEST] = cert_llsd[CERT_SHA1_DIGEST]; - LLCertificateStore::iterator found_store_cert = ca_store->find(cert_search_params); - if(found_store_cert != ca_store->end()) + cert_search_params[CERT_SHA1_DIGEST] = current_cert_info[CERT_SHA1_DIGEST]; + LLCertificateStore::iterator found_store_cert = find(cert_search_params); + if(found_store_cert != end()) { + mTrustedCertCache[sha1_hash] = std::pair(from_time, to_time); return; } // is the parent in the cert store? cert_search_params = LLSD::emptyMap(); - cert_search_params[CERT_SUBJECT_NAME_STRING] = cert_llsd[CERT_ISSUER_NAME_STRING]; - if (cert_llsd.has(CERT_AUTHORITY_KEY_IDENTIFIER)) + cert_search_params[CERT_SUBJECT_NAME_STRING] = current_cert_info[CERT_ISSUER_NAME_STRING]; + if (current_cert_info.has(CERT_AUTHORITY_KEY_IDENTIFIER)) { - LLSD cert_aki = cert_llsd[CERT_AUTHORITY_KEY_IDENTIFIER]; + LLSD cert_aki = current_cert_info[CERT_AUTHORITY_KEY_IDENTIFIER]; if(cert_aki.has(CERT_AUTHORITY_KEY_IDENTIFIER_ID)) { cert_search_params[CERT_SUBJECT_KEY_IDENTFIER] = cert_aki[CERT_AUTHORITY_KEY_IDENTIFIER_ID]; @@ -1087,11 +1130,10 @@ void LLBasicCertificateChain::validate(int validation_policy, cert_search_params[CERT_SERIAL_NUMBER] = cert_aki[CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL]; } } - found_store_cert = ca_store->find(cert_search_params); + found_store_cert = find(cert_search_params); - if(found_store_cert != ca_store->end()) + if(found_store_cert != end()) { - LLSD foo = (*found_store_cert)->getLLSD(); // validate the store cert against the depth _validateCert(validation_policy & VALIDATION_POLICY_CA_BASIC_CONSTRAINTS, (*found_store_cert), @@ -1105,11 +1147,16 @@ void LLBasicCertificateChain::validate(int validation_policy, throw LLCertValidationInvalidSignatureException(*current_cert); } // successfully validated. + mTrustedCertCache[sha1_hash] = std::pair(from_time, to_time); return; } previous_cert = (*current_cert); current_cert++; - depth++; + depth++; + if(current_cert != current_cert != cert_chain->end()) + { + (*current_cert)->getLLSD(current_cert_info); + } } if (validation_policy & VALIDATION_POLICY_TRUSTED) { @@ -1118,6 +1165,7 @@ void LLBasicCertificateChain::validate(int validation_policy, throw LLCertValidationTrustException((*this)[size()-1]); } + mTrustedCertCache[sha1_hash] = std::pair(from_time, to_time); } @@ -1155,7 +1203,7 @@ void LLSecAPIBasicHandler::init() "CA.pem"); - LL_INFOS("SECAPI") << "Loading certificate store from " << store_file << LL_ENDL; + LL_DEBUGS("SECAPI") << "Loading certificate store from " << store_file << LL_ENDL; mStore = new LLBasicCertificateStore(store_file); // grab the application CA.pem file that contains the well-known certs shipped @@ -1465,7 +1513,7 @@ void LLSecAPIBasicHandler::saveCredential(LLPointer cred, bool sav { credential["authenticator"] = cred->getAuthenticator(); } - LL_INFOS("SECAPI") << "Saving Credential " << cred->getGrid() << ":" << cred->userID() << " " << save_authenticator << LL_ENDL; + LL_DEBUGS("SECAPI") << "Saving Credential " << cred->getGrid() << ":" << cred->userID() << " " << save_authenticator << LL_ENDL; setProtectedData("credential", cred->getGrid(), credential); //*TODO: If we're saving Agni credentials, should we write the // credentials to the legacy password.dat/etc? diff --git a/indra/newview/llsechandler_basic.h b/indra/newview/llsechandler_basic.h index 4bbb73f062..3ddd36a81a 100644 --- a/indra/newview/llsechandler_basic.h +++ b/indra/newview/llsechandler_basic.h @@ -59,12 +59,13 @@ public: virtual std::string getPem() const; virtual std::vector getBinary() const; - virtual LLSD getLLSD() const; + virtual void getLLSD(LLSD &llsd); virtual X509* getOpenSSLX509() const; // set llsd elements for testing void setLLSD(const std::string name, const LLSD& value) { mLLSDInfo[name] = value; } + protected: // certificates are stored as X509 objects, as validation and @@ -173,8 +174,21 @@ public: // return the store id virtual std::string storeId() const; + // validate a certificate chain against a certificate store, using the + // given validation policy. + virtual void validate(int validation_policy, + LLPointer ca_chain, + const LLSD& validation_params); + protected: - std::vector >mCerts; + std::vector > mCerts; + + // cache of cert sha1 hashes to from/to date pairs, to improve + // performance of cert trust. Note, these are not the CA certs, + // but the certs that have been validated against this store. + typedef std::map > t_cert_cache; + t_cert_cache mTrustedCertCache; + std::string mFilename; }; @@ -189,11 +203,6 @@ public: virtual ~LLBasicCertificateChain() {} - // validate a certificate chain against a certificate store, using the - // given validation policy. - virtual void validate(int validation_policy, - LLPointer ca_store, - const LLSD& validation_params); }; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 0a464b3b6c..7022d83868 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2702,7 +2702,8 @@ LLSD transform_cert_args(LLPointer cert) { LLSD args = LLSD::emptyMap(); std::string value; - LLSD cert_info = cert->getLLSD(); + LLSD cert_info; + cert->getLLSD(cert_info); // convert all of the elements in the cert into // args for the xml dialog, so we have flexability to // display various parts of the cert by only modifying diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index da61840761..bc1a48e26a 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -247,7 +247,8 @@ int LLXMLRPCTransaction::Impl::_sslCertVerifyCallback(X509_STORE_CTX *ctx, void validation_params[CERT_HOSTNAME] = uri.hostName(); try { - chain->validate(VALIDATION_POLICY_SSL, store, validation_params); + // don't validate hostname. Let libcurl do it instead. That way, it'll handle redirects + store->validate(VALIDATION_POLICY_SSL & (~VALIDATION_POLICY_HOSTNAME), chain, validation_params); } catch (LLCertValidationTrustException& cert_exception) { diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index fd680b24f0..d1330e2270 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -950,15 +950,15 @@ namespace tut test_chain->add(new LLBasicCertificate(mX509IntermediateCert)); - test_chain->validate(0, test_store, validation_params); + test_store->validate(0, test_chain, validation_params); // add the root certificate to the chain and revalidate test_chain->add(new LLBasicCertificate(mX509RootCert)); - test_chain->validate(0, test_store, validation_params); + test_store->validate(0, test_chain, validation_params); // add the child cert at the head of the chain, and revalidate (3 deep chain) test_chain->insert(test_chain->begin(), new LLBasicCertificate(mX509ChildCert)); - test_chain->validate(0, test_store, validation_params); + test_store->validate(0, test_chain, validation_params); // basic failure cases test_chain = new LLBasicCertificateChain(NULL); @@ -967,14 +967,14 @@ namespace tut ensure_throws("no CA, with only a child cert", LLCertValidationTrustException, (*test_chain)[0], - test_chain->validate, + test_store->validate, VALIDATION_POLICY_TRUSTED, - test_store, + test_chain, validation_params); // validate without the trust flag. - test_chain->validate(0, test_store, validation_params); + test_store->validate(0, test_chain, validation_params); // clear out the store test_store = new LLBasicCertificateStore("mycertstore.pem"); @@ -983,9 +983,9 @@ namespace tut ensure_throws("no CA, with child and intermediate certs", LLCertValidationTrustException, (*test_chain)[1], - test_chain->validate, + test_store->validate, VALIDATION_POLICY_TRUSTED, - test_store, + test_chain, validation_params); // validate without the trust flag test_chain->validate(0, test_store, validation_params); @@ -994,7 +994,7 @@ namespace tut LLSD child_info = (*test_chain)[0]->getLLSD(); validation_params = LLSD::emptyMap(); validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_FROM].asDate().secondsSinceEpoch() + 1.0); - test_chain->validate(VALIDATION_POLICY_TIME, test_store, validation_params); + test_store->validate(VALIDATION_POLICY_TIME, test_chain, validation_params); validation_params = LLSD::emptyMap(); validation_params[CERT_VALIDATION_DATE] = child_info[CERT_VALID_FROM].asDate(); @@ -1005,9 +1005,9 @@ namespace tut ensure_throws("Child cert not yet valid" , LLCertValidationExpirationException, (*test_chain)[0], - test_chain->validate, + test_store->validate, VALIDATION_POLICY_TIME, - test_store, + test_chain, validation_params); validation_params = LLSD::emptyMap(); validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_TO].asDate().secondsSinceEpoch() + 1.0); @@ -1016,9 +1016,9 @@ namespace tut ensure_throws("Child cert expired", LLCertValidationExpirationException, (*test_chain)[0], - test_chain->validate, + test_store->validate, VALIDATION_POLICY_TIME, - test_store, + test_chain, validation_params); // test SSL KU @@ -1026,7 +1026,7 @@ namespace tut test_chain = new LLBasicCertificateChain(NULL); test_chain->add(new LLBasicCertificate(mX509ChildCert)); test_chain->add(new LLBasicCertificate(mX509IntermediateCert)); - test_chain->validate(VALIDATION_POLICY_SSL_KU, test_store, validation_params); + test_store->validate(VALIDATION_POLICY_SSL_KU, test_chain, validation_params); test_chain = new LLBasicCertificateChain(NULL); test_chain->add(new LLBasicCertificate(mX509TestCert)); @@ -1034,9 +1034,9 @@ namespace tut ensure_throws("Cert doesn't have ku", LLCertKeyUsageValidationException, (*test_chain)[0], - test_chain->validate, + test_store->validate, VALIDATION_POLICY_SSL_KU, - test_store, + test_chain, validation_params); // test sha1RSA validation @@ -1044,7 +1044,7 @@ namespace tut test_chain->add(new LLBasicCertificate(mSha1RSATestCert)); test_chain->add(new LLBasicCertificate(mSha1RSATestCA)); - test_chain->validate(0, test_store, validation_params); + test_store->validate(0, test_chain, validation_params); } }; -- cgit v1.3 From 40c672a61ffa9d59594cb38ee75d3a3b44dcd2a5 Mon Sep 17 00:00:00 2001 From: "Nyx (Neal Orman)" Date: Mon, 24 May 2010 17:16:03 -0400 Subject: AVP-92 FIX Avatar appears to be fat when opening the shape editor Wearable editor previews were affecting the in-world avatar and not properly resetting the changed parameters. Since the "body fat" slider is the last in the default tab for the shape editor, this leaves your avatar fat. Reconfigured the code to work through the wearables and most importantly included a call to updateVisualParameters() at the end of the render function. This appears to have fixed the issue - your avatar does not appear affected during the avatar preview generation anymore. Code reviewed by Seraph --- indra/newview/llscrollingpanelparam.cpp | 4 ++-- indra/newview/lltoolmorph.cpp | 8 ++++++-- indra/newview/lltoolmorph.h | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/newview/llscrollingpanelparam.cpp b/indra/newview/llscrollingpanelparam.cpp index a5518d87d4..242af6981c 100644 --- a/indra/newview/llscrollingpanelparam.cpp +++ b/indra/newview/llscrollingpanelparam.cpp @@ -73,9 +73,9 @@ LLScrollingPanelParam::LLScrollingPanelParam( const LLPanel::Params& panel_param F32 min_weight = param->getMinWeight(); F32 max_weight = param->getMaxWeight(); - mHintMin = new LLVisualParamHint( pos_x, pos_y, PARAM_HINT_WIDTH, PARAM_HINT_HEIGHT, mesh, (LLViewerVisualParam*) wearable->getVisualParam(param->getID()), min_weight); + mHintMin = new LLVisualParamHint( pos_x, pos_y, PARAM_HINT_WIDTH, PARAM_HINT_HEIGHT, mesh, (LLViewerVisualParam*) wearable->getVisualParam(param->getID()), wearable, min_weight); pos_x = getChild("right_border")->getRect().mLeft + left_border->getBorderWidth(); - mHintMax = new LLVisualParamHint( pos_x, pos_y, PARAM_HINT_WIDTH, PARAM_HINT_HEIGHT, mesh, (LLViewerVisualParam*) wearable->getVisualParam(param->getID()), max_weight ); + mHintMax = new LLVisualParamHint( pos_x, pos_y, PARAM_HINT_WIDTH, PARAM_HINT_HEIGHT, mesh, (LLViewerVisualParam*) wearable->getVisualParam(param->getID()), wearable, max_weight ); mHintMin->setAllowsUpdates( FALSE ); mHintMax->setAllowsUpdates( FALSE ); diff --git a/indra/newview/lltoolmorph.cpp b/indra/newview/lltoolmorph.cpp index 22176c037f..22e15dcff2 100644 --- a/indra/newview/lltoolmorph.cpp +++ b/indra/newview/lltoolmorph.cpp @@ -79,6 +79,7 @@ LLVisualParamHint::LLVisualParamHint( S32 width, S32 height, LLViewerJointMesh *mesh, LLViewerVisualParam *param, + LLWearable *wearable, F32 param_weight) : LLViewerDynamicTexture(width, height, 3, LLViewerDynamicTexture::ORDER_MIDDLE, TRUE ), @@ -86,6 +87,7 @@ LLVisualParamHint::LLVisualParamHint( mIsVisible( FALSE ), mJointMesh( mesh ), mVisualParam( param ), + mWearablePtr( wearable ), mVisualParamWeight( param_weight ), mAllowsUpdates( TRUE ), mDelayFrames( 0 ), @@ -151,7 +153,7 @@ BOOL LLVisualParamHint::needsRender() void LLVisualParamHint::preRender(BOOL clear_depth) { mLastParamWeight = mVisualParam->getWeight(); - mVisualParam->setWeight(mVisualParamWeight, FALSE); + mWearablePtr->setVisualParamWeight(mVisualParam->getID(), mVisualParamWeight, FALSE); gAgentAvatarp->setVisualParamWeight(mVisualParam->getID(), mVisualParamWeight, FALSE); gAgentAvatarp->setVisualParamWeight("Blink_Left", 0.f); gAgentAvatarp->setVisualParamWeight("Blink_Right", 0.f); @@ -250,10 +252,12 @@ BOOL LLVisualParamHint::render() gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); } gAgentAvatarp->setVisualParamWeight(mVisualParam->getID(), mLastParamWeight); - mVisualParam->setWeight(mLastParamWeight, FALSE); + mWearablePtr->setVisualParamWeight(mVisualParam->getID(), mLastParamWeight, FALSE); + gAgentAvatarp->updateVisualParams(); gGL.color4f(1,1,1,1); mGLTexturep->setGLTextureCreated(true); gGL.popUIMatrix(); + return TRUE; } diff --git a/indra/newview/lltoolmorph.h b/indra/newview/lltoolmorph.h index c332c296bd..3bffefaa55 100644 --- a/indra/newview/lltoolmorph.h +++ b/indra/newview/lltoolmorph.h @@ -62,6 +62,7 @@ public: S32 width, S32 height, LLViewerJointMesh *mesh, LLViewerVisualParam *param, + LLWearable *wearable, F32 param_weight); /*virtual*/ S8 getType() const ; @@ -89,6 +90,7 @@ protected: BOOL mIsVisible; // is this distortion hint visible? LLViewerJointMesh* mJointMesh; // mesh that this distortion applies to LLViewerVisualParam* mVisualParam; // visual param applied by this hint + LLWearable* mWearablePtr; // wearable we're editing F32 mVisualParamWeight; // weight for this visual parameter BOOL mAllowsUpdates; // updates are blocked unless this is true S32 mDelayFrames; // updates are blocked for this many frames -- cgit v1.3 From 670e316dd78e386e5d52da3162d87e0333b29d8a Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Mon, 24 May 2010 18:05:15 -0400 Subject: Static VFS cache tracing --- indra/llmessage/llassetstorage.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index bf0ed97143..970b6747f7 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -440,6 +440,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LL if (mShutDown) { + llinfos << "ASSET_TRACE cancelled " << uuid << " type " << LLAssetType::lookup(type) << " shutting down" << llendl; return; // don't get the asset or do any callbacks, we are shutting down } @@ -456,6 +457,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LL // Try static VFS first. if (findInStaticVFSAndInvokeCallback(uuid,type,callback,user_data)) { + llinfos << "ASSET_TRACE asset " << uuid << " found in static VFS" << llendl; return; } @@ -472,6 +474,8 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LL { callback(mVFS, uuid, type, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); } + + llinfos << "ASSET_TRACE asset " << uuid << " found in VFS" << llendl; } else { @@ -512,7 +516,6 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LL // This can be overridden by subclasses _queueDataRequest(uuid, type, callback, user_data, duplicate, is_priority); } - llinfos << "ASSET_TRACE asset " << uuid << " found in VFS" << llendl; } -- cgit v1.3 From 1077ab49c171c0f310f9b76b360ea2ad162a31ff Mon Sep 17 00:00:00 2001 From: "Andrew A. de Laix" Date: Mon, 24 May 2010 15:19:33 -0700 Subject: Just enough hackery to get minidumps into Wind'ohs crash reports. Code clean up needed. --- indra/cmake/GoogleBreakpad.cmake | 31 ++++++++++-------- indra/llcommon/llapp.cpp | 67 ++++++++++++++++++++++++++++++++++++++ indra/llcommon/llapp.h | 6 ++-- indra/newview/llappviewerwin32.cpp | 5 +-- indra/newview/llwindebug.cpp | 28 +++++++--------- 5 files changed, 102 insertions(+), 35 deletions(-) (limited to 'indra') diff --git a/indra/cmake/GoogleBreakpad.cmake b/indra/cmake/GoogleBreakpad.cmake index 0b9f4a00d0..b21b733500 100644 --- a/indra/cmake/GoogleBreakpad.cmake +++ b/indra/cmake/GoogleBreakpad.cmake @@ -1,14 +1,17 @@ -# -*- cmake -*- -include(Prebuilt) - -if (STANDALONE) - MESSAGE(FATAL_ERROR "*TODO standalone support for google breakad is unimplemented") - # *TODO - implement this include(FindGoogleBreakpad) -else (STANDALONE) - use_prebuilt_binary(google_breakpad) - set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler crash_generation_client common) - if (LINUX) - set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES breakpad_client) - endif (LINUX) -endif (STANDALONE) - +# -*- cmake -*- +include(Prebuilt) + +if (STANDALONE) + MESSAGE(FATAL_ERROR "*TODO standalone support for google breakad is unimplemented") + # *TODO - implement this include(FindGoogleBreakpad) +else (STANDALONE) + use_prebuilt_binary(google_breakpad) + set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler) + if(WINDOWS) + list(APPEND BREAKPAD_EXCEPTION_HANDLER_LIBRARIES crash_generation_client common) + endif(WINDOWS) + if (LINUX) + set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES breakpad_client) + endif (LINUX) +endif (STANDALONE) + diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index e766563c6f..e22ff869e7 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -30,6 +30,8 @@ * $/LicenseInfo$ */ +#include + #include "linden_common.h" #include "llapp.h" @@ -43,6 +45,8 @@ #include "llstl.h" // for DeletePointer() #include "lleventtimer.h" +#include "google_breakpad/exception_handler.h" + // // Signal handling // @@ -51,6 +55,12 @@ #if LL_WINDOWS LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop); BOOL ConsoleCtrlHandler(DWORD fdwCtrlType); +bool windows_post_minidump_callback(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded); #else # include # include // for fork() @@ -285,6 +295,13 @@ void LLApp::setupErrorHandling() // The viewer shouldn't be affected, sicne its a windowed app. SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ConsoleCtrlHandler, TRUE); + if(mExceptionHandler == 0) + { + llwarns << "adding breakpad exception handler" << llendl; + mExceptionHandler = new google_breakpad::ExceptionHandler( + L"C:\\Temp\\", 0, windows_post_minidump_callback, 0, google_breakpad::ExceptionHandler::HANDLER_ALL); + } + #else // // Start up signal handling. @@ -815,3 +832,53 @@ bool darwin_post_minidump_callback(const char *dump_dir, LLApp::runErrorHandler(); return true; } + +bool windows_post_minidump_callback(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded) +{ + char * path = LLApp::instance()->minidump_path; + S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH; + size_t bytesUsed; + + bytesUsed = wcstombs(path, dump_path, static_cast(remaining)); + remaining -= bytesUsed; + path += bytesUsed; + if(remaining > 0) + { + bytesUsed = wcstombs(path, minidump_id, static_cast(remaining)); + remaining -= bytesUsed; + path += bytesUsed; + } + if(remaining > 0) + { + strncpy(path, ".dmp", remaining); + } + + llinfos << "generated minidump: " << LLApp::instance()->minidump_path << llendl; + // *NOTE:Mani - this code is stolen from LLApp, where its never actually used. + //OSMessageBox("Attach Debugger Now", "Error", OSMB_OK); + // *TODO: Translate the signals/exceptions into cross-platform stuff + // Windows implementation + llinfos << "Entering Windows Exception Handler..." << llendl; + + if (LLApp::isError()) + { + llwarns << "Got another fatal signal while in the error handler, die now!" << llendl; + } + + // Flag status to error, so thread_error starts its work + LLApp::setError(); + + // Block in the exception handler until the app has stopped + // This is pretty sketchy, but appears to work just fine + while (!LLApp::isStopped()) + { + ms_sleep(10); + } + + return true; +} diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index 348eec0c48..725c13866f 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -38,8 +38,6 @@ #include "llsd.h" #include "lloptioninterface.h" -#include "google_breakpad/exception_handler.h" - // Forward declarations template class LLAtomic32; typedef LLAtomic32 LLAtomicU32; @@ -68,6 +66,10 @@ public: }; #endif +namespace google_breakpad { + class ExceptionHandler; // See exception_handler.h +} + class LL_COMMON_API LLApp : public LLOptionInterface { friend class LLErrorThread; diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 60a6d2f072..4bee8d9ac5 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -529,8 +529,9 @@ bool LLAppViewerWin32::initParseCommandLine(LLCommandLineParser& clp) } bool LLAppViewerWin32::restoreErrorTrap() -{ - return LLWinDebug::checkExceptionHandler(); +{ + return true; + //return LLWinDebug::checkExceptionHandler(); } void LLAppViewerWin32::handleSyncCrashTrace() diff --git a/indra/newview/llwindebug.cpp b/indra/newview/llwindebug.cpp index 59bc9dc62b..9f8585f163 100644 --- a/indra/newview/llwindebug.cpp +++ b/indra/newview/llwindebug.cpp @@ -718,7 +718,8 @@ BOOL PreventSetUnhandledExceptionFilter() // static void LLWinDebug::initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func) { - + return; +#if 0 static bool s_first_run = true; // Load the dbghelp dll now, instead of waiting for the crash. // Less potential for stack mangling @@ -762,34 +763,27 @@ void LLWinDebug::initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func) Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32FirstW"); Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32NextW"); - LPTOP_LEVEL_EXCEPTION_FILTER prev_filter; - prev_filter = SetUnhandledExceptionFilter(filter_func); - - // *REMOVE:Mani - //PreventSetUnhandledExceptionFilter(); + PVOID added_filter = + AddVectoredExceptionHandler(0, filter_func); - if(prev_filter != gFilterFunc) + if(added_filter == 0) { LL_WARNS("AppInit") - << "Replacing unknown exception (" << (void *)prev_filter << ") with (" << (void *)filter_func << ") !" << LL_ENDL; + << "Failed to add exception handler (" << added_filter << ") !" << LL_ENDL; } gFilterFunc = filter_func; +#endif } bool LLWinDebug::checkExceptionHandler() { bool ok = true; - LPTOP_LEVEL_EXCEPTION_FILTER prev_filter; - prev_filter = SetUnhandledExceptionFilter(gFilterFunc); - if (prev_filter != gFilterFunc) - { - LL_WARNS("AppInit") << "Our exception handler (" << (void *)gFilterFunc << ") replaced with " << prev_filter << "!" << LL_ENDL; - ok = false; - } + RemoveVectoredExceptionHandler(gFilterFunc); + PVOID filter = AddVectoredExceptionHandler(0, gFilterFunc); - if (prev_filter == NULL) + if (filter == NULL) { ok = FALSE; if (gFilterFunc == NULL) @@ -798,7 +792,7 @@ bool LLWinDebug::checkExceptionHandler() } else { - LL_WARNS("AppInit") << "Our exception handler (" << (void *)gFilterFunc << ") replaced with NULL!" << LL_ENDL; + LL_WARNS("AppInit") << "Failed to add exception handler (" << (void *)gFilterFunc << ")!" << LL_ENDL; } } -- cgit v1.3 From a7d1c68c787fcccf888632b9787c4cc9b30393f7 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 24 May 2010 15:31:10 -0700 Subject: Fixup some certificate related unit tests --- indra/newview/llsechandler_basic.cpp | 2 +- indra/newview/tests/llsechandler_basic_test.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 84ab9b9175..a81cde3126 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -1153,7 +1153,7 @@ void LLBasicCertificateStore::validate(int validation_policy, previous_cert = (*current_cert); current_cert++; depth++; - if(current_cert != current_cert != cert_chain->end()) + if(current_cert != cert_chain->end()) { (*current_cert)->getLLSD(current_cert_info); } diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index d1330e2270..dfbd596d39 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -328,7 +328,8 @@ namespace tut ensure_equals("Der Format is correct", memcmp(buffer, mDerFormat.c_str(), mDerFormat.length()), 0); - LLSD llsd_cert = test_cert->getLLSD(); + LLSD llsd_cert; + test_cert->getLLSD(llsd_cert); std::ostringstream llsd_value; llsd_value << LLSDOStreamer(llsd_cert) << std::endl; std::string llsd_cert_str = llsd_value.str(); @@ -988,10 +989,11 @@ namespace tut test_chain, validation_params); // validate without the trust flag - test_chain->validate(0, test_store, validation_params); + test_store->validate(0, test_chain, validation_params); // Test time validity - LLSD child_info = (*test_chain)[0]->getLLSD(); + LLSD child_info; + ((*test_chain)[0])->getLLSD(child_info); validation_params = LLSD::emptyMap(); validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_FROM].asDate().secondsSinceEpoch() + 1.0); test_store->validate(VALIDATION_POLICY_TIME, test_chain, validation_params); -- cgit v1.3 From 28bff2cd99ec97d50f07b620f489a9d8745d3add Mon Sep 17 00:00:00 2001 From: brad kittenbrink Date: Mon, 24 May 2010 15:42:22 -0700 Subject: Fix for mac build failing to find google_breakpad client libraries. --- indra/cmake/GoogleBreakpad.cmake | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/cmake/GoogleBreakpad.cmake b/indra/cmake/GoogleBreakpad.cmake index 0b9f4a00d0..8270c0fabb 100644 --- a/indra/cmake/GoogleBreakpad.cmake +++ b/indra/cmake/GoogleBreakpad.cmake @@ -6,9 +6,14 @@ if (STANDALONE) # *TODO - implement this include(FindGoogleBreakpad) else (STANDALONE) use_prebuilt_binary(google_breakpad) - set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler crash_generation_client common) + if (DARWIN) + set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler) + endif (DARWIN) if (LINUX) set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES breakpad_client) endif (LINUX) + if (WINDOWS) + set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler crash_generation_client common) + endif (WINDOWS) endif (STANDALONE) -- cgit v1.3 From f24c312f8369fbc6ec4eb0607e7e8ea636aacb82 Mon Sep 17 00:00:00 2001 From: "Andrew A. de Laix" Date: Mon, 24 May 2010 15:59:26 -0700 Subject: so long llwindebug, we hardly knew ye. --- indra/newview/CMakeLists.txt | 3 - indra/newview/llappviewer.cpp | 5 - indra/newview/llappviewerwin32.cpp | 55 --- indra/newview/llstartup.cpp | 1 - indra/newview/llviewermessage.cpp | 4 - indra/newview/llwindebug.cpp | 906 ------------------------------------- indra/newview/llwindebug.h | 75 --- 7 files changed, 1049 deletions(-) delete mode 100644 indra/newview/llwindebug.cpp delete mode 100644 indra/newview/llwindebug.h (limited to 'indra') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index f2bed843c9..4571aa1074 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1055,7 +1055,6 @@ set(viewer_HEADER_FILES llwearabletype.h llweb.h llwind.h - llwindebug.h llwlanimator.h llwldaycycle.h llwlparammanager.h @@ -1130,12 +1129,10 @@ endif (LINUX) if (WINDOWS) list(APPEND viewer_SOURCE_FILES llappviewerwin32.cpp - llwindebug.cpp ) list(APPEND viewer_HEADER_FILES llappviewerwin32.h - llwindebug.h ) # precompiled header configuration diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 56486c788b..e9ec0b8b77 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -104,7 +104,6 @@ #include #if LL_WINDOWS - #include "llwindebug.h" # include // For _SH_DENYWR in initMarkerFile #else # include // For initMarkerFile support @@ -3288,10 +3287,6 @@ void LLAppViewer::badNetworkHandler() mPurgeOnExit = TRUE; -#if LL_WINDOWS - // Generates the minidump. - LLWinDebug::generateCrashStacks(NULL); -#endif LLAppViewer::handleSyncViewerCrash(); LLAppViewer::handleViewerCrash(); diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 4bee8d9ac5..88d8dba8af 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -57,8 +57,6 @@ #include "llweb.h" #include "llsecondlifeurls.h" -#include "llwindebug.h" - #include "llviewernetwork.h" #include "llmd5.h" #include "llfindlocale.h" @@ -81,51 +79,6 @@ extern "C" { const std::string LLAppViewerWin32::sWindowClass = "Second Life"; -LONG WINAPI viewer_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop) -{ - // *NOTE:Mani - this code is stolen from LLApp, where its never actually used. - //OSMessageBox("Attach Debugger Now", "Error", OSMB_OK); - // *TODO: Translate the signals/exceptions into cross-platform stuff - // Windows implementation - _tprintf( _T("Entering Windows Exception Handler...\n") ); - llinfos << "Entering Windows Exception Handler..." << llendl; - - // Make sure the user sees something to indicate that the app crashed. - LONG retval; - - if (LLApp::isError()) - { - _tprintf( _T("Got another fatal signal while in the error handler, die now!\n") ); - llwarns << "Got another fatal signal while in the error handler, die now!" << llendl; - - retval = EXCEPTION_EXECUTE_HANDLER; - return retval; - } - - // Generate a minidump if we can. - // Before we wake the error thread... - // Which will start the crash reporting. - LLWinDebug::generateCrashStacks(exception_infop); - - // Flag status to error, so thread_error starts its work - LLApp::setError(); - - // Block in the exception handler until the app has stopped - // This is pretty sketchy, but appears to work just fine - while (!LLApp::isStopped()) - { - ms_sleep(10); - } - - // - // At this point, we always want to exit the app. There's no graceful - // recovery for an unhandled exception. - // - // Just kill the process. - retval = EXCEPTION_EXECUTE_HANDLER; - return retval; -} - // Create app mutex creates a unique global windows object. // If the object can be created it returns true, otherwise // it returns false. The false result can be used to determine @@ -191,8 +144,6 @@ int APIENTRY WINMAIN(HINSTANCE hInstance, gIconResource = MAKEINTRESOURCE(IDI_LL_ICON); LLAppViewerWin32* viewer_app_ptr = new LLAppViewerWin32(lpCmdLine); - - LLWinDebug::initExceptionHandler(viewer_windows_exception_handler); viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash); @@ -405,12 +356,6 @@ bool LLAppViewerWin32::cleanup() bool LLAppViewerWin32::initLogging() { - // Remove the crash stack log from previous executions. - // Since we've started logging a new instance of the app, we can assume - // *NOTE: This should happen before the we send a 'previous instance froze' - // crash report, but it must happen after we initialize the DirUtil. - LLWinDebug::clearCrashStacks(); - return LLAppViewer::initLogging(); } diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index a84bb98a90..e43b3b84c8 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -199,7 +199,6 @@ #include "llstartuplistener.h" #if LL_WINDOWS -#include "llwindebug.h" #include "lldxhardware.h" #endif diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index fb87e2d3b9..c677406223 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -110,10 +110,6 @@ #include // #include -#if LL_WINDOWS // For Windows specific error handler -#include "llwindebug.h" // For the invalid message handler -#endif - #include "llnotificationmanager.h" // #if LL_MSVC diff --git a/indra/newview/llwindebug.cpp b/indra/newview/llwindebug.cpp deleted file mode 100644 index 9f8585f163..0000000000 --- a/indra/newview/llwindebug.cpp +++ /dev/null @@ -1,906 +0,0 @@ -/** - * @file llwindebug.cpp - * @brief Windows debugging functions - * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include -#include -#include "llwindebug.h" -#include "llviewercontrol.h" -#include "lldir.h" -#include "llsd.h" -#include "llsdserialize.h" - -#pragma warning(disable: 4200) //nonstandard extension used : zero-sized array in struct/union -#pragma warning(disable: 4100) //unreferenced formal parameter - - -/* -LLSD Block for Windows Dump Information - - - Platform - - Process - - Module - - DateModified - - ExceptionCode - - ExceptionRead/WriteAddress - - Instruction - - Registers - - - EIP - ... - - - Call Stack - - - - ModuleName - - ModuleBaseAddress - - ModuleOffsetAddress - - Parameters - - - - - - - - - -*/ - - -extern void (*gCrashCallback)(void); - -// based on dbghelp.h -typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, - CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, - CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, - CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam - ); - -MINIDUMPWRITEDUMP f_mdwp = NULL; - -#undef UNICODE - -static LPTOP_LEVEL_EXCEPTION_FILTER gFilterFunc = NULL; - -HMODULE hDbgHelp; - -// Tool Help functions. -typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID); -typedef BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); -typedef BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); - -CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_; -MODULE32_FIRST Module32First_; -MODULE32_NEST Module32Next_; - -#define DUMP_SIZE_MAX 8000 //max size of our dump -#define CALL_TRACE_MAX ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40)) //max number of traced calls -#define NL L"\r\n" //new line - - -typedef struct STACK -{ - STACK * Ebp; - PBYTE Ret_Addr; - DWORD Param[0]; -} STACK, * PSTACK; - -BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr); -void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record, - const CONTEXT* context_record, - LLSD& info); - -void printError( CHAR* msg ) -{ - DWORD eNum; - TCHAR sysMsg[256]; - TCHAR* p; - - eNum = GetLastError( ); - FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, eNum, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language - sysMsg, 256, NULL ); - - // Trim the end of the line and terminate it with a null - p = sysMsg; - while( ( *p > 31 ) || ( *p == 9 ) ) - ++p; - do { *p-- = 0; } while( ( p >= sysMsg ) && - ( ( *p == '.' ) || ( *p < 33 ) ) ); - - // Display the message - printf( "\n WARNING: %s failed with error %d (%s)", msg, eNum, sysMsg ); -} - -BOOL GetProcessThreadIDs(DWORD process_id, std::vector& thread_ids) -{ - HANDLE hThreadSnap = INVALID_HANDLE_VALUE; - THREADENTRY32 te32; - - // Take a snapshot of all running threads - hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); - if( hThreadSnap == INVALID_HANDLE_VALUE ) - return( FALSE ); - - // Fill in the size of the structure before using it. - te32.dwSize = sizeof(THREADENTRY32 ); - - // Retrieve information about the first thread, - // and exit if unsuccessful - if( !Thread32First( hThreadSnap, &te32 ) ) - { - printError( "Thread32First" ); // Show cause of failure - CloseHandle( hThreadSnap ); // Must clean up the snapshot object! - return( FALSE ); - } - - // Now walk the thread list of the system, - // and display information about each thread - // associated with the specified process - do - { - if( te32.th32OwnerProcessID == process_id ) - { - thread_ids.push_back(te32.th32ThreadID); - } - } while( Thread32Next(hThreadSnap, &te32 ) ); - -// Don't forget to clean up the snapshot object. - CloseHandle( hThreadSnap ); - return( TRUE ); -} - -BOOL GetThreadCallStack(DWORD thread_id, LLSD& info) -{ - if(GetCurrentThreadId() == thread_id) - { - // Early exit for the current thread. - // Suspending the current thread would be a bad idea. - // Plus you can't retrieve a valid current thread context. - return false; - } - - HANDLE thread_handle = INVALID_HANDLE_VALUE; - thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); - if(INVALID_HANDLE_VALUE == thread_handle) - { - return FALSE; - } - - BOOL result = false; - if(-1 != SuspendThread(thread_handle)) - { - CONTEXT context_struct; - context_struct.ContextFlags = CONTEXT_FULL; - if(GetThreadContext(thread_handle, &context_struct)) - { - Get_Call_Stack(NULL, &context_struct, info); - result = true; - } - ResumeThread(thread_handle); - } - else - { - // Couldn't suspend thread. - } - - CloseHandle(thread_handle); - return result; -} - - -//Windows Call Stack Construction idea from -//http://www.codeproject.com/tools/minidump.asp - -//**************************************************************************************** -BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr) -//**************************************************************************************** -// Find module by Ret_Addr (address in the module). -// Return Module_Name (full path) and Module_Addr (start address). -// Return TRUE if found. -{ - MODULEENTRY32 M = {sizeof(M)}; - HANDLE hSnapshot; - - bool found = false; - - if (CreateToolhelp32Snapshot_) - { - hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0); - - if ((hSnapshot != INVALID_HANDLE_VALUE) && - Module32First_(hSnapshot, &M)) - { - do - { - if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize) - { - lstrcpyn(Module_Name, M.szExePath, MAX_PATH); - Module_Addr = M.modBaseAddr; - found = true; - break; - } - } while (Module32Next_(hSnapshot, &M)); - } - - CloseHandle(hSnapshot); - } - - return found; -} //Get_Module_By_Ret_Addr - -bool has_valid_call_before(PDWORD cur_stack_loc) -{ - PBYTE p_first_byte = (PBYTE)(*cur_stack_loc - 1); - PBYTE p_second_byte = (PBYTE)(*cur_stack_loc -2); - PBYTE p_fifth_byte = (PBYTE)(*cur_stack_loc - 5); - PBYTE p_sixth_byte = (PBYTE)(*cur_stack_loc - 6); - - // make sure we can read it - if(IsBadReadPtr(p_sixth_byte, 6 * sizeof(BYTE))) - { - return false; - } - - // check for 9a + 4 bytes - if(*p_fifth_byte == 0x9A) - { - return true; - } - - // Check for E8 + 4 bytes and last byte is 00 or FF - if(*p_fifth_byte == 0xE8 && (*p_first_byte == 0x00 || *p_first_byte == 0xFF)) - { - return true; - } - - // the other is six bytes - if(*p_sixth_byte == 0xFF || *p_second_byte == 0xFF) - { - return true; - } - - return false; -} - -PBYTE get_valid_frame(PBYTE esp) -{ - PDWORD cur_stack_loc = NULL; - const int max_search = 400; - WCHAR module_name[MAX_PATH]; - PBYTE module_addr = 0; - - // round to highest multiple of four - esp = (esp + (4 - ((int)esp % 4)) % 4); - - // scroll through stack a few hundred places. - for (cur_stack_loc = (PDWORD) esp; cur_stack_loc < (PDWORD)esp + max_search; cur_stack_loc += 1) - { - // if you can read the pointer, - if (IsBadReadPtr(cur_stack_loc, sizeof(PDWORD))) - { - continue; - } - - // check if it's in a module - if (!Get_Module_By_Ret_Addr((PBYTE)*cur_stack_loc, module_name, module_addr)) - { - continue; - } - - // check if the code before the instruction ptr is a call - if(!has_valid_call_before(cur_stack_loc)) - { - continue; - } - - // if these all pass, return that ebp, otherwise continue till we're dead - return (PBYTE)(cur_stack_loc - 1); - } - - return NULL; -} - -bool shouldUseStackWalker(PSTACK Ebp, int max_depth) -{ - WCHAR Module_Name[MAX_PATH]; - PBYTE Module_Addr = 0; - int depth = 0; - - while (depth < max_depth) - { - if (IsBadReadPtr(Ebp, sizeof(PSTACK)) || - IsBadReadPtr(Ebp->Ebp, sizeof(PSTACK)) || - Ebp->Ebp < Ebp || - Ebp->Ebp - Ebp > 0xFFFFFF || - IsBadCodePtr(FARPROC(Ebp->Ebp->Ret_Addr)) || - !Get_Module_By_Ret_Addr(Ebp->Ebp->Ret_Addr, Module_Name, Module_Addr)) - { - return true; - } - depth++; - Ebp = Ebp->Ebp; - } - - return false; -} - -//****************************************************************** -void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record, - const CONTEXT* context_record, - LLSD& info) -//****************************************************************** -// Fill Str with call stack info. -// pException can be either GetExceptionInformation() or NULL. -// If pException = NULL - get current call stack. -{ - LPWSTR Module_Name = new WCHAR[MAX_PATH]; - PBYTE Module_Addr = 0; - LLSD params; - PBYTE Esp = NULL; - LLSD tmp_info; - - bool fake_frame = false; - bool ebp_used = false; - const int HEURISTIC_MAX_WALK = 20; - int heuristic_walk_i = 0; - int Ret_Addr_I = 0; - - STACK Stack = {0, 0}; - PSTACK Ebp; - - if (exception_record && context_record) //fake frame for exception address - { - Stack.Ebp = (PSTACK)(context_record->Ebp); - Stack.Ret_Addr = (PBYTE)exception_record->ExceptionAddress; - Ebp = &Stack; - Esp = (PBYTE) context_record->Esp; - fake_frame = true; - } - else if(context_record) - { - Ebp = (PSTACK)(context_record->Ebp); - Esp = (PBYTE)(context_record->Esp); - } - else - { - Ebp = (PSTACK)&exception_record - 1; //frame addr of Get_Call_Stack() - Esp = (PBYTE)&exception_record; - - // Skip frame of Get_Call_Stack(). - if (!IsBadReadPtr(Ebp, sizeof(PSTACK))) - Ebp = Ebp->Ebp; //caller ebp - } - - // Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX. - // Break trace on wrong stack frame. - for (Ret_Addr_I = 0; - heuristic_walk_i < HEURISTIC_MAX_WALK && - Ret_Addr_I < CALL_TRACE_MAX && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr)); - Ret_Addr_I++) - { - // If module with Ebp->Ret_Addr found. - if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr)) - { - // Save module's address and full path. - tmp_info["CallStack"][Ret_Addr_I]["ModuleName"] = ll_convert_wide_to_string(Module_Name); - tmp_info["CallStack"][Ret_Addr_I]["ModuleAddress"] = (int)Module_Addr; - tmp_info["CallStack"][Ret_Addr_I]["CallOffset"] = (int)(Ebp->Ret_Addr - Module_Addr); - - // Save 5 params of the call. We don't know the real number of params. - if (fake_frame && !Ret_Addr_I) //fake frame for exception address - params[0] = "Exception Offset"; - else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD))) - { - for(int j = 0; j < 5; ++j) - { - params[j] = (int)Ebp->Param[j]; - } - } - tmp_info["CallStack"][Ret_Addr_I]["Parameters"] = params; - } - - tmp_info["CallStack"][Ret_Addr_I]["ReturnAddress"] = (int)Ebp->Ret_Addr; - - // get ready for next frame - // Set ESP to just after return address. Not the real esp, but just enough after the return address - if(!fake_frame) { - Esp = (PBYTE)Ebp + 8; - } - else - { - fake_frame = false; - } - - // is next ebp valid? - // only run if we've never found a good ebp - // and make sure the one after is valid as well - if( !ebp_used && - shouldUseStackWalker(Ebp, 2)) - { - heuristic_walk_i++; - PBYTE new_ebp = get_valid_frame(Esp); - if (new_ebp != NULL) - { - Ebp = (PSTACK)new_ebp; - } - } - else - { - ebp_used = true; - Ebp = Ebp->Ebp; - } - } -/* TODO remove or turn this code back on to edit the stack after i see a few raw ones. -Palmer - // Now go back through and edit out heuristic stacks that could very well be bogus. - // Leave the top and the last 3 stack chosen by the heuristic, however. - if(heuristic_walk_i > 2) - { - info["CallStack"][0] = tmp_info["CallStack"][0]; - std::string ttest = info["CallStack"][0]["ModuleName"]; - for(int cur_frame = 1; - (cur_frame + heuristic_walk_i - 2 < Ret_Addr_I); - ++cur_frame) - { - // edit out the middle heuristic found frames - info["CallStack"][cur_frame] = tmp_info["CallStack"][cur_frame + heuristic_walk_i - 2]; - } - } - else - { - info = tmp_info; - } -*/ - info = tmp_info; - info["HeuristicWalkI"] = heuristic_walk_i; - info["EbpUsed"] = ebp_used; - -} //Get_Call_Stack - -//*********************************** -void WINAPI Get_Version_Str(LLSD& info) -//*********************************** -// Fill Str with Windows version. -{ - OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)}; //EX for NT 5.0 and later - - if (!GetVersionEx((POSVERSIONINFO)&V)) - { - ZeroMemory(&V, sizeof(V)); - V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx((POSVERSIONINFO)&V); - } - - if (V.dwPlatformId != VER_PLATFORM_WIN32_NT) - V.dwBuildNumber = LOWORD(V.dwBuildNumber); //for 9x HIWORD(dwBuildNumber) = 0x04xx - - info["Platform"] = llformat("Windows: %d.%d.%d, SP %d.%d, Product Type %d", //SP - service pack, Product Type - VER_NT_WORKSTATION,... - V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor, V.wProductType); -} //Get_Version_Str - -//************************************************************* -LLSD WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException) -//************************************************************* -// Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str. -{ - LLSD info; - LPWSTR Str; - int Str_Len; -// int i; - LPWSTR Module_Name = new WCHAR[MAX_PATH]; - PBYTE Module_Addr; - HANDLE hFile; - FILETIME Last_Write_Time; - FILETIME Local_File_Time; - SYSTEMTIME T; - - Str = new WCHAR[DUMP_SIZE_MAX]; - Str_Len = 0; - if (!Str) - return NULL; - - Get_Version_Str(info); - - GetModuleFileName(NULL, Str, MAX_PATH); - info["Process"] = ll_convert_wide_to_string(Str); - info["ThreadID"] = (S32)GetCurrentThreadId(); - - // If exception occurred. - if (pException) - { - EXCEPTION_RECORD & E = *pException->ExceptionRecord; - CONTEXT & C = *pException->ContextRecord; - - // If module with E.ExceptionAddress found - save its path and date. - if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr)) - { - info["Module"] = ll_convert_wide_to_string(Module_Name); - - if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE) - { - if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time)) - { - FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time); - FileTimeToSystemTime(&Local_File_Time, &T); - - info["DateModified"] = llformat("%02d/%02d/%d", T.wMonth, T.wDay, T.wYear); - } - CloseHandle(hFile); - } - } - else - { - info["ExceptionAddr"] = (int)E.ExceptionAddress; - } - - info["ExceptionCode"] = (int)E.ExceptionCode; - - /* - //TODO: Fix this - if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) - { - // Access violation type - Write/Read. - LLSD exception_info; - exception_info["Type"] = E.ExceptionInformation[0] ? "Write" : "Read"; - exception_info["Address"] = llformat("%08x", E.ExceptionInformation[1]); - info["Exception Information"] = exception_info; - } - */ - - - // Save instruction that caused exception. - /* - std::string str; - for (i = 0; i < 16; i++) - str += llformat(" %02X", PBYTE(E.ExceptionAddress)[i]); - info["Instruction"] = str; - */ - LLSD registers; - registers["EAX"] = (int)C.Eax; - registers["EBX"] = (int)C.Ebx; - registers["ECX"] = (int)C.Ecx; - registers["EDX"] = (int)C.Edx; - registers["ESI"] = (int)C.Esi; - registers["EDI"] = (int)C.Edi; - registers["ESP"] = (int)C.Esp; - registers["EBP"] = (int)C.Ebp; - registers["EIP"] = (int)C.Eip; - registers["EFlags"] = (int)C.EFlags; - info["Registers"] = registers; - } //if (pException) - - // Save call stack info. - Get_Call_Stack(pException->ExceptionRecord, pException->ContextRecord, info); - - return info; -} //Get_Exception_Info - -#define UNICODE - - -class LLMemoryReserve { -public: - LLMemoryReserve(); - ~LLMemoryReserve(); - void reserve(); - void release(); -protected: - unsigned char *mReserve; - static const size_t MEMORY_RESERVATION_SIZE; -}; - -LLMemoryReserve::LLMemoryReserve() : - mReserve(NULL) -{ -}; - -LLMemoryReserve::~LLMemoryReserve() -{ - release(); -} - -// I dunno - this just seemed like a pretty good value. -const size_t LLMemoryReserve::MEMORY_RESERVATION_SIZE = 5 * 1024 * 1024; - -void LLMemoryReserve::reserve() -{ - if(NULL == mReserve) - mReserve = new unsigned char[MEMORY_RESERVATION_SIZE]; -}; - -void LLMemoryReserve::release() -{ - delete [] mReserve; - mReserve = NULL; -}; - -static LLMemoryReserve gEmergencyMemoryReserve; - -#ifndef _M_IX86 - #error "The following code only works for x86!" -#endif -LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter( - LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) -{ - if(lpTopLevelExceptionFilter == gFilterFunc) - return gFilterFunc; - - llinfos << "Someone tried to set the exception filter. Listing call stack modules" << llendl; - LLSD cs_info; - Get_Call_Stack(NULL, NULL, cs_info); - - if(cs_info.has("CallStack") && cs_info["CallStack"].isArray()) - { - LLSD cs = cs_info["CallStack"]; - for(LLSD::array_iterator i = cs.beginArray(); - i != cs.endArray(); - ++i) - { - llinfos << "Module: " << (*i)["ModuleName"] << llendl; - } - } - - return gFilterFunc; -} - -BOOL PreventSetUnhandledExceptionFilter() -{ - HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll")); - if (hKernel32 == NULL) - return FALSE; - - void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter"); - if(pOrgEntry == NULL) - return FALSE; - - unsigned char newJump[ 100 ]; - DWORD dwOrgEntryAddr = (DWORD)pOrgEntry; - dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far - void *pNewFunc = &MyDummySetUnhandledExceptionFilter; - DWORD dwNewEntryAddr = (DWORD) pNewFunc; - DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr; - - newJump[ 0 ] = 0xE9; // JMP absolute - memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc)); - SIZE_T bytesWritten; - BOOL bRet = WriteProcessMemory(GetCurrentProcess(), - pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten); - return bRet; -} - -// static -void LLWinDebug::initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func) -{ - return; -#if 0 - static bool s_first_run = true; - // Load the dbghelp dll now, instead of waiting for the crash. - // Less potential for stack mangling - - if (s_first_run) - { - // First, try loading from the directory that the app resides in. - std::string local_dll_name = gDirUtilp->findFile("dbghelp.dll", gDirUtilp->getWorkingDir(), gDirUtilp->getExecutableDir()); - - HMODULE hDll = NULL; - hDll = LoadLibraryA(local_dll_name.c_str()); - if (!hDll) - { - hDll = LoadLibrary(L"dbghelp.dll"); - } - - if (!hDll) - { - LL_WARNS("AppInit") << "Couldn't find dbghelp.dll!" << LL_ENDL; - } - else - { - f_mdwp = (MINIDUMPWRITEDUMP) GetProcAddress(hDll, "MiniDumpWriteDump"); - - if (!f_mdwp) - { - FreeLibrary(hDll); - hDll = NULL; - } - } - - gEmergencyMemoryReserve.reserve(); - - s_first_run = false; - } - - // Try to get Tool Help library functions. - HMODULE hKernel32; - hKernel32 = GetModuleHandle(_T("KERNEL32")); - CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot"); - Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32FirstW"); - Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32NextW"); - - PVOID added_filter = - AddVectoredExceptionHandler(0, filter_func); - - if(added_filter == 0) - { - LL_WARNS("AppInit") - << "Failed to add exception handler (" << added_filter << ") !" << LL_ENDL; - } - - gFilterFunc = filter_func; -#endif -} - -bool LLWinDebug::checkExceptionHandler() -{ - bool ok = true; - - RemoveVectoredExceptionHandler(gFilterFunc); - PVOID filter = AddVectoredExceptionHandler(0, gFilterFunc); - - if (filter == NULL) - { - ok = FALSE; - if (gFilterFunc == NULL) - { - LL_WARNS("AppInit") << "Exception handler uninitialized." << LL_ENDL; - } - else - { - LL_WARNS("AppInit") << "Failed to add exception handler (" << (void *)gFilterFunc << ")!" << LL_ENDL; - } - } - - return ok; -} - -void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename) -{ - if(f_mdwp == NULL || gDirUtilp == NULL) - { - return; - //write_debug("No way to generate a minidump, no MiniDumpWriteDump function!\n"); - } - else - { - std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, filename); - - HANDLE hFile = CreateFileA(dump_path.c_str(), - GENERIC_WRITE, - FILE_SHARE_WRITE, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (hFile != INVALID_HANDLE_VALUE) - { - // Write the dump, ignoring the return value - f_mdwp(GetCurrentProcess(), - GetCurrentProcessId(), - hFile, - type, - ExInfop, - NULL, - NULL); - - CloseHandle(hFile); - } - - } -} - -// static -void LLWinDebug::generateCrashStacks(struct _EXCEPTION_POINTERS *exception_infop) -{ - // *NOTE:Mani - This method is no longer the exception handler. - // Its called from viewer_windows_exception_handler() and other places. - - // - // Let go of a bunch of reserved memory to give library calls etc - // a chance to execute normally in the case that we ran out of - // memory. - // - LLSD info; - std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, - "SecondLifeException"); - std::string log_path = dump_path + ".log"; - - if (exception_infop) - { - // Since there is exception info... Release the hounds. - gEmergencyMemoryReserve.release(); - - if(gSavedSettings.getControl("SaveMinidump").notNull() && gSavedSettings.getBOOL("SaveMinidump")) - { - _MINIDUMP_EXCEPTION_INFORMATION ExInfo; - - ExInfo.ThreadId = ::GetCurrentThreadId(); - ExInfo.ExceptionPointers = exception_infop; - ExInfo.ClientPointers = NULL; - - writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp"); - writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp"); - } - - info = Get_Exception_Info(exception_infop); - } - - LLSD threads; - std::vector thread_ids; - GetProcessThreadIDs(GetCurrentProcessId(), thread_ids); - - for(std::vector::iterator th_itr = thread_ids.begin(); - th_itr != thread_ids.end(); - ++th_itr) - { - LLSD thread_info; - if(*th_itr != GetCurrentThreadId()) - { - GetThreadCallStack(*th_itr, thread_info); - } - - if(thread_info) - { - threads[llformat("ID %d", *th_itr)] = thread_info; - } - } - - info["Threads"] = threads; - - llofstream out_file(log_path); - LLSDSerialize::toPrettyXML(info, out_file); - out_file.close(); -} - -void LLWinDebug::clearCrashStacks() -{ - LLSD info; - std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLifeException.log"); - LLFile::remove(dump_path); -} diff --git a/indra/newview/llwindebug.h b/indra/newview/llwindebug.h deleted file mode 100644 index f4a6a2d54d..0000000000 --- a/indra/newview/llwindebug.h +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @file llwindebug.h - * @brief LLWinDebug class header file - * - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * - * Copyright (c) 2004-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#ifndef LL_LLWINDEBUG_H -#define LL_LLWINDEBUG_H - -#include "stdtypes.h" -#include - -class LLWinDebug -{ -public: - - /** - * @brief initialize the llwindebug exception filter callback - * - * Hand a windows unhandled exception filter to LLWinDebug - * This method should only be called to change the - * exception filter used by llwindebug. - * - * Setting filter_func to NULL will clear any custom filters. - **/ - static void initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func); - - /** - * @brief check the status of the exception filter. - * - * Resets unhandled exception filter to the filter specified - * w/ initExceptionFilter). - * Returns false if the exception filter was modified. - * - * *NOTE:Mani In the past mozlib has been accused of - * overriding the exception filter. If the mozlib filter - * is required, perhaps we can chain calls from our - * filter to mozlib's. - **/ - static bool checkExceptionHandler(); - - static void generateCrashStacks(struct _EXCEPTION_POINTERS *pExceptionInfo = NULL); - static void clearCrashStacks(); // Delete the crash stack file(s). - - static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename); -private: -}; - -#endif // LL_LLWINDEBUG_H -- cgit v1.3 From 32ad37b3f7ca48564bd15de2664f323ad4a2d367 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 24 May 2010 16:21:29 -0700 Subject: Few more touchups for the cert performance code --- indra/newview/llsechandler_basic.cpp | 5 ++--- indra/newview/tests/llsechandler_basic_test.cpp | 10 +++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index a81cde3126..5f24d4398a 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -1006,7 +1006,7 @@ void LLBasicCertificateStore::validate(int validation_policy, const LLSD& validation_params) { - if(size() < 1) + if(cert_chain->size() < 1) { throw LLCertException(NULL, "No certs in chain"); } @@ -1160,9 +1160,8 @@ void LLBasicCertificateStore::validate(int validation_policy, } if (validation_policy & VALIDATION_POLICY_TRUSTED) { - LLPointer untrusted_ca_cert = (*this)[size()-1]; // we reached the end without finding a trusted cert. - throw LLCertValidationTrustException((*this)[size()-1]); + throw LLCertValidationTrustException((*cert_chain)[cert_chain->size()-1]); } mTrustedCertCache[sha1_hash] = std::pair(from_time, to_time); diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index dfbd596d39..df0673a159 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -963,8 +963,15 @@ namespace tut // basic failure cases test_chain = new LLBasicCertificateChain(NULL); - //validate with only the child cert + //validate with only the child cert in chain, but child cert was previously + // trusted test_chain->add(new LLBasicCertificate(mX509ChildCert)); + + // validate without the trust flag. + test_store->validate(VALIDATION_POLICY_TRUSTED, test_chain, validation_params); + + // Validate with child cert but no parent, and no parent in CA store + test_store = new LLBasicCertificateStore("mycertstore.pem"); ensure_throws("no CA, with only a child cert", LLCertValidationTrustException, (*test_chain)[0], @@ -1033,6 +1040,7 @@ namespace tut test_chain = new LLBasicCertificateChain(NULL); test_chain->add(new LLBasicCertificate(mX509TestCert)); + test_store = new LLBasicCertificateStore("mycertstore.pem"); ensure_throws("Cert doesn't have ku", LLCertKeyUsageValidationException, (*test_chain)[0], -- cgit v1.3 From 637b1e4a3a4fd0e4952795aea851643107d4e965 Mon Sep 17 00:00:00 2001 From: "Andrew A. de Laix" Date: Mon, 24 May 2010 16:24:10 -0700 Subject: fix some dos line endings. --- indra/cmake/GoogleBreakpad.cmake | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'indra') diff --git a/indra/cmake/GoogleBreakpad.cmake b/indra/cmake/GoogleBreakpad.cmake index 0d023b20ec..8270c0fabb 100644 --- a/indra/cmake/GoogleBreakpad.cmake +++ b/indra/cmake/GoogleBreakpad.cmake @@ -1,19 +1,19 @@ -# -*- cmake -*- -include(Prebuilt) - -if (STANDALONE) - MESSAGE(FATAL_ERROR "*TODO standalone support for google breakad is unimplemented") - # *TODO - implement this include(FindGoogleBreakpad) -else (STANDALONE) - use_prebuilt_binary(google_breakpad) - if (DARWIN) - set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler) - endif (DARWIN) - if (LINUX) - set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES breakpad_client) - endif (LINUX) - if (WINDOWS) - set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler crash_generation_client common) - endif (WINDOWS) -endif (STANDALONE) - +# -*- cmake -*- +include(Prebuilt) + +if (STANDALONE) + MESSAGE(FATAL_ERROR "*TODO standalone support for google breakad is unimplemented") + # *TODO - implement this include(FindGoogleBreakpad) +else (STANDALONE) + use_prebuilt_binary(google_breakpad) + if (DARWIN) + set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler) + endif (DARWIN) + if (LINUX) + set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES breakpad_client) + endif (LINUX) + if (WINDOWS) + set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler crash_generation_client common) + endif (WINDOWS) +endif (STANDALONE) + -- cgit v1.3 From 7e487652e3b2155571bedc36a4b05001fee35b2e Mon Sep 17 00:00:00 2001 From: Richard Nelson Date: Mon, 24 May 2010 16:30:55 -0700 Subject: DEV-50271 WIP SLURL support for non-clickable names fixed merging logic for blockvalues like rect, uiimage, etc --- indra/llxuixml/llinitparam.cpp | 12 +++---- indra/llxuixml/llinitparam.h | 78 +++++++++++++++++++++--------------------- 2 files changed, 45 insertions(+), 45 deletions(-) (limited to 'indra') diff --git a/indra/llxuixml/llinitparam.cpp b/indra/llxuixml/llinitparam.cpp index 3d26cd2404..8d6aa405e2 100644 --- a/indra/llxuixml/llinitparam.cpp +++ b/indra/llxuixml/llinitparam.cpp @@ -139,7 +139,7 @@ namespace LLInitParam bool BaseBlock::validateBlock(bool emit_errors) const { - const BlockDescriptor& block_data = getBlockDescriptor(); + const BlockDescriptor& block_data = mostDerivedBlockDescriptor(); for (BlockDescriptor::param_validation_list_t::const_iterator it = block_data.mValidationList.begin(); it != block_data.mValidationList.end(); ++it) { const Param* param = getParamFromHandle(it->first); @@ -159,7 +159,7 @@ namespace LLInitParam { // named param is one like LLView::Params::follows // unnamed param is like LLView::Params::rect - implicit - const BlockDescriptor& block_data = getBlockDescriptor(); + const BlockDescriptor& block_data = mostDerivedBlockDescriptor(); for (BlockDescriptor::param_list_t::const_iterator it = block_data.mUnnamedParams.begin(); it != block_data.mUnnamedParams.end(); @@ -230,7 +230,7 @@ namespace LLInitParam { // named param is one like LLView::Params::follows // unnamed param is like LLView::Params::rect - implicit - const BlockDescriptor& block_data = getBlockDescriptor(); + const BlockDescriptor& block_data = mostDerivedBlockDescriptor(); for (BlockDescriptor::param_list_t::const_iterator it = block_data.mUnnamedParams.begin(); it != block_data.mUnnamedParams.end(); @@ -301,7 +301,7 @@ namespace LLInitParam bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack) { - BlockDescriptor& block_data = getBlockDescriptor(); + BlockDescriptor& block_data = mostDerivedBlockDescriptor(); bool names_left = name_stack.first != name_stack.second; if (names_left) @@ -386,7 +386,7 @@ namespace LLInitParam void BaseBlock::addSynonym(Param& param, const std::string& synonym) { - BlockDescriptor& block_data = getBlockDescriptor(); + BlockDescriptor& block_data = mostDerivedBlockDescriptor(); if (block_data.mInitializationState == BlockDescriptor::INITIALIZING) { param_handle_t handle = getHandleFromParam(¶m); @@ -445,7 +445,7 @@ namespace LLInitParam ParamDescriptor* BaseBlock::findParamDescriptor(param_handle_t handle) { - BlockDescriptor& descriptor = getBlockDescriptor(); + BlockDescriptor& descriptor = mostDerivedBlockDescriptor(); BlockDescriptor::all_params_list_t::iterator end_it = descriptor.mAllParams.end(); for (BlockDescriptor::all_params_list_t::iterator it = descriptor.mAllParams.begin(); it != end_it; diff --git a/indra/llxuixml/llinitparam.h b/indra/llxuixml/llinitparam.h index 0e9fd54bc1..3bddd50659 100644 --- a/indra/llxuixml/llinitparam.h +++ b/indra/llxuixml/llinitparam.h @@ -479,8 +479,8 @@ namespace LLInitParam bool serializeBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), const BaseBlock* diff_block = NULL) const; bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t()) const; - const BlockDescriptor& getBlockDescriptor() const { return *mBlockDescriptor; } - BlockDescriptor& getBlockDescriptor() { return *mBlockDescriptor; } + const BlockDescriptor& mostDerivedBlockDescriptor() const { return *mBlockDescriptor; } + BlockDescriptor& mostDerivedBlockDescriptor() { return *mBlockDescriptor; } // take all provided params from other and apply to self bool overwriteFrom(const BaseBlock& other) @@ -507,7 +507,7 @@ namespace LLInitParam BlockDescriptor* mBlockDescriptor; // most derived block descriptor - static BlockDescriptor& blockDescriptor() + static BlockDescriptor& selfBlockDescriptor() { static BlockDescriptor sBlockDescriptor; return sBlockDescriptor; @@ -559,7 +559,7 @@ namespace LLInitParam TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) : Param(block_descriptor.mCurrentBlockPtr) { - if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING) + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), &mergeWith, @@ -723,7 +723,7 @@ namespace LLInitParam : Param(block_descriptor.mCurrentBlockPtr), T(value) { - if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING) + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), &mergeWith, @@ -858,7 +858,7 @@ namespace LLInitParam { const self_t& src_typed_param = static_cast(src); self_t& dst_typed_param = static_cast(dst); - if (dst_typed_param.T::merge(dst_typed_param.BaseBlock::getBlockDescriptor(), src_typed_param, overwrite)) + if (dst_typed_param.T::merge(selfBlockDescriptor(), src_typed_param, overwrite || !dst_typed_param.isProvided())) { dst_typed_param.mData.clearKey(); return true; @@ -902,7 +902,7 @@ namespace LLInitParam mValues(value) { mCachedKeys.resize(mValues.size()); - if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING) + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), &mergeWith, @@ -1085,7 +1085,7 @@ namespace LLInitParam mLastParamGeneration(0) { mCachedKeys.resize(mValues.size()); - if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING) + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), &mergeWith, @@ -1273,24 +1273,23 @@ namespace LLInitParam // take all provided params from other and apply to self bool overwriteFrom(const self_t& other) { - return merge(blockDescriptor(), other, true); + return merge(selfBlockDescriptor(), other, true); } // take all provided params that are not already provided, and apply to self bool fillFrom(const self_t& other) { - return merge(blockDescriptor(), other, false); + return merge(selfBlockDescriptor(), other, false); } // merge with other block bool merge(BlockDescriptor& block_data, const self_t& other, bool overwrite) { // only merge a choice if we are overwriting with other's contents - // or we have never specified a choice locally, other than the default - if (overwrite || getLastChangeVersion() == 0) + if (overwrite) { mCurChoice = other.mCurChoice; - return BaseBlock::merge(blockDescriptor(), other, overwrite); + return BaseBlock::merge(selfBlockDescriptor(), other, overwrite); } return false; } @@ -1317,7 +1316,7 @@ namespace LLInitParam Choice() : mCurChoice(0) { - BaseBlock::init(blockDescriptor(), BaseBlock::blockDescriptor(), sizeof(DERIVED_BLOCK)); + BaseBlock::init(selfBlockDescriptor(), BaseBlock::selfBlockDescriptor(), sizeof(DERIVED_BLOCK)); } // Alternatives are mutually exclusive wrt other Alternatives in the same block. @@ -1334,13 +1333,14 @@ namespace LLInitParam typedef typename super_t::value_assignment_t value_assignment_t; explicit Alternative(const char* name, value_assignment_t val = DefaultInitializer::get()) - : super_t(DERIVED_BLOCK::blockDescriptor(), name, val, NULL, 0, 1), + : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, NULL, 0, 1), mOriginalValue(val) { // assign initial choice to first declared option - DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::blockDescriptor().mCurrentBlockPtr); - if (DERIVED_BLOCK::blockDescriptor().mInitializationState == BlockDescriptor::INITIALIZING - && blockp->mCurChoice == 0) + DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::selfBlockDescriptor().mCurrentBlockPtr); + if (LL_UNLIKELY( + DERIVED_BLOCK::selfBlockDescriptor().mInitializationState == BlockDescriptor::INITIALIZING + && blockp->mCurChoice == 0)) { blockp->mCurChoice = Param::enclosingBlock().getHandleFromParam(this); } @@ -1385,7 +1385,7 @@ namespace LLInitParam }; protected: - static BlockDescriptor& blockDescriptor() + static BlockDescriptor& selfBlockDescriptor() { static BlockDescriptor sBlockDescriptor; return sBlockDescriptor; @@ -1413,19 +1413,19 @@ namespace LLInitParam // take all provided params from other and apply to self bool overwriteFrom(const self_t& other) { - return BaseBlock::merge(blockDescriptor(), other, true); + return BaseBlock::merge(selfBlockDescriptor(), other, true); } // take all provided params that are not already provided, and apply to self bool fillFrom(const self_t& other) { - return BaseBlock::merge(blockDescriptor(), other, false); + return BaseBlock::merge(selfBlockDescriptor(), other, false); } protected: Block() { //#pragma message("Parsing LLInitParam::Block") - BaseBlock::init(blockDescriptor(), BASE_BLOCK::blockDescriptor(), sizeof(DERIVED_BLOCK)); + BaseBlock::init(selfBlockDescriptor(), BASE_BLOCK::selfBlockDescriptor(), sizeof(DERIVED_BLOCK)); } // @@ -1439,7 +1439,7 @@ namespace LLInitParam typedef typename super_t::value_assignment_t value_assignment_t; explicit Optional(const char* name = "", value_assignment_t val = DefaultInitializer::get()) - : super_t(DERIVED_BLOCK::blockDescriptor(), name, val, NULL, 0, 1) + : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, NULL, 0, 1) { //#pragma message("Parsing LLInitParam::Block::Optional") } @@ -1468,7 +1468,7 @@ namespace LLInitParam // mandatory parameters require a name to be parseable explicit Mandatory(const char* name = "", value_assignment_t val = DefaultInitializer::get()) - : super_t(DERIVED_BLOCK::blockDescriptor(), name, val, &validate, 1, 1) + : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, &validate, 1, 1) {} Mandatory& operator=(value_assignment_t val) @@ -1504,7 +1504,7 @@ namespace LLInitParam typedef typename container_t::const_iterator const_iterator; explicit Multiple(const char* name = "", value_assignment_t val = DefaultInitializer::get()) - : super_t(DERIVED_BLOCK::blockDescriptor(), name, val, &validate, RANGE::minCount(), RANGE::maxCount()) + : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, &validate, RANGE::minCount(), RANGE::maxCount()) {} using super_t::operator(); @@ -1532,10 +1532,10 @@ namespace LLInitParam { public: explicit Deprecated(const char* name) - : Param(DERIVED_BLOCK::blockDescriptor().mCurrentBlockPtr) + : Param(DERIVED_BLOCK::selfBlockDescriptor().mCurrentBlockPtr) { - BlockDescriptor& block_descriptor = DERIVED_BLOCK::blockDescriptor(); - if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING) + BlockDescriptor& block_descriptor = DERIVED_BLOCK::selfBlockDescriptor(); + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), NULL, @@ -1564,7 +1564,7 @@ namespace LLInitParam typedef Deprecated Ignored; protected: - static BlockDescriptor& blockDescriptor() + static BlockDescriptor& selfBlockDescriptor() { static BlockDescriptor sBlockDescriptor; return sBlockDescriptor; @@ -1577,12 +1577,12 @@ namespace LLInitParam public Param { public: - typedef enum e_value_valid - { + typedef enum e_value_age + { OLDER_THAN_BLOCK, // mData.mValue needs to be refreshed from the block parameters NEWER_THAN_BLOCK, // mData.mValue holds the authoritative value (which has been replicated to the block parameters via setBlockFromValue) SAME_AS_BLOCK // mData.mValue is derived from the block parameters, which are authoritative - } EValueValid; + } EValueAge; typedef BlockValue self_t; typedef Block, false> > block_t; @@ -1592,9 +1592,9 @@ namespace LLInitParam BlockValue(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) : Param(block_descriptor.mCurrentBlockPtr), - mData(value) + mData(value, NEWER_THAN_BLOCK) { - if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING) + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), &mergeWith, @@ -1802,13 +1802,13 @@ namespace LLInitParam struct Data : public key_cache_t { - Data(const T& value) + Data(const T& value, EValueAge age) : mValue(value), - mValueAge(SAME_AS_BLOCK) + mValueAge(age) {} - T mValue; - EValueValid mValueAge; + T mValue; + EValueAge mValueAge; }; // mutable to allow lazy updates on get @@ -1831,7 +1831,7 @@ namespace LLInitParam else { // merge individual parameters into destination - dst_typed_param.merge(block_t::blockDescriptor(), src_typed_param, overwrite); + dst_typed_param.merge(block_t::selfBlockDescriptor(), src_typed_param, overwrite); } return true; } -- cgit v1.3 From 8f4c8ebcd55c1ad384303802faaa10e33247914f Mon Sep 17 00:00:00 2001 From: "Andrew A. de Laix" Date: Mon, 24 May 2010 16:52:12 -0700 Subject: fix darwin build. --- indra/llcommon/llapp.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'indra') diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index e22ff869e7..9e0e8ea814 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -804,6 +804,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *) #endif // !WINDOWS +#ifdef LL_DARWIN bool darwin_post_minidump_callback(const char *dump_dir, const char *minidump_id, void *context, bool succeeded) @@ -832,7 +833,9 @@ bool darwin_post_minidump_callback(const char *dump_dir, LLApp::runErrorHandler(); return true; } +#endif +#ifdef LL_WINDOWS bool windows_post_minidump_callback(const wchar_t* dump_path, const wchar_t* minidump_id, void* context, @@ -882,3 +885,4 @@ bool windows_post_minidump_callback(const wchar_t* dump_path, return true; } +#endif \ No newline at end of file -- cgit v1.3 From 45a86b67518a579b166e1cf6a719d4aed4c35a39 Mon Sep 17 00:00:00 2001 From: "Andrew A. de Laix" Date: Mon, 24 May 2010 16:53:10 -0700 Subject: fix eof newline --- indra/llcommon/llapp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 9e0e8ea814..9ea1a18e5f 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -885,4 +885,4 @@ bool windows_post_minidump_callback(const wchar_t* dump_path, return true; } -#endif \ No newline at end of file +#endif -- cgit v1.3 From 563ae1c7e029ceef2aed0b85623c2f0a95d38b46 Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Tue, 25 May 2010 02:47:28 +0100 Subject: EXT-7337 WIP Replace the new column with a (New!) tag after the voice effect name --- indra/newview/llfloatervoiceeffect.cpp | 24 ++++++++++++---------- .../skins/default/xui/en/floater_voice_effect.xml | 12 ++++------- 2 files changed, 17 insertions(+), 19 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloatervoiceeffect.cpp b/indra/newview/llfloatervoiceeffect.cpp index 9529f947c1..46356cf161 100644 --- a/indra/newview/llfloatervoiceeffect.cpp +++ b/indra/newview/llfloatervoiceeffect.cpp @@ -145,6 +145,7 @@ void LLFloaterVoiceEffect::refreshEffectList() } } + // Add each Voice Effect template, if there are any (template list includes all usable effects) const voice_effect_list_t& template_list = effect_interface->getVoiceEffectTemplateList(); if (!template_list.empty()) { @@ -161,15 +162,21 @@ void LLFloaterVoiceEffect::refreshEffectList() effect_name += " " + getString("active_voice_effect"); } + // Tag available effects that are new this session + if (effect_properties["is_new"].asBoolean()) + { + effect_name += " " + getString("new_voice_effect"); + } + LLDate expiry_date = effect_properties["expiry_date"].asDate(); bool is_template_only = effect_properties["template_only"].asBoolean(); - bool is_new = effect_properties["is_new"].asBoolean(); std::string font_style = "NORMAL"; if (!is_template_only) { font_style = "BOLD"; } + LLSD element; element["id"] = effect_id; @@ -178,21 +185,16 @@ void LLFloaterVoiceEffect::refreshEffectList() element["columns"][0]["font"]["name"] = "SANSSERIF"; element["columns"][0]["font"]["style"] = font_style; - element["columns"][1]["column"] = "new"; - element["columns"][1]["value"] = is_new ? getString("new_voice_effect") : ""; - element["columns"][1]["font"]["name"] = "SANSSERIF"; - element["columns"][1]["font"]["style"] = font_style; - - element["columns"][2]["column"] = "expires"; + element["columns"][1]["column"] = "expires"; if (!is_template_only) { - element["columns"][2]["value"] = expiry_date; + element["columns"][1]["value"] = expiry_date; } else { - element["columns"][2]["value"] = ""; + element["columns"][1]["value"] = ""; } - element["columns"][2]["font"]["name"] = "SANSSERIF"; - element["columns"][2]["font"]["style"] = font_style; + element["columns"][1]["font"]["name"] = "SANSSERIF"; + element["columns"][1]["font"]["style"] = font_style; LLScrollListItem* sl_item = mVoiceEffectList->addElement(element, ADD_BOTTOM); // *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :( diff --git a/indra/newview/skins/default/xui/en/floater_voice_effect.xml b/indra/newview/skins/default/xui/en/floater_voice_effect.xml index 73e4a694eb..f79d4fd35c 100644 --- a/indra/newview/skins/default/xui/en/floater_voice_effect.xml +++ b/indra/newview/skins/default/xui/en/floater_voice_effect.xml @@ -20,7 +20,7 @@ (Active) - New! + (New!) - + dynamic_width="true" + label="Voice Effect" + name="name"/> Date: Tue, 25 May 2010 03:36:36 +0100 Subject: EXT-7337 WIP Further tweaking of Voice Effect preview layout --- indra/newview/llfloatervoiceeffect.cpp | 5 ++- .../skins/default/xui/en/floater_voice_effect.xml | 51 +++++++++++----------- 2 files changed, 29 insertions(+), 27 deletions(-) (limited to 'indra') diff --git a/indra/newview/llfloatervoiceeffect.cpp b/indra/newview/llfloatervoiceeffect.cpp index 46356cf161..b47d562995 100644 --- a/indra/newview/llfloatervoiceeffect.cpp +++ b/indra/newview/llfloatervoiceeffect.cpp @@ -189,19 +189,20 @@ void LLFloaterVoiceEffect::refreshEffectList() if (!is_template_only) { element["columns"][1]["value"] = expiry_date; + element["columns"][1]["type"] = "date"; } else { element["columns"][1]["value"] = ""; } element["columns"][1]["font"]["name"] = "SANSSERIF"; - element["columns"][1]["font"]["style"] = font_style; + element["columns"][1]["font"]["style"] = "NORMAL"; LLScrollListItem* sl_item = mVoiceEffectList->addElement(element, ADD_BOTTOM); // *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :( if(sl_item) { LLFontGL::StyleFlags style = is_template_only ? LLFontGL::NORMAL : LLFontGL::BOLD; - ((LLScrollListText*)sl_item->getColumn(0))->setFontStyle(style); + dynamic_cast(sl_item->getColumn(0))->setFontStyle(style); } } } diff --git a/indra/newview/skins/default/xui/en/floater_voice_effect.xml b/indra/newview/skins/default/xui/en/floater_voice_effect.xml index f79d4fd35c..4f4b20066d 100644 --- a/indra/newview/skins/default/xui/en/floater_voice_effect.xml +++ b/indra/newview/skins/default/xui/en/floater_voice_effect.xml @@ -11,8 +11,8 @@ label="Places" layout="topleft" min_height="350" - min_width="240" - width="300"> + min_width="300" + width="455"> (No Voice Effect) @@ -33,18 +33,19 @@ name="status_text" right="-10" top="25"> - Record a sample of your voice, and then select an effect to preview. Close this window to return to in-world voice. +Record a sample of your voice, and then select an effect to preview. +Close this window to return to in-world voice. @@ -56,41 +57,41 @@ left_delta="0" name="record_stop_btn" top_delta="0" - width="135"> + width="150"> + + [[URL] Get Voice Morphing] + + name="name" relative_width="0.41"/> + relative_width="0.59" /> - - [[URL] Get Voice Morphing] - -- cgit v1.3 From 2a99af8d949b982ea5832735c61ca249554f5b02 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Tue, 25 May 2010 06:26:10 +0100 Subject: Fix linux build from llinitparam problem... probably. --- indra/llxuixml/llinitparam.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llxuixml/llinitparam.h b/indra/llxuixml/llinitparam.h index 3bddd50659..582f5449d9 100644 --- a/indra/llxuixml/llinitparam.h +++ b/indra/llxuixml/llinitparam.h @@ -858,7 +858,7 @@ namespace LLInitParam { const self_t& src_typed_param = static_cast(src); self_t& dst_typed_param = static_cast(dst); - if (dst_typed_param.T::merge(selfBlockDescriptor(), src_typed_param, overwrite || !dst_typed_param.isProvided())) + if (dst_typed_param.T::merge(BaseBlock::selfBlockDescriptor(), src_typed_param, overwrite || !dst_typed_param.isProvided())) { dst_typed_param.mData.clearKey(); return true; -- cgit v1.3 From cb0589715265dc1568626fe238aac7417b44ef89 Mon Sep 17 00:00:00 2001 From: Vadim Savchuk Date: Tue, 25 May 2010 14:17:11 +0300 Subject: EXT-6726 WIP Added handlers for most of Appearance SP context/gear menus. Reviewed by Mike Antipov and Nyx at https://codereview.productengine.com/secondlife/r/428/ --HG-- branch : product-engine --- indra/llui/llview.cpp | 2 - indra/newview/llagentwearables.cpp | 62 +++++++++-- indra/newview/llagentwearables.h | 14 ++- indra/newview/llappearancemgr.cpp | 88 +++++++++++----- indra/newview/llappearancemgr.h | 6 +- indra/newview/llcofwearables.cpp | 69 ++++++++++++ indra/newview/llinventorybridge.cpp | 15 +-- indra/newview/llinventoryfunctions.cpp | 29 ++++++ indra/newview/llinventoryfunctions.h | 14 +++ indra/newview/llinventorymodel.cpp | 5 + indra/newview/llinventorymodel.h | 1 + indra/newview/lloutfitslist.cpp | 15 +++ indra/newview/llpanelobjectinventory.cpp | 6 +- indra/newview/llwearableitemslist.cpp | 116 ++++++++++++++++++++- indra/newview/llwearableitemslist.h | 9 ++ .../skins/default/xui/en/panel_outfit_edit.xml | 1 + 16 files changed, 397 insertions(+), 55 deletions(-) (limited to 'indra') diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index c091686ffb..bd56da9121 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -1319,8 +1319,6 @@ void LLView::drawChildren() if (viewp->getVisible() && viewp->getRect().isValid()) { - // check for bad data - llassert_always(viewp->getVisible() == TRUE); // Only draw views that are within the root view localRectToScreen(viewp->getRect(),&screenRect); if ( rootRect.overlaps(screenRect) && LLUI::sDirtyRect.overlaps(screenRect)) diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 6b5e43973e..68c4fa1ea0 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -49,6 +49,7 @@ #include "llmd5.h" #include "llnotificationsutil.h" #include "llpaneloutfitsinventory.h" +#include "llsidepanelappearance.h" #include "llsidetray.h" #include "lltexlayer.h" #include "llviewerregion.h" @@ -539,9 +540,15 @@ void LLAgentWearables::setWearableName(const LLUUID& item_id, const std::string& BOOL LLAgentWearables::isWearableModifiable(LLWearableType::EType type, U32 index) const { LLUUID item_id = getWearableItemID(type, index); - if (!item_id.isNull()) + return item_id.notNull() ? isWearableModifiable(item_id) : FALSE; +} + +BOOL LLAgentWearables::isWearableModifiable(const LLUUID& item_id) const +{ + const LLUUID& linked_id = gInventory.getLinkedItemID(item_id); + if (linked_id.notNull()) { - LLInventoryItem* item = gInventory.getItem(item_id); + LLInventoryItem* item = gInventory.getItem(linked_id); if (item && item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID())) { @@ -595,12 +602,13 @@ LLInventoryItem* LLAgentWearables::getWearableInventoryItem(LLWearableType::ETyp const LLWearable* LLAgentWearables::getWearableFromItemID(const LLUUID& item_id) const { + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); for (S32 i=0; i < LLWearableType::WT_COUNT; i++) { for (U32 j=0; j < getWearableCount((LLWearableType::EType)i); j++) { const LLWearable * curr_wearable = getWearable((LLWearableType::EType)i, j); - if (curr_wearable && (curr_wearable->getItemID() == item_id)) + if (curr_wearable && (curr_wearable->getItemID() == base_item_id)) { return curr_wearable; } @@ -812,6 +820,16 @@ LLWearable* LLAgentWearables::getTopWearable(const LLWearableType::EType type) return getWearable(type, count-1); } +LLWearable* LLAgentWearables::getBottomWearable(const LLWearableType::EType type) +{ + if (getWearableCount(type) == 0) + { + return NULL; + } + + return getWearable(type, 0); +} + U32 LLAgentWearables::getWearableCount(const LLWearableType::EType type) const { wearableentry_map_t::const_iterator wearable_iter = mWearableDatas.find(type); @@ -860,12 +878,7 @@ const LLUUID LLAgentWearables::getWearableAssetID(LLWearableType::EType type, U3 BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id) const { - const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); - if (getWearableFromItemID(base_item_id) != NULL) - { - return TRUE; - } - return FALSE; + return getWearableFromItemID(item_id) != NULL; } // MULTI-WEARABLE: DEPRECATED (see backwards compatibility) @@ -1862,6 +1875,20 @@ void LLAgentWearables::checkWearablesLoaded() const #endif } +// Returns false if the given wearable is already topmost/bottommost +// (depending on closer_to_body parameter). +bool LLAgentWearables::canMoveWearable(const LLUUID& item_id, bool closer_to_body) +{ + const LLWearable* wearable = getWearableFromItemID(item_id); + if (!wearable) return false; + + LLWearableType::EType wtype = wearable->getType(); + const LLWearable* marginal_wearable = closer_to_body ? getBottomWearable(wtype) : getTopWearable(wtype); + if (!marginal_wearable) return false; + + return wearable != marginal_wearable; +} + BOOL LLAgentWearables::areWearablesLoaded() const { checkWearablesLoaded(); @@ -1932,6 +1959,23 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos return false; } +// static +void LLAgentWearables::editWearable(const LLUUID& item_id) +{ + LLViewerInventoryItem* item; + LLWearable* wearable; + + if ((item = gInventory.getLinkedItem(item_id)) && + (wearable = gAgentWearables.getWearableFromAssetID(item->getAssetUUID())) && + gAgentWearables.isWearableModifiable(item->getUUID()) && + item->isFinished()) + { + LLPanel* panel = LLSideTray::getInstance()->showPanel("panel_outfit_edit", LLSD()); + // copied from LLPanelOutfitEdit::onEditWearableClicked() + LLSidepanelAppearance::editWearable(wearable, panel->getParent()); + } +} + void LLAgentWearables::updateServer() { sendAgentWearablesUpdate(); diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 734bd9fd47..1f19d1045b 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -71,17 +71,18 @@ protected: public: BOOL isWearingItem(const LLUUID& item_id) const; BOOL isWearableModifiable(LLWearableType::EType type, U32 index /*= 0*/) const; + BOOL isWearableModifiable(const LLUUID& item_id) const; + BOOL isWearableCopyable(LLWearableType::EType type, U32 index /*= 0*/) const; BOOL areWearablesLoaded() const; void updateWearablesLoaded(); void checkWearablesLoaded() const; + bool canMoveWearable(const LLUUID& item_id, bool closer_to_body); // Note: False for shape, skin, eyes, and hair, unless you have MORE than 1. bool canWearableBeRemoved(const LLWearable* wearable) const; void animateAllWearableParams(F32 delta, BOOL upload_bake); - - bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); //-------------------------------------------------------------------- // Accessors @@ -96,6 +97,7 @@ public: LLWearable* getWearable(const LLWearableType::EType type, U32 index /*= 0*/); const LLWearable* getWearable(const LLWearableType::EType type, U32 index /*= 0*/) const; LLWearable* getTopWearable(const LLWearableType::EType type); + LLWearable* getBottomWearable(const LLWearableType::EType type); U32 getWearableCount(const LLWearableType::EType type) const; U32 getWearableCount(const U32 tex_index) const; @@ -133,6 +135,14 @@ protected: void recoverMissingWearable(const LLWearableType::EType type, U32 index /*= 0*/); void recoverMissingWearableDone(); + //-------------------------------------------------------------------- + // Editing/moving wearables + //-------------------------------------------------------------------- + +public: + static void editWearable(const LLUUID& item_id); + bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); + //-------------------------------------------------------------------- // Removing wearables //-------------------------------------------------------------------- diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index dcef86a5fc..2eb7cfd34a 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -152,27 +152,6 @@ public: }; -//Inventory collect functor collecting wearables of a specific wearable type -class LLFindClothesOfType : public LLInventoryCollectFunctor -{ -public: - LLFindClothesOfType(LLWearableType::EType type) : mWearableType(type) {} - virtual ~LLFindClothesOfType() {} - virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) - { - if (!item) return false; - if (item->getType() != LLAssetType::AT_CLOTHING) return false; - - LLViewerInventoryItem *vitem = dynamic_cast(item); - if (!vitem || vitem->getWearableType() != mWearableType) return false; - - return true; - } - - const LLWearableType::EType mWearableType; -}; - - LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(): mFireCount(0) { @@ -671,7 +650,7 @@ const LLUUID LLAppearanceMgr::getBaseOutfitUUID() return outfit_cat->getUUID(); } -bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update) +bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace) { if (item_id_to_wear.isNull()) return false; @@ -692,6 +671,14 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up LLNotificationsUtil::add("CanNotChangeAppearanceUntilLoaded"); return false; } + + // Remove the existing wearables of the same type. + // Remove existing body parts anyway because we must not be able to wear e.g. two skins. + if (replace || item_to_wear->getType() == LLAssetType::AT_BODYPART) + { + removeCOFLinksOfType(item_to_wear->getWearableType(), false); + } + addCOFItemLink(item_to_wear, do_update); break; case LLAssetType::AT_OBJECT: @@ -711,6 +698,35 @@ void LLAppearanceMgr::changeOutfit(bool proceed, const LLUUID& category, bool ap LLAppearanceMgr::instance().updateCOF(category,append); } +void LLAppearanceMgr::replaceCurrentOutfit(const LLUUID& new_outfit) +{ + LLViewerInventoryCategory* cat = gInventory.getCategory(new_outfit); + wearInventoryCategory(cat, false, false); +} + +void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id) +{ + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + wearInventoryCategory(cat, false, true); +} + +void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWearables collector; + + gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector); + + LLInventoryModel::item_array_t::const_iterator it = items.begin(); + const LLInventoryModel::item_array_t::const_iterator it_end = items.end(); + for( ; it_end != it; ++it) + { + LLViewerInventoryItem* item = *it; + removeItemFromAvatar(item->getUUID()); + } +} + // Create a copy of src_id + contents as a subfolder of dst_id. void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id, LLPointer cb) @@ -1563,6 +1579,29 @@ void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, bool do_update) } } +void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, bool do_update) +{ + LLFindWearablesOfType filter_wearables_of_type(type); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLInventoryModel::item_array_t::const_iterator it; + + gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type); + for (it = items.begin(); it != items.end(); ++it) + { + const LLViewerInventoryItem* item = *it; + if (item->getIsLinkType()) // we must operate on links only + { + gInventory.purgeObject(item->getUUID()); + } + } + + if (do_update) + { + updateAppearanceFromCOF(); + } +} + bool sort_by_linked_uuid(const LLViewerInventoryItem* item1, const LLViewerInventoryItem* item2) { if (!item1 || !item2) @@ -1893,7 +1932,6 @@ void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove) } } - bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body) { if (!item || !item->isWearableType()) return false; @@ -1902,11 +1940,11 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; - LLFindClothesOfType filter_wearables_of_type(item->getWearableType()); + LLFindWearablesOfType filter_wearables_of_type(item->getWearableType()); gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type); if (items.empty()) return false; - //*TODO all items are not guarantied to have valid descriptions (check?) + // We assume that the items have valid descriptions. std::sort(items.begin(), items.end(), WearablesOrderComparator(item->getWearableType())); if (closer_to_body && items.front() == item) return false; diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index 516dada39d..96541beb7d 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -58,6 +58,9 @@ public: void wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append); void wearOutfitByName(const std::string& name); void changeOutfit(bool proceed, const LLUUID& category, bool append); + void replaceCurrentOutfit(const LLUUID& new_outfit); + void takeOffOutfit(const LLUUID& cat_id); + void addCategoryToCurrentOutfit(const LLUUID& cat_id); // Copy all items and the src category itself. void shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id, @@ -81,7 +84,7 @@ public: const LLUUID getBaseOutfitUUID(); // Wear/attach an item (from a user's inventory) on the agent - bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update = true); + bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update = true, bool replace = false); // Update the displayed outfit name in UI. void updatePanelOutfitName(const std::string& name); @@ -111,6 +114,7 @@ public: // Remove COF entries void removeCOFItemLinks(const LLUUID& item_id, bool do_update = true); + void removeCOFLinksOfType(LLWearableType::EType type, bool do_update = true); // Add COF link to ensemble folder. void addEnsembleLink(LLInventoryCategory* item, bool do_update = true); diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp index 47862ad921..dfc203111a 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -61,6 +61,11 @@ protected: /*virtual*/ LLContextMenu* createMenu() { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + + functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1); + registrar.add("Attachment.Detach", boost::bind(handleMultiple, take_off, mUUIDs)); + return createFromFile("menu_cof_attachment.xml"); } }; @@ -73,8 +78,49 @@ protected: /*virtual*/ LLContextMenu* createMenu() { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + LLUUID selected_id = mUUIDs.back(); + functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1); + + registrar.add("Clothing.TakeOff", boost::bind(handleMultiple, take_off, mUUIDs)); + registrar.add("Clothing.MoveUp", boost::bind(moveWearable, selected_id, false)); + registrar.add("Clothing.MoveDown", boost::bind(moveWearable, selected_id, true)); + registrar.add("Clothing.Edit", boost::bind(LLAgentWearables::editWearable, selected_id)); + + enable_registrar.add("Clothing.OnEnable", boost::bind(&CofClothingContextMenu::onEnable, this, _2)); + return createFromFile("menu_cof_clothing.xml"); } + + bool onEnable(const LLSD& data) + { + std::string param = data.asString(); + LLUUID selected_id = mUUIDs.back(); + + if ("move_up" == param) + { + return gAgentWearables.canMoveWearable(selected_id, false); + } + else if ("move_down" == param) + { + return gAgentWearables.canMoveWearable(selected_id, true); + } + else if ("edit" == param) + { + return gAgentWearables.isWearableModifiable(selected_id); + } + return true; + } + + // We don't use LLAppearanceMgr::moveWearable() directly because + // the item may be invalidated between setting the callback and calling it. + static bool moveWearable(const LLUUID& item_id, bool closer_to_body) + { + LLViewerInventoryItem* item = gInventory.getItem(item_id); + return LLAppearanceMgr::instance().moveWearable(item, closer_to_body); + } + }; ////////////////////////////////////////////////////////////////////////// @@ -85,8 +131,31 @@ protected: /*virtual*/ LLContextMenu* createMenu() { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + LLUUID selected_id = mUUIDs.back(); + + registrar.add("BodyPart.Replace", boost::bind(&LLAppearanceMgr::wearItemOnAvatar, + LLAppearanceMgr::getInstance(), selected_id, true, true)); + registrar.add("BodyPart.Edit", boost::bind(LLAgentWearables::editWearable, selected_id)); + + enable_registrar.add("BodyPart.OnEnable", boost::bind(&CofBodyPartContextMenu::onEnable, this, _2)); + return createFromFile("menu_cof_body_part.xml"); } + + bool onEnable(const LLSD& data) + { + std::string param = data.asString(); + LLUUID selected_id = mUUIDs.back(); + + if ("edit" == param) + { + return gAgentWearables.isWearableModifiable(selected_id); + } + + return true; + } }; ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 2d27c89074..35f7cbcd01 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -222,9 +222,7 @@ void LLInvFVBridge::cutToClipboard() // *TODO: make sure this does the right thing void LLInvFVBridge::showProperties() { - LLSD key; - key["id"] = mUUID; - LLSideTray::getInstance()->showPanel("sidepanel_inventory", key); + show_item_profile(mUUID); // Disable old properties floater; this is replaced by the sidepanel. /* @@ -4150,9 +4148,7 @@ void LLObjectBridge::openItem() LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); } - LLSD key; - key["id"] = mUUID; - LLSideTray::getInstance()->showPanel("sidepanel_inventory", key); + show_item_profile(mUUID); // Disable old properties floater; this is replaced by the sidepanel. /* @@ -4434,7 +4430,7 @@ void wear_inventory_item_on_avatar( LLInventoryItem* item ) lldebugs << "wear_inventory_item_on_avatar( " << item->getName() << " )" << llendl; - LLAppearanceMgr::instance().addCOFItemLink(item); + LLAppearanceMgr::getInstance()->wearItemOnAvatar(item->getUUID(), true, false); } } @@ -4892,8 +4888,7 @@ void LLWearableBridge::onEditOnAvatar(void* user_data) void LLWearableBridge::editOnAvatar() { - LLUUID linked_id = gInventory.getLinkedItemID(mUUID); - const LLWearable* wearable = gAgentWearables.getWearableFromItemID(linked_id); + const LLWearable* wearable = gAgentWearables.getWearableFromItemID(mUUID); if( wearable ) { // Set the tab to the right wearable. @@ -4983,7 +4978,7 @@ void LLWearableBridge::removeAllClothesFromAvatar() gAgentWearables.getWearableInventoryItem((LLWearableType::EType)itype, index)); if (!item) continue; - const LLUUID &item_id = gInventory.getLinkedItemID(item->getUUID()); + const LLUUID &item_id = item->getUUID(); const LLWearable *wearable = gAgentWearables.getWearableFromItemID(item_id); if (!wearable) continue; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 2b4d9fb25c..c38d45f0f5 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -74,6 +74,7 @@ #include "llscrollbar.h" #include "llscrollcontainer.h" #include "llselectmgr.h" +#include "llsidetray.h" #include "lltabcontainer.h" #include "lltooldraganddrop.h" #include "lluictrlfactory.h" @@ -160,6 +161,19 @@ BOOL get_is_item_worn(const LLUUID& id) return FALSE; } +void show_item_profile(const LLUUID& item_uuid) +{ + LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid); + LLSideTray::getInstance()->showPanel("sidepanel_inventory", LLSD().with("id", linked_uuid)); +} + +void show_item_original(const LLUUID& item_uuid) +{ + LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if (!active_panel) return; + active_panel->setSelection(gInventory.getLinkedItemID(item_uuid), TAKE_FOCUS_NO); +} + ///---------------------------------------------------------------------------- /// LLInventoryCollectFunctor implementations ///---------------------------------------------------------------------------- @@ -343,6 +357,21 @@ bool LLFindWearables::operator()(LLInventoryCategory* cat, return FALSE; } +bool LLFindWearablesOfType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + if (!item) return false; + if (item->getType() != LLAssetType::AT_CLOTHING && + item->getType() != LLAssetType::AT_BODYPART) + { + return false; + } + + LLViewerInventoryItem *vitem = dynamic_cast(item); + if (!vitem || vitem->getWearableType() != mWearableType) return false; + + return true; +} + ///---------------------------------------------------------------------------- /// LLAssetIDMatches ///---------------------------------------------------------------------------- diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 79b9b4a9cc..8b96ba29d9 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -46,6 +46,9 @@ // Is this item or its baseitem is worn, attached, etc... BOOL get_is_item_worn(const LLUUID& id); +void show_item_profile(const LLUUID& item_uuid); + +void show_item_original(const LLUUID& item_uuid); void change_item_parent(LLInventoryModel* model, LLViewerInventoryItem* item, @@ -262,6 +265,17 @@ public: LLInventoryItem* item); }; +//Inventory collect functor collecting wearables of a specific wearable type +class LLFindWearablesOfType : public LLInventoryCollectFunctor +{ +public: + LLFindWearablesOfType(LLWearableType::EType type) : mWearableType(type) {} + virtual ~LLFindWearablesOfType() {} + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); + + const LLWearableType::EType mWearableType; +}; + /** Inventory Collector Functions ** ** *******************************************************************************/ diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index c373512ace..23df6b6cc5 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -566,6 +566,11 @@ const LLUUID& LLInventoryModel::getLinkedItemID(const LLUUID& object_id) const return item->getLinkedUUID(); } +LLViewerInventoryItem* LLInventoryModel::getLinkedItem(const LLUUID& object_id) const +{ + return object_id.notNull() ? getItem(getLinkedItemID(object_id)) : NULL; +} + LLInventoryModel::item_array_t LLInventoryModel::collectLinkedItems(const LLUUID& id, const LLUUID& start_folder_id) { diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 1f7bd50328..7b56d0bdd1 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -258,6 +258,7 @@ public: // Get the inventoryID or item that this item points to, else just return object_id const LLUUID& getLinkedItemID(const LLUUID& object_id) const; + LLViewerInventoryItem* getLinkedItem(const LLUUID& object_id) const; private: mutable LLPointer mLastItem; // cache recent lookups diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 36832c9d16..17a2db7a43 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -38,6 +38,7 @@ #include "llaccordionctrl.h" #include "llaccordionctrltab.h" +#include "llagentwearables.h" #include "llappearancemgr.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" @@ -55,6 +56,20 @@ class OutfitContextMenu : public LLListContextMenu protected: /* virtual */ LLContextMenu* createMenu() { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + LLUUID selected_id = mUUIDs.front(); + + registrar.add("Outfit.WearReplace", + boost::bind(&LLAppearanceMgr::replaceCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); + registrar.add("Outfit.WearAdd", + boost::bind(&LLAppearanceMgr::addCategoryToCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); + registrar.add("Outfit.TakeOff", + boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id)); + // *TODO: implement this + // registrar.add("Outfit.Rename", boost::bind()); + // registrar.add("Outfit.Delete", boost::bind()); + return createFromFile("menu_outfit_tab.xml"); } }; diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 39ade40967..0d3beaa9a5 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -174,11 +174,7 @@ LLInventoryItem* LLTaskInvFVBridge::findItem() const void LLTaskInvFVBridge::showProperties() { - LLSD key; - key["object"] = mPanel->getTaskUUID(); - key["id"] = mUUID; - LLSideTray::getInstance()->showPanel("sidepanel_inventory", key); - + show_item_profile(mUUID); // Disable old properties floater; this is replaced by the sidepanel. /* diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index 5836252eac..fb7577c008 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -36,6 +36,7 @@ #include "lliconctrl.h" #include "llagentwearables.h" +#include "llappearancemgr.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" #include "llmenugl.h" // for LLContextMenu @@ -433,7 +434,120 @@ void LLWearableItemsList::onRightClick(S32 x, S32 y) // virtual LLContextMenu* LLWearableItemsList::ContextMenu::createMenu() { - return createFromFile("menu_wearable_list_item.xml"); + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + const uuid_vec_t& ids = mUUIDs; // selected items IDs + LLUUID selected_id = ids.front(); // ID of the first selected item + + functor_t wear = boost::bind(&LLAppearanceMgr::wearItemOnAvatar, LLAppearanceMgr::getInstance(), _1, true, false); + functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1); + + // Register handlers common for all wearable types. + registrar.add("Wearable.Wear", boost::bind(handleMultiple, wear, ids)); + registrar.add("Wearable.Edit", boost::bind(handleMultiple, LLAgentWearables::editWearable, ids)); + registrar.add("Wearable.ShowOriginal", boost::bind(show_item_original, selected_id)); + + // Register handlers for clothing. + registrar.add("Clothing.TakeOff", boost::bind(handleMultiple, take_off, ids)); + + // Register handlers for body parts. + + // Register handlers for attachments. + registrar.add("Attachment.Detach", boost::bind(handleMultiple, take_off, ids)); + registrar.add("Attachment.Profile", boost::bind(show_item_profile, selected_id)); + + // Create the menu. + LLContextMenu* menu = createFromFile("menu_wearable_list_item.xml"); + + // Determine which items should be visible/enabled. + updateItemsVisibility(menu); + return menu; +} + +void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu) +{ + if (!menu) + { + llwarns << "Invalid menu" << llendl; + return; + } + + const uuid_vec_t& ids = mUUIDs; // selected items IDs + U32 mask = 0; // mask of selected items' types + U32 nitems = ids.size(); // number of selected items + U32 nworn = 0; // number of worn items among the selected ones + U32 nwornlinks = 0; // number of worn links among the selected items + U32 neditable = 0; // number of editable items among the selected ones + + for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + LLUUID id = *it; + LLViewerInventoryItem* item = gInventory.getItem(id); + + if (!item) + { + llwarns << "Invalid item" << llendl; + // *NOTE: the logic below may not work in this case + continue; + } + + updateMask(mask, item->getType()); + + bool is_link = item->getIsLinkType(); + bool is_worn = get_is_item_worn(id); + bool is_editable = gAgentWearables.isWearableModifiable(id); + + if (is_worn) + { + ++nworn; + + if (is_link) + { + ++nwornlinks; + } + } + if (is_editable) + { + ++neditable; + } + } // for + + // *TODO: eliminate multiple traversals over the menu items + // *TODO: try disabling items rather than hiding them + // *FIX: we may hide *all* items and thus get an ugly empty menu + setMenuItemVisible(menu, "wear", nworn == 0); + setMenuItemVisible(menu, "edit", mask & (MASK_CLOTHING|MASK_BODYPART) && nitems == 1 && neditable == 1); + setMenuItemVisible(menu, "show_original", nitems == 1 && nwornlinks == nitems); + setMenuItemVisible(menu, "take_off", mask == MASK_CLOTHING && nworn == nitems); // selected only worn clothes + setMenuItemVisible(menu, "detach", mask == MASK_ATTACHMENT && nworn == nitems); + setMenuItemVisible(menu, "object_profile", mask == MASK_ATTACHMENT && nitems == 1); +} + +// We need this method to convert non-zero BOOL values to exactly 1 (TRUE). +// Otherwise code relying on a BOOL value being TRUE may fail +// (I experienced a weird assert in LLView::drawChildren() because of that. +void LLWearableItemsList::ContextMenu::setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val) +{ + menu->setItemVisible(name, val); +} + +void LLWearableItemsList::ContextMenu::updateMask(U32& mask, LLAssetType::EType at) +{ + if (at == LLAssetType::AT_CLOTHING) + { + mask |= MASK_CLOTHING; + } + else if (at == LLAssetType::AT_BODYPART) + { + mask |= MASK_BODYPART; + } + else if (at == LLAssetType::AT_OBJECT) + { + mask |= MASK_ATTACHMENT; + } + else + { + llwarns << "Unsupported asset type: " << at << llendl; + } } // EOF diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index d7b09ca934..7ad1b5a3ad 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -287,7 +287,16 @@ public: class ContextMenu : public LLListContextMenu, public LLSingleton { protected: + enum { + MASK_CLOTHING = 0x01, + MASK_BODYPART = 0x02, + MASK_ATTACHMENT = 0x04, + }; + /* virtual */ LLContextMenu* createMenu(); + void updateItemsVisibility(LLContextMenu* menu); + void setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val); + void updateMask(U32& mask, LLAssetType::EType at); }; struct Params : public LLInitParam::Block diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml index b473a7a282..895cc4e3cc 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml @@ -336,6 +336,7 @@ allow_select="true" layout="topleft" follows="all" + multi_select="true" width="310" height="140" left="0" -- cgit v1.3 From a781dd767c095470897049b5ae1a91523214b39a Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Tue, 25 May 2010 13:27:17 +0100 Subject: VWR-13743 FIXED Change Request: Change double click action for inventory wearables DEV-33391 FIXED Change Request: Change double click action for inventory wearables Pulled this from the AvP backlog and implemented it, as it's a pain point from Ben's usability findings. And people loooove this in 3rd party viewers. (transplanted from cd0857d027305aec3dbdfe1940a04f7827e3a656) --- indra/newview/llinventorybridge.cpp | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'indra') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 2d27c89074..0f532236e4 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -4143,21 +4143,9 @@ void LLObjectBridge::performAction(LLInventoryModel* model, std::string action) void LLObjectBridge::openItem() { - LLViewerInventoryItem* item = getItem(); - - if (item) - { - LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); - } - - LLSD key; - key["id"] = mUUID; - LLSideTray::getInstance()->showPanel("sidepanel_inventory", key); - - // Disable old properties floater; this is replaced by the sidepanel. - /* - LLFloaterReg::showInstance("properties", mUUID); - */ + // object double-click action is to wear/unwear object + performAction(getInventoryModel(), + get_is_item_worn(mUUID) ? "detach" : "attach"); } LLFontGL::StyleFlags LLObjectBridge::getLabelStyle() const -- cgit v1.3 From ecd93410ff819bb301f115e40c8f2d6fef8b2396 Mon Sep 17 00:00:00 2001 From: Andrew Polunin Date: Tue, 25 May 2010 15:31:07 +0300 Subject: EXT-7142 FIXED Upload functionality duplicated in the Build menu and Select Linked Parts menu moved up - LLPanelMainInventory::setUploadCostIfNeeded() - uncommented the line which enables retrieving of the upload cost price using LLGlobalEconomy class. - Implemented LLUploadCostCalculator event handler in the llviewermenu.cpp (the fact that it should be implemented in this file was discussed with Mike Antipov). - LLUploadCostCalculator::calculateCost() is implemented only for code readability: it's name is more descriptive than a code snippet placed directly in the constructor (LLPanelMainInventory::setUploadCostIfNeeded() works in similar way: it calculates cost_str only once). - 'Select Linked Parts' menu was moved below 'Edit Linked Parts'. - 'Upload' menu was added at the bottom, below 'Options' menu Reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/425/ --HG-- branch : product-engine --- indra/newview/llpanelmaininventory.cpp | 2 +- indra/newview/llviewermenu.cpp | 39 +++++++ indra/newview/skins/default/xui/en/menu_viewer.xml | 113 ++++++++++++++++----- 3 files changed, 125 insertions(+), 29 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 5ff51b8165..54d1b46016 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -1187,7 +1187,7 @@ void LLPanelMainInventory::setUploadCostIfNeeded() LLMenuItemBranchGL* upload_menu = mMenuAdd->findChild("upload"); if(upload_menu) { - S32 upload_cost = -1;//LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); + S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); std::string cost_str; // getPriceUpload() returns -1 if no data available yet. diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 10ceab2656..d0ac103f56 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -108,6 +108,7 @@ #include "lluilistener.h" #include "llappearancemgr.h" #include "lltrans.h" +#include "lleconomy.h" using namespace LLVOAvatarDefines; @@ -7658,6 +7659,42 @@ class LLWorldToggleCameraControls : public view_listener_t } }; +class LLUploadCostCalculator : public view_listener_t +{ + std::string mCostStr; + + bool handleEvent(const LLSD& userdata) + { + std::string menu_name = userdata.asString(); + gMenuHolder->childSetLabelArg(menu_name, "[COST]", mCostStr); + + return true; + } + + void calculateCost(); + +public: + LLUploadCostCalculator() + { + calculateCost(); + } +}; + +void LLUploadCostCalculator::calculateCost() +{ + S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); + + // getPriceUpload() returns -1 if no data available yet. + if(upload_cost >= 0) + { + mCostStr = llformat("%d", upload_cost); + } + else + { + mCostStr = llformat("%d", gSavedSettings.getU32("DefaultUploadCost")); + } +} + void show_navbar_context_menu(LLView* ctrl, S32 x, S32 y) { static LLMenuGL* show_navbar_context_menu = LLUICtrlFactory::getInstance()->createFromFile("menu_hide_navbar.xml", @@ -7699,6 +7736,8 @@ void initialize_menus() enable.add("IsGodCustomerService", boost::bind(&is_god_customer_service)); enable.add("IsGodCustomerService", boost::bind(&is_god_customer_service)); + view_listener_t::addEnable(new LLUploadCostCalculator(), "Upload.CalculateCosts"); + // Agent commit.add("Agent.toggleFlying", boost::bind(&LLAgent::toggleFlying)); enable.add("Agent.enableFlying", boost::bind(&LLAgent::enableFlying)); diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 16c2581d63..aff8f7ddf4 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -521,6 +521,52 @@ + + + + + + + + + + + + + + + + + + + label="Image (L$[COST])..." + layout="topleft" + name="Upload Image" + shortcut="control|U"> + function="File.UploadImage" + parameter="" /> + function="File.EnableUpload" /> + + label="Sound (L$[COST])..." + layout="topleft" + name="Upload Sound"> + function="File.UploadSound" + parameter="" /> + function="File.EnableUpload" /> + + label="Animation (L$[COST])..." + layout="topleft" + name="Upload Animation"> + function="File.UploadAnim" + parameter="" /> + function="File.EnableUpload" /> + + label="Bulk (L$[COST] per file)..." + layout="topleft" + name="Bulk Upload"> - + function="File.UploadBulk" + parameter="" /> + - + \ No newline at end of file -- cgit v1.3 From 574e6cb3907ad61747732883c361f114c17809f0 Mon Sep 17 00:00:00 2001 From: Mike Antipov Date: Tue, 25 May 2010 15:36:05 +0300 Subject: EXT-7471 PARTIAL FIXED Ensure bottom tray chatbar can always be resized by user. Seems, there is a problem somewhere in rendering of LLLAyoutStack children. Last visible panel marked as user_resize=true can not be resized and has no resize cursor. Because we have no time & resources to investigate this closely a workaround is implemented for bottom tray: * set user_resize for layout panel with Speak button which is always visible to user; * set user_resize=false to don't have Speak button panel resizable. Reviewed by Vadim Savchuk at https://codereview.productengine.com/secondlife/r/438/ --HG-- branch : product-engine --- indra/newview/skins/default/xui/en/panel_bottomtray.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index bfc40a8638..ac61c7da5d 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -67,7 +67,7 @@ mouse_opaque="false" name="speak_panel" top_delta="0" - user_resize="false" + user_resize="true" width="110"> - - - - - - - - - - - - - - - - - - - - + + + + Rotate Camera Around Focus + + + Zoom Camera Towards Focus + + + Move Camera Up and Down, Left and Right + + + Camera modes + + + Orbit Zoom Pan + + + Preset Views + + + View Object + + + + + + + + + Front View + + + + + + + + Side View + + + + + + + + Rear View + + + + + + + + Object View + + + + + + + + Mouselook View + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.3 From a0351c6ea4446bfa8fc85450c2603251cd703c2c Mon Sep 17 00:00:00 2001 From: Andrew Dyukov Date: Thu, 27 May 2010 18:41:28 +0300 Subject: EXT-7200 FIXED Integrated art missing from appearance. Replaced "Shop" button text with icon, changed sizes of buttons according to comments in ticket, also changed heights of hovered and selected icons for items- made them equal to heights of panels they are used on. Reviewed by Neal Orman at https://codereview.productengine.com/secondlife/r/410/ --HG-- branch : product-engine --- .../newview/skins/default/textures/icons/Shop.png | Bin 0 -> 647 bytes indra/newview/skins/default/textures/textures.xml | 1 + .../default/xui/en/panel_body_parts_list_item.xml | 32 +++++++++++------ .../xui/en/panel_bodyparts_list_button_bar.xml | 4 +-- .../xui/en/panel_clothing_list_button_bar.xml | 4 +-- .../default/xui/en/panel_clothing_list_item.xml | 39 ++++++++++++++------- .../xui/en/panel_dummy_clothing_list_item.xml | 10 +++--- .../skins/default/xui/en/sidepanel_appearance.xml | 2 +- 8 files changed, 59 insertions(+), 33 deletions(-) create mode 100644 indra/newview/skins/default/textures/icons/Shop.png (limited to 'indra') diff --git a/indra/newview/skins/default/textures/icons/Shop.png b/indra/newview/skins/default/textures/icons/Shop.png new file mode 100644 index 0000000000..d7e0001dc6 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/Shop.png differ diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 57f23c7e76..29c72a414b 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -453,6 +453,7 @@ with the same filename but different name + diff --git a/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml b/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml index e3f6045e27..2edd643cc5 100644 --- a/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml @@ -1,7 +1,7 @@ - + height="23" + width="23" + tab_stop="false"> + + - - - - - - - - - - - - - + + + + Rotate Camera Around Focus + + + Zoom Camera Towards Focus + + + Move Camera Up and Down, Left and Right + + + Camera modes + + + Orbit Zoom Pan + + + Preset Views + + + View Object + + + + + + + + + Front View + + + + + + + + Side View + + + + + + + + Rear View + + + + + + + + Object View + + + + + + + + Mouselook View + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/widgets/panel_camera_item.xml b/indra/newview/skins/default/xui/en/widgets/panel_camera_item.xml index a77b81f76f..98707b8495 100644 --- a/indra/newview/skins/default/xui/en/widgets/panel_camera_item.xml +++ b/indra/newview/skins/default/xui/en/widgets/panel_camera_item.xml @@ -1,70 +1,70 @@ - - - - - - - - Text - - \ No newline at end of file + + + + + + + + Text + + -- cgit v1.3 From e25d0d34ba1b88008ef3bfbf40fb43f498bc1bea Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 28 May 2010 12:01:26 +0100 Subject: CID-481 Checker: FORWARD_NULL Function: LLTaskInvFVBridge::createObjectBridge(LLPanelObjectInventory *, LLInventoryObject *) File: /indra/newview/llpanelobjectinventory.cpp --- indra/newview/llpanelobjectinventory.cpp | 54 +++++++++++++++----------------- 1 file changed, 26 insertions(+), 28 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 0d3beaa9a5..c557e9b85d 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -1249,29 +1249,30 @@ LLTaskInvFVBridge* LLTaskInvFVBridge::createObjectBridge(LLPanelObjectInventory* { LLTaskInvFVBridge* new_bridge = NULL; const LLInventoryItem* item = dynamic_cast(object); + const U32 itemflags = ( NULL == item ? 0 : item->getFlags() ); LLAssetType::EType type = object->getType(); switch(type) { case LLAssetType::AT_TEXTURE: new_bridge = new LLTaskTextureBridge(panel, - object->getUUID(), - object->getName()); + object->getUUID(), + object->getName()); break; case LLAssetType::AT_SOUND: new_bridge = new LLTaskSoundBridge(panel, - object->getUUID(), - object->getName()); + object->getUUID(), + object->getName()); break; case LLAssetType::AT_LANDMARK: new_bridge = new LLTaskLandmarkBridge(panel, - object->getUUID(), - object->getName()); + object->getUUID(), + object->getName()); break; case LLAssetType::AT_CALLINGCARD: new_bridge = new LLTaskCallingCardBridge(panel, - object->getUUID(), - object->getName()); + object->getUUID(), + object->getName()); break; case LLAssetType::AT_SCRIPT: // OLD SCRIPTS DEPRECATED - JC @@ -1281,45 +1282,42 @@ LLTaskInvFVBridge* LLTaskInvFVBridge::createObjectBridge(LLPanelObjectInventory* // object->getName()); break; case LLAssetType::AT_OBJECT: - { - U32 flags = ( NULL == item ? 0 : item->getFlags() ); - new_bridge = new LLTaskObjectBridge(panel, - object->getUUID(), - object->getName(), - flags); - } + new_bridge = new LLTaskObjectBridge(panel, + object->getUUID(), + object->getName(), + itemflags); break; case LLAssetType::AT_NOTECARD: new_bridge = new LLTaskNotecardBridge(panel, - object->getUUID(), - object->getName()); + object->getUUID(), + object->getName()); break; case LLAssetType::AT_ANIMATION: new_bridge = new LLTaskAnimationBridge(panel, - object->getUUID(), - object->getName()); + object->getUUID(), + object->getName()); break; case LLAssetType::AT_GESTURE: new_bridge = new LLTaskGestureBridge(panel, - object->getUUID(), - object->getName()); + object->getUUID(), + object->getName()); break; case LLAssetType::AT_CLOTHING: case LLAssetType::AT_BODYPART: new_bridge = new LLTaskWearableBridge(panel, - object->getUUID(), - object->getName(), - item->getFlags()); + object->getUUID(), + object->getName(), + itemflags); break; case LLAssetType::AT_CATEGORY: new_bridge = new LLTaskCategoryBridge(panel, - object->getUUID(), - object->getName()); + object->getUUID(), + object->getName()); break; case LLAssetType::AT_LSL_TEXT: new_bridge = new LLTaskLSLBridge(panel, - object->getUUID(), - object->getName()); + object->getUUID(), + object->getName()); break; default: llinfos << "Unhandled inventory type (llassetstorage.h): " -- cgit v1.3 From c68e4c559718740bc19eb3c9c71a11d450bae3b5 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 28 May 2010 12:04:49 +0100 Subject: CID-478 Checker: NULL_RETURNS Function: LLCOFWearables::buildBodypartListItem(LLViewerInventoryItem *) File: /indra/newview/llcofwearables.cpp --- indra/newview/llcofwearables.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp index dfc203111a..661449c60c 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -340,14 +340,15 @@ LLPanelClothingListItem* LLCOFWearables::buildClothingListItem(LLViewerInventory LLPanelBodyPartsListItem* LLCOFWearables::buildBodypartListItem(LLViewerInventoryItem* item) { llassert(item); - + if (!item) return NULL; LLPanelBodyPartsListItem* item_panel = LLPanelBodyPartsListItem::create(item); if (!item_panel) return NULL; //updating verbs //we don't need to use permissions of a link but of an actual/linked item if (item->getLinkedItem()) item = item->getLinkedItem(); - + llassert(item); + if (!item) return NULL; bool allow_modify = item->getPermissions().allowModifyBy(gAgentID); item_panel->setShowLockButton(!allow_modify); item_panel->setShowEditButton(allow_modify); -- cgit v1.3 From 89990d746e7cef9e96c6aa350d25309f7c0c29de Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 28 May 2010 12:06:03 +0100 Subject: CID-477 Checker: NULL_RETURNS Function: LLCOFWearables::buildClothingListItem(LLViewerInventoryItem *, bool, bool) File: /indra/newview/llcofwearables.cpp --- indra/newview/llcofwearables.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp index 661449c60c..0864d63919 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -308,13 +308,15 @@ void LLCOFWearables::populateAttachmentsAndBodypartsLists(const LLInventoryModel LLPanelClothingListItem* LLCOFWearables::buildClothingListItem(LLViewerInventoryItem* item, bool first, bool last) { llassert(item); - + if (!item) return NULL; LLPanelClothingListItem* item_panel = LLPanelClothingListItem::create(item); if (!item_panel) return NULL; //updating verbs //we don't need to use permissions of a link but of an actual/linked item if (item->getLinkedItem()) item = item->getLinkedItem(); + llassert(item); + if (!item) return NULL; bool allow_modify = item->getPermissions().allowModifyBy(gAgentID); -- cgit v1.3 From b3f758785dd0ee165276c0ac97e1823e44536cc8 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 28 May 2010 12:07:45 +0100 Subject: CID-479 Checker: UNINIT_CTOR Function: LLVivoxVoiceClient::sessionState::sessionState() File: /indra/newview/llvoicevivox.cpp --- indra/newview/llvoicevivox.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'indra') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 1597691347..f90d46bc9c 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -5538,6 +5538,7 @@ LLVivoxVoiceClient::sessionState::sessionState() : mVoiceEnabled(false), mReconnect(false), mVolumeDirty(false), + mMuteDirty(false), mParticipantsChanged(false) { } -- cgit v1.3 From 14e03b56fc363d83004d0c0ae5d79a9bedf68eae Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 28 May 2010 12:09:48 +0100 Subject: CID-480 Checker: UNINIT_CTOR Function: LLVivoxVoiceClient::participantState::participantState(const std::basic_string, std::allocator>&) File: /indra/newview/llvoicevivox.cpp --- indra/newview/llvoicevivox.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index f90d46bc9c..c6c155f0f0 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -4088,7 +4088,9 @@ LLVivoxVoiceClient::participantState::participantState(const std::string &uri) : mLastSpokeTimestamp(0.f), mPower(0.f), mVolume(LLVoiceClient::VOLUME_DEFAULT), + mUserVolume(0), mOnMuteList(false), + mVolumeSet(false), mVolumeDirty(false), mAvatarIDValid(false), mIsSelf(false) -- cgit v1.3 From 878e1d06d5e9ac3bb6c002d3fd7342f5beda2017 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 28 May 2010 12:20:06 +0100 Subject: CID-476 Checker: NULL_RETURNS Function: LLPanelEditWearable::updateScrollingPanelUI() File: /indra/newview/llpaneleditwearable.cpp --- indra/newview/llpaneleditwearable.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra') diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index 9eccceca66..36f2d05fab 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -1095,6 +1095,8 @@ void LLPanelEditWearable::updateScrollingPanelUI() if(panel && (mWearablePtr->getItemID().notNull())) { const LLEditWearableDictionary::WearableEntry *wearable_entry = LLEditWearableDictionary::getInstance()->getWearable(type); + llassert(wearable_entry); + if (!wearable_entry) return; U8 num_subparts = wearable_entry->mSubparts.size(); LLScrollingPanelParam::sUpdateDelayFrames = 0; -- cgit v1.3 From 4f0d41090469c5b43f50308433e3cadbac9f368a Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Fri, 28 May 2010 12:28:40 +0100 Subject: EXT-7496 FIXED Turn OFF extra Voice debug logging when merging Voice Morphing --- indra/newview/app_settings/logcontrol.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index f907f184f7..937c4e4c6a 100644 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -41,7 +41,8 @@ tags - Voice + + -- cgit v1.3 From 3927d183278e4cc11396a2c78b9393bcf6b0e802 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 28 May 2010 12:31:43 +0100 Subject: CID-466 Checker: NULL_RETURNS Function: LLInventoryItemsList::refresh() File: /indra/newview/llinventoryitemslist.cpp --- indra/newview/llinventoryitemslist.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/newview/llinventoryitemslist.cpp b/indra/newview/llinventoryitemslist.cpp index 1c3eb547bb..750cdfb678 100644 --- a/indra/newview/llinventoryitemslist.cpp +++ b/indra/newview/llinventoryitemslist.cpp @@ -388,7 +388,7 @@ void LLInventoryItemsList::refresh() computeDifference(getIDs(), added_items, removed_items); bool add_limit_exceeded = false; - unsigned nadded = 0; + unsigned int nadded = 0; uuid_vec_t::const_iterator it = added_items.begin(); for( ; added_items.end() != it; ++it) @@ -400,8 +400,12 @@ void LLInventoryItemsList::refresh() } LLViewerInventoryItem* item = gInventory.getItem(*it); // Do not rearrange items on each adding, let's do that on filter call - addNewItem(item, false); - ++nadded; + llassert(item); + if (item) + { + addNewItem(item, false); + ++nadded; + } } it = removed_items.begin(); -- cgit v1.3 From 6f8e96159a59b3e519cf7a5abba3ae34abcab725 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 28 May 2010 12:41:54 +0100 Subject: CID-448 Checker: UNINIT_CTOR Function: LLVoiceClient::LLVoiceClient() File: /indra/newview/llvoiceclient.cpp --- indra/newview/llvoiceclient.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 91353281a8..42e44634b6 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -81,8 +81,10 @@ std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserv /////////////////////////////////////////////////////////////////////////////////////////////// LLVoiceClient::LLVoiceClient() + : + mVoiceModule(NULL), + m_servicePump(NULL) { - mVoiceModule = NULL; } //--------------------------------------------------- -- cgit v1.3 From 58fb834a6ea3698ec6d89cb07e543d088dc69c00 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 28 May 2010 12:46:31 +0100 Subject: CID-443 Checker: FORWARD_NULL Function: LLBasicCertificateVector::BasicIteratorImpl::equals(const LLPointer &) const File: /indra/newview/llsechandler_basic.h --- indra/newview/llsechandler_basic.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra') diff --git a/indra/newview/llsechandler_basic.h b/indra/newview/llsechandler_basic.h index 4bbb73f062..407e74ad00 100644 --- a/indra/newview/llsechandler_basic.h +++ b/indra/newview/llsechandler_basic.h @@ -116,6 +116,8 @@ public: virtual bool equals(const LLPointer& _iter) const { const BasicIteratorImpl *rhs_iter = dynamic_cast(_iter.get()); + llassert(rhs_iter); + if (!rhs_iter) return 0; return (mIter == rhs_iter->mIter); } virtual LLPointer get() -- cgit v1.3 From 7e0b36d6102f5e285296cd8e0dc6961b5c73c7cb Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 28 May 2010 12:47:45 +0100 Subject: CID-442 Checker: FORWARD_NULL Function: LLBasicCertificateVector::insert(LLCertificateVector::iterator, LLPointer) File: /indra/newview/llsechandler_basic.cpp --- indra/newview/llsechandler_basic.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index edf5ce9b60..c4c13ccdca 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -541,7 +541,7 @@ LLBasicCertificateVector::iterator LLBasicCertificateVector::find(const LLSD& pa // Insert a certificate into the store. If the certificate already // exists in the store, nothing is done. void LLBasicCertificateVector::insert(iterator _iter, - LLPointer cert) + LLPointer cert) { LLSD cert_info = cert->getLLSD(); if (cert_info.isMap() && cert_info.has(CERT_SHA1_DIGEST)) @@ -551,7 +551,11 @@ void LLBasicCertificateVector::insert(iterator _iter, if(find(existing_cert_info) == end()) { BasicIteratorImpl *basic_iter = dynamic_cast(_iter.mImpl.get()); - mCerts.insert(basic_iter->mIter, cert); + llassert(basic_iter); + if (basic_iter) + { + mCerts.insert(basic_iter->mIter, cert); + } } } } -- cgit v1.3 From 4fb77380279bdf853b7f213ba8997511720468dd Mon Sep 17 00:00:00 2001 From: Lynx Linden Date: Fri, 28 May 2010 15:10:52 +0100 Subject: Write breakpad minidump files to the SL log directory. Also, clean out old minidump files when we start up. --- indra/llcommon/llapp.cpp | 38 ++++++++++++++++++++++++++++++-------- indra/llcommon/llapp.h | 16 +++++++++++----- indra/newview/llappviewer.cpp | 13 +++++++++++-- 3 files changed, 52 insertions(+), 15 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index da14020f2b..861122a4ac 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -140,6 +140,8 @@ void LLApp::commonCtor() mExceptionHandler = 0; + // initialize the buffer to write the minidump filename to + // (this is used to avoid allocating memory in the crash handler) memset(minidump_path, 0, MAX_MINDUMP_PATH_LENGTH); } @@ -359,8 +361,15 @@ void LLApp::setError() setStatus(APP_STATUS_ERROR); } +void LLApp::setMiniDumpDir(const std::string &path) +{ + llassert(mExceptionHandler); + mExceptionHandler->set_dump_path(path); +} + void LLApp::writeMiniDump() { + llassert(mExceptionHandler); mExceptionHandler->WriteMinidump(); } @@ -786,17 +795,25 @@ bool unix_post_minidump_callback(const char *dump_dir, // The path must not be truncated. llassert((dirPathLength + idLength + 5) <= LLApp::MAX_MINDUMP_PATH_LENGTH); - char * path = LLApp::instance()->minidump_path; + char * path = LLApp::instance()->getMiniDumpFilename(); S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH; strncpy(path, dump_dir, remaining); remaining -= dirPathLength; path += dirPathLength; - strncpy(path, minidump_id, remaining); - remaining -= idLength; - path += idLength; - strncpy(path, ".dmp", remaining); + if (remaining > 0 && dirPathLength > 0 && path[-1] != '/') + { + *path++ = '/'; + --remaining; + } + if (remaining > 0) + { + strncpy(path, minidump_id, remaining); + remaining -= idLength; + path += idLength; + strncpy(path, ".dmp", remaining); + } - llinfos << "generated minidump: " << LLApp::instance()->minidump_path << llendl; + llinfos << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << llendl; LLApp::runErrorHandler(); return true; } @@ -810,13 +827,18 @@ bool windows_post_minidump_callback(const wchar_t* dump_path, MDRawAssertionInfo* assertion, bool succeeded) { - char * path = LLApp::instance()->minidump_path; + char * path = LLApp::instance()->getMiniDumpFilename(); S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH; size_t bytesUsed; bytesUsed = wcstombs(path, dump_path, static_cast(remaining)); remaining -= bytesUsed; path += bytesUsed; + if(remaining > 0 && bytesUsed > 0 && path[-1] != '\\') + { + *path++ = '\\'; + --remaining; + } if(remaining > 0) { bytesUsed = wcstombs(path, minidump_id, static_cast(remaining)); @@ -828,7 +850,7 @@ bool windows_post_minidump_callback(const wchar_t* dump_path, strncpy(path, ".dmp", remaining); } - llinfos << "generated minidump: " << LLApp::instance()->minidump_path << llendl; + llinfos << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << llendl; // *NOTE:Mani - this code is stolen from LLApp, where its never actually used. //OSMessageBox("Attach Debugger Now", "Error", OSMB_OK); // *TODO: Translate the signals/exceptions into cross-platform stuff diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index a6294a5e1a..fef05a7939 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -234,9 +234,16 @@ public: static void runErrorHandler(); // run shortly after we detect an error, ran in the relatively robust context of the LLErrorThread - preferred. //@} - // + // the maximum length of the minidump filename returned by getMiniDumpFilename() + static const U32 MAX_MINDUMP_PATH_LENGTH = 256; + + // change the directory where Breakpad minidump files are written to + void setMiniDumpDir(const std::string &path); + + // Return the Google Breakpad minidump filename after a crash. + char *getMiniDumpFilename() { return minidump_path; } + // Write out a Google Breakpad minidump file. - // void writeMiniDump(); #if !LL_WINDOWS @@ -274,9 +281,6 @@ public: typedef std::map string_map; string_map mOptionMap; // Contains all command-line options and arguments in a map - // Contains the path to minidump file after a crash. - static const U32 MAX_MINDUMP_PATH_LENGTH = 256; - char minidump_path[MAX_MINDUMP_PATH_LENGTH]; protected: static void setStatus(EAppStatus status); // Use this to change the application status. @@ -298,6 +302,8 @@ protected: private: void startErrorThread(); + // Contains the filename of the minidump file after a crash. + char minidump_path[MAX_MINDUMP_PATH_LENGTH]; // *NOTE: On Windows, we need a routine to reset the structured // exception handler when some evil driver has taken it over for diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 00a2678a9a..3528deb9e0 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -602,6 +602,14 @@ bool LLAppViewer::init() if (!initConfiguration()) return false; + // write Google Breakpad minidump files to our log directory + std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); + logdir += gDirUtilp->getDirDelimiter(); + setMiniDumpDir(logdir); + + // remove any old minidump files from the log directory + gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp"); + // Although initLogging() is the right place to mess with // setFatalFunction(), we can't query gSavedSettings until after // initConfiguration(). @@ -2574,9 +2582,10 @@ void LLAppViewer::handleViewerCrash() gDebugInfo["FirstLogin"] = (LLSD::Boolean) gAgent.isFirstLogin(); gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall"); - if(pApp->minidump_path[0] != 0) + char *minidump_file = pApp->getMiniDumpFilename(); + if(minidump_file && minidump_file[0] != 0) { - gDebugInfo["MinidumpPath"] = pApp->minidump_path; + gDebugInfo["MinidumpPath"] = minidump_file; } if(gLogoutInProgress) -- cgit v1.3 From 590a3d891c138f881d7a3f7c2758e778256e3891 Mon Sep 17 00:00:00 2001 From: Lynx Linden Date: Fri, 28 May 2010 15:40:16 +0100 Subject: On Windows, you have to pass the minidump path as a wstring. --- indra/llcommon/llapp.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'indra') diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 861122a4ac..1ea888f2e0 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -43,6 +43,7 @@ #include "lllivefile.h" #include "llmemory.h" #include "llstl.h" // for DeletePointer() +#include "llstring.h" #include "lleventtimer.h" #include "google_breakpad/exception_handler.h" @@ -364,7 +365,11 @@ void LLApp::setError() void LLApp::setMiniDumpDir(const std::string &path) { llassert(mExceptionHandler); +#ifdef LL_WINDOWS + mExceptionHandler->set_dump_path(utf8str_to_wstring(path)); +#else mExceptionHandler->set_dump_path(path); +#endif } void LLApp::writeMiniDump() -- cgit v1.3 From 80d9cde59656964335613f99dfa63bb834278416 Mon Sep 17 00:00:00 2001 From: Lynx Linden Date: Fri, 28 May 2010 16:15:00 +0100 Subject: Try using mbstowcs() to convert to std::wstring. LLWString cannot be converted to std::wstring, apparently. --- indra/llcommon/llapp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 1ea888f2e0..eedec0b24e 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -366,7 +366,9 @@ void LLApp::setMiniDumpDir(const std::string &path) { llassert(mExceptionHandler); #ifdef LL_WINDOWS - mExceptionHandler->set_dump_path(utf8str_to_wstring(path)); + wchar_t buffer[MAX_MINDUMP_PATH_LENGTH]; + mbstowcs(buffer, path.c_str(), MAX_MINDUMP_PATH_LENGTH); + mExceptionHandler->set_dump_path(std::wstring(buffer)); #else mExceptionHandler->set_dump_path(path); #endif -- cgit v1.3 From e07deaa3dac6b9bc651034543c174e974b4b9ad2 Mon Sep 17 00:00:00 2001 From: "Nyx (Neal Orman)" Date: Fri, 28 May 2010 11:38:37 -0400 Subject: AVP-44 WIP architectural cleanup for multi-wearables Implemented some resident-suggested tweaks to better support multiwearables code reviewed by Seraph --- indra/newview/llagentwearables.cpp | 14 +++++++------- indra/newview/llagentwearables.h | 5 +++-- indra/newview/llappearancemgr.cpp | 5 +++-- indra/newview/llwearabletype.cpp | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) (limited to 'indra') diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index cc9e68d593..e5796f8e63 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -718,16 +718,16 @@ U32 LLAgentWearables::pushWearable(const LLWearableType::EType type, LLWearable { // no null wearables please! llwarns << "Null wearable sent for type " << type << llendl; - return MAX_WEARABLES_PER_TYPE; + return MAX_CLOTHING_PER_TYPE; } - if (type < LLWearableType::WT_COUNT || mWearableDatas[type].size() < MAX_WEARABLES_PER_TYPE) + if (type < LLWearableType::WT_COUNT || mWearableDatas[type].size() < MAX_CLOTHING_PER_TYPE) { mWearableDatas[type].push_back(wearable); wearableUpdated(wearable); checkWearableAgainstInventory(wearable); return mWearableDatas[type].size()-1; } - return MAX_WEARABLES_PER_TYPE; + return MAX_CLOTHING_PER_TYPE; } void LLAgentWearables::wearableUpdated(LLWearable *wearable) @@ -764,7 +764,7 @@ void LLAgentWearables::popWearable(LLWearable *wearable) U32 index = getWearableIndex(wearable); LLWearableType::EType type = wearable->getType(); - if (index < MAX_WEARABLES_PER_TYPE && index < getWearableCount(type)) + if (index < MAX_CLOTHING_PER_TYPE && index < getWearableCount(type)) { popWearable(type, index); } @@ -785,7 +785,7 @@ U32 LLAgentWearables::getWearableIndex(LLWearable *wearable) { if (wearable == NULL) { - return MAX_WEARABLES_PER_TYPE; + return MAX_CLOTHING_PER_TYPE; } const LLWearableType::EType type = wearable->getType(); @@ -793,7 +793,7 @@ U32 LLAgentWearables::getWearableIndex(LLWearable *wearable) if (wearable_iter == mWearableDatas.end()) { llwarns << "tried to get wearable index with an invalid type!" << llendl; - return MAX_WEARABLES_PER_TYPE; + return MAX_CLOTHING_PER_TYPE; } const wearableentry_vec_t& wearable_vec = wearable_iter->second; for(U32 index = 0; index < wearable_vec.size(); index++) @@ -804,7 +804,7 @@ U32 LLAgentWearables::getWearableIndex(LLWearable *wearable) } } - return MAX_WEARABLES_PER_TYPE; + return MAX_CLOTHING_PER_TYPE; } const LLWearable* LLAgentWearables::getWearable(const LLWearableType::EType type, U32 index) const diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index c53b1333fc..5d5c5ae371 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -102,6 +102,9 @@ public: U32 getWearableCount(const LLWearableType::EType type) const; U32 getWearableCount(const U32 tex_index) const; + static const U32 MAX_CLOTHING_PER_TYPE = 5; + + //-------------------------------------------------------------------- // Setters //-------------------------------------------------------------------- @@ -274,8 +277,6 @@ private: LLPointer mCB; }; - static const U32 MAX_WEARABLES_PER_TYPE = 1; - }; // LLAgentWearables extern LLAgentWearables gAgentWearables; diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 8cc4436188..c417f8bdf5 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -970,7 +970,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING, false); // Reduce wearables to max of one per type. removeDuplicateItems(wear_items); - filterWearableItems(wear_items, 5); + filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE); // - Attachments: include COF contents only if appending. LLInventoryModel::item_array_t obj_items; @@ -1525,11 +1525,12 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update else { LLPointer cb = do_update ? new ModifiedCOFCallback : 0; + const std::string description = vitem->getIsLinkType() ? vitem->getDescription() : ""; link_inventory_item( gAgent.getID(), vitem->getLinkedUUID(), getCOF(), vitem->getName(), - vitem->getDescription(), + description, LLAssetType::AT_LINK, cb); } diff --git a/indra/newview/llwearabletype.cpp b/indra/newview/llwearabletype.cpp index c692df06ad..2a14ace38c 100644 --- a/indra/newview/llwearabletype.cpp +++ b/indra/newview/llwearabletype.cpp @@ -85,7 +85,7 @@ LLWearableDictionary::LLWearableDictionary() addEntry(LLWearableType::WT_TATTOO, new WearableEntry("tattoo", "New Tattoo", LLAssetType::AT_CLOTHING, LLInventoryIcon::ICONNAME_CLOTHING_TATTOO)); addEntry(LLWearableType::WT_INVALID, new WearableEntry("invalid", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryIcon::ICONNAME_NONE)); addEntry(LLWearableType::WT_NONE, new WearableEntry("none", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryIcon::ICONNAME_NONE)); - addEntry(LLWearableType::WT_COUNT, NULL); + addEntry(LLWearableType::WT_COUNT, new WearableEntry("count", "Invalid Wearable", LLAssetType::AT_NONE, LLInventoryIcon::ICONNAME_NONE)); } // static -- cgit v1.3 From d42fd6cf7fa3ac4d7e2ef35cef50eb7740b4ec6d Mon Sep 17 00:00:00 2001 From: Paul Guslisty Date: Fri, 28 May 2010 20:10:10 +0300 Subject: EXT-7472 FIXED Open add to outfit panel by (+) button click on unwearable items and by selecting 'Replace' menu item click body part context menu Main changes: - Added callback for a '(+) button' to the LLCOFCallbacks and bind it with LLPanelOutfitEdit::onAddWearableClicked - Created the callback(LLPanelOutfitEdit::onReplaceBodyPartMenuItemClicked) for 'Replace' menu item of context menu Related changes: - Changed LLFilteredWearableListManager so that it can use different functors (subclasses of LLInventoryCollectFunctor) as a criterion for LLInventoryItemsList filtering. Before it used only LLFindNonLinksByMask filter. Moved LLFindNonLinksByMask from to the llfilteredwearablelist.cpp to the llinventoryfunctions.h - Created getter 'LLPanelDummyClothingListItem::getWearableType()' for LLPanelDummyClothingListItem - Made 'add wearables panel' a member of LLPanelOutfitEdit so that not to use findChild each time panel is needed Reviewed by Igor Borovkov at http://jira.secondlife.com/browse/EXT-7472 --HG-- branch : product-engine --- indra/newview/llcofwearables.cpp | 16 +++++++++-- indra/newview/llcofwearables.h | 3 ++ indra/newview/llfilteredwearablelist.cpp | 46 +++++++++--------------------- indra/newview/llfilteredwearablelist.h | 9 +++--- indra/newview/llinventoryfunctions.cpp | 5 ++++ indra/newview/llinventoryfunctions.h | 35 ++++++++++++++++++++++- indra/newview/llpaneloutfitedit.cpp | 49 +++++++++++++++++++++++++++++--- indra/newview/llpaneloutfitedit.h | 9 ++++++ indra/newview/llwearableitemslist.cpp | 5 ++++ indra/newview/llwearableitemslist.h | 1 + 10 files changed, 134 insertions(+), 44 deletions(-) (limited to 'indra') diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp index dfc203111a..6e7a7fe937 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -43,6 +43,8 @@ #include "llmenugl.h" #include "llviewermenu.h" #include "llwearableitemslist.h" +#include "llpaneloutfitedit.h" +#include "llsidetray.h" static LLRegisterPanelClassWrapper t_cof_accodion_list_adaptor("accordion_list_adaptor"); @@ -135,8 +137,10 @@ protected: LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; LLUUID selected_id = mUUIDs.back(); - registrar.add("BodyPart.Replace", boost::bind(&LLAppearanceMgr::wearItemOnAvatar, - LLAppearanceMgr::getInstance(), selected_id, true, true)); + // *HACK* need to pass pointer to LLPanelOutfitEdit instead of LLSideTray::getInstance()->getPanel(). + // LLSideTray::getInstance()->getPanel() is rather slow variant + LLPanelOutfitEdit* panel_oe = dynamic_cast(LLSideTray::getInstance()->getPanel("panel_outfit_edit")); + registrar.add("BodyPart.Replace", boost::bind(&LLPanelOutfitEdit::onReplaceBodyPartMenuItemClicked, panel_oe, selected_id)); registrar.add("BodyPart.Edit", boost::bind(LLAgentWearables::editWearable, selected_id)); enable_registrar.add("BodyPart.OnEnable", boost::bind(&CofBodyPartContextMenu::onEnable, this, _2)); @@ -416,6 +420,7 @@ void LLCOFWearables::addClothingTypesDummies(const LLAppearanceMgr::wearables_by LLWearableType::EType w_type = static_cast(type); LLPanelInventoryListItemBase* item_panel = LLPanelDummyClothingListItem::create(w_type); if(!item_panel) continue; + item_panel->childSetAction("btn_add", mCOFCallbacks.mAddWearable); mClothing->addItem(item_panel, LLUUID::null, ADD_BOTTOM, false); } } @@ -435,6 +440,13 @@ bool LLCOFWearables::getSelectedUUIDs(uuid_vec_t& selected_ids) return selected_ids.size() != 0; } +LLPanel* LLCOFWearables::getSelectedItem() +{ + if (!mLastSelectedList) return NULL; + + return mLastSelectedList->getSelectedItem(); +} + void LLCOFWearables::clear() { mAttachments->clear(); diff --git a/indra/newview/llcofwearables.h b/indra/newview/llcofwearables.h index 590aa709dd..8f8bda2be8 100644 --- a/indra/newview/llcofwearables.h +++ b/indra/newview/llcofwearables.h @@ -107,6 +107,7 @@ public: typedef boost::function cof_callback_t; + cof_callback_t mAddWearable; cof_callback_t mMoveWearableCloser; cof_callback_t mMoveWearableFurther; cof_callback_t mEditWearable; @@ -123,6 +124,8 @@ public: LLUUID getSelectedUUID(); bool getSelectedUUIDs(uuid_vec_t& selected_ids); + LLPanel* getSelectedItem(); + void refresh(); void clear(); diff --git a/indra/newview/llfilteredwearablelist.cpp b/indra/newview/llfilteredwearablelist.cpp index fd99f673e0..28e159421c 100644 --- a/indra/newview/llfilteredwearablelist.cpp +++ b/indra/newview/llfilteredwearablelist.cpp @@ -37,32 +37,10 @@ #include "llinventoryitemslist.h" #include "llinventorymodel.h" -class LLFindNonLinksByMask : public LLInventoryCollectFunctor -{ -public: - LLFindNonLinksByMask(U64 mask) - : mFilterMask(mask) - {} - - virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) - { - if(item && !item->getIsLinkType() && (mFilterMask & (1LL << item->getInventoryType())) ) - { - return true; - } - - return false; - } -private: - U64 mFilterMask; -}; - -////////////////////////////////////////////////////////////////////////// - -LLFilteredWearableListManager::LLFilteredWearableListManager(LLInventoryItemsList* list, U64 filter_mask) +LLFilteredWearableListManager::LLFilteredWearableListManager(LLInventoryItemsList* list, LLInventoryCollectFunctor* collector) : mWearableList(list) -, mFilterMask(filter_mask) +, mCollector(collector) { llassert(mWearableList); gInventory.addObserver(this); @@ -84,9 +62,9 @@ void LLFilteredWearableListManager::changed(U32 mask) populateList(); } -void LLFilteredWearableListManager::setFilterMask(U64 mask) +void LLFilteredWearableListManager::setFilterCollector(LLInventoryCollectFunctor* collector) { - mFilterMask = mask; + mCollector = collector; populateList(); } @@ -94,14 +72,16 @@ void LLFilteredWearableListManager::populateList() { LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t item_array; - LLFindNonLinksByMask collector(mFilterMask); - gInventory.collectDescendentsIf( - gInventory.getRootFolderID(), - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - collector); + if(mCollector) + { + gInventory.collectDescendentsIf( + gInventory.getRootFolderID(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + *mCollector); + } // Probably will also need to get items from Library (waiting for reply in EXT-6724). diff --git a/indra/newview/llfilteredwearablelist.h b/indra/newview/llfilteredwearablelist.h index 0780c02442..b7825c07af 100644 --- a/indra/newview/llfilteredwearablelist.h +++ b/indra/newview/llfilteredwearablelist.h @@ -32,6 +32,7 @@ #ifndef LL_LLFILTEREDWEARABLELIST_H #define LL_LLFILTEREDWEARABLELIST_H +#include "llinventoryfunctions.h" #include "llinventoryobserver.h" class LLInventoryItemsList; @@ -42,7 +43,7 @@ class LLFilteredWearableListManager : public LLInventoryObserver LOG_CLASS(LLFilteredWearableListManager); public: - LLFilteredWearableListManager(LLInventoryItemsList* list, U64 filter_mask); + LLFilteredWearableListManager(LLInventoryItemsList* list, LLInventoryCollectFunctor* collector); ~LLFilteredWearableListManager(); /** LLInventoryObserver implementation @@ -51,9 +52,9 @@ public: /*virtual*/ void changed(U32 mask); /** - * Sets new filter and applies it immediately + * Sets new collector and applies it immediately */ - void setFilterMask(U64 mask); + void setFilterCollector(LLInventoryCollectFunctor* collector); /** * Populates wearable list with filtered data. @@ -62,7 +63,7 @@ public: private: LLInventoryItemsList* mWearableList; - U64 mFilterMask; + LLInventoryCollectFunctor* mCollector; }; #endif //LL_LLFILTEREDWEARABLELIST_H diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index c38d45f0f5..6301ff0d6d 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -372,6 +372,11 @@ bool LLFindWearablesOfType::operator()(LLInventoryCategory* cat, LLInventoryItem return true; } +void LLFindWearablesOfType::setType(LLWearableType::EType type) +{ + mWearableType = type; +} + ///---------------------------------------------------------------------------- /// LLAssetIDMatches ///---------------------------------------------------------------------------- diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 8b96ba29d9..bb365573d7 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -251,6 +251,37 @@ public: }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFindNonLinksByMask +// +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLFindNonLinksByMask : public LLInventoryCollectFunctor +{ +public: + LLFindNonLinksByMask(U64 mask) + : mFilterMask(mask) + {} + + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) + { + if(item && !item->getIsLinkType() && (mFilterMask & (1LL << item->getInventoryType())) ) + { + return true; + } + + return false; + } + + void setFilterMask(U64 mask) + { + mFilterMask = mask; + } + +private: + U64 mFilterMask; +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLFindWearables // @@ -272,8 +303,10 @@ public: LLFindWearablesOfType(LLWearableType::EType type) : mWearableType(type) {} virtual ~LLFindWearablesOfType() {} virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); + void setType(LLWearableType::EType type); - const LLWearableType::EType mWearableType; +private: + LLWearableType::EType mWearableType; }; /** Inventory Collector Functions diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index ae4b288588..8d7a2b33be 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -67,6 +67,7 @@ #include "llsidepanelappearance.h" #include "lltoggleablemenu.h" #include "llwearablelist.h" +#include "llwearableitemslist.h" static LLRegisterPanelClassWrapper t_outfit_edit("panel_outfit_edit"); @@ -214,7 +215,10 @@ LLPanelOutfitEdit::LLPanelOutfitEdit() mCOFObserver(NULL), mGearMenu(NULL), mCOFDragAndDropObserver(NULL), - mInitialized(false) + mInitialized(false), + mAddWearablesPanel(NULL), + mWearableListMaskCollector(NULL), + mWearableListTypeCollector(NULL) { mSavedFolderState = new LLSaveFolderState(); mSavedFolderState->setApply(FALSE); @@ -236,6 +240,9 @@ LLPanelOutfitEdit::~LLPanelOutfitEdit() delete mCOFObserver; delete mCOFDragAndDropObserver; + + delete mWearableListMaskCollector; + delete mWearableListTypeCollector; } BOOL LLPanelOutfitEdit::postBuild() @@ -261,6 +268,7 @@ BOOL LLPanelOutfitEdit::postBuild() mCOFWearables = getChild("cof_wearables_list"); mCOFWearables->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onOutfitItemSelectionChange, this)); + mCOFWearables->getCOFCallbacks().mAddWearable = boost::bind(&LLPanelOutfitEdit::onAddWearableClicked, this); mCOFWearables->getCOFCallbacks().mEditWearable = boost::bind(&LLPanelOutfitEdit::onEditWearableClicked, this); mCOFWearables->getCOFCallbacks().mDeleteWearable = boost::bind(&LLPanelOutfitEdit::onRemoveFromOutfitClicked, this); mCOFWearables->getCOFCallbacks().mMoveWearableCloser = boost::bind(&LLPanelOutfitEdit::moveWearable, this, true); @@ -268,6 +276,7 @@ BOOL LLPanelOutfitEdit::postBuild() mCOFWearables->childSetAction("add_btn", boost::bind(&LLPanelOutfitEdit::toggleAddWearablesPanel, this)); + mAddWearablesPanel = getChild("add_wearables_panel"); mInventoryItemsPanel = getChild("inventory_items"); mInventoryItemsPanel->setFilterTypes(ALL_ITEMS_MASK); @@ -306,9 +315,12 @@ BOOL LLPanelOutfitEdit::postBuild() save_registar.add("Outfit.SaveAsNew.Action", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, true)); mSaveMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_save_outfit.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mWearableListMaskCollector = new LLFindNonLinksByMask(ALL_ITEMS_MASK); + mWearableListTypeCollector = new LLFindWearablesOfType(LLWearableType::WT_NONE); + mWearableItemsPanel = getChild("filtered_wearables_panel"); mWearableItemsList = getChild("filtered_wearables_list"); - mWearableListManager = new LLFilteredWearableListManager(mWearableItemsList, ALL_ITEMS_MASK); + mWearableListManager = new LLFilteredWearableListManager(mWearableItemsList, mWearableListMaskCollector); return TRUE; } @@ -334,7 +346,8 @@ void LLPanelOutfitEdit::moveWearable(bool closer_to_body) void LLPanelOutfitEdit::toggleAddWearablesPanel() { - childSetVisible("add_wearables_panel", !childIsVisible("add_wearables_panel")); + BOOL current_visibility = mAddWearablesPanel->getVisible(); + mAddWearablesPanel->setVisible(!current_visibility); } void LLPanelOutfitEdit::showWearablesFilter() @@ -407,7 +420,9 @@ void LLPanelOutfitEdit::onTypeFilterChanged(LLUICtrl* ctrl) { U32 curr_filter_type = type_filter->getCurrentIndex(); mInventoryItemsPanel->setFilterTypes(mLookItemTypes[curr_filter_type].inventoryMask); - mWearableListManager->setFilterMask(mLookItemTypes[curr_filter_type].inventoryMask); + + mWearableListMaskCollector->setFilterMask(mLookItemTypes[curr_filter_type].inventoryMask); + mWearableListManager->setFilterCollector(mWearableListMaskCollector); } mSavedFolderState->setApply(TRUE); @@ -487,6 +502,25 @@ void LLPanelOutfitEdit::onAddToOutfitClicked(void) LLAppearanceMgr::getInstance()->wearItemOnAvatar(selected_id); } +void LLPanelOutfitEdit::onAddWearableClicked(void) +{ + LLPanelDummyClothingListItem* item = dynamic_cast(mCOFWearables->getSelectedItem()); + + if(item) + { + showFilteredWearableItemsList(item->getWearableType()); + } +} + +void LLPanelOutfitEdit::onReplaceBodyPartMenuItemClicked(LLUUID selected_item_id) +{ + LLViewerInventoryItem* item = gInventory.getLinkedItem(selected_item_id); + + if (item && item->getType() == LLAssetType::AT_BODYPART) + { + showFilteredWearableItemsList(item->getWearableType()); + } +} void LLPanelOutfitEdit::onRemoveFromOutfitClicked(void) { @@ -722,4 +756,11 @@ void LLPanelOutfitEdit::onGearMenuItemClick(const LLSD& data) } } +void LLPanelOutfitEdit::showFilteredWearableItemsList(LLWearableType::EType type) +{ + mWearableListTypeCollector->setType(type); + mWearableListManager->setFilterCollector(mWearableListTypeCollector); + mAddWearablesPanel->setVisible(TRUE); +} + // EOF diff --git a/indra/newview/llpaneloutfitedit.h b/indra/newview/llpaneloutfitedit.h index 1bf69c5606..dbfeb2c375 100644 --- a/indra/newview/llpaneloutfitedit.h +++ b/indra/newview/llpaneloutfitedit.h @@ -59,6 +59,8 @@ class LLToggleableMenu; class LLFilterEditor; class LLFilteredWearableListManager; class LLMenuGL; +class LLFindNonLinksByMask; +class LLFindWearablesOfType; class LLPanelOutfitEdit : public LLPanel { @@ -103,6 +105,8 @@ public: void onOutfitItemSelectionChange(void); void onRemoveFromOutfitClicked(void); void onEditWearableClicked(void); + void onAddWearableClicked(void); + void onReplaceBodyPartMenuItemClicked(LLUUID selected_item_id); void displayCurrentOutfit(); void updateCurrentOutfitName(); @@ -129,6 +133,7 @@ private: void onGearButtonClick(LLUICtrl* clicked_button); void onGearMenuItemClick(const LLSD& data); + void showFilteredWearableItemsList(LLWearableType::EType type); LLTextBox* mCurrentOutfitName; @@ -141,6 +146,10 @@ private: LLButton* mFolderViewBtn; LLButton* mListViewBtn; LLToggleableMenu* mSaveMenu; + LLPanel* mAddWearablesPanel; + + LLFindNonLinksByMask* mWearableListMaskCollector; + LLFindWearablesOfType* mWearableListTypeCollector; LLFilteredWearableListManager* mWearableListManager; LLInventoryItemsList* mWearableItemsList; diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index fb7577c008..161cd40cfc 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -251,6 +251,11 @@ BOOL LLPanelDummyClothingListItem::postBuild() return TRUE; } +LLWearableType::EType LLPanelDummyClothingListItem::getWearableType() const +{ + return mWearableType; +} + LLPanelDummyClothingListItem::LLPanelDummyClothingListItem(LLWearableType::EType w_type) : LLPanelWearableListItem(NULL) , mWearableType(w_type) diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index 7ad1b5a3ad..de024ed220 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -162,6 +162,7 @@ public: /*virtual*/ void updateItem(); /*virtual*/ BOOL postBuild(); + LLWearableType::EType getWearableType() const; protected: LLPanelDummyClothingListItem(LLWearableType::EType w_type); -- cgit v1.3 From 05626c3371395d76b1f2e6b7fcbe64ef12f7d974 Mon Sep 17 00:00:00 2001 From: Yuri Chebotarev Date: Fri, 28 May 2010 20:13:18 +0300 Subject: EXT-7013 FIX time formatting function didn't work for some parameters for Japanise (like weekdays). reviewed by Vadim Savchuk https://codereview.productengine.com/secondlife/r/457/ --HG-- branch : product-engine --- indra/llcommon/llstring.cpp | 103 ++++++++++++++++++++++++- indra/llcommon/llstring.h | 18 +++++ indra/newview/llappviewer.cpp | 11 ++- indra/newview/skins/default/xui/en/strings.xml | 15 ++++ indra/newview/skins/default/xui/ja/strings.xml | 20 ++++- 5 files changed, 164 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index b5a73ec1d1..8197e3e642 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -676,6 +676,17 @@ long LLStringOps::sLocalTimeOffset = 0; bool LLStringOps::sPacificDaylightTime = 0; std::map LLStringOps::datetimeToCodes; +std::vector LLStringOps::sWeekDayList; +std::vector LLStringOps::sWeekDayShortList; +std::vector LLStringOps::sMonthList; +std::vector LLStringOps::sMonthShortList; + + +std::string LLStringOps::sDayFormat; +std::string LLStringOps::sAM; +std::string LLStringOps::sPM; + + S32 LLStringOps::collate(const llwchar* a, const llwchar* b) { #if LL_WINDOWS @@ -724,6 +735,50 @@ void LLStringOps::setupDatetimeInfo (bool daylight) datetimeToCodes["timezone"] = "%Z"; // PST } +void tokenizeStringToArray(const std::string& data, std::vector& output) +{ + output.clear(); + size_t length = data.size(); + + // tokenize it and put it in the array + std::string cur_word; + for(size_t i = 0; i < length; ++i) + { + if(data[i] == ':') + { + output.push_back(cur_word); + cur_word.clear(); + } + else + { + cur_word.append(1, data[i]); + } + } + output.push_back(cur_word); +} + +void LLStringOps::setupWeekDaysNames(const std::string& data) +{ + tokenizeStringToArray(data,sWeekDayList); +} +void LLStringOps::setupWeekDaysShortNames(const std::string& data) +{ + tokenizeStringToArray(data,sWeekDayShortList); +} +void LLStringOps::setupMonthNames(const std::string& data) +{ + tokenizeStringToArray(data,sMonthList); +} +void LLStringOps::setupMonthShortNames(const std::string& data) +{ + tokenizeStringToArray(data,sMonthShortList); +} +void LLStringOps::setupDayFormat(const std::string& data) +{ + sDayFormat = data; +} + + std::string LLStringOps::getDatetimeCode (std::string key) { std::map::iterator iter; @@ -998,7 +1053,53 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token, } return true; } - replacement = datetime.toHTTPDateString(code); + + //EXT-7013 + //few codes are not suppotred by strtime function (example - weekdays for Japanise) + //so use predefined ones + + //if sWeekDayList is not empty than current locale doesn't support + //weekday name. + time_t loc_seconds = (time_t) secFromEpoch; + if(LLStringOps::sWeekDayList.size() == 7 && code == "%A") + { + struct tm * gmt = gmtime (&loc_seconds); + replacement = LLStringOps::sWeekDayList[gmt->tm_wday]; + } + else if(LLStringOps::sWeekDayShortList.size() == 7 && code == "%a") + { + struct tm * gmt = gmtime (&loc_seconds); + replacement = LLStringOps::sWeekDayShortList[gmt->tm_wday]; + } + else if(LLStringOps::sMonthList.size() == 12 && code == "%B") + { + struct tm * gmt = gmtime (&loc_seconds); + replacement = LLStringOps::sWeekDayList[gmt->tm_mon]; + } + else if( !LLStringOps::sDayFormat.empty() && code == "%d" ) + { + struct tm * gmt = gmtime (&loc_seconds); + LLStringUtil::format_map_t args; + args["[MDAY]"] = llformat ("%d", gmt->tm_mday); + replacement = LLStringOps::sDayFormat; + LLStringUtil::format(replacement, args); + } + else if( !LLStringOps::sAM.empty() && !LLStringOps::sPM.empty() && code == "%p" ) + { + struct tm * gmt = gmtime (&loc_seconds); + if(gmt->tm_hour<12) + { + replacement = LLStringOps::sAM; + } + else + { + replacement = LLStringOps::sPM; + } + } + else + { + replacement = datetime.toHTTPDateString(code); + } // *HACK: delete leading zero from hour string in case 'hour12' (code = %I) time format // to show time without leading zero, e.g. 08:16 -> 8:16 (EXT-2738). diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 96588b29b9..ad8f8632a2 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -154,9 +154,19 @@ private: static long sPacificTimeOffset; static long sLocalTimeOffset; static bool sPacificDaylightTime; + static std::map datetimeToCodes; public: + static std::vector sWeekDayList; + static std::vector sWeekDayShortList; + static std::vector sMonthList; + static std::vector sMonthShortList; + static std::string sDayFormat; + + static std::string sAM; + static std::string sPM; + static char toUpper(char elem) { return toupper((unsigned char)elem); } static llwchar toUpper(llwchar elem) { return towupper(elem); } @@ -185,6 +195,14 @@ public: static S32 collate(const llwchar* a, const llwchar* b); static void setupDatetimeInfo(bool pacific_daylight_time); + + static void setupWeekDaysNames(const std::string& data); + static void setupWeekDaysShortNames(const std::string& data); + static void setupMonthNames(const std::string& data); + static void setupMonthShortNames(const std::string& data); + static void setupDayFormat(const std::string& data); + + static long getPacificTimeOffset(void) { return sPacificTimeOffset;} static long getLocalTimeOffset(void) { return sLocalTimeOffset;} // Is the Pacific time zone (aka server time zone) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f7f7cb599e..7b17933b5c 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -890,7 +890,16 @@ bool LLAppViewer::init() } LLViewerMedia::initClass(); - + + LLStringOps::setupWeekDaysNames(LLTrans::getString("dateTimeWeekdaysNames")); + LLStringOps::setupWeekDaysShortNames(LLTrans::getString("dateTimeWeekdaysShortNames")); + LLStringOps::setupMonthNames(LLTrans::getString("dateTimeMonthNames")); + LLStringOps::setupMonthShortNames(LLTrans::getString("dateTimeMonthShortNames")); + LLStringOps::setupDayFormat(LLTrans::getString("dateTimeDayFormat")); + + LLStringOps::sAM = LLTrans::getString("dateTimeAM"); + LLStringOps::sPM = LLTrans::getString("dateTimePM"); + return true; } diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index e57b30185d..9247e3f74c 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -3183,4 +3183,19 @@ Abuse Report Please check secondlife.com/status to see if there is a known problem with the service. + + + + + + + + diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index 57120ff7ec..a06dcc54e4 100644 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -1,4 +1,4 @@ - + + + Sunday:Monday:Tuesday:Wednesday:Thursday:Friday:Saturday + + + Son:Mon:Tue:Wed:Thu:Fri:Sat + + + January:February:March:April:May:June:July:August:September:October:November:December + + + Jan:Feb:Mar:Apr:May:Jun:Jul:Aug:Sep:Oct:Nov:Dec + + + [MDAY] D + + AM + PM -- cgit v1.3 From fd98253aecb4a26a9a41cf5b2af0802a1b9e19d5 Mon Sep 17 00:00:00 2001 From: Aimee Linden Date: Fri, 28 May 2010 18:46:22 +0100 Subject: Fix borked merge, DOH! --- indra/newview/llvoiceclient.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 76cf7ad3dd..e8635d7f1a 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -117,7 +117,6 @@ LLVoiceClient::LLVoiceClient() : mVoiceModule(NULL), m_servicePump(NULL), - mVoiceModule(NULL), mVoiceEffectEnabled(LLCachedControl(gSavedSettings, "VoiceMorphingEnabled")), mVoiceEffectDefault(LLCachedControl(gSavedPerAccountSettings, "VoiceEffectDefault")) { -- cgit v1.3 From 0b96ae29cf0b2baf4732e09a28a5f76d3df05727 Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Fri, 28 May 2010 14:02:44 -0400 Subject: Fixed linux compile error due to missing forward declare in lltextureview.h. --- indra/newview/lltextureview.h | 1 + 1 file changed, 1 insertion(+) (limited to 'indra') diff --git a/indra/newview/lltextureview.h b/indra/newview/lltextureview.h index 6072cd3f11..dfd9c42c2c 100644 --- a/indra/newview/lltextureview.h +++ b/indra/newview/lltextureview.h @@ -38,6 +38,7 @@ class LLViewerFetchedTexture; class LLTextureBar; class LLGLTexMemBar; +class LLAvatarTexBar; class LLTextureView : public LLContainerView { -- cgit v1.3 From 3f5abfb2bf58032242d6c24328463558a1828b20 Mon Sep 17 00:00:00 2001 From: Eli Linden Date: Fri, 28 May 2010 11:47:38 -0700 Subject: ND-46735 WIP FR JA linguistic --- .../newview/skins/default/xui/fr/floater_preview_gesture.xml | 12 ++++++------ indra/newview/skins/default/xui/fr/floater_tools.xml | 2 +- indra/newview/skins/default/xui/fr/notifications.xml | 2 +- indra/newview/skins/default/xui/fr/panel_edit_pick.xml | 4 ++-- .../skins/default/xui/fr/panel_group_control_panel.xml | 2 +- indra/newview/skins/default/xui/fr/panel_group_notices.xml | 6 +++--- indra/newview/skins/default/xui/fr/panel_notes.xml | 2 +- indra/newview/skins/default/xui/fr/panel_people.xml | 2 +- .../skins/default/xui/fr/panel_preferences_advanced.xml | 2 +- indra/newview/skins/default/xui/fr/panel_profile.xml | 2 +- indra/newview/skins/default/xui/fr/strings.xml | 2 +- .../newview/skins/default/xui/ja/floater_avatar_textures.xml | 2 +- indra/newview/skins/default/xui/ja/floater_god_tools.xml | 2 +- indra/newview/skins/default/xui/ja/floater_moveview.xml | 12 ++++++------ indra/newview/skins/default/xui/ja/menu_avatar_self.xml | 4 ++-- .../skins/default/xui/ja/panel_bodyparts_list_button_bar.xml | 4 ++-- .../skins/default/xui/ja/panel_clothing_list_button_bar.xml | 2 +- indra/newview/skins/default/xui/ja/panel_edit_shape.xml | 2 +- indra/newview/skins/default/xui/ja/panel_region_general.xml | 2 +- indra/newview/skins/default/xui/ja/panel_region_texture.xml | 4 ++-- 20 files changed, 36 insertions(+), 36 deletions(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/fr/floater_preview_gesture.xml b/indra/newview/skins/default/xui/fr/floater_preview_gesture.xml index 7133f8754c..1d164ac661 100644 --- a/indra/newview/skins/default/xui/fr/floater_preview_gesture.xml +++ b/indra/newview/skins/default/xui/fr/floater_preview_gesture.xml @@ -31,10 +31,10 @@ Description : - Déclencheur : + Déclench. : - Remplacer par : + Rempl. par : @@ -50,20 +50,20 @@ Étapes : - - - - - Date: Tue, 1 Jun 2010 14:13:56 +0300 Subject: EXT-6710 FIXED Added inventory items sharing confirmation and modified alert about non-copy objects sharing. Current behavior: * user will be prompted about sharing items regardless is it one or more items; * user will be prompted about trying to share one ore more non-copy items; * in case user tries to share non-copy items among with copy items — copy tems will be shared and user will be prompted about sharing non-copy items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/475/ --HG-- branch : product-engine --- indra/newview/llavataractions.cpp | 159 ++++++++++++++++++--- indra/newview/llfloateravatarpicker.cpp | 2 +- indra/newview/llgiveinventory.cpp | 46 +++--- indra/newview/llgiveinventory.h | 7 +- indra/newview/lltooldraganddrop.cpp | 50 ++++++- indra/newview/lltooldraganddrop.h | 3 +- .../newview/skins/default/xui/en/notifications.xml | 26 +++- indra/newview/skins/default/xui/en/strings.xml | 1 + 8 files changed, 250 insertions(+), 44 deletions(-) (limited to 'indra') diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 875ed72a12..01d6c3a8d5 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -475,32 +475,83 @@ namespace action_give_inventory return acceptable; } - /** - * Performs "give inventory" operations for provided avatars. - * - * Sends one requests to give all selected inventory items for each passed avatar. - * Avatars are represent by two vectors: names and UUIDs which must be sychronized with each other. - * - * @param avatar_names - avatar names request to be sent. - * @param avatar_uuids - avatar names request to be sent. - */ - static void give_inventory(const std::vector& avatar_names, const uuid_vec_t& avatar_uuids) + static void build_residents_string(const std::vector& avatar_names, std::string& residents_string) { - llassert(avatar_names.size() == avatar_uuids.size()); + llassert(avatar_names.size() > 0); + + const std::string& separator = LLTrans::getString("words_separator"); + for (std::vector::const_iterator it = avatar_names.begin(); ; ) + { + residents_string.append(*it); + if (++it == avatar_names.end()) + { + break; + } + residents_string.append(separator); + } + } + + static void build_items_string(const uuid_set_t& inventory_selected_uuids , std::string& items_string) + { + llassert(inventory_selected_uuids.size() > 0); + + const std::string& separator = LLTrans::getString("words_separator"); + for (uuid_set_t::const_iterator it = inventory_selected_uuids.begin(); ; ) + { + LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); + if (NULL != inv_cat) + { + items_string = inv_cat->getName(); + break; + } + LLViewerInventoryItem* inv_item = gInventory.getItem(*it); + if (NULL != inv_item) + { + items_string.append(inv_item->getName()); + } + if(++it == inventory_selected_uuids.end()) + { + break; + } + items_string.append(separator); + } + } + + struct LLShareInfo : public LLSingleton + { + std::vector mAvatarNames; + uuid_vec_t mAvatarUuids; + }; + + static void give_inventory_cb(const LLSD& notification, const LLSD& response) + { + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + // if Cancel pressed + if (option == 1) + { + return; + } LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); - if (NULL == active_panel) return; + if (NULL == active_panel) + { + return; + } const uuid_set_t inventory_selected_uuids = active_panel->getRootFolder()->getSelectionList(); - if (inventory_selected_uuids.empty()) return; + if (inventory_selected_uuids.empty()) + { + return; + } - S32 count = llmin(avatar_names.size(), avatar_uuids.size()); + S32 count = LLShareInfo::instance().mAvatarNames.size(); + bool shared = false; // iterate through avatars for(S32 i = 0; i < count; ++i) { - const std::string& avatar_name = avatar_names[i]; - const LLUUID& avatar_uuid = avatar_uuids[i]; + const std::string& avatar_name = LLShareInfo::instance().mAvatarNames[i]; + const LLUUID& avatar_uuid = LLShareInfo::instance().mAvatarUuids[i]; // Start up IM before give the item const LLUUID session_id = gIMMgr->addSession(avatar_name, IM_NOTHING_SPECIAL, avatar_uuid); @@ -508,6 +559,9 @@ namespace action_give_inventory uuid_set_t::const_iterator it = inventory_selected_uuids.begin(); const uuid_set_t::const_iterator it_end = inventory_selected_uuids.end(); + const std::string& separator = LLTrans::getString("words_separator"); + std::string noncopy_item_names; + LLSD noncopy_items = LLSD::emptyArray(); // iterate through selected inventory objects for (; it != it_end; ++it) { @@ -515,12 +569,82 @@ namespace action_give_inventory if (inv_cat) { LLGiveInventory::doGiveInventoryCategory(avatar_uuid, inv_cat, session_id); + shared = true; break; } LLViewerInventoryItem* inv_item = gInventory.getItem(*it); - LLGiveInventory::doGiveInventoryItem(avatar_uuid, inv_item, session_id); + if (!inv_item->getPermissions().allowCopyBy(gAgentID)) + { + if (!noncopy_item_names.empty()) + { + noncopy_item_names.append(separator); + } + noncopy_item_names.append(inv_item->getName()); + noncopy_items.append(*it); + } + else + { + LLGiveInventory::doGiveInventoryItem(avatar_uuid, inv_item, session_id); + shared = true; + } + } + if (noncopy_items.beginArray() != noncopy_items.endArray()) + { + LLSD substitutions; + substitutions["ITEMS"] = noncopy_item_names; + LLSD payload; + payload["agent_id"] = avatar_uuid; + payload["items"] = noncopy_items; + LLNotificationsUtil::add("CannotCopyWarning", substitutions, payload, + &LLGiveInventory::handleCopyProtectedItem); + break; } } + if (shared) + { + LLFloaterReg::hideInstance("avatar_picker"); + LLNotificationsUtil::add("ItemsShared"); + } + } + + /** + * Performs "give inventory" operations for provided avatars. + * + * Sends one requests to give all selected inventory items for each passed avatar. + * Avatars are represent by two vectors: names and UUIDs which must be sychronized with each other. + * + * @param avatar_names - avatar names request to be sent. + * @param avatar_uuids - avatar names request to be sent. + */ + static void give_inventory(const std::vector& avatar_names, const uuid_vec_t& avatar_uuids) + { + llassert(avatar_names.size() == avatar_uuids.size()); + + + LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); + if (NULL == active_panel) + { + return; + } + + const uuid_set_t inventory_selected_uuids = active_panel->getRootFolder()->getSelectionList(); + if (inventory_selected_uuids.empty()) + { + return; + } + + std::string residents; + build_residents_string(avatar_names, residents); + + std::string items; + build_items_string(inventory_selected_uuids, items); + + LLSD substitutions; + substitutions["RESIDENTS"] = residents; + substitutions["ITEMS"] = items; + LLShareInfo::instance().mAvatarNames = avatar_names; + LLShareInfo::instance().mAvatarUuids = avatar_uuids; + LLNotificationsUtil::add("ShareItemsConfirmation", substitutions, LLSD(), &give_inventory_cb); } } @@ -532,7 +656,6 @@ void LLAvatarActions::shareWithAvatars() LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(give_inventory, _1, _2), TRUE, FALSE); picker->setOkBtnEnableCb(boost::bind(is_give_inventory_acceptable)); - LLNotificationsUtil::add("ShareNotification"); } // static diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp index b40c19c2c6..96364f9418 100644 --- a/indra/newview/llfloateravatarpicker.cpp +++ b/indra/newview/llfloateravatarpicker.cpp @@ -437,7 +437,7 @@ BOOL LLFloaterAvatarPicker::handleDragAndDrop(S32 x, S32 y, MASK mask, session_id = gIMMgr->addSession(avatar_name, IM_NOTHING_SPECIAL, dest_agent_id); } return LLToolDragAndDrop::handleGiveDragAndDrop(dest_agent_id, session_id, drop, - cargo_type, cargo_data, accept); + cargo_type, cargo_data, accept, getName()); } } } diff --git a/indra/newview/llgiveinventory.cpp b/indra/newview/llgiveinventory.cpp index d83d895dd0..4a13e1e504 100644 --- a/indra/newview/llgiveinventory.cpp +++ b/indra/newview/llgiveinventory.cpp @@ -192,15 +192,16 @@ bool LLGiveInventory::isInventoryGroupGiveAcceptable(const LLInventoryItem* item } // static -void LLGiveInventory::doGiveInventoryItem(const LLUUID& to_agent, +bool LLGiveInventory::doGiveInventoryItem(const LLUUID& to_agent, const LLInventoryItem* item, const LLUUID& im_session_id/* = LLUUID::null*/) { + bool res = true; llinfos << "LLGiveInventory::giveInventory()" << llendl; if (!isInventoryGiveAcceptable(item)) { - return; + return false; } if (item->getPermissions().allowCopyBy(gAgentID)) { @@ -210,12 +211,19 @@ void LLGiveInventory::doGiveInventoryItem(const LLUUID& to_agent, else { // ask if the agent is sure. + LLSD substitutions; + substitutions["ITEMS"] = item->getName(); LLSD payload; payload["agent_id"] = to_agent; - payload["item_id"] = item->getUUID(); - LLNotificationsUtil::add("CannotCopyWarning", LLSD(), payload, + LLSD items = LLSD::emptyArray(); + items.append(item->getUUID()); + payload["items"] = items; + LLNotificationsUtil::add("CannotCopyWarning", substitutions, payload, &LLGiveInventory::handleCopyProtectedItem); + res = false; } + + return res; } void LLGiveInventory::doGiveInventoryCategory(const LLUUID& to_agent, @@ -318,25 +326,29 @@ void LLGiveInventory::logInventoryOffer(const LLUUID& to_agent, const LLUUID &im bool LLGiveInventory::handleCopyProtectedItem(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + LLSD itmes = notification["payload"]["items"]; LLInventoryItem* item = NULL; switch(option) { case 0: // "Yes" - item = gInventory.getItem(notification["payload"]["item_id"].asUUID()); - if (item) - { - LLGiveInventory::commitGiveInventoryItem(notification["payload"]["agent_id"].asUUID(), - item); - // delete it for now - it will be deleted on the server - // quickly enough. - gInventory.deleteObject(notification["payload"]["item_id"].asUUID()); - gInventory.notifyObservers(); - } - else + for (LLSD::array_iterator it = itmes.beginArray(); it != itmes.endArray(); it++) { - LLNotificationsUtil::add("CannotGiveItem"); + item = gInventory.getItem((*it).asUUID()); + if (item) + { + LLGiveInventory::commitGiveInventoryItem(notification["payload"]["agent_id"].asUUID(), + item); + // delete it for now - it will be deleted on the server + // quickly enough. + gInventory.deleteObject(item->getUUID()); + gInventory.notifyObservers(); + } + else + { + LLNotificationsUtil::add("CannotGiveItem"); + } + break; } - break; default: // no, cancel, whatever, who cares, not yes. LLNotificationsUtil::add("TransactionCancelled"); diff --git a/indra/newview/llgiveinventory.h b/indra/newview/llgiveinventory.h index e589a0cc69..86f0f2fe86 100644 --- a/indra/newview/llgiveinventory.h +++ b/indra/newview/llgiveinventory.h @@ -61,7 +61,7 @@ public: /** * Gives passed inventory item to specified avatar in specified session. */ - static void doGiveInventoryItem(const LLUUID& to_agent, + static bool doGiveInventoryItem(const LLUUID& to_agent, const LLInventoryItem* item, const LLUUID& im_session_id = LLUUID::null); @@ -72,6 +72,9 @@ public: const LLInventoryCategory* item, const LLUUID &session_id = LLUUID::null); + // give inventory item functionality + static bool handleCopyProtectedItem(const LLSD& notification, const LLSD& response); + private: // this class is not intended to be instantiated. LLGiveInventory(); @@ -82,8 +85,6 @@ private: static void logInventoryOffer(const LLUUID& to_agent, const LLUUID &im_session_id = LLUUID::null); - // give inventory item functionality - static bool handleCopyProtectedItem(const LLSD& notification, const LLSD& response); static void commitGiveInventoryItem(const LLUUID& to_agent, const LLInventoryItem* item, const LLUUID &im_session_id = LLUUID::null); diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 8a8bdd2dba..bc77ac5fd1 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -48,7 +48,7 @@ #include "llgiveinventory.h" #include "llhudmanager.h" #include "llhudeffecttrail.h" -//#include "llimview.h" +#include "llimview.h" #include "llinventorybridge.h" #include "llinventorydefines.h" #include "llinventoryfunctions.h" @@ -1422,12 +1422,42 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL } +static void give_inventory_cb(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + // if Cancel pressed + if (option == 1) + { + return; + } + + LLSD payload = notification["payload"]; + const LLUUID& session_id = payload["session_id"]; + const LLUUID& agent_id = payload["agent_id"]; + LLViewerInventoryItem * inv_item = gInventory.getItem(payload["item_id"]); + if (NULL == inv_item) + { + llassert(NULL != inv_item); + return; + } + + if (LLGiveInventory::doGiveInventoryItem(agent_id, inv_item, session_id)) + { + if ("avatarpicker" == payload["d&d_dest"].asString()) + { + LLFloaterReg::hideInstance("avatar_picker"); + } + LLNotificationsUtil::add("ItemsShared"); + } +} + // function used as drag-and-drop handler for simple agent give inventory requests //static bool LLToolDragAndDrop::handleGiveDragAndDrop(LLUUID dest_agent, LLUUID session_id, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, - EAcceptance* accept) + EAcceptance* accept, + const LLSD& dest) { // check the type switch(cargo_type) @@ -1452,7 +1482,21 @@ bool LLToolDragAndDrop::handleGiveDragAndDrop(LLUUID dest_agent, LLUUID session_ *accept = ACCEPT_YES_COPY_SINGLE; if (drop) { - LLGiveInventory::doGiveInventoryItem(dest_agent, inv_item, session_id); + LLIMModel::LLIMSession * session = LLIMModel::instance().findIMSession(session_id); + if (NULL == session) + { + llassert(NULL != session); + return false; + } + LLSD substitutions; + substitutions["RESIDENTS"] = session->mName; + substitutions["ITEMS"] = inv_item->getName(); + LLSD payload; + payload["agent_id"] = dest_agent; + payload["item_id"] = inv_item->getUUID(); + payload["session_id"] = session_id; + payload["d&d_dest"] = dest.asString(); + LLNotificationsUtil::add("ShareItemsConfirmation", substitutions, payload, &give_inventory_cb); } } else diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h index 18334b5ee1..36e69c1db3 100644 --- a/indra/newview/lltooldraganddrop.h +++ b/indra/newview/lltooldraganddrop.h @@ -246,7 +246,8 @@ public: static bool handleGiveDragAndDrop(LLUUID agent, LLUUID session, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, - EAcceptance* accept); + EAcceptance* accept, + const LLSD& dest = LLSD()); // Classes used for determining 3d drag and drop types. private: diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 079d029eab..5185b78f96 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -1090,7 +1090,9 @@ The region [REGION] does not allow terraforming. icon="alertmodal.tga" name="CannotCopyWarning" type="alertmodal"> -You do not have permission to copy this item and will lose it from your inventory if you give it away. Do you really want to offer this item? +You do not have permission to copy the following items: +[ITEMS] +and will lose it from your inventory if you give it away. Do you really want to offer these items? Drag items from inventory onto a person in the resident picker + + +Are you sure you want to share the following items: + +[ITEMS] + +With the following Residents: + +[RESIDENTS] + + + +Items are successfully shared. + , Despite our best efforts, something unexpected has gone wrong. -- cgit v1.3 From fdb5debe4f6ddf16c5f1008d0b89a1dd0e9a65fb Mon Sep 17 00:00:00 2001 From: Mike Antipov Date: Tue, 1 Jun 2010 14:55:40 +0300 Subject: EXT-7255 COMMENTS Updated comments about implementation details. --HG-- branch : product-engine --- indra/newview/skins/default/xui/en/panel_outfit_edit.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml index abd96c89e7..93b5bb54e6 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml @@ -150,8 +150,8 @@ - + Date: Tue, 1 Jun 2010 15:22:05 +0300 Subject: EXT-6710 ADDITIONAL FIX win build fixed --HG-- branch : product-engine --- indra/newview/llgiveinventory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llgiveinventory.cpp b/indra/newview/llgiveinventory.cpp index 4a13e1e504..aebf1b4c26 100644 --- a/indra/newview/llgiveinventory.cpp +++ b/indra/newview/llgiveinventory.cpp @@ -347,8 +347,8 @@ bool LLGiveInventory::handleCopyProtectedItem(const LLSD& notification, const LL { LLNotificationsUtil::add("CannotGiveItem"); } - break; } + break; default: // no, cancel, whatever, who cares, not yes. LLNotificationsUtil::add("TransactionCancelled"); -- cgit v1.3 From 1e090dafa6271ec2acb92441bcbbbbe058124be0 Mon Sep 17 00:00:00 2001 From: Andrew Dyukov Date: Tue, 1 Jun 2010 16:42:31 +0300 Subject: EXT-7087 ADDITIONAL FIX Style changes regarding image_flash attribute Removed default "FlashIconAbsent" value of image_flash button attribute. Changed check for presence of thic icon in code accordingly. --HG-- branch : product-engine --- indra/llui/llbutton.cpp | 4 ++-- indra/llui/llbutton.h | 3 +-- .../newview/skins/default/xui/en/widgets/button.xml | 2 +- .../skins/default/xui/en/widgets/tab_container.xml | 21 +++++++++------------ 4 files changed, 13 insertions(+), 17 deletions(-) (limited to 'indra') diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index a8f72183fd..34f3049f2e 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -636,8 +636,8 @@ void LLButton::draw() if (mFlashing) { - // if we have icon for flashing, use it as image for button - if(flash && mImageFlash->getName() != "FlashIconAbsent") + // if button should flash and we have icon for flashing, use it as image for button + if(flash && mImageFlash) { // setting flash to false to avoid its further influence on glow flash = false; diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index b251c3d65d..9bd566d3c9 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -314,8 +314,7 @@ private: /* There are two ways an image can flash- by making changes in color according to flash_color attribute or by changing icon from current to the one specified in image_flash. Second way is used only if - the name of flash icon is different from "FlashIconAbsent" which is there by default. First way is used - otherwise. */ + flash icon name is set in attributes(by default it isn't). First way is used otherwise. */ LLPointer mImageFlash; LLUIColor mHighlightColor; diff --git a/indra/newview/skins/default/xui/en/widgets/button.xml b/indra/newview/skins/default/xui/en/widgets/button.xml index 6dcc27b469..2d0a1728d5 100644 --- a/indra/newview/skins/default/xui/en/widgets/button.xml +++ b/indra/newview/skins/default/xui/en/widgets/button.xml @@ -2,12 +2,12 @@ - - + + Date: Wed, 16 Jun 2010 16:54:14 -0700 Subject: web browser test should always use internal browser --- indra/newview/llviewermenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 2c7ae539ce..cc33dfcfef 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -7209,7 +7209,7 @@ void handle_web_browser_test(const LLSD& param) { url = "about:blank"; } - LLWeb::loadURL(url); + LLWeb::loadURLInternal(url); } void handle_buy_currency_test(void*) -- cgit v1.3 From 4aef5f54de70a8d729c93f7eb11995a7d23feed0 Mon Sep 17 00:00:00 2001 From: Richard Nelson Date: Wed, 16 Jun 2010 17:32:51 -0700 Subject: EXT-7734 FIX In-world object properteis often don't show correct information reviewed by Leyla --- indra/newview/llinventoryfunctions.cpp | 5 ++ indra/newview/llinventoryfunctions.h | 1 + indra/newview/llpanelobjectinventory.cpp | 12 +-- indra/newview/llselectmgr.h | 15 ++-- indra/newview/llsidepaneltaskinfo.cpp | 27 ++++++- indra/newview/llsidepaneltaskinfo.h | 4 +- .../skins/default/xui/en/sidepanel_item_info.xml | 89 +++++++++++----------- 7 files changed, 86 insertions(+), 67 deletions(-) (limited to 'indra') diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 68fc86467c..3913e1805f 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -327,6 +327,11 @@ BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) return FALSE; } +void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id) +{ + LLSideTray::getInstance()->showPanel("sidepanel_inventory", LLSD().with("id", item_uuid).with("object", object_id)); +} + void show_item_profile(const LLUUID& item_uuid) { LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid); diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 93c56e1b8a..aa4ac98241 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -53,6 +53,7 @@ BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id); BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id); void show_item_profile(const LLUUID& item_uuid); +void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id); void show_item_original(const LLUUID& item_uuid); diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index da809b7baa..f683cbc8d0 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -176,17 +176,7 @@ LLInventoryItem* LLTaskInvFVBridge::findItem() const void LLTaskInvFVBridge::showProperties() { - show_item_profile(mUUID); - - // Disable old properties floater; this is replaced by the sidepanel. - /* - LLFloaterProperties* floater = LLFloaterReg::showTypedInstance("properties", mUUID); - if (floater) - { - floater->setObjectID(mPanel->getTaskUUID()); - } - */ - + show_task_item_profile(mUUID, mPanel->getTaskUUID()); } struct LLBuyInvItemData diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index d315f40ff3..668c04cf15 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -257,7 +257,6 @@ public: LLObjectSelection(); void updateEffects(); - void cleanupNodes(); BOOL isEmpty() const; @@ -281,11 +280,6 @@ public: template bool getSelectedTEValue(LLSelectedTEGetFunctor* func, T& res); template bool isMultipleTEValue(LLSelectedTEGetFunctor* func, const T& ignore_value); - void addNode(LLSelectNode *nodep); - void addNodeAtEnd(LLSelectNode *nodep); - void moveNodeToFront(LLSelectNode *nodep); - void removeNode(LLSelectNode *nodep); - void deleteAllNodes(); // Delete all nodes S32 getNumNodes(); LLSelectNode* findNode(LLViewerObject* objectp); @@ -312,6 +306,15 @@ public: ESelectType getSelectType() const { return mSelectType; } +private: + void addNode(LLSelectNode *nodep); + void addNodeAtEnd(LLSelectNode *nodep); + void moveNodeToFront(LLSelectNode *nodep); + void removeNode(LLSelectNode *nodep); + void deleteAllNodes(); + void cleanupNodes(); + + private: list_t mList; const LLObjectSelection &operator=(const LLObjectSelection &); diff --git a/indra/newview/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp index e76397671b..3ab71eac64 100644 --- a/indra/newview/llsidepaneltaskinfo.cpp +++ b/indra/newview/llsidepaneltaskinfo.cpp @@ -80,6 +80,7 @@ static LLRegisterPanelClassWrapper t_task_info("sidepanel_t LLSidepanelTaskInfo::LLSidepanelTaskInfo() { setMouseOpaque(FALSE); + LLSelectMgr::instance().mUpdateSignal.connect(boost::bind(&LLSidepanelTaskInfo::refreshAll, this)); } @@ -271,7 +272,6 @@ void LLSidepanelTaskInfo::refresh() // BUG: fails if a root and non-root are both single-selected. const BOOL is_perm_modify = (mObjectSelection->getFirstRootNode() && LLSelectMgr::getInstance()->selectGetRootsModify()) || LLSelectMgr::getInstance()->selectGetModify(); - const LLFocusableElement* keyboard_focus_view = gFocusMgr.getKeyboardFocus(); S32 string_index = 0; std::string MODIFY_INFO_STRINGS[] = @@ -365,14 +365,14 @@ void LLSidepanelTaskInfo::refresh() if (is_one_object) { - if (keyboard_focus_view != LineEditorObjectName) + if (!LineEditorObjectName->hasFocus()) { childSetText("Object Name",nodep->mName); } if (LineEditorObjectDesc) { - if (keyboard_focus_view != LineEditorObjectDesc) + if (!LineEditorObjectDesc->hasFocus()) { LineEditorObjectDesc->setText(nodep->mDescription); } @@ -1178,9 +1178,30 @@ void LLSidepanelTaskInfo::save() onCommitIncludeInSearch(getChild("search_check"), this); } +// removes keyboard focus so that all fields can be updated +// and then restored focus +void LLSidepanelTaskInfo::refreshAll() +{ + // update UI as soon as we have an object + // but remove keyboard focus first so fields are free to update + LLFocusableElement* focus = NULL; + if (hasFocus()) + { + focus = gFocusMgr.getKeyboardFocus(); + setFocus(FALSE); + } + refresh(); + if (focus) + { + focus->setFocus(TRUE); + } +} + + void LLSidepanelTaskInfo::setObjectSelection(LLObjectSelectionHandle selection) { mObjectSelection = selection; + refreshAll(); } LLSidepanelTaskInfo* LLSidepanelTaskInfo::getActivePanel() diff --git a/indra/newview/llsidepaneltaskinfo.h b/indra/newview/llsidepaneltaskinfo.h index 15274c90db..010173e84e 100644 --- a/indra/newview/llsidepaneltaskinfo.h +++ b/indra/newview/llsidepaneltaskinfo.h @@ -67,6 +67,8 @@ protected: /*virtual*/ void save(); /*virtual*/ void updateVerbs(); + void refreshAll(); // ignore current keyboard focus and update all fields + // statics static void onClickClaim(void*); static void onClickRelease(void*); @@ -120,7 +122,7 @@ protected: LLViewerObject* getObject(); private: LLViewerObject* mObject; - LLObjectSelectionHandle mObjectSelection; + LLObjectSelectionHandle mObjectSelection; static LLSidepanelTaskInfo* sActivePanel; }; diff --git a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml index e2bd6f375e..b3bc618eec 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml @@ -159,51 +159,49 @@ name="LabelCreatorName" top_delta="6" width="140"> - Nicole Linden - - @@ -61,22 +73,22 @@ To reconnect to Nearby Voice simply close this window. left_delta="0" name="record_stop_btn" top_delta="0" - width="150"> + width="100"> - [[URL] Get Voice Morphing] + right="-10"> + [[URL] Subscribe Now] + top="95"> + label="Voice Name" + name="name" + relative_width="0.60" /> + relative_width="0.30" /> + diff --git a/indra/newview/skins/default/xui/en/panel_voice_effect.xml b/indra/newview/skins/default/xui/en/panel_voice_effect.xml index c575ca468c..93e79b7328 100644 --- a/indra/newview/skins/default/xui/en/panel_voice_effect.xml +++ b/indra/newview/skins/default/xui/en/panel_voice_effect.xml @@ -6,7 +6,7 @@ name="panel_voice_effect" width="200"> - No Voice Morph + Voice Morphing Off Preview Voice Morphing â–¶ @@ -19,11 +19,11 @@ follows="left|top|right" height="23" name="voice_effect" - tool_tip="Select a Voice Morphing effect to change your voice." + tool_tip="Select a Voice Morph to change your voice" top_pad="0" width="200"> -- cgit v1.3 From cc0d5eae4d59adf41d59248e1b481a66db791b07 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 18 Jun 2010 16:17:34 +0100 Subject: Fix llpreviewgesture.h build borkage. --- indra/newview/llpreviewgesture.h | 2 +- install.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpreviewgesture.h b/indra/newview/llpreviewgesture.h index 1814f0d403..b141b14445 100644 --- a/indra/newview/llpreviewgesture.h +++ b/indra/newview/llpreviewgesture.h @@ -35,8 +35,8 @@ #include "llassettype.h" #include "llpreview.h" +#include "llmultigesture.h" -class LLMultiGesture; class LLLineEditor; class LLTextBox; class LLCheckBoxCtrl; diff --git a/install.xml b/install.xml index 5f444dd016..f69d781c67 100644 --- a/install.xml +++ b/install.xml @@ -988,9 +988,9 @@ anguage Infrstructure (CLI) international standard linux md5sum - 7a5e02d252b76ab41bdca58506fa4147 + 414d72dd59e3d83c96f0e1531360792e url - http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/llqtwebkit-linux-20100521.tar.bz2 + http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/llqtwebkit-linux-20100618.tar.bz2 windows -- cgit v1.3 From c7f0cabc0eb530efdbbc0bf74bf33979be23e6fa Mon Sep 17 00:00:00 2001 From: Vladimir Pchelko Date: Fri, 18 Jun 2010 20:30:52 +0300 Subject: EXT-7821 FIXED Editor type was changed to the type as in "Name:" editor in "My Inventory"->"Object Profile". Fix details: Using line_editor instead of text_editor. Enabled ascii filter for entering, because (for example) Russian charters corrupts outfit items and makes Edit Outfit very buggy. Reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/608/ --HG-- branch : product-engine --- indra/newview/llpaneleditwearable.cpp | 16 ++++++++-------- indra/newview/llpaneleditwearable.h | 4 ++-- .../skins/default/xui/en/panel_edit_wearable.xml | 20 +++++++++++--------- 3 files changed, 21 insertions(+), 19 deletions(-) (limited to 'indra') diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index 1aedfec86f..4402b2130f 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -652,7 +652,7 @@ BOOL LLPanelEditWearable::postBuild() // handled at appearance panel level? //mBtnBack->setClickedCallback(boost::bind(&LLPanelEditWearable::onBackButtonClicked, this)); - mTextEditor = getChild("description"); + mNameEditor = getChild("description"); mPanelTitle = getChild("edit_wearable_title"); mDescTitle = getChild("description_text"); @@ -758,7 +758,7 @@ BOOL LLPanelEditWearable::isDirty() const if (mWearablePtr) { if (mWearablePtr->isDirty() || - mWearablePtr->getName().compare(mTextEditor->getText()) != 0) + mWearablePtr->getName().compare(mNameEditor->getText()) != 0) { isDirty = TRUE; } @@ -796,7 +796,7 @@ void LLPanelEditWearable::onRevertButtonClicked(void* userdata) void LLPanelEditWearable::onSaveAsButtonClicked() { LLSD args; - args["DESC"] = mTextEditor->getText(); + args["DESC"] = mNameEditor->getText(); LLNotificationsUtil::add("SaveWearableAs", args, LLSD(), boost::bind(&LLPanelEditWearable::saveAsCallback, this, _1, _2)); } @@ -810,7 +810,7 @@ void LLPanelEditWearable::saveAsCallback(const LLSD& notification, const LLSD& r LLStringUtil::trim(wearable_name); if( !wearable_name.empty() ) { - mTextEditor->setText(wearable_name); + mNameEditor->setText(wearable_name); saveChanges(); } } @@ -956,10 +956,10 @@ void LLPanelEditWearable::saveChanges() U32 index = gAgentWearables.getWearableIndex(mWearablePtr); - if (mWearablePtr->getName().compare(mTextEditor->getText()) != 0) + if (mWearablePtr->getName().compare(mNameEditor->getText()) != 0) { // the name of the wearable has changed, re-save wearable with new name - gAgentWearables.saveWearableAs(mWearablePtr->getType(), index, mTextEditor->getText(), FALSE); + gAgentWearables.saveWearableAs(mWearablePtr->getType(), index, mNameEditor->getText(), FALSE); } else { @@ -976,7 +976,7 @@ void LLPanelEditWearable::revertChanges() } mWearablePtr->revertValues(); - mTextEditor->setText(mWearablePtr->getName()); + mNameEditor->setText(mWearablePtr->getName()); } void LLPanelEditWearable::showWearable(LLWearable* wearable, BOOL show) @@ -1018,7 +1018,7 @@ void LLPanelEditWearable::showWearable(LLWearable* wearable, BOOL show) mDescTitle->setText(description_title); // set name - mTextEditor->setText(wearable->getName()); + mNameEditor->setText(wearable->getName()); updatePanelPickerControls(type); updateTypeSpecificControls(type); diff --git a/indra/newview/llpaneleditwearable.h b/indra/newview/llpaneleditwearable.h index c63671fcc9..61441435cd 100644 --- a/indra/newview/llpaneleditwearable.h +++ b/indra/newview/llpaneleditwearable.h @@ -41,7 +41,6 @@ class LLCheckBoxCtrl; class LLWearable; -class LLTextEditor; class LLTextBox; class LLViewerInventoryItem; class LLViewerVisualParam; @@ -49,6 +48,7 @@ class LLVisualParamHint; class LLViewerJointMesh; class LLAccordionCtrlTab; class LLJoint; +class LLLineEditor; class LLPanelEditWearable : public LLPanel { @@ -140,7 +140,7 @@ private: // This text editor reference will change each time we edit a new wearable - // it will be grabbed from the currently visible panel - LLTextEditor *mTextEditor; + LLLineEditor *mNameEditor; // The following panels will be shown/hidden based on what wearable we're editing // body parts diff --git a/indra/newview/skins/default/xui/en/panel_edit_wearable.xml b/indra/newview/skins/default/xui/en/panel_edit_wearable.xml index 950c4a5fdb..3106ed34ff 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_wearable.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_wearable.xml @@ -225,15 +225,17 @@ left="0" tool_tip="Female" top="7" width="16" /> - + Date: Fri, 18 Jun 2010 12:33:17 -0500 Subject: EXT-7910 allow GPU and feature table to load from a custom location --- indra/newview/app_settings/settings.xml | 11 +++++++++++ indra/newview/llfeaturemanager.cpp | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2244f05104..1d635ef18a 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2840,6 +2840,17 @@ Boolean Value 0 + + FeatureManagerHTTPTable + + Comment + Base directory for HTTP feature/gpu table fetches + Persist + 1 + Type + String + Value + http://viewer-settings.secondlife.com FPSLogFrequency diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index 4fdb010162..f32fcd6b7f 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -496,11 +496,11 @@ private: void fetch_table(std::string table) { - const std::string base = "http://viewer-settings.s3.amazonaws.com/"; + const std::string base = gSavedSettings.getString("FeatureManagerHTTPTable"); const std::string filename = llformat(table.c_str(), LLVersionInfo::getVersion().c_str()); - const std::string url = base + filename; + const std::string url = base + "/" + filename; const std::string path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); -- cgit v1.3 From d8ec78e325ee3add1a5968ea0a3051de16958404 Mon Sep 17 00:00:00 2001 From: Leyla Farazha Date: Fri, 18 Jun 2010 10:51:45 -0700 Subject: EXT-7152 TOS breaks Critical Message in 2.0.0 and 2.0.1 removed cancel button --- indra/newview/skins/default/xui/en/floater_critical.xml | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/floater_critical.xml b/indra/newview/skins/default/xui/en/floater_critical.xml index 7b5451553f..05c958e051 100644 --- a/indra/newview/skins/default/xui/en/floater_critical.xml +++ b/indra/newview/skins/default/xui/en/floater_critical.xml @@ -6,6 +6,7 @@ height="500" layout="topleft" name="modal container" + open_centered="true" width="600"> diff --git a/indra/newview/skins/default/xui/en/panel_topinfo_bar.xml b/indra/newview/skins/default/xui/en/panel_topinfo_bar.xml index d3fb77f135..d8f4297e0c 100644 --- a/indra/newview/skins/default/xui/en/panel_topinfo_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_topinfo_bar.xml @@ -10,7 +10,6 @@ name="topinfo_bar" width="1024">