diff options
Diffstat (limited to 'indra')
267 files changed, 4999 insertions, 8336 deletions
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 86fc2dfff5..adc134c48c 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -83,8 +83,8 @@ if (WINDOWS) add_definitions(/WX) endif (NOT VS_DISABLE_FATAL_WARNINGS) - # configure win32 API for windows XP+ compatibility - set(WINVER "0x0501" CACHE STRING "Win32 API Target version (see http://msdn.microsoft.com/en-us/library/aa383745%28v=VS.85%29.aspx)") + # configure Win32 API for Windows Vista+ compatibility + set(WINVER "0x0600" CACHE STRING "Win32 API Target version (see http://msdn.microsoft.com/en-us/library/aa383745%28v=VS.85%29.aspx)") add_definitions("/DWINVER=${WINVER}" "/D_WIN32_WINNT=${WINVER}") endif (WINDOWS) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 6dc8e3dfbf..13a31cbce7 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -24,7 +24,6 @@ set(cmake_SOURCE_FILES DirectX.cmake DragDrop.cmake EXPAT.cmake -## ExamplePlugin.cmake FindAPR.cmake FindAutobuild.cmake FindBerkeleyDB.cmake @@ -100,6 +99,7 @@ set(cmake_SOURCE_FILES Variables.cmake ViewerMiscLibs.cmake VisualLeakDetector.cmake + LibVLCPlugin.cmake XmlRpcEpi.cmake ZLIB.cmake ) diff --git a/indra/cmake/LLWindow.cmake b/indra/cmake/LLWindow.cmake index ba07a80f05..80af7ff2ab 100644 --- a/indra/cmake/LLWindow.cmake +++ b/indra/cmake/LLWindow.cmake @@ -18,7 +18,7 @@ else (USESYSTEMLIBS) use_prebuilt_binary(SDL) set (SDL_FOUND TRUE) set (SDL_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/i686-linux) - set (SDL_LIBRARY SDL directfb fusion direct) + set (SDL_LIBRARY SDL directfb fusion direct X11) endif (LINUX) endif (USESYSTEMLIBS) diff --git a/indra/cmake/LibVLCPlugin.cmake b/indra/cmake/LibVLCPlugin.cmake new file mode 100644 index 0000000000..4472676fb4 --- /dev/null +++ b/indra/cmake/LibVLCPlugin.cmake @@ -0,0 +1,27 @@ +# -*- cmake -*- +include(Linking) +include(Prebuilt) + +if (USESYSTEMLIBS) + set(LIBVLCPLUGIN OFF CACHE BOOL + "LIBVLCPLUGIN support for the llplugin/llmedia test apps.") +else (USESYSTEMLIBS) + use_prebuilt_binary(vlc-bin) + set(LIBVLCPLUGIN ON CACHE BOOL + "LIBVLCPLUGIN support for the llplugin/llmedia test apps.") + set(VLC_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/vlc) +endif (USESYSTEMLIBS) + +if (WINDOWS) + set(VLC_PLUGIN_LIBRARIES + libvlc.lib + libvlccore.lib + ) +elseif (DARWIN) +elseif (LINUX) + # Specify a full path to make sure we get a static link + set(VLC_PLUGIN_LIBRARIES + ${LIBS_PREBUILT_DIR}/lib/libvlc.a + ${LIBS_PREBUILT_DIR}/lib/libvlccore.a + ) +endif (WINDOWS) diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt index 32aa08a2b7..9f9ca6c39c 100644 --- a/indra/edit-me-to-trigger-new-build.txt +++ b/indra/edit-me-to-trigger-new-build.txt @@ -1,3 +1,4 @@ 2014-02-25 10:34 + diff --git a/indra/lib/python/indra/base/__init__.py b/indra/lib/python/indra/base/__init__.py deleted file mode 100755 index 2904fd3380..0000000000 --- a/indra/lib/python/indra/base/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -"""\ -@file __init__.py -@brief Initialization file for the indra.base module. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" diff --git a/indra/lib/python/indra/base/cllsd_test.py b/indra/lib/python/indra/base/cllsd_test.py deleted file mode 100755 index 1f06898ffd..0000000000 --- a/indra/lib/python/indra/base/cllsd_test.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/python -## -## $LicenseInfo:firstyear=2011&license=viewerlgpl$ -## Second Life Viewer Source Code -## Copyright (C) 2011, Linden Research, Inc. -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Lesser General Public -## License as published by the Free Software Foundation; -## version 2.1 of the License only. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public -## License along with this library; if not, write to the Free Software -## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -## -## Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -## $/LicenseInfo$ -from indra.base import llsd, lluuid -from datetime import datetime -import cllsd -import time, sys - -class myint(int): - pass - -values = ( - '&<>', - u'\u81acj', - llsd.uri('http://foo<'), - lluuid.UUID(), - llsd.LLSD(['thing']), - 1, - myint(31337), - sys.maxint + 10, - llsd.binary('foo'), - [], - {}, - {u'f&\u1212': 3}, - 3.1, - True, - None, - datetime.fromtimestamp(time.time()), - ) - -def valuator(values): - for v in values: - yield v - -longvalues = () # (values, list(values), iter(values), valuator(values)) - -for v in values + longvalues: - print '%r => %r' % (v, cllsd.llsd_to_xml(v)) - -a = [[{'a':3}]] * 1000000 - -s = time.time() -print hash(cllsd.llsd_to_xml(a)) -e = time.time() -t1 = e - s -print t1 - -s = time.time() -print hash(llsd.LLSDXMLFormatter()._format(a)) -e = time.time() -t2 = e - s -print t2 - -print 'Speedup:', t2 / t1 diff --git a/indra/lib/python/indra/base/config.py b/indra/lib/python/indra/base/config.py deleted file mode 100755 index adafa29b51..0000000000 --- a/indra/lib/python/indra/base/config.py +++ /dev/null @@ -1,266 +0,0 @@ -"""\ -@file config.py -@brief Utility module for parsing and accessing the indra.xml config file. - -$LicenseInfo:firstyear=2006&license=mit$ - -Copyright (c) 2006-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import copy -import errno -import os -import traceback -import time -import types - -from os.path import dirname, getmtime, join, realpath -from indra.base import llsd - -_g_config = None - -class IndraConfig(object): - """ - IndraConfig loads a 'indra' xml configuration file - and loads into memory. This representation in memory - can get updated to overwrite values or add new values. - - The xml configuration file is considered a live file and changes - to the file are checked and reloaded periodically. If a value had - been overwritten via the update or set method, the loaded values - from the file are ignored (the values from the update/set methods - override) - """ - def __init__(self, indra_config_file): - self._indra_config_file = indra_config_file - self._reload_check_interval = 30 # seconds - self._last_check_time = 0 - self._last_mod_time = 0 - - self._config_overrides = {} - self._config_file_dict = {} - self._combined_dict = {} - - self._load() - - def _load(self): - # if you initialize the IndraConfig with None, no attempt - # is made to load any files - if self._indra_config_file is None: - return - - config_file = open(self._indra_config_file) - self._config_file_dict = llsd.parse(config_file.read()) - self._combine_dictionaries() - config_file.close() - - self._last_mod_time = self._get_last_modified_time() - self._last_check_time = time.time() # now - - def _get_last_modified_time(self): - """ - Returns the mtime (last modified time) of the config file, - if such exists. - """ - if self._indra_config_file is not None: - return os.path.getmtime(self._indra_config_file) - - return 0 - - def _combine_dictionaries(self): - self._combined_dict = {} - self._combined_dict.update(self._config_file_dict) - self._combined_dict.update(self._config_overrides) - - def _reload_if_necessary(self): - now = time.time() - - if (now - self._last_check_time) > self._reload_check_interval: - self._last_check_time = now - try: - modtime = self._get_last_modified_time() - if modtime > self._last_mod_time: - self._load() - except OSError, e: - if e.errno == errno.ENOENT: # file not found - # someone messed with our internal state - # or removed the file - - print 'WARNING: Configuration file has been removed ' + (self._indra_config_file) - print 'Disabling reloading of configuration file.' - - traceback.print_exc() - - self._indra_config_file = None - self._last_check_time = 0 - self._last_mod_time = 0 - else: - raise # pass the exception along to the caller - - def __getitem__(self, key): - self._reload_if_necessary() - - return self._combined_dict[key] - - def get(self, key, default = None): - try: - return self.__getitem__(key) - except KeyError: - return default - - def __setitem__(self, key, value): - """ - Sets the value of the config setting of key to be newval - - Once any key/value pair is changed via the set method, - that key/value pair will remain set with that value until - change via the update or set method - """ - self._config_overrides[key] = value - self._combine_dictionaries() - - def set(self, key, newval): - return self.__setitem__(key, newval) - - def update(self, new_conf): - """ - Load an XML file and apply its map as overrides or additions - to the existing config. Update can be a file or a dict. - - Once any key/value pair is changed via the update method, - that key/value pair will remain set with that value until - change via the update or set method - """ - if isinstance(new_conf, dict): - overrides = new_conf - else: - # assuming that it is a filename - config_file = open(new_conf) - overrides = llsd.parse(config_file.read()) - config_file.close() - - self._config_overrides.update(overrides) - self._combine_dictionaries() - - def as_dict(self): - """ - Returns immutable copy of the IndraConfig as a dictionary - """ - return copy.deepcopy(self._combined_dict) - -def load(config_xml_file = None): - global _g_config - - load_default_files = config_xml_file is None - if load_default_files: - ## going from: - ## "/opt/linden/indra/lib/python/indra/base/config.py" - ## to: - ## "/opt/linden/etc/indra.xml" - config_xml_file = realpath( - dirname(realpath(__file__)) + "../../../../../../etc/indra.xml") - - try: - _g_config = IndraConfig(config_xml_file) - except IOError: - # Failure to load passed in file - # or indra.xml default file - if load_default_files: - try: - config_xml_file = realpath( - dirname(realpath(__file__)) + "../../../../../../etc/globals.xml") - _g_config = IndraConfig(config_xml_file) - return - except IOError: - # Failure to load globals.xml - # fall to code below - pass - - # Either failed to load passed in file - # or failed to load all default files - _g_config = IndraConfig(None) - -def dump(indra_xml_file, indra_cfg = None, update_in_mem=False): - ''' - Dump config contents into a file - Kindof reverse of load. - Optionally takes a new config to dump. - Does NOT update global config unless requested. - ''' - global _g_config - - if not indra_cfg: - if _g_config is None: - return - - indra_cfg = _g_config.as_dict() - - if not indra_cfg: - return - - config_file = open(indra_xml_file, 'w') - _config_xml = llsd.format_xml(indra_cfg) - config_file.write(_config_xml) - config_file.close() - - if update_in_mem: - update(indra_cfg) - -def update(new_conf): - global _g_config - - if _g_config is None: - # To keep with how this function behaved - # previously, a call to update - # before the global is defined - # make a new global config which does not - # load data from a file. - _g_config = IndraConfig(None) - - return _g_config.update(new_conf) - -def get(key, default = None): - global _g_config - - if _g_config is None: - load() - - return _g_config.get(key, default) - -def set(key, newval): - """ - Sets the value of the config setting of key to be newval - - Once any key/value pair is changed via the set method, - that key/value pair will remain set with that value until - change via the update or set method or program termination - """ - global _g_config - - if _g_config is None: - _g_config = IndraConfig(None) - - _g_config.set(key, newval) - -def get_config(): - global _g_config - return _g_config diff --git a/indra/lib/python/indra/base/llsd.py b/indra/lib/python/indra/base/llsd.py deleted file mode 100755 index 4527b115f9..0000000000 --- a/indra/lib/python/indra/base/llsd.py +++ /dev/null @@ -1,1052 +0,0 @@ -"""\ -@file llsd.py -@brief Types as well as parsing and formatting functions for handling LLSD. - -$LicenseInfo:firstyear=2006&license=mit$ - -Copyright (c) 2006-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import datetime -import base64 -import string -import struct -import time -import types -import re - -from indra.util.fastest_elementtree import ElementTreeError, fromstring -from indra.base import lluuid - -# cllsd.c in server/server-1.25 has memory leaks, -# so disabling cllsd for now -#try: -# import cllsd -#except ImportError: -# cllsd = None -cllsd = None - -int_regex = re.compile(r"[-+]?\d+") -real_regex = re.compile(r"[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?") -alpha_regex = re.compile(r"[a-zA-Z]+") -date_regex = re.compile(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})T" - r"(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})" - r"(?P<second_float>(\.\d+)?)Z") -#date: d"YYYY-MM-DDTHH:MM:SS.FFFFFFZ" - -class LLSDParseError(Exception): - pass - -class LLSDSerializationError(TypeError): - pass - - -class binary(str): - pass - -class uri(str): - pass - - -BOOL_TRUE = ('1', '1.0', 'true') -BOOL_FALSE = ('0', '0.0', 'false', '') - - -def format_datestr(v): - """ Formats a datetime or date object into the string format shared by xml and notation serializations.""" - if hasattr(v, 'microsecond'): - return v.isoformat() + 'Z' - else: - return v.strftime('%Y-%m-%dT%H:%M:%SZ') - -def parse_datestr(datestr): - """Parses a datetime object from the string format shared by xml and notation serializations.""" - if datestr == "": - return datetime.datetime(1970, 1, 1) - - match = re.match(date_regex, datestr) - if not match: - raise LLSDParseError("invalid date string '%s'." % datestr) - - year = int(match.group('year')) - month = int(match.group('month')) - day = int(match.group('day')) - hour = int(match.group('hour')) - minute = int(match.group('minute')) - second = int(match.group('second')) - seconds_float = match.group('second_float') - microsecond = 0 - if seconds_float: - microsecond = int(float('0' + seconds_float) * 1e6) - return datetime.datetime(year, month, day, hour, minute, second, microsecond) - - -def bool_to_python(node): - val = node.text or '' - if val in BOOL_TRUE: - return True - else: - return False - -def int_to_python(node): - val = node.text or '' - if not val.strip(): - return 0 - return int(val) - -def real_to_python(node): - val = node.text or '' - if not val.strip(): - return 0.0 - return float(val) - -def uuid_to_python(node): - return lluuid.UUID(node.text) - -def str_to_python(node): - return node.text or '' - -def bin_to_python(node): - return binary(base64.decodestring(node.text or '')) - -def date_to_python(node): - val = node.text or '' - if not val: - val = "1970-01-01T00:00:00Z" - return parse_datestr(val) - - -def uri_to_python(node): - val = node.text or '' - if not val: - return None - return uri(val) - -def map_to_python(node): - result = {} - for index in range(len(node))[::2]: - result[node[index].text] = to_python(node[index+1]) - return result - -def array_to_python(node): - return [to_python(child) for child in node] - - -NODE_HANDLERS = dict( - undef=lambda x: None, - boolean=bool_to_python, - integer=int_to_python, - real=real_to_python, - uuid=uuid_to_python, - string=str_to_python, - binary=bin_to_python, - date=date_to_python, - uri=uri_to_python, - map=map_to_python, - array=array_to_python, - ) - -def to_python(node): - return NODE_HANDLERS[node.tag](node) - -class Nothing(object): - pass - - -class LLSDXMLFormatter(object): - def __init__(self): - self.type_map = { - type(None) : self.UNDEF, - bool : self.BOOLEAN, - int : self.INTEGER, - long : self.INTEGER, - float : self.REAL, - lluuid.UUID : self.UUID, - binary : self.BINARY, - str : self.STRING, - unicode : self.STRING, - uri : self.URI, - datetime.datetime : self.DATE, - datetime.date : self.DATE, - list : self.ARRAY, - tuple : self.ARRAY, - types.GeneratorType : self.ARRAY, - dict : self.MAP, - LLSD : self.LLSD - } - - def elt(self, name, contents=None): - if(contents is None or contents is ''): - return "<%s />" % (name,) - else: - if type(contents) is unicode: - contents = contents.encode('utf-8') - return "<%s>%s</%s>" % (name, contents, name) - - def xml_esc(self, v): - if type(v) is unicode: - v = v.encode('utf-8') - return v.replace('&', '&').replace('<', '<').replace('>', '>') - - def LLSD(self, v): - return self.generate(v.thing) - def UNDEF(self, v): - return self.elt('undef') - def BOOLEAN(self, v): - if v: - return self.elt('boolean', 'true') - else: - return self.elt('boolean', 'false') - def INTEGER(self, v): - return self.elt('integer', v) - def REAL(self, v): - return self.elt('real', v) - def UUID(self, v): - if(v.isNull()): - return self.elt('uuid') - else: - return self.elt('uuid', v) - def BINARY(self, v): - return self.elt('binary', base64.encodestring(v)) - def STRING(self, v): - return self.elt('string', self.xml_esc(v)) - def URI(self, v): - return self.elt('uri', self.xml_esc(str(v))) - def DATE(self, v): - return self.elt('date', format_datestr(v)) - def ARRAY(self, v): - return self.elt('array', ''.join([self.generate(item) for item in v])) - def MAP(self, v): - return self.elt( - 'map', - ''.join(["%s%s" % (self.elt('key', self.xml_esc(str(key))), self.generate(value)) - for key, value in v.items()])) - - typeof = type - def generate(self, something): - t = self.typeof(something) - if self.type_map.has_key(t): - return self.type_map[t](something) - else: - raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % ( - t, something)) - - def _format(self, something): - return '<?xml version="1.0" ?>' + self.elt("llsd", self.generate(something)) - - def format(self, something): - if cllsd: - return cllsd.llsd_to_xml(something) - return self._format(something) - -_g_xml_formatter = None -def format_xml(something): - global _g_xml_formatter - if _g_xml_formatter is None: - _g_xml_formatter = LLSDXMLFormatter() - return _g_xml_formatter.format(something) - -class LLSDXMLPrettyFormatter(LLSDXMLFormatter): - def __init__(self, indent_atom = None): - # Call the super class constructor so that we have the type map - super(LLSDXMLPrettyFormatter, self).__init__() - - # Override the type map to use our specialized formatters to - # emit the pretty output. - self.type_map[list] = self.PRETTY_ARRAY - self.type_map[tuple] = self.PRETTY_ARRAY - self.type_map[types.GeneratorType] = self.PRETTY_ARRAY, - self.type_map[dict] = self.PRETTY_MAP - - # Private data used for indentation. - self._indent_level = 1 - if indent_atom is None: - self._indent_atom = ' ' - else: - self._indent_atom = indent_atom - - def _indent(self): - "Return an indentation based on the atom and indentation level." - return self._indent_atom * self._indent_level - - def PRETTY_ARRAY(self, v): - rv = [] - rv.append('<array>\n') - self._indent_level = self._indent_level + 1 - rv.extend(["%s%s\n" % - (self._indent(), - self.generate(item)) - for item in v]) - self._indent_level = self._indent_level - 1 - rv.append(self._indent()) - rv.append('</array>') - return ''.join(rv) - - def PRETTY_MAP(self, v): - rv = [] - rv.append('<map>\n') - self._indent_level = self._indent_level + 1 - keys = v.keys() - keys.sort() - rv.extend(["%s%s\n%s%s\n" % - (self._indent(), - self.elt('key', key), - self._indent(), - self.generate(v[key])) - for key in keys]) - self._indent_level = self._indent_level - 1 - rv.append(self._indent()) - rv.append('</map>') - return ''.join(rv) - - def format(self, something): - data = [] - data.append('<?xml version="1.0" ?>\n<llsd>') - data.append(self.generate(something)) - data.append('</llsd>\n') - return '\n'.join(data) - -def format_pretty_xml(something): - """@brief Serialize a python object as 'pretty' llsd xml. - - The output conforms to the LLSD DTD, unlike the output from the - standard python xml.dom DOM::toprettyxml() method which does not - preserve significant whitespace. - This function is not necessarily suited for serializing very large - objects. It is not optimized by the cllsd module, and sorts on - dict (llsd map) keys alphabetically to ease human reading. - """ - return LLSDXMLPrettyFormatter().format(something) - -class LLSDNotationFormatter(object): - def __init__(self): - self.type_map = { - type(None) : self.UNDEF, - bool : self.BOOLEAN, - int : self.INTEGER, - long : self.INTEGER, - float : self.REAL, - lluuid.UUID : self.UUID, - binary : self.BINARY, - str : self.STRING, - unicode : self.STRING, - uri : self.URI, - datetime.datetime : self.DATE, - datetime.date : self.DATE, - list : self.ARRAY, - tuple : self.ARRAY, - types.GeneratorType : self.ARRAY, - dict : self.MAP, - LLSD : self.LLSD - } - - def LLSD(self, v): - return self.generate(v.thing) - def UNDEF(self, v): - return '!' - def BOOLEAN(self, v): - if v: - return 'true' - else: - return 'false' - def INTEGER(self, v): - return "i%s" % v - def REAL(self, v): - return "r%s" % v - def UUID(self, v): - return "u%s" % v - def BINARY(self, v): - return 'b64"' + base64.encodestring(v) + '"' - def STRING(self, v): - if isinstance(v, unicode): - v = v.encode('utf-8') - return "'%s'" % v.replace("\\", "\\\\").replace("'", "\\'") - def URI(self, v): - return 'l"%s"' % str(v).replace("\\", "\\\\").replace('"', '\\"') - def DATE(self, v): - return 'd"%s"' % format_datestr(v) - def ARRAY(self, v): - return "[%s]" % ','.join([self.generate(item) for item in v]) - def MAP(self, v): - def fix(key): - if isinstance(key, unicode): - return key.encode('utf-8') - return key - return "{%s}" % ','.join(["'%s':%s" % (fix(key).replace("\\", "\\\\").replace("'", "\\'"), self.generate(value)) - for key, value in v.items()]) - - def generate(self, something): - t = type(something) - handler = self.type_map.get(t) - if handler: - return handler(something) - else: - try: - return self.ARRAY(iter(something)) - except TypeError: - raise LLSDSerializationError( - "Cannot serialize unknown type: %s (%s)" % (t, something)) - - def format(self, something): - return self.generate(something) - -def format_notation(something): - return LLSDNotationFormatter().format(something) - -def _hex_as_nybble(hex): - if (hex >= '0') and (hex <= '9'): - return ord(hex) - ord('0') - elif (hex >= 'a') and (hex <='f'): - return 10 + ord(hex) - ord('a') - elif (hex >= 'A') and (hex <='F'): - return 10 + ord(hex) - ord('A'); - -class LLSDBinaryParser(object): - def __init__(self): - pass - - def parse(self, buffer, ignore_binary = False): - """ - This is the basic public interface for parsing. - - @param buffer the binary data to parse in an indexable sequence. - @param ignore_binary parser throws away data in llsd binary nodes. - @return returns a python object. - """ - self._buffer = buffer - self._index = 0 - self._keep_binary = not ignore_binary - return self._parse() - - def _parse(self): - cc = self._buffer[self._index] - self._index += 1 - if cc == '{': - return self._parse_map() - elif cc == '[': - return self._parse_array() - elif cc == '!': - return None - elif cc == '0': - return False - elif cc == '1': - return True - elif cc == 'i': - # 'i' = integer - idx = self._index - self._index += 4 - return struct.unpack("!i", self._buffer[idx:idx+4])[0] - elif cc == ('r'): - # 'r' = real number - idx = self._index - self._index += 8 - return struct.unpack("!d", self._buffer[idx:idx+8])[0] - elif cc == 'u': - # 'u' = uuid - idx = self._index - self._index += 16 - return lluuid.uuid_bits_to_uuid(self._buffer[idx:idx+16]) - elif cc == 's': - # 's' = string - return self._parse_string() - elif cc in ("'", '"'): - # delimited/escaped string - return self._parse_string_delim(cc) - elif cc == 'l': - # 'l' = uri - return uri(self._parse_string()) - elif cc == ('d'): - # 'd' = date in seconds since epoch - idx = self._index - self._index += 8 - seconds = struct.unpack("!d", self._buffer[idx:idx+8])[0] - return datetime.datetime.fromtimestamp(seconds) - elif cc == 'b': - binary = self._parse_string() - if self._keep_binary: - return binary - # *NOTE: maybe have a binary placeholder which has the - # length. - return None - else: - raise LLSDParseError("invalid binary token at byte %d: %d" % ( - self._index - 1, ord(cc))) - - def _parse_map(self): - rv = {} - size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] - self._index += 4 - count = 0 - cc = self._buffer[self._index] - self._index += 1 - key = '' - while (cc != '}') and (count < size): - if cc == 'k': - key = self._parse_string() - elif cc in ("'", '"'): - key = self._parse_string_delim(cc) - else: - raise LLSDParseError("invalid map key at byte %d." % ( - self._index - 1,)) - value = self._parse() - rv[key] = value - count += 1 - cc = self._buffer[self._index] - self._index += 1 - if cc != '}': - raise LLSDParseError("invalid map close token at byte %d." % ( - self._index,)) - return rv - - def _parse_array(self): - rv = [] - size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] - self._index += 4 - count = 0 - cc = self._buffer[self._index] - while (cc != ']') and (count < size): - rv.append(self._parse()) - count += 1 - cc = self._buffer[self._index] - if cc != ']': - raise LLSDParseError("invalid array close token at byte %d." % ( - self._index,)) - self._index += 1 - return rv - - def _parse_string(self): - size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] - self._index += 4 - rv = self._buffer[self._index:self._index+size] - self._index += size - return rv - - def _parse_string_delim(self, delim): - list = [] - found_escape = False - found_hex = False - found_digit = False - byte = 0 - while True: - cc = self._buffer[self._index] - self._index += 1 - if found_escape: - if found_hex: - if found_digit: - found_escape = False - found_hex = False - found_digit = False - byte <<= 4 - byte |= _hex_as_nybble(cc) - list.append(chr(byte)) - byte = 0 - else: - found_digit = True - byte = _hex_as_nybble(cc) - elif cc == 'x': - found_hex = True - else: - if cc == 'a': - list.append('\a') - elif cc == 'b': - list.append('\b') - elif cc == 'f': - list.append('\f') - elif cc == 'n': - list.append('\n') - elif cc == 'r': - list.append('\r') - elif cc == 't': - list.append('\t') - elif cc == 'v': - list.append('\v') - else: - list.append(cc) - found_escape = False - elif cc == '\\': - found_escape = True - elif cc == delim: - break - else: - list.append(cc) - return ''.join(list) - -class LLSDNotationParser(object): - """ Parse LLSD notation: - map: { string:object, string:object } - array: [ object, object, object ] - undef: ! - boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE - integer: i#### - real: r#### - uuid: u#### - string: "g\'day" | 'have a "nice" day' | s(size)"raw data" - uri: l"escaped" - date: d"YYYY-MM-DDTHH:MM:SS.FFZ" - binary: b##"ff3120ab1" | b(size)"raw data" - """ - def __init__(self): - pass - - def parse(self, buffer, ignore_binary = False): - """ - This is the basic public interface for parsing. - - @param buffer the notation string to parse. - @param ignore_binary parser throws away data in llsd binary nodes. - @return returns a python object. - """ - if buffer == "": - return False - - self._buffer = buffer - self._index = 0 - return self._parse() - - def _parse(self): - cc = self._buffer[self._index] - self._index += 1 - if cc == '{': - return self._parse_map() - elif cc == '[': - return self._parse_array() - elif cc == '!': - return None - elif cc == '0': - return False - elif cc == '1': - return True - elif cc in ('F', 'f'): - self._skip_alpha() - return False - elif cc in ('T', 't'): - self._skip_alpha() - return True - elif cc == 'i': - # 'i' = integer - return self._parse_integer() - elif cc == ('r'): - # 'r' = real number - return self._parse_real() - elif cc == 'u': - # 'u' = uuid - return self._parse_uuid() - elif cc in ("'", '"', 's'): - return self._parse_string(cc) - elif cc == 'l': - # 'l' = uri - delim = self._buffer[self._index] - self._index += 1 - val = uri(self._parse_string(delim)) - if len(val) == 0: - return None - return val - elif cc == ('d'): - # 'd' = date in seconds since epoch - return self._parse_date() - elif cc == 'b': - return self._parse_binary() - else: - raise LLSDParseError("invalid token at index %d: %d" % ( - self._index - 1, ord(cc))) - - def _parse_binary(self): - i = self._index - if self._buffer[i:i+2] == '64': - q = self._buffer[i+2] - e = self._buffer.find(q, i+3) - try: - return base64.decodestring(self._buffer[i+3:e]) - finally: - self._index = e + 1 - else: - raise LLSDParseError('random horrible binary format not supported') - - def _parse_map(self): - """ map: { string:object, string:object } """ - rv = {} - cc = self._buffer[self._index] - self._index += 1 - key = '' - found_key = False - while (cc != '}'): - if not found_key: - if cc in ("'", '"', 's'): - key = self._parse_string(cc) - found_key = True - elif cc.isspace() or cc == ',': - cc = self._buffer[self._index] - self._index += 1 - else: - raise LLSDParseError("invalid map key at byte %d." % ( - self._index - 1,)) - elif cc.isspace() or cc == ':': - cc = self._buffer[self._index] - self._index += 1 - continue - else: - self._index += 1 - value = self._parse() - rv[key] = value - found_key = False - cc = self._buffer[self._index] - self._index += 1 - - return rv - - def _parse_array(self): - """ array: [ object, object, object ] """ - rv = [] - cc = self._buffer[self._index] - while (cc != ']'): - if cc.isspace() or cc == ',': - self._index += 1 - cc = self._buffer[self._index] - continue - rv.append(self._parse()) - cc = self._buffer[self._index] - - if cc != ']': - raise LLSDParseError("invalid array close token at index %d." % ( - self._index,)) - self._index += 1 - return rv - - def _parse_uuid(self): - match = re.match(lluuid.UUID.uuid_regex, self._buffer[self._index:]) - if not match: - raise LLSDParseError("invalid uuid token at index %d." % self._index) - - (start, end) = match.span() - start += self._index - end += self._index - self._index = end - return lluuid.UUID(self._buffer[start:end]) - - def _skip_alpha(self): - match = re.match(alpha_regex, self._buffer[self._index:]) - if match: - self._index += match.end() - - def _parse_date(self): - delim = self._buffer[self._index] - self._index += 1 - datestr = self._parse_string(delim) - return parse_datestr(datestr) - - def _parse_real(self): - match = re.match(real_regex, self._buffer[self._index:]) - if not match: - raise LLSDParseError("invalid real token at index %d." % self._index) - - (start, end) = match.span() - start += self._index - end += self._index - self._index = end - return float( self._buffer[start:end] ) - - def _parse_integer(self): - match = re.match(int_regex, self._buffer[self._index:]) - if not match: - raise LLSDParseError("invalid integer token at index %d." % self._index) - - (start, end) = match.span() - start += self._index - end += self._index - self._index = end - return int( self._buffer[start:end] ) - - def _parse_string(self, delim): - """ string: "g\'day" | 'have a "nice" day' | s(size)"raw data" """ - rv = "" - - if delim in ("'", '"'): - rv = self._parse_string_delim(delim) - elif delim == 's': - rv = self._parse_string_raw() - else: - raise LLSDParseError("invalid string token at index %d." % self._index) - - return rv - - - def _parse_string_delim(self, delim): - """ string: "g'day 'un" | 'have a "nice" day' """ - list = [] - found_escape = False - found_hex = False - found_digit = False - byte = 0 - while True: - cc = self._buffer[self._index] - self._index += 1 - if found_escape: - if found_hex: - if found_digit: - found_escape = False - found_hex = False - found_digit = False - byte <<= 4 - byte |= _hex_as_nybble(cc) - list.append(chr(byte)) - byte = 0 - else: - found_digit = True - byte = _hex_as_nybble(cc) - elif cc == 'x': - found_hex = True - else: - if cc == 'a': - list.append('\a') - elif cc == 'b': - list.append('\b') - elif cc == 'f': - list.append('\f') - elif cc == 'n': - list.append('\n') - elif cc == 'r': - list.append('\r') - elif cc == 't': - list.append('\t') - elif cc == 'v': - list.append('\v') - else: - list.append(cc) - found_escape = False - elif cc == '\\': - found_escape = True - elif cc == delim: - break - else: - list.append(cc) - return ''.join(list) - - def _parse_string_raw(self): - """ string: s(size)"raw data" """ - # Read the (size) portion. - cc = self._buffer[self._index] - self._index += 1 - if cc != '(': - raise LLSDParseError("invalid string token at index %d." % self._index) - - rparen = self._buffer.find(')', self._index) - if rparen == -1: - raise LLSDParseError("invalid string token at index %d." % self._index) - - size = int(self._buffer[self._index:rparen]) - - self._index = rparen + 1 - delim = self._buffer[self._index] - self._index += 1 - if delim not in ("'", '"'): - raise LLSDParseError("invalid string token at index %d." % self._index) - - rv = self._buffer[self._index:(self._index + size)] - self._index += size - cc = self._buffer[self._index] - self._index += 1 - if cc != delim: - raise LLSDParseError("invalid string token at index %d." % self._index) - - return rv - -def format_binary(something): - return '<?llsd/binary?>\n' + _format_binary_recurse(something) - -def _format_binary_recurse(something): - def _format_list(something): - array_builder = [] - array_builder.append('[' + struct.pack('!i', len(something))) - for item in something: - array_builder.append(_format_binary_recurse(item)) - array_builder.append(']') - return ''.join(array_builder) - - if something is None: - return '!' - elif isinstance(something, LLSD): - return _format_binary_recurse(something.thing) - elif isinstance(something, bool): - if something: - return '1' - else: - return '0' - elif isinstance(something, (int, long)): - return 'i' + struct.pack('!i', something) - elif isinstance(something, float): - return 'r' + struct.pack('!d', something) - elif isinstance(something, lluuid.UUID): - return 'u' + something._bits - elif isinstance(something, binary): - return 'b' + struct.pack('!i', len(something)) + something - elif isinstance(something, str): - return 's' + struct.pack('!i', len(something)) + something - elif isinstance(something, unicode): - something = something.encode('utf-8') - return 's' + struct.pack('!i', len(something)) + something - elif isinstance(something, uri): - return 'l' + struct.pack('!i', len(something)) + something - elif isinstance(something, datetime.datetime): - seconds_since_epoch = time.mktime(something.timetuple()) - return 'd' + struct.pack('!d', seconds_since_epoch) - elif isinstance(something, (list, tuple)): - return _format_list(something) - elif isinstance(something, dict): - map_builder = [] - map_builder.append('{' + struct.pack('!i', len(something))) - for key, value in something.items(): - if isinstance(key, unicode): - key = key.encode('utf-8') - map_builder.append('k' + struct.pack('!i', len(key)) + key) - map_builder.append(_format_binary_recurse(value)) - map_builder.append('}') - return ''.join(map_builder) - else: - try: - return _format_list(list(something)) - except TypeError: - raise LLSDSerializationError( - "Cannot serialize unknown type: %s (%s)" % - (type(something), something)) - - -def parse_binary(binary): - if binary.startswith('<?llsd/binary?>'): - just_binary = binary.split('\n', 1)[1] - else: - just_binary = binary - return LLSDBinaryParser().parse(just_binary) - -def parse_xml(something): - try: - return to_python(fromstring(something)[0]) - except ElementTreeError, err: - raise LLSDParseError(*err.args) - -def parse_notation(something): - return LLSDNotationParser().parse(something) - -def parse(something): - try: - something = string.lstrip(something) #remove any pre-trailing whitespace - if something.startswith('<?llsd/binary?>'): - return parse_binary(something) - # This should be better. - elif something.startswith('<'): - return parse_xml(something) - else: - return parse_notation(something) - except KeyError, e: - raise Exception('LLSD could not be parsed: %s' % (e,)) - -class LLSD(object): - def __init__(self, thing=None): - self.thing = thing - - def __str__(self): - return self.toXML(self.thing) - - parse = staticmethod(parse) - toXML = staticmethod(format_xml) - toPrettyXML = staticmethod(format_pretty_xml) - toBinary = staticmethod(format_binary) - toNotation = staticmethod(format_notation) - - -undef = LLSD(None) - -XML_MIME_TYPE = 'application/llsd+xml' -BINARY_MIME_TYPE = 'application/llsd+binary' - -# register converters for llsd in mulib, if it is available -try: - from mulib import stacked, mu - stacked.NoProducer() # just to exercise stacked - mu.safe_load(None) # just to exercise mu -except: - # mulib not available, don't print an error message since this is normal - pass -else: - mu.add_parser(parse, XML_MIME_TYPE) - mu.add_parser(parse, 'application/llsd+binary') - - def llsd_convert_xml(llsd_stuff, request): - request.write(format_xml(llsd_stuff)) - - def llsd_convert_binary(llsd_stuff, request): - request.write(format_binary(llsd_stuff)) - - for typ in [LLSD, dict, list, tuple, str, int, long, float, bool, unicode, type(None)]: - stacked.add_producer(typ, llsd_convert_xml, XML_MIME_TYPE) - stacked.add_producer(typ, llsd_convert_xml, 'application/xml') - stacked.add_producer(typ, llsd_convert_xml, 'text/xml') - - stacked.add_producer(typ, llsd_convert_binary, 'application/llsd+binary') - - stacked.add_producer(LLSD, llsd_convert_xml, '*/*') - - # in case someone is using the legacy mu.xml wrapper, we need to - # tell mu to produce application/xml or application/llsd+xml - # (based on the accept header) from raw xml. Phoenix 2008-07-21 - stacked.add_producer(mu.xml, mu.produce_raw, XML_MIME_TYPE) - stacked.add_producer(mu.xml, mu.produce_raw, 'application/xml') - - - -# mulib wsgi stuff -# try: -# from mulib import mu, adapters -# -# # try some known attributes from mulib to be ultra-sure we've imported it -# mu.get_current -# adapters.handlers -# except: -# # mulib not available, don't print an error message since this is normal -# pass -# else: -# def llsd_xml_handler(content_type): -# def handle_llsd_xml(env, start_response): -# llsd_stuff, _ = mu.get_current(env) -# result = format_xml(llsd_stuff) -# start_response("200 OK", [('Content-Type', content_type)]) -# env['mu.negotiated_type'] = content_type -# yield result -# return handle_llsd_xml -# -# def llsd_binary_handler(content_type): -# def handle_llsd_binary(env, start_response): -# llsd_stuff, _ = mu.get_current(env) -# result = format_binary(llsd_stuff) -# start_response("200 OK", [('Content-Type', content_type)]) -# env['mu.negotiated_type'] = content_type -# yield result -# return handle_llsd_binary -# -# adapters.DEFAULT_PARSERS[XML_MIME_TYPE] = parse - -# for typ in [LLSD, dict, list, tuple, str, int, float, bool, unicode, type(None)]: -# for content_type in (XML_MIME_TYPE, 'application/xml'): -# adapters.handlers.set_handler(typ, llsd_xml_handler(content_type), content_type) -# -# adapters.handlers.set_handler(typ, llsd_binary_handler(BINARY_MIME_TYPE), BINARY_MIME_TYPE) -# -# adapters.handlers.set_handler(LLSD, llsd_xml_handler(XML_MIME_TYPE), '*/*') diff --git a/indra/lib/python/indra/base/lluuid.py b/indra/lib/python/indra/base/lluuid.py deleted file mode 100755 index 7413ffe10d..0000000000 --- a/indra/lib/python/indra/base/lluuid.py +++ /dev/null @@ -1,319 +0,0 @@ -"""\ -@file lluuid.py -@brief UUID parser/generator. - -$LicenseInfo:firstyear=2004&license=mit$ - -Copyright (c) 2004-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import random, socket, string, time, re -import uuid -try: - # Python 2.6 - from hashlib import md5 -except ImportError: - # Python 2.5 and earlier - from md5 import new as md5 - -def _int2binstr(i,l): - s='' - for a in range(l): - s=chr(i&0xFF)+s - i>>=8 - return s - -def _binstr2int(s): - i = long(0) - for c in s: - i = (i<<8) + ord(c) - return i - -class UUID(object): - """ - A class which represents a 16 byte integer. Stored as a 16 byte 8 - bit character string. - - The string version is to be of the form: - AAAAAAAA-AAAA-BBBB-BBBB-BBBBBBCCCCCC (a 128-bit number in hex) - where A=network address, B=timestamp, C=random. - """ - - NULL_STR = "00000000-0000-0000-0000-000000000000" - - # the UUIDREGEX_STRING is helpful for parsing UUID's in text - hex_wildcard = r"[0-9a-fA-F]" - word = hex_wildcard + r"{4,4}-" - long_word = hex_wildcard + r"{8,8}-" - very_long_word = hex_wildcard + r"{12,12}" - UUID_REGEX_STRING = long_word + word + word + word + very_long_word - uuid_regex = re.compile(UUID_REGEX_STRING) - - rand = random.Random() - ip = '' - try: - ip = socket.gethostbyname(socket.gethostname()) - except(socket.gaierror, socket.error): - # no ip address, so just default to somewhere in 10.x.x.x - ip = '10' - for i in range(3): - ip += '.' + str(rand.randrange(1,254)) - hexip = ''.join(["%04x" % long(i) for i in ip.split('.')]) - lastid = '' - - def __init__(self, possible_uuid=None): - """ - Initialize to first valid UUID in argument (if a string), - or to null UUID if none found or argument is not supplied. - - If the argument is a UUID, the constructed object will be a copy of it. - """ - self._bits = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - if possible_uuid is None: - return - - if isinstance(possible_uuid, type(self)): - self.set(possible_uuid) - return - - uuid_match = UUID.uuid_regex.search(possible_uuid) - if uuid_match: - uuid_string = uuid_match.group() - s = string.replace(uuid_string, '-', '') - self._bits = _int2binstr(string.atol(s[:8],16),4) + \ - _int2binstr(string.atol(s[8:16],16),4) + \ - _int2binstr(string.atol(s[16:24],16),4) + \ - _int2binstr(string.atol(s[24:],16),4) - - def __len__(self): - """ - Used by the len() builtin. - """ - return 36 - - def __nonzero__(self): - return self._bits != "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - - def __str__(self): - uuid_string = self.toString() - return uuid_string - - __repr__ = __str__ - - def __getitem__(self, index): - return str(self)[index] - - def __eq__(self, other): - if isinstance(other, (str, unicode)): - return other == str(self) - return self._bits == getattr(other, '_bits', '') - - def __ne__(self, other): - return not self.__eq__(other) - - def __le__(self, other): - return self._bits <= other._bits - - def __ge__(self, other): - return self._bits >= other._bits - - def __lt__(self, other): - return self._bits < other._bits - - def __gt__(self, other): - return self._bits > other._bits - - def __hash__(self): - return hash(self._bits) - - def set(self, uuid): - self._bits = uuid._bits - - def setFromString(self, uuid_string): - """ - Given a string version of a uuid, set self bits - appropriately. Returns self. - """ - s = string.replace(uuid_string, '-', '') - self._bits = _int2binstr(string.atol(s[:8],16),4) + \ - _int2binstr(string.atol(s[8:16],16),4) + \ - _int2binstr(string.atol(s[16:24],16),4) + \ - _int2binstr(string.atol(s[24:],16),4) - return self - - def setFromMemoryDump(self, gdb_string): - """ - We expect to get gdb_string as four hex units. eg: - 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2 - Which will be translated to: - db547d14-1b3f4bc3-9b984f71-d22f890a - Returns self. - """ - s = string.replace(gdb_string, '0x', '') - s = string.replace(s, ' ', '') - t = '' - for i in range(8,40,8): - for j in range(0,8,2): - t = t + s[i-j-2:i-j] - self.setFromString(t) - - def toString(self): - """ - Return as a string matching the LL standard - AAAAAAAA-AAAA-BBBB-BBBB-BBBBBBCCCCCC (a 128-bit number in hex) - where A=network address, B=timestamp, C=random. - """ - return uuid_bits_to_string(self._bits) - - def getAsString(self): - """ - Return a different string representation of the form - AAAAAAAA-AAAABBBB-BBBBBBBB-BBCCCCCC (a 128-bit number in hex) - where A=network address, B=timestamp, C=random. - """ - i1 = _binstr2int(self._bits[0:4]) - i2 = _binstr2int(self._bits[4:8]) - i3 = _binstr2int(self._bits[8:12]) - i4 = _binstr2int(self._bits[12:16]) - return '%08lx-%08lx-%08lx-%08lx' % (i1,i2,i3,i4) - - def generate(self): - """ - Generate a new uuid. This algorithm is slightly different - from c++ implementation for portability reasons. - Returns self. - """ - m = md5() - m.update(uuid.uuid1().bytes) - self._bits = m.digest() - return self - - def isNull(self): - """ - Returns 1 if the uuid is null - ie, equal to default uuid. - """ - return (self._bits == "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0") - - def xor(self, rhs): - """ - xors self with rhs. - """ - v1 = _binstr2int(self._bits[0:4]) ^ _binstr2int(rhs._bits[0:4]) - v2 = _binstr2int(self._bits[4:8]) ^ _binstr2int(rhs._bits[4:8]) - v3 = _binstr2int(self._bits[8:12]) ^ _binstr2int(rhs._bits[8:12]) - v4 = _binstr2int(self._bits[12:16]) ^ _binstr2int(rhs._bits[12:16]) - self._bits = _int2binstr(v1,4) + \ - _int2binstr(v2,4) + \ - _int2binstr(v3,4) + \ - _int2binstr(v4,4) - - -# module-level null constant -NULL = UUID() - -def printTranslatedMemory(four_hex_uints): - """ - We expect to get the string as four hex units. eg: - 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2 - Which will be translated to: - db547d14-1b3f4bc3-9b984f71-d22f890a - """ - uuid = UUID() - uuid.setFromMemoryDump(four_hex_uints) - print uuid.toString() - -def isUUID(id_str): - """ - This function returns: - - 1 if the string passed is a UUID - - 0 is the string passed is not a UUID - - None if it neither of the if's below is satisfied - """ - if not id_str or len(id_str) < 5 or len(id_str) > 36: - return 0 - - if isinstance(id_str, UUID) or UUID.uuid_regex.match(id_str): - return 1 - - return None - -def isPossiblyID(id_str): - """ - This function returns 1 if the string passed has some uuid-like - characteristics. Otherwise returns 0. - """ - - is_uuid = isUUID(id_str) - if is_uuid is not None: - return is_uuid - - # build a string which matches every character. - hex_wildcard = r"[0-9a-fA-F]" - chars = len(id_str) - next = min(chars, 8) - matcher = hex_wildcard+"{"+str(next)+","+str(next)+"}" - chars = chars - next - if chars > 0: - matcher = matcher + "-" - chars = chars - 1 - for block in range(3): - next = max(min(chars, 4), 0) - if next: - matcher = matcher + hex_wildcard+"{"+str(next)+","+str(next)+"}" - chars = chars - next - if chars > 0: - matcher = matcher + "-" - chars = chars - 1 - if chars > 0: - next = min(chars, 12) - matcher = matcher + hex_wildcard+"{"+str(next)+","+str(next)+"}" - #print matcher - uuid_matcher = re.compile(matcher) - if uuid_matcher.match(id_str): - return 1 - return 0 - -def uuid_bits_to_string(bits): - i1 = _binstr2int(bits[0:4]) - i2 = _binstr2int(bits[4:6]) - i3 = _binstr2int(bits[6:8]) - i4 = _binstr2int(bits[8:10]) - i5 = _binstr2int(bits[10:12]) - i6 = _binstr2int(bits[12:16]) - return '%08lx-%04lx-%04lx-%04lx-%04lx%08lx' % (i1,i2,i3,i4,i5,i6) - -def uuid_bits_to_uuid(bits): - return UUID(uuid_bits_to_string(bits)) - - -try: - from mulib import stacked - stacked.NoProducer() # just to exercise stacked -except: - #print "Couldn't import mulib.stacked, not registering UUID converter" - pass -else: - def convertUUID(uuid, req): - req.write(str(uuid)) - - stacked.add_producer(UUID, convertUUID, "*/*") - stacked.add_producer(UUID, convertUUID, "text/html") diff --git a/indra/lib/python/indra/base/metrics.py b/indra/lib/python/indra/base/metrics.py deleted file mode 100755 index ff8380265f..0000000000 --- a/indra/lib/python/indra/base/metrics.py +++ /dev/null @@ -1,121 +0,0 @@ -"""\ -@file metrics.py -@author Phoenix -@date 2007-11-27 -@brief simple interface for logging metrics - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import sys -try: - import syslog -except ImportError: - # Windows - import sys - class syslog(object): - # wrap to a lame syslog for windows - _logfp = sys.stderr - def syslog(msg): - _logfp.write(msg) - if not msg.endswith('\n'): - _logfp.write('\n') - syslog = staticmethod(syslog) - -from indra.base.llsd import format_notation - -def record_metrics(table, stats): - "Write a standard metrics log" - _log("LLMETRICS", table, stats) - -def record_event(table, data): - "Write a standard logmessage log" - _log("LLLOGMESSAGE", table, data) - -def set_destination(dest): - """Set the destination of metrics logs for this process. - - If you do not call this function prior to calling a logging - method, that function will open sys.stdout as a destination. - Attempts to set dest to None will throw a RuntimeError. - @param dest a file-like object which will be the destination for logs.""" - if dest is None: - raise RuntimeError("Attempt to unset metrics destination.") - global _destination - _destination = dest - -def destination(): - """Get the destination of the metrics logs for this process. - Returns None if no destination is set""" - global _destination - return _destination - -class SysLogger(object): - "A file-like object which writes to syslog." - def __init__(self, ident='indra', logopt = None, facility = None): - try: - if logopt is None: - logopt = syslog.LOG_CONS | syslog.LOG_PID - if facility is None: - facility = syslog.LOG_LOCAL0 - syslog.openlog(ident, logopt, facility) - import atexit - atexit.register(syslog.closelog) - except AttributeError: - # No syslog module on Windows - pass - - def write(str): - syslog.syslog(str) - write = staticmethod(write) - - def flush(): - pass - flush = staticmethod(flush) - -# -# internal API -# -_sequence_id = 0 -_destination = None - -def _next_id(): - global _sequence_id - next = _sequence_id - _sequence_id += 1 - return next - -def _dest(): - global _destination - if _destination is None: - # this default behavior is documented in the metrics functions above. - _destination = sys.stdout - return _destination - -def _log(header, table, data): - log_line = "%s (%d) %s %s" \ - % (header, _next_id(), table, format_notation(data)) - dest = _dest() - dest.write(log_line) - dest.flush() diff --git a/indra/lib/python/indra/ipc/httputil.py b/indra/lib/python/indra/ipc/httputil.py deleted file mode 100755 index d53f34a771..0000000000 --- a/indra/lib/python/indra/ipc/httputil.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/python -## $LicenseInfo:firstyear=2011&license=viewerlgpl$ -## Second Life Viewer Source Code -## Copyright (C) 2011, Linden Research, Inc. -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Lesser General Public -## License as published by the Free Software Foundation; -## version 2.1 of the License only. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public -## License along with this library; if not, write to the Free Software -## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -## -## Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -## $/LicenseInfo$ - -import warnings - -warnings.warn("indra.ipc.httputil has been deprecated; use eventlet.httpc instead", DeprecationWarning, 2) - -from eventlet.httpc import * - - -makeConnection = make_connection diff --git a/indra/lib/python/indra/ipc/llsdhttp.py b/indra/lib/python/indra/ipc/llsdhttp.py deleted file mode 100755 index cbe8ee1eca..0000000000 --- a/indra/lib/python/indra/ipc/llsdhttp.py +++ /dev/null @@ -1,100 +0,0 @@ -"""\ -@file llsdhttp.py -@brief Functions to ease moving llsd over http - -$LicenseInfo:firstyear=2006&license=mit$ - -Copyright (c) 2006-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import os.path -import os -import urlparse - -from indra.base import llsd - -from eventlet import httpc - -suite = httpc.HttpSuite(llsd.format_xml, llsd.parse, 'application/llsd+xml') -delete = suite.delete -delete_ = suite.delete_ -get = suite.get -get_ = suite.get_ -head = suite.head -head_ = suite.head_ -post = suite.post -post_ = suite.post_ -put = suite.put -put_ = suite.put_ -request = suite.request -request_ = suite.request_ - -# import every httpc error exception into our namespace for convenience -for x in httpc.status_to_error_map.itervalues(): - globals()[x.__name__] = x -ConnectionError = httpc.ConnectionError -Retriable = httpc.Retriable - -for x in (httpc.ConnectionError,): - globals()[x.__name__] = x - - -def postFile(url, filename): - f = open(filename) - body = f.read() - f.close() - llsd_body = llsd.parse(body) - return post_(url, llsd_body) - - -# deprecated in favor of get_ -def getStatus(url, use_proxy=False): - status, _headers, _body = get_(url, use_proxy=use_proxy) - return status - -# deprecated in favor of put_ -def putStatus(url, data): - status, _headers, _body = put_(url, data) - return status - -# deprecated in favor of delete_ -def deleteStatus(url): - status, _headers, _body = delete_(url) - return status - -# deprecated in favor of post_ -def postStatus(url, data): - status, _headers, _body = post_(url, data) - return status - - -def postFileStatus(url, filename): - status, _headers, body = postFile(url, filename) - return status, body - - -def getFromSimulator(path, use_proxy=False): - return get('http://' + simulatorHostAndPort + path, use_proxy=use_proxy) - - -def postToSimulator(path, data=None): - return post('http://' + simulatorHostAndPort + path, data) diff --git a/indra/lib/python/indra/ipc/mysql_pool.py b/indra/lib/python/indra/ipc/mysql_pool.py deleted file mode 100755 index e5855a3091..0000000000 --- a/indra/lib/python/indra/ipc/mysql_pool.py +++ /dev/null @@ -1,81 +0,0 @@ -"""\ -@file mysql_pool.py -@brief Thin wrapper around eventlet.db_pool that chooses MySQLdb and Tpool. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import MySQLdb -from eventlet import db_pool - -class DatabaseConnector(db_pool.DatabaseConnector): - def __init__(self, credentials, *args, **kwargs): - super(DatabaseConnector, self).__init__(MySQLdb, credentials, - conn_pool=db_pool.ConnectionPool, - *args, **kwargs) - - # get is extended relative to eventlet.db_pool to accept a port argument - def get(self, host, dbname, port=3306): - key = (host, dbname, port) - if key not in self._databases: - new_kwargs = self._kwargs.copy() - new_kwargs['db'] = dbname - new_kwargs['host'] = host - new_kwargs['port'] = port - new_kwargs.update(self.credentials_for(host)) - dbpool = ConnectionPool(*self._args, **new_kwargs) - self._databases[key] = dbpool - - return self._databases[key] - -class ConnectionPool(db_pool.TpooledConnectionPool): - """A pool which gives out saranwrapped MySQLdb connections from a pool - """ - - def __init__(self, *args, **kwargs): - super(ConnectionPool, self).__init__(MySQLdb, *args, **kwargs) - - def get(self): - conn = super(ConnectionPool, self).get() - # annotate the connection object with the details on the - # connection; this is used elsewhere to check that you haven't - # suddenly changed databases in midstream while making a - # series of queries on a connection. - arg_names = ['host','user','passwd','db','port','unix_socket','conv','connect_timeout', - 'compress', 'named_pipe', 'init_command', 'read_default_file', 'read_default_group', - 'cursorclass', 'use_unicode', 'charset', 'sql_mode', 'client_flag', 'ssl', - 'local_infile'] - # you could have constructed this connectionpool with a mix of - # keyword and non-keyword arguments, but we want to annotate - # the connection object with a dict so it's easy to check - # against so here we are converting the list of non-keyword - # arguments (in self._args) into a dict of keyword arguments, - # and merging that with the actual keyword arguments - # (self._kwargs). The arg_names variable lists the - # constructor arguments for MySQLdb Connection objects. - converted_kwargs = dict([ (arg_names[i], arg) for i, arg in enumerate(self._args) ]) - converted_kwargs.update(self._kwargs) - conn.connection_parameters = converted_kwargs - return conn - diff --git a/indra/lib/python/indra/ipc/russ.py b/indra/lib/python/indra/ipc/russ.py deleted file mode 100755 index ac780f128b..0000000000 --- a/indra/lib/python/indra/ipc/russ.py +++ /dev/null @@ -1,165 +0,0 @@ -"""\ -@file russ.py -@brief Recursive URL Substitution Syntax helpers -@author Phoenix - -Many details on how this should work is available on the wiki: -https://wiki.secondlife.com/wiki/Recursive_URL_Substitution_Syntax - -Adding features to this should be reflected in that page in the -implementations section. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import urllib -from indra.ipc import llsdhttp - -class UnbalancedBraces(Exception): - pass - -class UnknownDirective(Exception): - pass - -class BadDirective(Exception): - pass - -def format_value_for_path(value): - if type(value) in [list, tuple]: - # *NOTE: treat lists as unquoted path components so that the quoting - # doesn't get out-of-hand. This is a workaround for the fact that - # russ always quotes, even if the data it's given is already quoted, - # and it's not safe to simply unquote a path directly, so if we want - # russ to substitute urls parts inside other url parts we always - # have to do so via lists of unquoted path components. - return '/'.join([urllib.quote(str(item)) for item in value]) - else: - return urllib.quote(str(value)) - -def format(format_str, context): - """@brief Format format string according to rules for RUSS. -@see https://osiris.lindenlab.com/mediawiki/index.php/Recursive_URL_Substitution_Syntax -@param format_str The input string to format. -@param context A map used for string substitutions. -@return Returns the formatted string. If no match, the braces remain intact. -""" - while True: - #print "format_str:", format_str - all_matches = _find_sub_matches(format_str) - if not all_matches: - break - substitutions = 0 - while True: - matches = all_matches.pop() - # we work from right to left to make sure we do not - # invalidate positions earlier in format_str - matches.reverse() - for pos in matches: - # Use index since _find_sub_matches should have raised - # an exception, and failure to find now is an exception. - end = format_str.index('}', pos) - #print "directive:", format_str[pos+1:pos+5] - if format_str[pos + 1] == '$': - value = context[format_str[pos + 2:end]] - if value is not None: - value = format_value_for_path(value) - elif format_str[pos + 1] == '%': - value = _build_query_string( - context.get(format_str[pos + 2:end])) - elif format_str[pos+1:pos+5] == 'http' or format_str[pos+1:pos+5] == 'file': - value = _fetch_url_directive(format_str[pos + 1:end]) - else: - raise UnknownDirective, format_str[pos:end + 1] - if value is not None: - format_str = format_str[:pos]+str(value)+format_str[end+1:] - substitutions += 1 - - # If there were any substitutions at this depth, re-parse - # since this may have revealed new things to substitute - if substitutions: - break - if not all_matches: - break - - # If there were no substitutions at all, and we have exhausted - # the possible matches, bail. - if not substitutions: - break - return format_str - -def _find_sub_matches(format_str): - """@brief Find all of the substitution matches. -@param format_str the RUSS conformant format string. -@return Returns an array of depths of arrays of positional matches in input. -""" - depth = 0 - matches = [] - for pos in range(len(format_str)): - if format_str[pos] == '{': - depth += 1 - if not len(matches) == depth: - matches.append([]) - matches[depth - 1].append(pos) - continue - if format_str[pos] == '}': - depth -= 1 - continue - if not depth == 0: - raise UnbalancedBraces, format_str - return matches - -def _build_query_string(query_dict): - """\ - @breif given a dict, return a query string. utility wrapper for urllib. - @param query_dict input query dict - @returns Returns an urlencoded query string including leading '?'. - """ - if query_dict: - keys = query_dict.keys() - keys.sort() - def stringize(value): - if type(value) in (str,unicode): - return value - else: - return str(value) - query_list = [urllib.quote(str(key)) + '=' + urllib.quote(stringize(query_dict[key])) for key in keys] - return '?' + '&'.join(query_list) - else: - return '' - -def _fetch_url_directive(directive): - "*FIX: This only supports GET" - commands = directive.split('|') - resource = llsdhttp.get(commands[0]) - if len(commands) == 3: - resource = _walk_resource(resource, commands[2]) - return resource - -def _walk_resource(resource, path): - path = path.split('/') - for child in path: - if not child: - continue - resource = resource[child] - return resource diff --git a/indra/lib/python/indra/ipc/servicebuilder.py b/indra/lib/python/indra/ipc/servicebuilder.py deleted file mode 100755 index 0a0ce2b4e2..0000000000 --- a/indra/lib/python/indra/ipc/servicebuilder.py +++ /dev/null @@ -1,134 +0,0 @@ -"""\ -@file servicebuilder.py -@author Phoenix -@brief Class which will generate service urls. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -from indra.base import config -from indra.ipc import llsdhttp -from indra.ipc import russ - -# *NOTE: agent presence relies on this variable existing and being current, it is a huge hack -services_config = {} -try: - services_config = llsdhttp.get(config.get('services-config')) -except: - pass - -_g_builder = None -def _builder(): - global _g_builder - if _g_builder is None: - _g_builder = ServiceBuilder() - return _g_builder - -def build(name, context={}, **kwargs): - """ Convenience method for using a global, singleton, service builder. Pass arguments either via a dict or via python keyword arguments, or both! - - Example use: - > context = {'channel':'Second Life Release', 'version':'1.18.2.0'} - > servicebuilder.build('version-manager-version', context) - 'http://int.util.vaak.lindenlab.com/channel/Second%20Life%20Release/1.18.2.0' - > servicebuilder.build('version-manager-version', channel='Second Life Release', version='1.18.2.0') - 'http://int.util.vaak.lindenlab.com/channel/Second%20Life%20Release/1.18.2.0' - > servicebuilder.build('version-manager-version', context, version='1.18.1.2') - 'http://int.util.vaak.lindenlab.com/channel/Second%20Life%20Release/1.18.1.2' - """ - global _g_builder - if _g_builder is None: - _g_builder = ServiceBuilder() - return _g_builder.buildServiceURL(name, context, **kwargs) - -def build_path(name, context={}, **kwargs): - context = context.copy() # shouldn't modify the caller's dictionary - context.update(kwargs) - return _builder().buildPath(name, context) - -class ServiceBuilder(object): - def __init__(self, services_definition = services_config): - """\ - @brief - @brief Create a ServiceBuilder. - @param services_definition Complete services definition, services.xml. - """ - # no need to keep a copy of the services section of the - # complete services definition, but it doesn't hurt much. - self.services = services_definition['services'] - self.builders = {} - for service in self.services: - service_builder = service.get('service-builder') - if not service_builder: - continue - if isinstance(service_builder, dict): - # We will be constructing several builders - for name, builder in service_builder.iteritems(): - full_builder_name = service['name'] + '-' + name - self.builders[full_builder_name] = builder - else: - self.builders[service['name']] = service_builder - - def buildPath(self, name, context): - """\ - @brief given the environment on construction, return a service path. - @param name The name of the service. - @param context A dict of name value lookups for the service. - @returns Returns the - """ - return russ.format(self.builders[name], context) - - def buildServiceURL(self, name, context={}, **kwargs): - """\ - @brief given the environment on construction, return a service URL. - @param name The name of the service. - @param context A dict of name value lookups for the service. - @param kwargs Any keyword arguments are treated as members of the - context, this allows you to be all 31337 by writing shit like: - servicebuilder.build('name', param=value) - @returns Returns the - """ - context = context.copy() # shouldn't modify the caller's dictionary - context.update(kwargs) - base_url = config.get('services-base-url') - svc_path = russ.format(self.builders[name], context) - return base_url + svc_path - - -def on_in(query_name, host_key, schema_key): - """\ - @brief Constructs an on/in snippet (for running named queries) - from a schema name and two keys referencing values stored in - indra.xml. - - @param query_name Name of the query. - @param host_key Logical name of destination host. Will be - looked up in indra.xml. - @param schema_key Logical name of destination schema. Will - be looked up in indra.xml. - """ - return "on/config:%s/in/config:%s/%s" % (host_key.strip('/'), - schema_key.strip('/'), - query_name.lstrip('/')) - diff --git a/indra/lib/python/indra/ipc/siesta.py b/indra/lib/python/indra/ipc/siesta.py deleted file mode 100755 index d867e71537..0000000000 --- a/indra/lib/python/indra/ipc/siesta.py +++ /dev/null @@ -1,468 +0,0 @@ -"""\ -@file siesta.py -@brief A tiny llsd based RESTful web services framework - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -from indra.base import config -from indra.base import llsd -from webob import exc -import webob -import re, socket - -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO - -try: - import cjson - json_decode = cjson.decode - json_encode = cjson.encode - JsonDecodeError = cjson.DecodeError - JsonEncodeError = cjson.EncodeError -except ImportError: - import simplejson - json_decode = simplejson.loads - json_encode = simplejson.dumps - JsonDecodeError = ValueError - JsonEncodeError = TypeError - - -llsd_parsers = { - 'application/json': json_decode, - llsd.BINARY_MIME_TYPE: llsd.parse_binary, - 'application/llsd+notation': llsd.parse_notation, - llsd.XML_MIME_TYPE: llsd.parse_xml, - 'application/xml': llsd.parse_xml, - } - - -def mime_type(content_type): - '''Given a Content-Type header, return only the MIME type.''' - - return content_type.split(';', 1)[0].strip().lower() - -class BodyLLSD(object): - '''Give a webob Request or Response an llsd based "content" property. - - Getting the content property parses the body, and caches the result. - - Setting the content property formats a payload, and the body property - is set.''' - - def _llsd__get(self): - '''Get, set, or delete the LLSD value stored in this object.''' - - try: - return self._llsd - except AttributeError: - if not self.body: - raise AttributeError('No llsd attribute has been set') - else: - mtype = mime_type(self.content_type) - try: - parser = llsd_parsers[mtype] - except KeyError: - raise exc.HTTPUnsupportedMediaType( - 'Content type %s not supported' % mtype).exception - try: - self._llsd = parser(self.body) - except (llsd.LLSDParseError, JsonDecodeError, TypeError), err: - raise exc.HTTPBadRequest( - 'Could not parse body: %r' % err.args).exception - return self._llsd - - def _llsd__set(self, val): - req = getattr(self, 'request', None) - if req is not None: - formatter, ctype = formatter_for_request(req) - self.content_type = ctype - else: - formatter, ctype = formatter_for_mime_type( - mime_type(self.content_type)) - self.body = formatter(val) - - def _llsd__del(self): - if hasattr(self, '_llsd'): - del self._llsd - - content = property(_llsd__get, _llsd__set, _llsd__del) - - -class Response(webob.Response, BodyLLSD): - '''Response class with LLSD support. - - A sensible default content type is used. - - Setting the llsd property also sets the body. Getting the llsd - property parses the body if necessary. - - If you set the body property directly, the llsd property will be - deleted.''' - - default_content_type = 'application/llsd+xml' - - def _body__set(self, body): - if hasattr(self, '_llsd'): - del self._llsd - super(Response, self)._body__set(body) - - def cache_forever(self): - self.cache_expires(86400 * 365) - - body = property(webob.Response._body__get, _body__set, - webob.Response._body__del, - webob.Response._body__get.__doc__) - - -class Request(webob.Request, BodyLLSD): - '''Request class with LLSD support. - - Sensible content type and accept headers are used by default. - - Setting the content property also sets the body. Getting the content - property parses the body if necessary. - - If you set the body property directly, the content property will be - deleted.''' - - default_content_type = 'application/llsd+xml' - default_accept = ('application/llsd+xml; q=0.5, ' - 'application/llsd+notation; q=0.3, ' - 'application/llsd+binary; q=0.2, ' - 'application/xml; q=0.1, ' - 'application/json; q=0.0') - - def __init__(self, environ=None, *args, **kwargs): - if environ is None: - environ = {} - else: - environ = environ.copy() - if 'CONTENT_TYPE' not in environ: - environ['CONTENT_TYPE'] = self.default_content_type - if 'HTTP_ACCEPT' not in environ: - environ['HTTP_ACCEPT'] = self.default_accept - super(Request, self).__init__(environ, *args, **kwargs) - - def _body__set(self, body): - if hasattr(self, '_llsd'): - del self._llsd - super(Request, self)._body__set(body) - - def path_urljoin(self, *parts): - return '/'.join([path_url.rstrip('/')] + list(parts)) - - body = property(webob.Request._body__get, _body__set, - webob.Request._body__del, webob.Request._body__get.__doc__) - - def create_response(self, content=None, status='200 OK', - conditional_response=webob.NoDefault): - resp = self.ResponseClass(status=status, request=self, - conditional_response=conditional_response) - resp.content = content - return resp - - def curl(self): - '''Create and fill out a pycurl easy object from this request.''' - - import pycurl - c = pycurl.Curl() - c.setopt(pycurl.URL, self.url()) - if self.headers: - c.setopt(pycurl.HTTPHEADER, - ['%s: %s' % (k, self.headers[k]) for k in self.headers]) - c.setopt(pycurl.FOLLOWLOCATION, True) - c.setopt(pycurl.AUTOREFERER, True) - c.setopt(pycurl.MAXREDIRS, 16) - c.setopt(pycurl.NOSIGNAL, True) - c.setopt(pycurl.READFUNCTION, self.body_file.read) - c.setopt(pycurl.SSL_VERIFYHOST, 2) - - if self.method == 'POST': - c.setopt(pycurl.POST, True) - post301 = getattr(pycurl, 'POST301', None) - if post301 is not None: - # Added in libcurl 7.17.1. - c.setopt(post301, True) - elif self.method == 'PUT': - c.setopt(pycurl.PUT, True) - elif self.method != 'GET': - c.setopt(pycurl.CUSTOMREQUEST, self.method) - return c - -Request.ResponseClass = Response -Response.RequestClass = Request - - -llsd_formatters = { - 'application/json': json_encode, - 'application/llsd+binary': llsd.format_binary, - 'application/llsd+notation': llsd.format_notation, - 'application/llsd+xml': llsd.format_xml, - 'application/xml': llsd.format_xml, - } - -formatter_qualities = ( - ('application/llsd+xml', 1.0), - ('application/llsd+notation', 0.5), - ('application/llsd+binary', 0.4), - ('application/xml', 0.3), - ('application/json', 0.2), - ) - -def formatter_for_mime_type(mime_type): - '''Return a formatter that encodes to the given MIME type. - - The result is a pair of function and MIME type.''' - try: - return llsd_formatters[mime_type], mime_type - except KeyError: - raise exc.HTTPInternalServerError( - 'Could not use MIME type %r to format response' % - mime_type).exception - - -def formatter_for_request(req): - '''Return a formatter that encodes to the preferred type of the client. - - The result is a pair of function and actual MIME type.''' - ctype = req.accept.best_match(formatter_qualities) - try: - return llsd_formatters[ctype], ctype - except KeyError: - raise exc.HTTPNotAcceptable().exception - - -def wsgi_adapter(func, environ, start_response): - '''Adapt a Siesta callable to act as a WSGI application.''' - # Process the request as appropriate. - try: - req = Request(environ) - #print req.urlvars - resp = func(req, **req.urlvars) - if not isinstance(resp, webob.Response): - try: - formatter, ctype = formatter_for_request(req) - resp = req.ResponseClass(formatter(resp), content_type=ctype) - resp._llsd = resp - except (JsonEncodeError, TypeError), err: - resp = exc.HTTPInternalServerError( - detail='Could not format response') - except exc.HTTPException, e: - resp = e - except socket.error, e: - resp = exc.HTTPInternalServerError(detail=e.args[1]) - return resp(environ, start_response) - - -def llsd_callable(func): - '''Turn a callable into a Siesta application.''' - - def replacement(environ, start_response): - return wsgi_adapter(func, environ, start_response) - - return replacement - - -def llsd_method(http_method, func): - def replacement(environ, start_response): - if environ['REQUEST_METHOD'] == http_method: - return wsgi_adapter(func, environ, start_response) - return exc.HTTPMethodNotAllowed()(environ, start_response) - - return replacement - - -http11_methods = 'OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT'.split() -http11_methods.sort() - -def llsd_class(cls): - '''Turn a class into a Siesta application. - - A new instance is created for each request. A HTTP method FOO is - turned into a call to the handle_foo method of the instance.''' - - def foo(req, **kwargs): - instance = cls() - method = req.method.lower() - try: - handler = getattr(instance, 'handle_' + method) - except AttributeError: - allowed = [m for m in http11_methods - if hasattr(instance, 'handle_' + m.lower())] - raise exc.HTTPMethodNotAllowed( - headers={'Allow': ', '.join(allowed)}).exception - #print "kwargs: ", kwargs - return handler(req, **kwargs) - - def replacement(environ, start_response): - return wsgi_adapter(foo, environ, start_response) - - return replacement - - -def curl(reqs): - import pycurl - - m = pycurl.CurlMulti() - curls = [r.curl() for r in reqs] - io = {} - for c in curls: - fp = StringIO() - hdr = StringIO() - c.setopt(pycurl.WRITEFUNCTION, fp.write) - c.setopt(pycurl.HEADERFUNCTION, hdr.write) - io[id(c)] = fp, hdr - m.handles = curls - try: - while True: - ret, num_handles = m.perform() - if ret != pycurl.E_CALL_MULTI_PERFORM: - break - finally: - m.close() - - for req, c in zip(reqs, curls): - fp, hdr = io[id(c)] - hdr.seek(0) - status = hdr.readline().rstrip() - headers = [] - name, values = None, None - - # XXX We don't currently handle bogus header data. - - for line in hdr.readlines(): - if not line[0].isspace(): - if name: - headers.append((name, ' '.join(values))) - name, value = line.strip().split(':', 1) - value = [value] - else: - values.append(line.strip()) - if name: - headers.append((name, ' '.join(values))) - - resp = c.ResponseClass(fp.getvalue(), status, headers, request=req) - - -route_re = re.compile(r''' - \{ # exact character "{" - (\w*) # "config" or variable (restricted to a-z, 0-9, _) - (?:([:~])([^}]+))? # optional :type or ~regex part - \} # exact character "}" - ''', re.VERBOSE) - -predefined_regexps = { - 'uuid': r'[a-f0-9][a-f0-9-]{31,35}', - 'int': r'\d+', - 'host': r'[a-z0-9][a-z0-9\-\.]*', - } - -def compile_route(route): - fp = StringIO() - last_pos = 0 - for match in route_re.finditer(route): - #print "matches: ", match.groups() - fp.write(re.escape(route[last_pos:match.start()])) - var_name = match.group(1) - sep = match.group(2) - expr = match.group(3) - if var_name == 'config': - expr = re.escape(str(config.get(var_name))) - else: - if expr: - if sep == ':': - expr = predefined_regexps[expr] - # otherwise, treat what follows '~' as a regexp - else: - expr = '[^/]+' - if var_name != '': - expr = '(?P<%s>%s)' % (var_name, expr) - else: - expr = '(%s)' % (expr,) - fp.write(expr) - last_pos = match.end() - fp.write(re.escape(route[last_pos:])) - compiled_route = '^%s$' % fp.getvalue() - #print route, "->", compiled_route - return compiled_route - -class Router(object): - '''WSGI routing class. Parses a URL and hands off a request to - some other WSGI application. If no suitable application is found, - responds with a 404.''' - - def __init__(self): - self._new_routes = [] - self._routes = [] - self._paths = [] - - def add(self, route, app, methods=None): - self._new_routes.append((route, app, methods)) - - def _create_routes(self): - for route, app, methods in self._new_routes: - self._paths.append(route) - self._routes.append( - (re.compile(compile_route(route)), - app, - methods and dict.fromkeys(methods))) - self._new_routes = [] - - def __call__(self, environ, start_response): - # load up the config from the config file. Only needs to be - # done once per interpreter. This is the entry point of all - # siesta applications, so this is where we trap it. - _conf = config.get_config() - if _conf is None: - import os.path - fname = os.path.join( - environ.get('ll.config_dir', '/local/linden/etc'), - 'indra.xml') - config.load(fname) - - # proceed with handling the request - self._create_routes() - path_info = environ['PATH_INFO'] - request_method = environ['REQUEST_METHOD'] - allowed = [] - for regex, app, methods in self._routes: - m = regex.match(path_info) - if m: - #print "groupdict:",m.groupdict() - if not methods or request_method in methods: - environ['paste.urlvars'] = m.groupdict() - return app(environ, start_response) - else: - allowed += methods - if allowed: - allowed = dict.fromkeys(allows).keys() - allowed.sort() - resp = exc.HTTPMethodNotAllowed( - headers={'Allow': ', '.join(allowed)}) - else: - resp = exc.HTTPNotFound() - return resp(environ, start_response) diff --git a/indra/lib/python/indra/ipc/siesta_test.py b/indra/lib/python/indra/ipc/siesta_test.py deleted file mode 100755 index a35eed2460..0000000000 --- a/indra/lib/python/indra/ipc/siesta_test.py +++ /dev/null @@ -1,235 +0,0 @@ -#!/usr/bin/python -## $LicenseInfo:firstyear=2011&license=viewerlgpl$ -## Second Life Viewer Source Code -## Copyright (C) 2011, Linden Research, Inc. -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Lesser General Public -## License as published by the Free Software Foundation; -## version 2.1 of the License only. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public -## License along with this library; if not, write to the Free Software -## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -## -## Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -## $/LicenseInfo$ -from indra.base import llsd, lluuid -from indra.ipc import siesta -import datetime, math, unittest -from webob import exc - - -class ClassApp(object): - def handle_get(self, req): - pass - - def handle_post(self, req): - return req.llsd - - -def callable_app(req): - if req.method == 'UNDERPANTS': - raise exc.HTTPMethodNotAllowed() - elif req.method == 'GET': - return None - return req.llsd - - -class TestBase: - def test_basic_get(self): - req = siesta.Request.blank('/') - self.assertEquals(req.get_response(self.server).body, - llsd.format_xml(None)) - - def test_bad_method(self): - req = siesta.Request.blank('/') - req.environ['REQUEST_METHOD'] = 'UNDERPANTS' - self.assertEquals(req.get_response(self.server).status_int, - exc.HTTPMethodNotAllowed.code) - - json_safe = { - 'none': None, - 'bool_true': True, - 'bool_false': False, - 'int_zero': 0, - 'int_max': 2147483647, - 'int_min': -2147483648, - 'long_zero': 0, - 'long_max': 2147483647L, - 'long_min': -2147483648L, - 'float_zero': 0, - 'float': math.pi, - 'float_huge': 3.14159265358979323846e299, - 'str_empty': '', - 'str': 'foo', - u'unic\u1e51de_empty': u'', - u'unic\u1e51de': u'\u1e4exx\u10480', - } - json_safe['array'] = json_safe.values() - json_safe['tuple'] = tuple(json_safe.values()) - json_safe['dict'] = json_safe.copy() - - json_unsafe = { - 'uuid_empty': lluuid.UUID(), - 'uuid_full': lluuid.UUID('dc61ab0530200d7554d23510559102c1a98aab1b'), - 'binary_empty': llsd.binary(), - 'binary': llsd.binary('f\0\xff'), - 'uri_empty': llsd.uri(), - 'uri': llsd.uri('http://www.secondlife.com/'), - 'datetime_empty': datetime.datetime(1970,1,1), - 'datetime': datetime.datetime(1999,9,9,9,9,9), - } - json_unsafe.update(json_safe) - json_unsafe['array'] = json_unsafe.values() - json_unsafe['tuple'] = tuple(json_unsafe.values()) - json_unsafe['dict'] = json_unsafe.copy() - json_unsafe['iter'] = iter(json_unsafe.values()) - - def _test_client_content_type_good(self, content_type, ll): - def run(ll): - req = siesta.Request.blank('/') - req.environ['REQUEST_METHOD'] = 'POST' - req.content_type = content_type - req.llsd = ll - req.accept = content_type - resp = req.get_response(self.server) - self.assertEquals(resp.status_int, 200) - return req, resp - - if False and isinstance(ll, dict): - def fixup(v): - if isinstance(v, float): - return '%.5f' % v - if isinstance(v, long): - return int(v) - if isinstance(v, (llsd.binary, llsd.uri)): - return v - if isinstance(v, (tuple, list)): - return [fixup(i) for i in v] - if isinstance(v, dict): - return dict([(k, fixup(i)) for k, i in v.iteritems()]) - return v - for k, v in ll.iteritems(): - l = [k, v] - req, resp = run(l) - self.assertEquals(fixup(resp.llsd), fixup(l)) - - run(ll) - - def test_client_content_type_json_good(self): - self._test_client_content_type_good('application/json', self.json_safe) - - def test_client_content_type_llsd_xml_good(self): - self._test_client_content_type_good('application/llsd+xml', - self.json_unsafe) - - def test_client_content_type_llsd_notation_good(self): - self._test_client_content_type_good('application/llsd+notation', - self.json_unsafe) - - def test_client_content_type_llsd_binary_good(self): - self._test_client_content_type_good('application/llsd+binary', - self.json_unsafe) - - def test_client_content_type_xml_good(self): - self._test_client_content_type_good('application/xml', - self.json_unsafe) - - def _test_client_content_type_bad(self, content_type): - req = siesta.Request.blank('/') - req.environ['REQUEST_METHOD'] = 'POST' - req.body = '\0invalid nonsense under all encodings' - req.content_type = content_type - self.assertEquals(req.get_response(self.server).status_int, - exc.HTTPBadRequest.code) - - def test_client_content_type_json_bad(self): - self._test_client_content_type_bad('application/json') - - def test_client_content_type_llsd_xml_bad(self): - self._test_client_content_type_bad('application/llsd+xml') - - def test_client_content_type_llsd_notation_bad(self): - self._test_client_content_type_bad('application/llsd+notation') - - def test_client_content_type_llsd_binary_bad(self): - self._test_client_content_type_bad('application/llsd+binary') - - def test_client_content_type_xml_bad(self): - self._test_client_content_type_bad('application/xml') - - def test_client_content_type_bad(self): - req = siesta.Request.blank('/') - req.environ['REQUEST_METHOD'] = 'POST' - req.body = 'XXX' - req.content_type = 'application/nonsense' - self.assertEquals(req.get_response(self.server).status_int, - exc.HTTPUnsupportedMediaType.code) - - def test_request_default_content_type(self): - req = siesta.Request.blank('/') - self.assertEquals(req.content_type, req.default_content_type) - - def test_request_default_accept(self): - req = siesta.Request.blank('/') - from webob import acceptparse - self.assertEquals(str(req.accept).replace(' ', ''), - req.default_accept.replace(' ', '')) - - def test_request_llsd_auto_body(self): - req = siesta.Request.blank('/') - req.llsd = {'a': 2} - self.assertEquals(req.body, '<?xml version="1.0" ?><llsd><map>' - '<key>a</key><integer>2</integer></map></llsd>') - - def test_request_llsd_mod_body_changes_llsd(self): - req = siesta.Request.blank('/') - req.llsd = {'a': 2} - req.body = '<?xml version="1.0" ?><llsd><integer>1337</integer></llsd>' - self.assertEquals(req.llsd, 1337) - - def test_request_bad_llsd_fails(self): - def crashme(ctype): - def boom(): - class foo(object): pass - req = siesta.Request.blank('/') - req.content_type = ctype - req.llsd = foo() - for mime_type in siesta.llsd_parsers: - self.assertRaises(TypeError, crashme(mime_type)) - - -class ClassServer(TestBase, unittest.TestCase): - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - self.server = siesta.llsd_class(ClassApp) - - -class CallableServer(TestBase, unittest.TestCase): - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - self.server = siesta.llsd_callable(callable_app) - - -class RouterServer(unittest.TestCase): - def test_router(self): - def foo(req, quux): - print quux - - r = siesta.Router() - r.add('/foo/{quux:int}', siesta.llsd_callable(foo), methods=['GET']) - req = siesta.Request.blank('/foo/33') - req.get_response(r) - - req = siesta.Request.blank('/foo/bar') - self.assertEquals(req.get_response(r).status_int, - exc.HTTPNotFound.code) - -if __name__ == '__main__': - unittest.main() diff --git a/indra/lib/python/indra/ipc/webdav.py b/indra/lib/python/indra/ipc/webdav.py deleted file mode 100755 index 98b8499b6a..0000000000 --- a/indra/lib/python/indra/ipc/webdav.py +++ /dev/null @@ -1,597 +0,0 @@ -""" -@file webdav.py -@brief Classes to make manipulation of a webdav store easier. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import sys, os, httplib, urlparse -import socket, time -import xml.dom.minidom -import syslog -# import signal - -__revision__ = '0' - -dav_debug = False - - -# def urlsafe_b64decode (enc): -# return base64.decodestring (enc.replace ('_', '/').replace ('-', '+')) - -# def urlsafe_b64encode (str): -# return base64.encodestring (str).replace ('+', '-').replace ('/', '_') - - -class DAVError (Exception): - """ Base class for exceptions in this module. """ - def __init__ (self, status=0, message='', body='', details=''): - self.status = status - self.message = message - self.body = body - self.details = details - Exception.__init__ (self, '%d:%s:%s%s' % (self.status, self.message, - self.body, self.details)) - - def print_to_stderr (self): - """ print_to_stderr docstring """ - print >> sys.stderr, str (self.status) + ' ' + self.message - print >> sys.stderr, str (self.details) - - -class Timeout (Exception): - """ Timeout docstring """ - def __init__ (self, arg=''): - Exception.__init__ (self, arg) - - -def alarm_handler (signum, frame): - """ alarm_handler docstring """ - raise Timeout ('caught alarm') - - -class WebDAV: - """ WebDAV docstring """ - def __init__ (self, url, proxy=None, retries_before_fail=6): - self.init_url = url - self.init_proxy = proxy - self.retries_before_fail = retries_before_fail - url_parsed = urlparse.urlsplit (url) - - self.top_path = url_parsed[ 2 ] - # make sure top_path has a trailing / - if self.top_path == None or self.top_path == '': - self.top_path = '/' - elif len (self.top_path) > 1 and self.top_path[-1:] != '/': - self.top_path += '/' - - if dav_debug: - syslog.syslog ('new WebDAV %s : %s' % (str (url), str (proxy))) - - if proxy: - proxy_parsed = urlparse.urlsplit (proxy) - self.host_header = url_parsed[ 1 ] - host_and_port = proxy_parsed[ 1 ].split (':') - self.host = host_and_port[ 0 ] - if len (host_and_port) > 1: - self.port = int(host_and_port[ 1 ]) - else: - self.port = 80 - else: # no proxy - host_and_port = url_parsed[ 1 ].split (':') - self.host_header = None - self.host = host_and_port[ 0 ] - if len (host_and_port) > 1: - self.port = int(host_and_port[ 1 ]) - else: - self.port = 80 - - self.connection = False - self.connect () - - - def log (self, msg, depth=0): - """ log docstring """ - if dav_debug and depth == 0: - host = str (self.init_url) - if host == 'http://int.tuco.lindenlab.com:80/asset/': - host = 'tuco' - if host == 'http://harriet.lindenlab.com/asset-keep/': - host = 'harriet/asset-keep' - if host == 'http://harriet.lindenlab.com/asset-flag/': - host = 'harriet/asset-flag' - if host == 'http://harriet.lindenlab.com/asset/': - host = 'harriet/asset' - if host == 'http://ozzy.lindenlab.com/asset/': - host = 'ozzy/asset' - if host == 'http://station11.lindenlab.com:12041/:': - host = 'station11:12041' - proxy = str (self.init_proxy) - if proxy == 'None': - proxy = '' - if proxy == 'http://int.tuco.lindenlab.com:3128/': - proxy = 'tuco' - syslog.syslog ('WebDAV (%s:%s) %s' % (host, proxy, str (msg))) - - - def connect (self): - """ connect docstring """ - self.log ('connect') - self.connection = httplib.HTTPConnection (self.host, self.port) - - def __err (self, response, details): - """ __err docstring """ - raise DAVError (response.status, response.reason, response.read (), - str (self.init_url) + ':' + \ - str (self.init_proxy) + ':' + str (details)) - - def request (self, method, path, body=None, headers=None, - read_all=True, body_hook = None, recurse=0, allow_cache=True): - """ request docstring """ - # self.log ('request %s %s' % (method, path)) - if headers == None: - headers = {} - if not allow_cache: - headers['Pragma'] = 'no-cache' - headers['cache-control'] = 'no-cache' - try: - if method.lower () != 'purge': - if path.startswith ('/'): - path = path[1:] - if self.host_header: # use proxy - headers[ 'host' ] = self.host_header - fullpath = 'http://%s%s%s' % (self.host_header, - self.top_path, path) - else: # no proxy - fullpath = self.top_path + path - else: - fullpath = path - - self.connection.request (method, fullpath, body, headers) - if body_hook: - body_hook () - - # signal.signal (signal.SIGALRM, alarm_handler) - # try: - # signal.alarm (120) - # signal.alarm (0) - # except Timeout, e: - # if recurse < 6: - # return self.retry_request (method, path, body, headers, - # read_all, body_hook, recurse) - # else: - # raise DAVError (0, 'timeout', self.host, - # (method, path, body, headers, recurse)) - - response = self.connection.getresponse () - - if read_all: - while len (response.read (1024)) > 0: - pass - if (response.status == 500 or \ - response.status == 503 or \ - response.status == 403) and \ - recurse < self.retries_before_fail: - return self.retry_request (method, path, body, headers, - read_all, body_hook, recurse) - return response - except (httplib.ResponseNotReady, - httplib.BadStatusLine, - socket.error): - # if the server hangs up on us (keepalive off, broken pipe), - # we need to reconnect and try again. - if recurse < self.retries_before_fail: - return self.retry_request (method, path, body, headers, - read_all, body_hook, recurse) - raise DAVError (0, 'reconnect failed', self.host, - (method, path, body, headers, recurse)) - - - def retry_request (self, method, path, body, headers, - read_all, body_hook, recurse): - """ retry_request docstring """ - time.sleep (10.0 * recurse) - self.connect () - return self.request (method, path, body, headers, - read_all, body_hook, recurse+1) - - - - def propfind (self, path, body=None, depth=1): - """ propfind docstring """ - # self.log ('propfind %s' % path) - headers = {'Content-Type':'text/xml; charset="utf-8"', - 'Depth':str(depth)} - response = self.request ('PROPFIND', path, body, headers, False) - if response.status == 207: - return response # Multi-Status - self.__err (response, ('PROPFIND', path, body, headers, 0)) - - - def purge (self, path): - """ issue a squid purge command """ - headers = {'Accept':'*/*'} - response = self.request ('PURGE', path, None, headers) - if response.status == 200 or response.status == 404: - # 200 if it was purge, 404 if it wasn't there. - return response - self.__err (response, ('PURGE', path, None, headers)) - - - def get_file_size (self, path): - """ - Use propfind to ask a webdav server what the size of - a file is. If used on a directory (collection) return 0 - """ - self.log ('get_file_size %s' % path) - # "getcontentlength" property - # 8.1.1 Example - Retrieving Named Properties - # http://docs.python.org/lib/module-xml.dom.html - nsurl = 'http://apache.org/dav/props/' - doc = xml.dom.minidom.Document () - propfind_element = doc.createElementNS (nsurl, "D:propfind") - propfind_element.setAttributeNS (nsurl, 'xmlns:D', 'DAV:') - doc.appendChild (propfind_element) - prop_element = doc.createElementNS (nsurl, "D:prop") - propfind_element.appendChild (prop_element) - con_len_element = doc.createElementNS (nsurl, "D:getcontentlength") - prop_element.appendChild (con_len_element) - - response = self.propfind (path, doc.toxml ()) - doc.unlink () - - resp_doc = xml.dom.minidom.parseString (response.read ()) - cln = resp_doc.getElementsByTagNameNS ('DAV:','getcontentlength')[ 0 ] - try: - content_length = int (cln.childNodes[ 0 ].nodeValue) - except IndexError: - return 0 - resp_doc.unlink () - return content_length - - - def file_exists (self, path): - """ - do an http head on the given file. return True if it succeeds - """ - self.log ('file_exists %s' % path) - expect_gzip = path.endswith ('.gz') - response = self.request ('HEAD', path) - got_gzip = response.getheader ('Content-Encoding', '').strip () - if got_gzip.lower () == 'x-gzip' and expect_gzip == False: - # the asset server fakes us out if we ask for the non-gzipped - # version of an asset, but the server has the gzipped version. - return False - return response.status == 200 - - - def mkdir (self, path): - """ mkdir docstring """ - self.log ('mkdir %s' % path) - headers = {} - response = self.request ('MKCOL', path, None, headers) - if response.status == 201: - return # success - if response.status == 405: - return # directory already existed? - self.__err (response, ('MKCOL', path, None, headers, 0)) - - - def delete (self, path): - """ delete docstring """ - self.log ('delete %s' % path) - headers = {'Depth':'infinity'} # collections require infinity - response = self.request ('DELETE', path, None, headers) - if response.status == 204: - return # no content - if response.status == 404: - return # hmm - self.__err (response, ('DELETE', path, None, headers, 0)) - - - def list_directory (self, path, dir_filter=None, allow_cache=True, - minimum_cache_time=False): - """ - Request an http directory listing and parse the filenames out of lines - like: '<LI><A HREF="X"> X</A>'. If a filter function is provided, - only return filenames that the filter returns True for. - - This is sort of grody, but it seems faster than other ways of getting - this information from an isilon. - """ - self.log ('list_directory %s' % path) - - def try_match (lline, before, after): - """ try_match docstring """ - try: - blen = len (before) - asset_start_index = lline.index (before) - asset_end_index = lline.index (after, asset_start_index + blen) - asset = line[ asset_start_index + blen : asset_end_index ] - - if not dir_filter or dir_filter (asset): - return [ asset ] - return [] - except ValueError: - return [] - - if len (path) > 0 and path[-1:] != '/': - path += '/' - - response = self.request ('GET', path, None, {}, False, - allow_cache=allow_cache) - - if allow_cache and minimum_cache_time: # XXX - print response.getheader ('Date') - # s = "2005-12-06T12:13:14" - # from datetime import datetime - # from time import strptime - # datetime(*strptime(s, "%Y-%m-%dT%H:%M:%S")[0:6]) - # datetime.datetime(2005, 12, 6, 12, 13, 14) - - if response.status != 200: - self.__err (response, ('GET', path, None, {}, 0)) - assets = [] - for line in response.read ().split ('\n'): - lline = line.lower () - if lline.find ("parent directory") == -1: - # isilon file - assets += try_match (lline, '<li><a href="', '"> ') - # apache dir - assets += try_match (lline, 'alt="[dir]"> <a href="', '/">') - # apache file - assets += try_match (lline, 'alt="[ ]"> <a href="', '">') - return assets - - - def __tmp_filename (self, path_and_file): - """ __tmp_filename docstring """ - head, tail = os.path.split (path_and_file) - if head != '': - return head + '/.' + tail + '.' + str (os.getpid ()) - else: - return head + '.' + tail + '.' + str (os.getpid ()) - - - def __put__ (self, filesize, body_hook, remotefile): - """ __put__ docstring """ - headers = {'Content-Length' : str (filesize)} - remotefile_tmp = self.__tmp_filename (remotefile) - response = self.request ('PUT', remotefile_tmp, None, - headers, True, body_hook) - if not response.status in (201, 204): # created, no content - self.__err (response, ('PUT', remotefile, None, headers, 0)) - if filesize != self.get_file_size (remotefile_tmp): - try: - self.delete (remotefile_tmp) - except: - pass - raise DAVError (0, 'tmp upload error', remotefile_tmp) - # move the file to its final location - try: - self.rename (remotefile_tmp, remotefile) - except DAVError, exc: - if exc.status == 403: # try to clean up the tmp file - try: - self.delete (remotefile_tmp) - except: - pass - raise - if filesize != self.get_file_size (remotefile): - raise DAVError (0, 'file upload error', str (remotefile_tmp)) - - - def put_string (self, strng, remotefile): - """ put_string docstring """ - self.log ('put_string %d -> %s' % (len (strng), remotefile)) - filesize = len (strng) - def body_hook (): - """ body_hook docstring """ - self.connection.send (strng) - self.__put__ (filesize, body_hook, remotefile) - - - def put_file (self, localfile, remotefile): - """ - Send a local file to a remote webdav store. First, upload to - a temporary filename. Next make sure the file is the size we - expected. Next, move the file to its final location. Next, - check the file size at the final location. - """ - self.log ('put_file %s -> %s' % (localfile, remotefile)) - filesize = os.path.getsize (localfile) - def body_hook (): - """ body_hook docstring """ - handle = open (localfile) - while True: - data = handle.read (1300) - if len (data) == 0: - break - self.connection.send (data) - handle.close () - self.__put__ (filesize, body_hook, remotefile) - - - def create_empty_file (self, remotefile): - """ create an empty file """ - self.log ('touch_file %s' % (remotefile)) - headers = {'Content-Length' : '0'} - response = self.request ('PUT', remotefile, None, headers) - if not response.status in (201, 204): # created, no content - self.__err (response, ('PUT', remotefile, None, headers, 0)) - if self.get_file_size (remotefile) != 0: - raise DAVError (0, 'file upload error', str (remotefile)) - - - def __get_file_setup (self, remotefile, check_size=True): - """ __get_file_setup docstring """ - if check_size: - remotesize = self.get_file_size (remotefile) - response = self.request ('GET', remotefile, None, {}, False) - if response.status != 200: - self.__err (response, ('GET', remotefile, None, {}, 0)) - try: - content_length = int (response.getheader ("Content-Length")) - except TypeError: - content_length = None - if check_size: - if content_length != remotesize: - raise DAVError (0, 'file DL size error', remotefile) - return (response, content_length) - - - def __get_file_read (self, writehandle, response, content_length): - """ __get_file_read docstring """ - if content_length != None: - so_far_length = 0 - while so_far_length < content_length: - data = response.read (content_length - so_far_length) - if len (data) == 0: - raise DAVError (0, 'short file download') - so_far_length += len (data) - writehandle.write (data) - while len (response.read ()) > 0: - pass - else: - while True: - data = response.read () - if (len (data) < 1): - break - writehandle.write (data) - - - def get_file (self, remotefile, localfile, check_size=True): - """ - Get a remote file from a webdav server. Download to a local - tmp file, then move into place. Sanity check file sizes as - we go. - """ - self.log ('get_file %s -> %s' % (remotefile, localfile)) - (response, content_length) = \ - self.__get_file_setup (remotefile, check_size) - localfile_tmp = self.__tmp_filename (localfile) - handle = open (localfile_tmp, 'w') - self.__get_file_read (handle, response, content_length) - handle.close () - if check_size: - if content_length != os.path.getsize (localfile_tmp): - raise DAVError (0, 'file DL size error', - remotefile+','+localfile) - os.rename (localfile_tmp, localfile) - - - def get_file_as_string (self, remotefile, check_size=True): - """ - download a file from a webdav server and return it as a string. - """ - self.log ('get_file_as_string %s' % remotefile) - (response, content_length) = \ - self.__get_file_setup (remotefile, check_size) - # (tmp_handle, tmp_filename) = tempfile.mkstemp () - tmp_handle = os.tmpfile () - self.__get_file_read (tmp_handle, response, content_length) - tmp_handle.seek (0) - ret = tmp_handle.read () - tmp_handle.close () - # os.unlink (tmp_filename) - return ret - - - def get_post_as_string (self, remotefile, body): - """ - Do an http POST, send body, get response and return it. - """ - self.log ('get_post_as_string %s' % remotefile) - # headers = {'Content-Type':'application/x-www-form-urlencoded'} - headers = {'Content-Type':'text/xml; charset="utf-8"'} - # b64body = urlsafe_b64encode (asset_url) - response = self.request ('POST', remotefile, body, headers, False) - if response.status != 200: - self.__err (response, ('POST', remotefile, body, headers, 0)) - try: - content_length = int (response.getheader ('Content-Length')) - except TypeError: - content_length = None - tmp_handle = os.tmpfile () - self.__get_file_read (tmp_handle, response, content_length) - tmp_handle.seek (0) - ret = tmp_handle.read () - tmp_handle.close () - return ret - - - def __destination_command (self, verb, remotesrc, dstdav, remotedst): - """ - self and dstdav should point to the same http server. - """ - if len (remotedst) > 0 and remotedst[ 0 ] == '/': - remotedst = remotedst[1:] - headers = {'Destination': 'http://%s:%d%s%s' % (dstdav.host, - dstdav.port, - dstdav.top_path, - remotedst)} - response = self.request (verb, remotesrc, None, headers) - if response.status == 201: - return # created - if response.status == 204: - return # no content - self.__err (response, (verb, remotesrc, None, headers, 0)) - - - def rename (self, remotesrc, remotedst): - """ rename a file on a webdav server """ - self.log ('rename %s -> %s' % (remotesrc, remotedst)) - self.__destination_command ('MOVE', remotesrc, self, remotedst) - def xrename (self, remotesrc, dstdav, remotedst): - """ rename a file on a webdav server """ - self.log ('xrename %s -> %s' % (remotesrc, remotedst)) - self.__destination_command ('MOVE', remotesrc, dstdav, remotedst) - - - def copy (self, remotesrc, remotedst): - """ copy a file on a webdav server """ - self.log ('copy %s -> %s' % (remotesrc, remotedst)) - self.__destination_command ('COPY', remotesrc, self, remotedst) - def xcopy (self, remotesrc, dstdav, remotedst): - """ copy a file on a webdav server """ - self.log ('xcopy %s -> %s' % (remotesrc, remotedst)) - self.__destination_command ('COPY', remotesrc, dstdav, remotedst) - - -def put_string (data, url): - """ - upload string s to a url - """ - url_parsed = urlparse.urlsplit (url) - dav = WebDAV ('%s://%s/' % (url_parsed[ 0 ], url_parsed[ 1 ])) - dav.put_string (data, url_parsed[ 2 ]) - - -def get_string (url, check_size=True): - """ - return the contents of a url as a string - """ - url_parsed = urlparse.urlsplit (url) - dav = WebDAV ('%s://%s/' % (url_parsed[ 0 ], url_parsed[ 1 ])) - return dav.get_file_as_string (url_parsed[ 2 ], check_size) diff --git a/indra/lib/python/indra/ipc/xml_rpc.py b/indra/lib/python/indra/ipc/xml_rpc.py deleted file mode 100755 index 47536c10c3..0000000000 --- a/indra/lib/python/indra/ipc/xml_rpc.py +++ /dev/null @@ -1,273 +0,0 @@ -"""\ -@file xml_rpc.py -@brief An implementation of a parser/generator for the XML-RPC xml format. - -$LicenseInfo:firstyear=2006&license=mit$ - -Copyright (c) 2006-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - - -from greenlet import greenlet - -from mulib import mu - -from xml.sax import handler -from xml.sax import parseString - - -# States -class Expected(object): - def __init__(self, tag): - self.tag = tag - - def __getattr__(self, name): - return type(self)(name) - - def __repr__(self): - return '%s(%r)' % ( - type(self).__name__, self.tag) - - -class START(Expected): - pass - - -class END(Expected): - pass - - -class STR(object): - tag = '' - - -START = START('') -END = END('') - - -class Malformed(Exception): - pass - - -class XMLParser(handler.ContentHandler): - def __init__(self, state_machine, next_states): - handler.ContentHandler.__init__(self) - self.state_machine = state_machine - if not isinstance(next_states, tuple): - next_states = (next_states, ) - self.next_states = next_states - self._character_buffer = '' - - def assertState(self, state, name, *rest): - if not isinstance(self.next_states, tuple): - self.next_states = (self.next_states, ) - for next in self.next_states: - if type(state) == type(next): - if next.tag and next.tag != name: - raise Malformed( - "Expected %s, got %s %s %s" % ( - next, state, name, rest)) - break - else: - raise Malformed( - "Expected %s, got %s %s %s" % ( - self.next_states, state, name, rest)) - - def startElement(self, name, attrs): - self.assertState(START, name.lower(), attrs) - self.next_states = self.state_machine.switch(START, (name.lower(), dict(attrs))) - - def endElement(self, name): - if self._character_buffer.strip(): - characters = self._character_buffer.strip() - self._character_buffer = '' - self.assertState(STR, characters) - self.next_states = self.state_machine.switch(characters) - self.assertState(END, name.lower()) - self.next_states = self.state_machine.switch(END, name.lower()) - - def error(self, exc): - self.bozo = 1 - self.exc = exc - - def fatalError(self, exc): - self.error(exc) - raise exc - - def characters(self, characters): - self._character_buffer += characters - - -def parse(what): - child = greenlet(xml_rpc) - me = greenlet.getcurrent() - startup_states = child.switch(me) - parser = XMLParser(child, startup_states) - try: - parseString(what, parser) - except Malformed: - print what - raise - return child.switch() - - -def xml_rpc(yielder): - yielder.switch(START.methodcall) - yielder.switch(START.methodname) - methodName = yielder.switch(STR) - yielder.switch(END.methodname) - - yielder.switch(START.params) - - root = None - params = [] - while True: - state, _ = yielder.switch(START.param, END.params) - if state == END: - break - - yielder.switch(START.value) - - params.append( - handle(yielder)) - - yielder.switch(END.value) - yielder.switch(END.param) - - yielder.switch(END.methodcall) - ## Resume parse - yielder.switch() - ## Return result to parse - return methodName.strip(), params - - -def handle(yielder): - _, (tag, attrs) = yielder.switch(START) - if tag in ['int', 'i4']: - result = int(yielder.switch(STR)) - elif tag == 'boolean': - result = bool(int(yielder.switch(STR))) - elif tag == 'string': - result = yielder.switch(STR) - elif tag == 'double': - result = float(yielder.switch(STR)) - elif tag == 'datetime.iso8601': - result = yielder.switch(STR) - elif tag == 'base64': - result = base64.b64decode(yielder.switch(STR)) - elif tag == 'struct': - result = {} - while True: - state, _ = yielder.switch(START.member, END.struct) - if state == END: - break - - yielder.switch(START.name) - key = yielder.switch(STR) - yielder.switch(END.name) - - yielder.switch(START.value) - result[key] = handle(yielder) - yielder.switch(END.value) - - yielder.switch(END.member) - ## We already handled </struct> above, don't want to handle it below - return result - elif tag == 'array': - result = [] - yielder.switch(START.data) - while True: - state, _ = yielder.switch(START.value, END.data) - if state == END: - break - - result.append(handle(yielder)) - - yielder.switch(END.value) - - yielder.switch(getattr(END, tag)) - - return result - - -VALUE = mu.tag_factory('value') -BOOLEAN = mu.tag_factory('boolean') -INT = mu.tag_factory('int') -STRUCT = mu.tag_factory('struct') -MEMBER = mu.tag_factory('member') -NAME = mu.tag_factory('name') -ARRAY = mu.tag_factory('array') -DATA = mu.tag_factory('data') -STRING = mu.tag_factory('string') -DOUBLE = mu.tag_factory('double') -METHODRESPONSE = mu.tag_factory('methodResponse') -PARAMS = mu.tag_factory('params') -PARAM = mu.tag_factory('param') - -mu.inline_elements['string'] = True -mu.inline_elements['boolean'] = True -mu.inline_elements['name'] = True - - -def _generate(something): - if isinstance(something, dict): - result = STRUCT() - for key, value in something.items(): - result[ - MEMBER[ - NAME[key], _generate(value)]] - return VALUE[result] - elif isinstance(something, list): - result = DATA() - for item in something: - result[_generate(item)] - return VALUE[ARRAY[[result]]] - elif isinstance(something, basestring): - return VALUE[STRING[something]] - elif isinstance(something, bool): - if something: - return VALUE[BOOLEAN['1']] - return VALUE[BOOLEAN['0']] - elif isinstance(something, int): - return VALUE[INT[something]] - elif isinstance(something, float): - return VALUE[DOUBLE[something]] - -def generate(*args): - params = PARAMS() - for arg in args: - params[PARAM[_generate(arg)]] - return METHODRESPONSE[params] - - -if __name__ == '__main__': - print parse("""<?xml version="1.0"?> <methodCall> <methodName>examples.getStateName</methodName> <params> <param> <value><i4>41</i4></value> </param> </params> </methodCall> -""") - - - - - - - - - diff --git a/indra/lib/python/indra/util/fastest_elementtree.py b/indra/lib/python/indra/util/fastest_elementtree.py deleted file mode 100755 index 4fcf662dd9..0000000000 --- a/indra/lib/python/indra/util/fastest_elementtree.py +++ /dev/null @@ -1,64 +0,0 @@ -"""\ -@file fastest_elementtree.py -@brief Concealing some gnarly import logic in here. This should export the interface of elementtree. - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -# The parsing exception raised by the underlying library depends -# on the ElementTree implementation we're using, so we provide an -# alias here. -# -# Use ElementTreeError as the exception type for catching parsing -# errors. - - -# Using cElementTree might cause some unforeseen problems, so here's a -# convenient off switch. -use_celementree = True - -try: - if not use_celementree: - raise ImportError() - # Python 2.3 and 2.4. - from cElementTree import * - ElementTreeError = SyntaxError -except ImportError: - try: - if not use_celementree: - raise ImportError() - # Python 2.5 and above. - from xml.etree.cElementTree import * - ElementTreeError = SyntaxError - except ImportError: - # Pure Python code. - try: - # Python 2.3 and 2.4. - from elementtree.ElementTree import * - except ImportError: - # Python 2.5 and above. - from xml.etree.ElementTree import * - - # The pure Python ElementTree module uses Expat for parsing. - from xml.parsers.expat import ExpatError as ElementTreeError diff --git a/indra/lib/python/indra/util/helpformatter.py b/indra/lib/python/indra/util/helpformatter.py deleted file mode 100755 index ba5c9b67d1..0000000000 --- a/indra/lib/python/indra/util/helpformatter.py +++ /dev/null @@ -1,52 +0,0 @@ -"""\ -@file helpformatter.py -@author Phoenix -@brief Class for formatting optparse descriptions. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import optparse -import textwrap - -class Formatter(optparse.IndentedHelpFormatter): - def __init__( - self, - p_indentIncrement = 2, - p_maxHelpPosition = 24, - p_width = 79, - p_shortFirst = 1) : - optparse.HelpFormatter.__init__( - self, - p_indentIncrement, - p_maxHelpPosition, - p_width, - p_shortFirst) - def format_description(self, p_description): - t_descWidth = self.width - self.current_indent - t_indent = " " * (self.current_indent + 2) - return "\n".join( - [textwrap.fill(descr, t_descWidth, initial_indent = t_indent, - subsequent_indent = t_indent) - for descr in p_description.split("\n")] ) diff --git a/indra/lib/python/indra/util/iterators.py b/indra/lib/python/indra/util/iterators.py deleted file mode 100755 index 9013fa6303..0000000000 --- a/indra/lib/python/indra/util/iterators.py +++ /dev/null @@ -1,63 +0,0 @@ -"""\ -@file iterators.py -@brief Useful general-purpose iterators. - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -from __future__ import nested_scopes - -def iter_chunks(rows, aggregate_size=100): - """ - Given an iterable set of items (@p rows), produces lists of up to @p - aggregate_size items at a time, for example: - - iter_chunks([1,2,3,4,5,6,7,8,9,10], 3) - - Values for @p aggregate_size < 1 will raise ValueError. - - Will return a generator that produces, in the following order: - - [1, 2, 3] - - [4, 5, 6] - - [7, 8, 9] - - [10] - """ - if aggregate_size < 1: - raise ValueError() - - def iter_chunks_inner(): - row_iter = iter(rows) - done = False - agg = [] - while not done: - try: - row = row_iter.next() - agg.append(row) - except StopIteration: - done = True - if agg and (len(agg) >= aggregate_size or done): - yield agg - agg = [] - - return iter_chunks_inner() diff --git a/indra/lib/python/indra/util/iterators_test.py b/indra/lib/python/indra/util/iterators_test.py deleted file mode 100755 index 66928c8e7d..0000000000 --- a/indra/lib/python/indra/util/iterators_test.py +++ /dev/null @@ -1,72 +0,0 @@ -"""\ -@file iterators_test.py -@brief Test cases for iterators module. - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import unittest - -from indra.util.iterators import iter_chunks - -class TestIterChunks(unittest.TestCase): - """Unittests for iter_chunks""" - def test_bad_agg_size(self): - rows = [1,2,3,4] - self.assertRaises(ValueError, iter_chunks, rows, 0) - self.assertRaises(ValueError, iter_chunks, rows, -1) - - try: - for i in iter_chunks(rows, 0): - pass - except ValueError: - pass - else: - self.fail() - - try: - result = list(iter_chunks(rows, 0)) - except ValueError: - pass - else: - self.fail() - def test_empty(self): - rows = [] - result = list(iter_chunks(rows)) - self.assertEqual(result, []) - def test_small(self): - rows = [[1]] - result = list(iter_chunks(rows, 2)) - self.assertEqual(result, [[[1]]]) - def test_size(self): - rows = [[1],[2]] - result = list(iter_chunks(rows, 2)) - self.assertEqual(result, [[[1],[2]]]) - def test_multi_agg(self): - rows = [[1],[2],[3],[4],[5]] - result = list(iter_chunks(rows, 2)) - self.assertEqual(result, [[[1],[2]],[[3],[4]],[[5]]]) - -if __name__ == "__main__": - unittest.main() diff --git a/indra/lib/python/indra/util/llperformance.py b/indra/lib/python/indra/util/llperformance.py deleted file mode 100755 index 57dd64de3f..0000000000 --- a/indra/lib/python/indra/util/llperformance.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/env python -"""\ -@file llperformance.py - -$LicenseInfo:firstyear=2010&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2010-2011, Linden Research, Inc. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 of the License only. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -$/LicenseInfo$ -""" - -# ------------------------------------------------ -# Sim metrics utility functions. - -import glob, os, time, sys, stat, exceptions - -from indra.base import llsd - -gBlockMap = {} #Map of performance metric data with function hierarchy information. -gCurrentStatPath = "" - -gIsLoggingEnabled=False - -class LLPerfStat: - def __init__(self,key): - self.mTotalTime = 0 - self.mNumRuns = 0 - self.mName=key - self.mTimeStamp = int(time.time()*1000) - self.mUTCTime = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) - - def __str__(self): - return "%f" % self.mTotalTime - - def start(self): - self.mStartTime = int(time.time() * 1000000) - self.mNumRuns += 1 - - def stop(self): - execution_time = int(time.time() * 1000000) - self.mStartTime - self.mTotalTime += execution_time - - def get_map(self): - results={} - results['name']=self.mName - results['utc_time']=self.mUTCTime - results['timestamp']=self.mTimeStamp - results['us']=self.mTotalTime - results['count']=self.mNumRuns - return results - -class PerfError(exceptions.Exception): - def __init__(self): - return - - def __Str__(self): - print "","Unfinished LLPerfBlock" - -class LLPerfBlock: - def __init__( self, key ): - global gBlockMap - global gCurrentStatPath - global gIsLoggingEnabled - - #Check to see if we're running metrics right now. - if gIsLoggingEnabled: - self.mRunning = True #Mark myself as running. - - self.mPreviousStatPath = gCurrentStatPath - gCurrentStatPath += "/" + key - if gCurrentStatPath not in gBlockMap: - gBlockMap[gCurrentStatPath] = LLPerfStat(key) - - self.mStat = gBlockMap[gCurrentStatPath] - self.mStat.start() - - def finish( self ): - global gBlockMap - global gIsLoggingEnabled - - if gIsLoggingEnabled: - self.mStat.stop() - self.mRunning = False - gCurrentStatPath = self.mPreviousStatPath - -# def __del__( self ): -# if self.mRunning: -# #SPATTERS FIXME -# raise PerfError - -class LLPerformance: - #-------------------------------------------------- - # Determine whether or not we want to log statistics - - def __init__( self, process_name = "python" ): - self.process_name = process_name - self.init_testing() - self.mTimeStamp = int(time.time()*1000) - self.mUTCTime = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) - - def init_testing( self ): - global gIsLoggingEnabled - - host_performance_file = "/dev/shm/simperf/simperf_proc_config.llsd" - - #If file exists, open - if os.path.exists(host_performance_file): - file = open (host_performance_file,'r') - - #Read serialized LLSD from file. - body = llsd.parse(file.read()) - - #Calculate time since file last modified. - stats = os.stat(host_performance_file) - now = time.time() - mod = stats[stat.ST_MTIME] - age = now - mod - - if age < ( body['duration'] ): - gIsLoggingEnabled = True - - - def get ( self ): - global gIsLoggingEnabled - return gIsLoggingEnabled - - #def output(self,ptr,path): - # if 'stats' in ptr: - # stats = ptr['stats'] - # self.mOutputPtr[path] = stats.get_map() - - # if 'children' in ptr: - # children=ptr['children'] - - # curptr = self.mOutputPtr - # curchildren={} - # curptr['children'] = curchildren - - # for key in children: - # curchildren[key]={} - # self.mOutputPtr = curchildren[key] - # self.output(children[key],path + '/' + key) - - def done(self): - global gBlockMap - - if not self.get(): - return - - output_name = "/dev/shm/simperf/%s_proc.%d.llsd" % (self.process_name, os.getpid()) - output_file = open(output_name, 'w') - process_info = { - "name" : self.process_name, - "pid" : os.getpid(), - "ppid" : os.getppid(), - "timestamp" : self.mTimeStamp, - "utc_time" : self.mUTCTime, - } - output_file.write(llsd.format_notation(process_info)) - output_file.write('\n') - - for key in gBlockMap.keys(): - gBlockMap[key] = gBlockMap[key].get_map() - output_file.write(llsd.format_notation(gBlockMap)) - output_file.write('\n') - output_file.close() - diff --git a/indra/lib/python/indra/util/llsubprocess.py b/indra/lib/python/indra/util/llsubprocess.py deleted file mode 100755 index 7e0e115d14..0000000000 --- a/indra/lib/python/indra/util/llsubprocess.py +++ /dev/null @@ -1,117 +0,0 @@ -"""\ -@file llsubprocess.py -@author Phoenix -@date 2008-01-18 -@brief The simplest possible wrapper for a common sub-process paradigm. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import os -import popen2 -import time -import select - -class Timeout(RuntimeError): - "Exception raised when a subprocess times out." - pass - -def run(command, args=None, data=None, timeout=None): - """\ -@brief Run command with arguments - -This is it. This is the function I want to run all the time when doing -subprocces, but end up copying the code everywhere. none of the -standard commands are secure and provide a way to specify input, get -all the output, and get the result. -@param command A string specifying a process to launch. -@param args Arguments to be passed to command. Must be list, tuple or None. -@param data input to feed to the command. -@param timeout Maximum number of many seconds to run. -@return Returns (result, stdout, stderr) from process. -""" - cmd = [command] - if args: - cmd.extend([str(arg) for arg in args]) - #print "cmd: ","' '".join(cmd) - child = popen2.Popen3(cmd, True) - #print child.pid - out = [] - err = [] - result = -1 - time_left = timeout - tochild = [child.tochild.fileno()] - while True: - time_start = time.time() - #print "time:",time_left - p_in, p_out, p_err = select.select( - [child.fromchild.fileno(), child.childerr.fileno()], - tochild, - [], - time_left) - if p_in: - new_line = os.read(child.fromchild.fileno(), 32 * 1024) - if new_line: - #print "line:",new_line - out.append(new_line) - new_line = os.read(child.childerr.fileno(), 32 * 1024) - if new_line: - #print "error:", new_line - err.append(new_line) - if p_out: - if data: - #print "p_out" - bytes = os.write(child.tochild.fileno(), data) - data = data[bytes:] - if len(data) == 0: - data = None - tochild = [] - child.tochild.close() - result = child.poll() - if result != -1: - # At this point, the child process has exited and result - # is the return value from the process. Between the time - # we called select() and poll() the process may have - # exited so read all the data left on the child process - # stdout and stderr. - last = child.fromchild.read() - if last: - out.append(last) - last = child.childerr.read() - if last: - err.append(last) - child.tochild.close() - child.fromchild.close() - child.childerr.close() - break - if time_left is not None: - time_left -= (time.time() - time_start) - if time_left < 0: - raise Timeout - #print "result:",result - out = ''.join(out) - #print "stdout:", out - err = ''.join(err) - #print "stderr:", err - return result, out, err diff --git a/indra/lib/python/indra/util/named_query.py b/indra/lib/python/indra/util/named_query.py deleted file mode 100755 index 6bf956107d..0000000000 --- a/indra/lib/python/indra/util/named_query.py +++ /dev/null @@ -1,592 +0,0 @@ -"""\ -@file named_query.py -@author Ryan Williams, Phoenix -@date 2007-07-31 -@brief An API for running named queries. - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import errno -import MySQLdb -import MySQLdb.cursors -import os -import os.path -import re -import time - -from indra.base import llsd -from indra.base import config - -DEBUG = False -NQ_FILE_SUFFIX = config.get('named-query-file-suffix', '.nq') -NQ_FILE_SUFFIX_LEN = len(NQ_FILE_SUFFIX) - -_g_named_manager = None - -def _init_g_named_manager(sql_dir = None): - """Initializes a global NamedManager object to point at a - specified named queries hierarchy. - - This function is intended entirely for testing purposes, - because it's tricky to control the config from inside a test.""" - global NQ_FILE_SUFFIX - NQ_FILE_SUFFIX = config.get('named-query-file-suffix', '.nq') - global NQ_FILE_SUFFIX_LEN - NQ_FILE_SUFFIX_LEN = len(NQ_FILE_SUFFIX) - - if sql_dir is None: - sql_dir = config.get('named-query-base-dir') - - # extra fallback directory in case config doesn't return what we want - if sql_dir is None: - sql_dir = os.path.abspath( - os.path.join( - os.path.realpath(os.path.dirname(__file__)), "..", "..", "..", "..", "web", "dataservice", "sql")) - - global _g_named_manager - _g_named_manager = NamedQueryManager( - os.path.abspath(os.path.realpath(sql_dir))) - -def get(name, schema = None): - "Get the named query object to be used to perform queries" - if _g_named_manager is None: - _init_g_named_manager() - return _g_named_manager.get(name).for_schema(schema) - -def sql(connection, name, params): - # use module-global NamedQuery object to perform default substitution - return get(name).sql(connection, params) - -def run(connection, name, params, expect_rows = None): - """\ -@brief given a connection, run a named query with the params - -Note that this function will fetch ALL rows. -@param connection The connection to use -@param name The name of the query to run -@param params The parameters passed into the query -@param expect_rows The number of rows expected. Set to 1 if return_as_map is true. Raises ExpectationFailed if the number of returned rows doesn't exactly match. Kind of a hack. -@return Returns the result set as a list of dicts. -""" - return get(name).run(connection, params, expect_rows) - -class ExpectationFailed(Exception): - """ Exception that is raised when an expectation for an sql query - is not met.""" - def __init__(self, message): - Exception.__init__(self, message) - self.message = message - -class NamedQuery(object): - def __init__(self, name, filename): - """ Construct a NamedQuery object. The name argument is an - arbitrary name as a handle for the query, and the filename is - a path to a file or a file-like object containing an llsd named - query document.""" - self._stat_interval_seconds = 5 # 5 seconds - self._name = name - if (filename is not None and isinstance(filename, (str, unicode)) - and NQ_FILE_SUFFIX != filename[-NQ_FILE_SUFFIX_LEN:]): - filename = filename + NQ_FILE_SUFFIX - self._location = filename - self._alternative = dict() - self._last_mod_time = 0 - self._last_check_time = 0 - self.deleted = False - self.load_contents() - - def name(self): - """ The name of the query. """ - return self._name - - def get_modtime(self): - """ Returns the mtime (last modified time) of the named query - filename. For file-like objects, expect a modtime of 0""" - if self._location and isinstance(self._location, (str, unicode)): - return os.path.getmtime(self._location) - return 0 - - def load_contents(self): - """ Loads and parses the named query file into self. Does - nothing if self.location is nonexistant.""" - if self._location: - if isinstance(self._location, (str, unicode)): - contents = llsd.parse(open(self._location).read()) - else: - # we probably have a file-like object. Godspeed! - contents = llsd.parse(self._location.read()) - self._reference_contents(contents) - # Check for alternative implementations - try: - for name, alt in self._contents['alternative'].items(): - nq = NamedQuery(name, None) - nq._reference_contents(alt) - self._alternative[name] = nq - except KeyError, e: - pass - self._last_mod_time = self.get_modtime() - self._last_check_time = time.time() - - def _reference_contents(self, contents): - "Helper method which builds internal structure from parsed contents" - self._contents = contents - self._ttl = int(self._contents.get('ttl', 0)) - self._return_as_map = bool(self._contents.get('return_as_map', False)) - self._legacy_dbname = self._contents.get('legacy_dbname', None) - - # reset these before doing the sql conversion because we will - # read them there. reset these while loading so we pick up - # changes. - self._around = set() - self._append = set() - self._integer = set() - self._options = self._contents.get('dynamic_where', {}) - for key in self._options: - if isinstance(self._options[key], basestring): - self._options[key] = self._convert_sql(self._options[key]) - elif isinstance(self._options[key], list): - lines = [] - for line in self._options[key]: - lines.append(self._convert_sql(line)) - self._options[key] = lines - else: - moreopt = {} - for kk in self._options[key]: - moreopt[kk] = self._convert_sql(self._options[key][kk]) - self._options[key] = moreopt - self._base_query = self._convert_sql(self._contents['base_query']) - self._query_suffix = self._convert_sql( - self._contents.get('query_suffix', '')) - - def _convert_sql(self, sql): - """convert the parsed sql into a useful internal structure. - - This function has to turn the named query format into a pyformat - style. It also has to look for %:name% and :name% and - ready them for use in LIKE statements""" - if sql: - # This first sub is to properly escape any % signs that - # are meant to be literally passed through to mysql in the - # query. It leaves any %'s that are used for - # like-expressions. - expr = re.compile("(?<=[^a-zA-Z0-9_-])%(?=[^:])") - sql = expr.sub('%%', sql) - - # This should tackle the rest of the %'s in the query, by - # converting them to LIKE clauses. - expr = re.compile("(%?):([a-zA-Z][a-zA-Z0-9_-]*)%") - sql = expr.sub(self._prepare_like, sql) - expr = re.compile("#:([a-zA-Z][a-zA-Z0-9_-]*)") - sql = expr.sub(self._prepare_integer, sql) - expr = re.compile(":([a-zA-Z][a-zA-Z0-9_-]*)") - sql = expr.sub("%(\\1)s", sql) - return sql - - def _prepare_like(self, match): - """This function changes LIKE statement replace behavior - - It works by turning %:name% to %(_name_around)s and :name% to - %(_name_append)s. Since a leading '_' is not a valid keyname - input (enforced via unit tests), it will never clash with - existing keys. Then, when building the statement, the query - runner will generate corrected strings.""" - if match.group(1) == '%': - # there is a leading % so this is treated as prefix/suffix - self._around.add(match.group(2)) - return "%(" + self._build_around_key(match.group(2)) + ")s" - else: - # there is no leading %, so this is suffix only - self._append.add(match.group(2)) - return "%(" + self._build_append_key(match.group(2)) + ")s" - - def _build_around_key(self, key): - return "_" + key + "_around" - - def _build_append_key(self, key): - return "_" + key + "_append" - - def _prepare_integer(self, match): - """This function adjusts the sql for #:name replacements - - It works by turning #:name to %(_name_as_integer)s. Since a - leading '_' is not a valid keyname input (enforced via unit - tests), it will never clash with existing keys. Then, when - building the statement, the query runner will generate - corrected strings.""" - self._integer.add(match.group(1)) - return "%(" + self._build_integer_key(match.group(1)) + ")s" - - def _build_integer_key(self, key): - return "_" + key + "_as_integer" - - def _strip_wildcards_to_list(self, value): - """Take string, and strip out the LIKE special characters. - - Technically, this is database dependant, but postgresql and - mysql use the same wildcards, and I am not aware of a general - way to handle this. I think you need a sql statement of the - form: - - LIKE_STRING( [ANY,ONE,str]... ) - - which would treat ANY as their any string, and ONE as their - single glyph, and str as something that needs database - specific encoding to not allow any % or _ to affect the query. - - As it stands, I believe it's impossible to write a named query - style interface which uses like to search the entire space of - text available. Imagine the query: - - % of brain used by average linden - - In order to search for %, it must be escaped, so once you have - escaped the string to not do wildcard searches, and be escaped - for the database, and then prepended the wildcard you come - back with one of: - - 1) %\% of brain used by average linden - 2) %%% of brain used by average linden - - Then, when passed to the database to be escaped to be database - safe, you get back: - - 1) %\\% of brain used by average linden - : which means search for any character sequence, followed by a - backslash, followed by any sequence, followed by ' of - brain...' - 2) %%% of brain used by average linden - : which (I believe) means search for a % followed by any - character sequence followed by 'of brain...' - - Neither of which is what we want! - - So, we need a vendor (or extention) for LIKE_STRING. Anyone - want to write it?""" - if isinstance(value, unicode): - utf8_value = value - else: - utf8_value = unicode(value, "utf-8") - esc_list = [] - remove_chars = set(u"%_") - for glyph in utf8_value: - if glyph in remove_chars: - continue - esc_list.append(glyph.encode("utf-8")) - return esc_list - - def delete(self): - """ Makes this query unusable by deleting all the members and - setting the deleted member. This is desired when the on-disk - query has been deleted but the in-memory copy remains.""" - # blow away all members except _name, _location, and deleted - name, location = self._name, self._location - for key in self.__dict__.keys(): - del self.__dict__[key] - self.deleted = True - self._name, self._location = name, location - - def ttl(self): - """ Estimated time to live of this query. Used for web - services to set the Expires header.""" - return self._ttl - - def legacy_dbname(self): - return self._legacy_dbname - - def return_as_map(self): - """ Returns true if this query is configured to return its - results as a single map (as opposed to a list of maps, the - normal behavior).""" - - return self._return_as_map - - def for_schema(self, db_name): - "Look trough the alternates and return the correct query" - if db_name is None: - return self - try: - return self._alternative[db_name] - except KeyError, e: - pass - return self - - def run(self, connection, params, expect_rows = None, use_dictcursor = True): - """given a connection, run a named query with the params - - Note that this function will fetch ALL rows. We do this because it - opens and closes the cursor to generate the values, and this - isn't a generator so the cursor has no life beyond the method call. - - @param cursor The connection to use (this generates its own cursor for the query) - @param name The name of the query to run - @param params The parameters passed into the query - @param expect_rows The number of rows expected. Set to 1 if return_as_map is true. Raises ExpectationFailed if the number of returned rows doesn't exactly match. Kind of a hack. - @param use_dictcursor Set to false to use a normal cursor and manually convert the rows to dicts. - @return Returns the result set as a list of dicts, or, if the named query has return_as_map set to true, returns a single dict. - """ - if use_dictcursor: - cursor = connection.cursor(MySQLdb.cursors.DictCursor) - else: - cursor = connection.cursor() - - full_query, params = self._construct_sql(params) - if DEBUG: - print "SQL:", self.sql(connection, params) - rows = cursor.execute(full_query, params) - - # *NOTE: the expect_rows argument is a very cheesy way to get some - # validation on the result set. If you want to add more expectation - # logic, do something more object-oriented and flexible. Or use an ORM. - if(self._return_as_map): - expect_rows = 1 - if expect_rows is not None and rows != expect_rows: - cursor.close() - raise ExpectationFailed("Statement expected %s rows, got %s. Sql: '%s' %s" % ( - expect_rows, rows, full_query, params)) - - # convert to dicts manually if we're not using a dictcursor - if use_dictcursor: - result_set = cursor.fetchall() - else: - if cursor.description is None: - # an insert or something - x = cursor.fetchall() - cursor.close() - return x - - names = [x[0] for x in cursor.description] - - result_set = [] - for row in cursor.fetchall(): - converted_row = {} - for idx, col_name in enumerate(names): - converted_row[col_name] = row[idx] - result_set.append(converted_row) - - cursor.close() - if self._return_as_map: - return result_set[0] - return result_set - - def _construct_sql(self, params): - """ Returns a query string and a dictionary of parameters, - suitable for directly passing to the execute() method.""" - self.refresh() - - # build the query from the options available and the params - base_query = [] - base_query.append(self._base_query) - for opt, extra_where in self._options.items(): - if type(extra_where) in (dict, list, tuple): - if opt in params: - base_query.append(extra_where[params[opt]]) - else: - if opt in params and params[opt]: - base_query.append(extra_where) - if self._query_suffix: - base_query.append(self._query_suffix) - full_query = '\n'.join(base_query) - - # Go through the query and rewrite all of the ones with the - # @:name syntax. - rewrite = _RewriteQueryForArray(params) - expr = re.compile("@%\(([a-zA-Z][a-zA-Z0-9_-]*)\)s") - full_query = expr.sub(rewrite.operate, full_query) - params.update(rewrite.new_params) - - # build out the params for like. We only have to do this - # parameters which were detected to have ued the where syntax - # during load. - # - # * treat the incoming string as utf-8 - # * strip wildcards - # * append or prepend % as appropriate - new_params = {} - for key in params: - if key in self._around: - new_value = ['%'] - new_value.extend(self._strip_wildcards_to_list(params[key])) - new_value.append('%') - new_params[self._build_around_key(key)] = ''.join(new_value) - if key in self._append: - new_value = self._strip_wildcards_to_list(params[key]) - new_value.append('%') - new_params[self._build_append_key(key)] = ''.join(new_value) - if key in self._integer: - new_params[self._build_integer_key(key)] = int(params[key]) - params.update(new_params) - - return full_query, params - - def sql(self, connection, params): - """ Generates an SQL statement from the named query document - and a dictionary of parameters. - - *NOTE: Only use for debugging, because it uses the - non-standard MySQLdb 'literal' method. - """ - if not DEBUG: - import warnings - warnings.warn("Don't use named_query.sql() when not debugging. Used on %s" % self._location) - # do substitution using the mysql (non-standard) 'literal' - # function to do the escaping. - full_query, params = self._construct_sql(params) - return full_query % connection.literal(params) - - - def refresh(self): - """ Refresh self from the file on the filesystem. - - This is optimized to be callable as frequently as you wish, - without adding too much load. It does so by only stat-ing the - file every N seconds, where N defaults to 5 and is - configurable through the member _stat_interval_seconds. If the stat - reveals that the file has changed, refresh will re-parse the - contents of the file and use them to update the named query - instance. If the stat reveals that the file has been deleted, - refresh will call self.delete to make the in-memory - representation unusable.""" - now = time.time() - if(now - self._last_check_time > self._stat_interval_seconds): - self._last_check_time = now - try: - modtime = self.get_modtime() - if(modtime > self._last_mod_time): - self.load_contents() - except OSError, e: - if e.errno == errno.ENOENT: # file not found - self.delete() # clean up self - raise # pass the exception along to the caller so they know that this query disappeared - -class NamedQueryManager(object): - """ Manages the lifespan of NamedQuery objects, drawing from a - directory hierarchy of named query documents. - - In practice this amounts to a memory cache of NamedQuery objects.""" - - def __init__(self, named_queries_dir): - """ Initializes a manager to look for named queries in a - directory.""" - self._dir = os.path.abspath(os.path.realpath(named_queries_dir)) - self._cached_queries = {} - - def sql(self, connection, name, params): - nq = self.get(name) - return nq.sql(connection, params) - - def get(self, name): - """ Returns a NamedQuery instance based on the name, either - from memory cache, or by parsing from disk. - - The name is simply a relative path to the directory associated - with the manager object. Before returning the instance, the - NamedQuery object is cached in memory, so that subsequent - accesses don't have to read from disk or do any parsing. This - means that NamedQuery objects returned by this method are - shared across all users of the manager object. - NamedQuery.refresh is used to bring the NamedQuery objects in - sync with the actual files on disk.""" - nq = self._cached_queries.get(name) - if nq is None: - nq = NamedQuery(name, os.path.join(self._dir, name)) - self._cached_queries[name] = nq - else: - try: - nq.refresh() - except OSError, e: - if e.errno == errno.ENOENT: # file not found - del self._cached_queries[name] - raise # pass exception along to caller so they know that the query disappeared - - return nq - -class _RewriteQueryForArray(object): - "Helper class for rewriting queries with the @:name syntax" - def __init__(self, params): - self.params = params - self.new_params = dict() - - def operate(self, match): - "Given a match, return the string that should be in use" - key = match.group(1) - value = self.params[key] - if type(value) in (list,tuple): - rv = [] - for idx in range(len(value)): - # if the value@idx is array-like, we are - # probably dealing with a VALUES - new_key = "_%s_%s"%(key, str(idx)) - val_item = value[idx] - if type(val_item) in (list, tuple, dict): - if type(val_item) is dict: - # this is because in Python, the order of - # key, value retrieval from the dict is not - # guaranteed to match what the input intended - # and for VALUES, order is important. - # TODO: Implemented ordered dict in LLSD parser? - raise ExpectationFailed('Only lists/tuples allowed,\ - received dict') - values_keys = [] - for value_idx, item in enumerate(val_item): - # we want a key of the format : - # key_#replacement_#value_row_#value_col - # ugh... so if we are replacing 10 rows in user_note, - # the first values clause would read (for @:user_notes) :- - # ( :_user_notes_0_1_1, :_user_notes_0_1_2, :_user_notes_0_1_3 ) - # the input LLSD for VALUES will look like: - # <llsd>... - # <map> - # <key>user_notes</key> - # <array> - # <array> <!-- row 1 for VALUES --> - # <string>...</string> - # <string>...</string> - # <string>...</string> - # </array> - # ... - # </array> - # </map> - # ... </llsd> - values_key = "%s_%s"%(new_key, value_idx) - self.new_params[values_key] = item - values_keys.append("%%(%s)s"%values_key) - # now collapse all these new place holders enclosed in () - # from [':_key_0_1_1', ':_key_0_1_2', ':_key_0_1_3,...] - # rv will have [ '(:_key_0_1_1, :_key_0_1_2, :_key_0_1_3)', ] - # which is flattened a few lines below join(rv) - rv.append('(%s)' % ','.join(values_keys)) - else: - self.new_params[new_key] = val_item - rv.append("%%(%s)s"%new_key) - return ','.join(rv) - else: - # not something that can be expanded, so just drop the - # leading @ in the front of the match. This will mean that - # the single value we have, be it a string, int, whatever - # (other than dict) will correctly show up, eg: - # - # where foo in (@:foobar) -- foobar is a string, so we get - # where foo in (:foobar) - return match.group(0)[1:] diff --git a/indra/lib/python/indra/util/shutil2.py b/indra/lib/python/indra/util/shutil2.py deleted file mode 100755 index 9e2e7a6ded..0000000000 --- a/indra/lib/python/indra/util/shutil2.py +++ /dev/null @@ -1,84 +0,0 @@ -''' -@file shutil2.py -@brief a better shutil.copytree replacement - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -''' - -# -# shutil2.py -# Taken from http://www.scons.org/wiki/AccumulateBuilder -# the stock copytree sucks because it insists that the -# target dir not exist -# - -import os.path -import shutil - -def copytree(src, dest, symlinks=False): - """My own copyTree which does not fail if the directory exists. - - Recursively copy a directory tree using copy2(). - - If the optional symlinks flag is true, symbolic links in the - source tree result in symbolic links in the destination tree; if - it is false, the contents of the files pointed to by symbolic - links are copied. - - Behavior is meant to be identical to GNU 'cp -R'. - """ - def copyItems(src, dest, symlinks=False): - """Function that does all the work. - - It is necessary to handle the two 'cp' cases: - - destination does exist - - destination does not exist - - See 'cp -R' documentation for more details - """ - for item in os.listdir(src): - srcPath = os.path.join(src, item) - if os.path.isdir(srcPath): - srcBasename = os.path.basename(srcPath) - destDirPath = os.path.join(dest, srcBasename) - if not os.path.exists(destDirPath): - os.makedirs(destDirPath) - copyItems(srcPath, destDirPath) - elif os.path.islink(item) and symlinks: - linkto = os.readlink(item) - os.symlink(linkto, dest) - else: - shutil.copy2(srcPath, dest) - - # case 'cp -R src/ dest/' where dest/ already exists - if os.path.exists(dest): - destPath = os.path.join(dest, os.path.basename(src)) - if not os.path.exists(destPath): - os.makedirs(destPath) - # case 'cp -R src/ dest/' where dest/ does not exist - else: - os.makedirs(dest) - destPath = dest - # actually copy the files - copyItems(src, destPath) diff --git a/indra/lib/python/indra/util/simperf_host_xml_parser.py b/indra/lib/python/indra/util/simperf_host_xml_parser.py deleted file mode 100755 index 672c1050c2..0000000000 --- a/indra/lib/python/indra/util/simperf_host_xml_parser.py +++ /dev/null @@ -1,338 +0,0 @@ -#!/usr/bin/env python -"""\ -@file simperf_host_xml_parser.py -@brief Digest collector's XML dump and convert to simple dict/list structure - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import sys, os, getopt, time -import simplejson -from xml import sax - - -def usage(): - print "Usage:" - print sys.argv[0] + " [options]" - print " Convert RRD's XML dump to JSON. Script to convert the simperf_host_collector-" - print " generated RRD dump into JSON. Steps include converting selected named" - print " fields from GAUGE type to COUNTER type by computing delta with preceding" - print " values. Top-level named fields are:" - print - print " lastupdate Time (javascript timestamp) of last data sample" - print " step Time in seconds between samples" - print " ds Data specification (name/type) for each column" - print " database Table of data samples, one time step per row" - print - print "Options:" - print " -i, --in Input settings filename. (Default: stdin)" - print " -o, --out Output settings filename. (Default: stdout)" - print " -h, --help Print this message and exit." - print - print "Example: %s -i rrddump.xml -o rrddump.json" % sys.argv[0] - print - print "Interfaces:" - print " class SimPerfHostXMLParser() # SAX content handler" - print " def simperf_host_xml_fixup(parser) # post-parse value fixup" - -class SimPerfHostXMLParser(sax.handler.ContentHandler): - - def __init__(self): - pass - - def startDocument(self): - self.rrd_last_update = 0 # public - self.rrd_step = 0 # public - self.rrd_ds = [] # public - self.rrd_records = [] # public - self._rrd_level = 0 - self._rrd_parse_state = 0 - self._rrd_chars = "" - self._rrd_capture = False - self._rrd_ds_val = {} - self._rrd_data_row = [] - self._rrd_data_row_has_nan = False - - def endDocument(self): - pass - - # Nasty little ad-hoc state machine to extract the elements that are - # necessary from the 'rrdtool dump' XML output. The same element - # name '<ds>' is used for two different data sets so we need to pay - # some attention to the actual structure to get the ones we want - # and ignore the ones we don't. - - def startElement(self, name, attrs): - self._rrd_level = self._rrd_level + 1 - self._rrd_capture = False - if self._rrd_level == 1: - if name == "rrd" and self._rrd_parse_state == 0: - self._rrd_parse_state = 1 # In <rrd> - self._rrd_capture = True - self._rrd_chars = "" - elif self._rrd_level == 2: - if self._rrd_parse_state == 1: - if name == "lastupdate": - self._rrd_parse_state = 2 # In <rrd><lastupdate> - self._rrd_capture = True - self._rrd_chars = "" - elif name == "step": - self._rrd_parse_state = 3 # In <rrd><step> - self._rrd_capture = True - self._rrd_chars = "" - elif name == "ds": - self._rrd_parse_state = 4 # In <rrd><ds> - self._rrd_ds_val = {} - self._rrd_chars = "" - elif name == "rra": - self._rrd_parse_state = 5 # In <rrd><rra> - elif self._rrd_level == 3: - if self._rrd_parse_state == 4: - if name == "name": - self._rrd_parse_state = 6 # In <rrd><ds><name> - self._rrd_capture = True - self._rrd_chars = "" - elif name == "type": - self._rrd_parse_state = 7 # In <rrd><ds><type> - self._rrd_capture = True - self._rrd_chars = "" - elif self._rrd_parse_state == 5: - if name == "database": - self._rrd_parse_state = 8 # In <rrd><rra><database> - elif self._rrd_level == 4: - if self._rrd_parse_state == 8: - if name == "row": - self._rrd_parse_state = 9 # In <rrd><rra><database><row> - self._rrd_data_row = [] - self._rrd_data_row_has_nan = False - elif self._rrd_level == 5: - if self._rrd_parse_state == 9: - if name == "v": - self._rrd_parse_state = 10 # In <rrd><rra><database><row><v> - self._rrd_capture = True - self._rrd_chars = "" - - def endElement(self, name): - self._rrd_capture = False - if self._rrd_parse_state == 10: - self._rrd_capture = self._rrd_level == 6 - if self._rrd_level == 5: - if self._rrd_chars == "NaN": - self._rrd_data_row_has_nan = True - else: - self._rrd_data_row.append(self._rrd_chars) - self._rrd_parse_state = 9 # In <rrd><rra><database><row> - elif self._rrd_parse_state == 9: - if self._rrd_level == 4: - if not self._rrd_data_row_has_nan: - self.rrd_records.append(self._rrd_data_row) - self._rrd_parse_state = 8 # In <rrd><rra><database> - elif self._rrd_parse_state == 8: - if self._rrd_level == 3: - self._rrd_parse_state = 5 # In <rrd><rra> - elif self._rrd_parse_state == 7: - if self._rrd_level == 3: - self._rrd_ds_val["type"] = self._rrd_chars - self._rrd_parse_state = 4 # In <rrd><ds> - elif self._rrd_parse_state == 6: - if self._rrd_level == 3: - self._rrd_ds_val["name"] = self._rrd_chars - self._rrd_parse_state = 4 # In <rrd><ds> - elif self._rrd_parse_state == 5: - if self._rrd_level == 2: - self._rrd_parse_state = 1 # In <rrd> - elif self._rrd_parse_state == 4: - if self._rrd_level == 2: - self.rrd_ds.append(self._rrd_ds_val) - self._rrd_parse_state = 1 # In <rrd> - elif self._rrd_parse_state == 3: - if self._rrd_level == 2: - self.rrd_step = long(self._rrd_chars) - self._rrd_parse_state = 1 # In <rrd> - elif self._rrd_parse_state == 2: - if self._rrd_level == 2: - self.rrd_last_update = long(self._rrd_chars) - self._rrd_parse_state = 1 # In <rrd> - elif self._rrd_parse_state == 1: - if self._rrd_level == 1: - self._rrd_parse_state = 0 # At top - - if self._rrd_level: - self._rrd_level = self._rrd_level - 1 - - def characters(self, content): - if self._rrd_capture: - self._rrd_chars = self._rrd_chars + content.strip() - -def _make_numeric(value): - try: - value = float(value) - except: - value = "" - return value - -def simperf_host_xml_fixup(parser, filter_start_time = None, filter_end_time = None): - # Fixup for GAUGE fields that are really COUNTS. They - # were forced to GAUGE to try to disable rrdtool's - # data interpolation/extrapolation for non-uniform time - # samples. - fixup_tags = [ "cpu_user", - "cpu_nice", - "cpu_sys", - "cpu_idle", - "cpu_waitio", - "cpu_intr", - # "file_active", - # "file_free", - # "inode_active", - # "inode_free", - "netif_in_kb", - "netif_in_pkts", - "netif_in_errs", - "netif_in_drop", - "netif_out_kb", - "netif_out_pkts", - "netif_out_errs", - "netif_out_drop", - "vm_page_in", - "vm_page_out", - "vm_swap_in", - "vm_swap_out", - #"vm_mem_total", - #"vm_mem_used", - #"vm_mem_active", - #"vm_mem_inactive", - #"vm_mem_free", - #"vm_mem_buffer", - #"vm_swap_cache", - #"vm_swap_total", - #"vm_swap_used", - #"vm_swap_free", - "cpu_interrupts", - "cpu_switches", - "cpu_forks" ] - - col_count = len(parser.rrd_ds) - row_count = len(parser.rrd_records) - - # Process the last row separately, just to make all values numeric. - for j in range(col_count): - parser.rrd_records[row_count - 1][j] = _make_numeric(parser.rrd_records[row_count - 1][j]) - - # Process all other row/columns. - last_different_row = row_count - 1 - current_row = row_count - 2 - while current_row >= 0: - # Check for a different value than the previous row. If everything is the same - # then this is probably just a filler/bogus entry. - is_different = False - for j in range(col_count): - parser.rrd_records[current_row][j] = _make_numeric(parser.rrd_records[current_row][j]) - if parser.rrd_records[current_row][j] != parser.rrd_records[last_different_row][j]: - # We're good. This is a different row. - is_different = True - - if not is_different: - # This is a filler/bogus entry. Just ignore it. - for j in range(col_count): - parser.rrd_records[current_row][j] = float('nan') - else: - # Some tags need to be converted into deltas. - for j in range(col_count): - if parser.rrd_ds[j]["name"] in fixup_tags: - parser.rrd_records[last_different_row][j] = \ - parser.rrd_records[last_different_row][j] - parser.rrd_records[current_row][j] - last_different_row = current_row - - current_row -= 1 - - # Set fixup_tags in the first row to 'nan' since they aren't useful anymore. - for j in range(col_count): - if parser.rrd_ds[j]["name"] in fixup_tags: - parser.rrd_records[0][j] = float('nan') - - # Add a timestamp to each row and to the catalog. Format and name - # chosen to match other simulator logging (hopefully). - start_time = parser.rrd_last_update - (parser.rrd_step * (row_count - 1)) - # Build a filtered list of rrd_records if we are limited to a time range. - filter_records = False - if filter_start_time is not None or filter_end_time is not None: - filter_records = True - filtered_rrd_records = [] - if filter_start_time is None: - filter_start_time = start_time * 1000 - if filter_end_time is None: - filter_end_time = parser.rrd_last_update * 1000 - - for i in range(row_count): - record_timestamp = (start_time + (i * parser.rrd_step)) * 1000 - parser.rrd_records[i].insert(0, record_timestamp) - if filter_records: - if filter_start_time <= record_timestamp and record_timestamp <= filter_end_time: - filtered_rrd_records.append(parser.rrd_records[i]) - - if filter_records: - parser.rrd_records = filtered_rrd_records - - parser.rrd_ds.insert(0, {"type": "GAUGE", "name": "javascript_timestamp"}) - - -def main(argv=None): - opts, args = getopt.getopt(sys.argv[1:], "i:o:h", ["in=", "out=", "help"]) - input_file = sys.stdin - output_file = sys.stdout - for o, a in opts: - if o in ("-i", "--in"): - input_file = open(a, 'r') - if o in ("-o", "--out"): - output_file = open(a, 'w') - if o in ("-h", "--help"): - usage() - sys.exit(0) - - # Using the SAX parser as it is at least 4X faster and far, far - # smaller on this dataset than the DOM-based interface in xml.dom.minidom. - # With SAX and a 5.4MB xml file, this requires about seven seconds of - # wall-clock time and 32MB VSZ. With the DOM interface, about 22 seconds - # and over 270MB VSZ. - - handler = SimPerfHostXMLParser() - sax.parse(input_file, handler) - if input_file != sys.stdin: - input_file.close() - - # Various format fixups: string-to-num, gauge-to-counts, add - # a time stamp, etc. - simperf_host_xml_fixup(handler) - - # Create JSONable dict with interesting data and format/print it - print >>output_file, simplejson.dumps({ "step" : handler.rrd_step, - "lastupdate": handler.rrd_last_update * 1000, - "ds" : handler.rrd_ds, - "database" : handler.rrd_records }) - - return 0 - -if __name__ == "__main__": - sys.exit(main()) diff --git a/indra/lib/python/indra/util/simperf_oprof_interface.py b/indra/lib/python/indra/util/simperf_oprof_interface.py deleted file mode 100755 index 547d2f9980..0000000000 --- a/indra/lib/python/indra/util/simperf_oprof_interface.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/env python -"""\ -@file simperf_oprof_interface.py -@brief Manage OProfile data collection on a host - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import sys, os, getopt -import simplejson - - -def usage(): - print "Usage:" - print sys.argv[0] + " [options]" - print " Digest the OProfile report forms that come out of the" - print " simperf_oprof_ctl program's -r/--report command. The result" - print " is an array of dictionaires with the following keys:" - print - print " symbol Name of sampled, calling, or called procedure" - print " file Executable or library where symbol resides" - print " percentage Percentage contribution to profile, calls or called" - print " samples Sample count" - print " calls Methods called by the method in question (full only)" - print " called_by Methods calling the method (full only)" - print - print " For 'full' reports the two keys 'calls' and 'called_by' are" - print " themselves arrays of dictionaries based on the first four keys." - print - print "Return Codes:" - print " None. Aggressively digests everything. Will likely mung results" - print " if a program or library has whitespace in its name." - print - print "Options:" - print " -i, --in Input settings filename. (Default: stdin)" - print " -o, --out Output settings filename. (Default: stdout)" - print " -h, --help Print this message and exit." - print - print "Interfaces:" - print " class SimPerfOProfileInterface()" - -class SimPerfOProfileInterface: - def __init__(self): - self.isBrief = True # public - self.isValid = False # public - self.result = [] # public - - def parse(self, input): - in_samples = False - for line in input: - if in_samples: - if line[0:6] == "------": - self.isBrief = False - self._parseFull(input) - else: - self._parseBrief(input, line) - self.isValid = True - return - try: - hd1, remain = line.split(None, 1) - if hd1 == "samples": - in_samples = True - except ValueError: - pass - - def _parseBrief(self, input, line1): - try: - fld1, fld2, fld3, fld4 = line1.split(None, 3) - self.result.append({"samples" : fld1, - "percentage" : fld2, - "file" : fld3, - "symbol" : fld4.strip("\n")}) - except ValueError: - pass - for line in input: - try: - fld1, fld2, fld3, fld4 = line.split(None, 3) - self.result.append({"samples" : fld1, - "percentage" : fld2, - "file" : fld3, - "symbol" : fld4.strip("\n")}) - except ValueError: - pass - - def _parseFull(self, input): - state = 0 # In 'called_by' section - calls = [] - called_by = [] - current = {} - for line in input: - if line[0:6] == "------": - if len(current): - current["calls"] = calls - current["called_by"] = called_by - self.result.append(current) - state = 0 - calls = [] - called_by = [] - current = {} - else: - try: - fld1, fld2, fld3, fld4 = line.split(None, 3) - tmp = {"samples" : fld1, - "percentage" : fld2, - "file" : fld3, - "symbol" : fld4.strip("\n")} - except ValueError: - continue - if line[0] != " ": - current = tmp - state = 1 # In 'calls' section - elif state == 0: - called_by.append(tmp) - else: - calls.append(tmp) - if len(current): - current["calls"] = calls - current["called_by"] = called_by - self.result.append(current) - - -def main(argv=None): - opts, args = getopt.getopt(sys.argv[1:], "i:o:h", ["in=", "out=", "help"]) - input_file = sys.stdin - output_file = sys.stdout - for o, a in opts: - if o in ("-i", "--in"): - input_file = open(a, 'r') - if o in ("-o", "--out"): - output_file = open(a, 'w') - if o in ("-h", "--help"): - usage() - sys.exit(0) - - oprof = SimPerfOProfileInterface() - oprof.parse(input_file) - if input_file != sys.stdin: - input_file.close() - - # Create JSONable dict with interesting data and format/print it - print >>output_file, simplejson.dumps(oprof.result) - - return 0 - -if __name__ == "__main__": - sys.exit(main()) diff --git a/indra/lib/python/indra/util/simperf_proc_interface.py b/indra/lib/python/indra/util/simperf_proc_interface.py deleted file mode 100755 index de061f68cc..0000000000 --- a/indra/lib/python/indra/util/simperf_proc_interface.py +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/env python -"""\ -@file simperf_proc_interface.py -@brief Utility to extract log messages from *.<pid>.llsd files containing performance statistics. - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -# ---------------------------------------------------- -# Utility to extract log messages from *.<pid>.llsd -# files that contain performance statistics. - -# ---------------------------------------------------- -import sys, os - -if os.path.exists("setup-path.py"): - execfile("setup-path.py") - -from indra.base import llsd - -DEFAULT_PATH="/dev/shm/simperf/" - - -# ---------------------------------------------------- -# Pull out the stats and return a single document -def parse_logfile(filename, target_column=None, verbose=False): - full_doc = [] - # Open source temp log file. Let exceptions percolate up. - sourcefile = open( filename,'r') - - if verbose: - print "Reading " + filename - - # Parse and output all lines from the temp file - for line in sourcefile.xreadlines(): - partial_doc = llsd.parse(line) - if partial_doc is not None: - if target_column is None: - full_doc.append(partial_doc) - else: - trim_doc = { target_column: partial_doc[target_column] } - if target_column != "fps": - trim_doc[ 'fps' ] = partial_doc[ 'fps' ] - trim_doc[ '/total_time' ] = partial_doc[ '/total_time' ] - trim_doc[ 'utc_time' ] = partial_doc[ 'utc_time' ] - full_doc.append(trim_doc) - - sourcefile.close() - return full_doc - -# Extract just the meta info line, and the timestamp of the first/last frame entry. -def parse_logfile_info(filename, verbose=False): - # Open source temp log file. Let exceptions percolate up. - sourcefile = open(filename, 'rU') # U is to open with Universal newline support - - if verbose: - print "Reading " + filename - - # The first line is the meta info line. - info_line = sourcefile.readline() - if not info_line: - sourcefile.close() - return None - - # The rest of the lines are frames. Read the first and last to get the time range. - info = llsd.parse( info_line ) - info['start_time'] = None - info['end_time'] = None - first_frame = sourcefile.readline() - if first_frame: - try: - info['start_time'] = int(llsd.parse(first_frame)['timestamp']) - except: - pass - - # Read the file backwards to find the last two lines. - sourcefile.seek(0, 2) - file_size = sourcefile.tell() - offset = 1024 - num_attempts = 0 - end_time = None - if file_size < offset: - offset = file_size - while 1: - sourcefile.seek(-1*offset, 2) - read_str = sourcefile.read(offset) - # Remove newline at the end - if read_str[offset - 1] == '\n': - read_str = read_str[0:-1] - lines = read_str.split('\n') - full_line = None - if len(lines) > 2: # Got two line - try: - end_time = llsd.parse(lines[-1])['timestamp'] - except: - # We couldn't parse this line. Try once more. - try: - end_time = llsd.parse(lines[-2])['timestamp'] - except: - # Nope. Just move on. - pass - break - if len(read_str) == file_size: # Reached the beginning - break - offset += 1024 - - info['end_time'] = int(end_time) - - sourcefile.close() - return info - - -def parse_proc_filename(filename): - try: - name_as_list = filename.split(".") - cur_stat_type = name_as_list[0].split("_")[0] - cur_pid = name_as_list[1] - except IndexError, ValueError: - return (None, None) - return (cur_pid, cur_stat_type) - -# ---------------------------------------------------- -def get_simstats_list(path=None): - """ Return stats (pid, type) listed in <type>_proc.<pid>.llsd """ - if path is None: - path = DEFAULT_PATH - simstats_list = [] - for file_name in os.listdir(path): - if file_name.endswith(".llsd") and file_name != "simperf_proc_config.llsd": - simstats_info = parse_logfile_info(path + file_name) - if simstats_info is not None: - simstats_list.append(simstats_info) - return simstats_list - -def get_log_info_list(pid=None, stat_type=None, path=None, target_column=None, verbose=False): - """ Return data from all llsd files matching the pid and stat type """ - if path is None: - path = DEFAULT_PATH - log_info_list = {} - for file_name in os.listdir ( path ): - if file_name.endswith(".llsd") and file_name != "simperf_proc_config.llsd": - (cur_pid, cur_stat_type) = parse_proc_filename(file_name) - if cur_pid is None: - continue - if pid is not None and pid != cur_pid: - continue - if stat_type is not None and stat_type != cur_stat_type: - continue - log_info_list[cur_pid] = parse_logfile(path + file_name, target_column, verbose) - return log_info_list - -def delete_simstats_files(pid=None, stat_type=None, path=None): - """ Delete *.<pid>.llsd files """ - if path is None: - path = DEFAULT_PATH - del_list = [] - for file_name in os.listdir(path): - if file_name.endswith(".llsd") and file_name != "simperf_proc_config.llsd": - (cur_pid, cur_stat_type) = parse_proc_filename(file_name) - if cur_pid is None: - continue - if pid is not None and pid != cur_pid: - continue - if stat_type is not None and stat_type != cur_stat_type: - continue - del_list.append(cur_pid) - # Allow delete related exceptions to percolate up if this fails. - os.unlink(os.path.join(DEFAULT_PATH, file_name)) - return del_list - diff --git a/indra/lib/python/indra/util/term.py b/indra/lib/python/indra/util/term.py deleted file mode 100755 index 8c316a1f12..0000000000 --- a/indra/lib/python/indra/util/term.py +++ /dev/null @@ -1,222 +0,0 @@ -''' -@file term.py -@brief a better shutil.copytree replacement - -$LicenseInfo:firstyear=2007&license=mit$ - -Copyright (c) 2007-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -''' - -#http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475116 - -import sys, re - -class TerminalController: - """ - A class that can be used to portably generate formatted output to - a terminal. - - `TerminalController` defines a set of instance variables whose - values are initialized to the control sequence necessary to - perform a given action. These can be simply included in normal - output to the terminal: - - >>> term = TerminalController() - >>> print 'This is '+term.GREEN+'green'+term.NORMAL - - Alternatively, the `render()` method can used, which replaces - '${action}' with the string required to perform 'action': - - >>> term = TerminalController() - >>> print term.render('This is ${GREEN}green${NORMAL}') - - If the terminal doesn't support a given action, then the value of - the corresponding instance variable will be set to ''. As a - result, the above code will still work on terminals that do not - support color, except that their output will not be colored. - Also, this means that you can test whether the terminal supports a - given action by simply testing the truth value of the - corresponding instance variable: - - >>> term = TerminalController() - >>> if term.CLEAR_SCREEN: - ... print 'This terminal supports clearning the screen.' - - Finally, if the width and height of the terminal are known, then - they will be stored in the `COLS` and `LINES` attributes. - """ - # Cursor movement: - BOL = '' #: Move the cursor to the beginning of the line - UP = '' #: Move the cursor up one line - DOWN = '' #: Move the cursor down one line - LEFT = '' #: Move the cursor left one char - RIGHT = '' #: Move the cursor right one char - - # Deletion: - CLEAR_SCREEN = '' #: Clear the screen and move to home position - CLEAR_EOL = '' #: Clear to the end of the line. - CLEAR_BOL = '' #: Clear to the beginning of the line. - CLEAR_EOS = '' #: Clear to the end of the screen - - # Output modes: - BOLD = '' #: Turn on bold mode - BLINK = '' #: Turn on blink mode - DIM = '' #: Turn on half-bright mode - REVERSE = '' #: Turn on reverse-video mode - NORMAL = '' #: Turn off all modes - - # Cursor display: - HIDE_CURSOR = '' #: Make the cursor invisible - SHOW_CURSOR = '' #: Make the cursor visible - - # Terminal size: - COLS = None #: Width of the terminal (None for unknown) - LINES = None #: Height of the terminal (None for unknown) - - # Foreground colors: - BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' - - # Background colors: - BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = '' - BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = '' - - _STRING_CAPABILITIES = """ - BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 - CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold - BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0 - HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split() - _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split() - _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split() - - def __init__(self, term_stream=sys.stdout): - """ - Create a `TerminalController` and initialize its attributes - with appropriate values for the current terminal. - `term_stream` is the stream that will be used for terminal - output; if this stream is not a tty, then the terminal is - assumed to be a dumb terminal (i.e., have no capabilities). - """ - # Curses isn't available on all platforms - try: import curses - except: return - - # If the stream isn't a tty, then assume it has no capabilities. - if not term_stream.isatty(): return - - # Check the terminal type. If we fail, then assume that the - # terminal has no capabilities. - try: curses.setupterm() - except: return - - # Look up numeric capabilities. - self.COLS = curses.tigetnum('cols') - self.LINES = curses.tigetnum('lines') - - # Look up string capabilities. - for capability in self._STRING_CAPABILITIES: - (attrib, cap_name) = capability.split('=') - setattr(self, attrib, self._tigetstr(cap_name) or '') - - # Colors - set_fg = self._tigetstr('setf') - if set_fg: - for i,color in zip(range(len(self._COLORS)), self._COLORS): - setattr(self, color, curses.tparm(set_fg, i) or '') - set_fg_ansi = self._tigetstr('setaf') - if set_fg_ansi: - for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): - setattr(self, color, curses.tparm(set_fg_ansi, i) or '') - set_bg = self._tigetstr('setb') - if set_bg: - for i,color in zip(range(len(self._COLORS)), self._COLORS): - setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '') - set_bg_ansi = self._tigetstr('setab') - if set_bg_ansi: - for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): - setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '') - - def _tigetstr(self, cap_name): - # String capabilities can include "delays" of the form "$<2>". - # For any modern terminal, we should be able to just ignore - # these, so strip them out. - import curses - cap = curses.tigetstr(cap_name) or '' - return re.sub(r'\$<\d+>[/*]?', '', cap) - - def render(self, template): - """ - Replace each $-substitutions in the given template string with - the corresponding terminal control string (if it's defined) or - '' (if it's not). - """ - return re.sub(r'\$\$|\${\w+}', self._render_sub, template) - - def _render_sub(self, match): - s = match.group() - if s == '$$': return s - else: return getattr(self, s[2:-1]) - -####################################################################### -# Example use case: progress bar -####################################################################### - -class ProgressBar: - """ - A 3-line progress bar, which looks like:: - - Header - 20% [===========----------------------------------] - progress message - - The progress bar is colored, if the terminal supports color - output; and adjusts to the width of the terminal. - """ - BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}\n' - HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n' - - def __init__(self, term, header): - self.term = term - if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL): - raise ValueError("Terminal isn't capable enough -- you " - "should use a simpler progress dispaly.") - self.width = self.term.COLS or 75 - self.bar = term.render(self.BAR) - self.header = self.term.render(self.HEADER % header.center(self.width)) - self.cleared = 1 #: true if we haven't drawn the bar yet. - self.update(0, '') - - def update(self, percent, message): - if self.cleared: - sys.stdout.write(self.header) - self.cleared = 0 - n = int((self.width-10)*percent) - sys.stdout.write( - self.term.BOL + self.term.UP + self.term.CLEAR_EOL + - (self.bar % (100*percent, '='*n, '-'*(self.width-10-n))) + - self.term.CLEAR_EOL + message.center(self.width)) - - def clear(self): - if not self.cleared: - sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL + - self.term.UP + self.term.CLEAR_EOL + - self.term.UP + self.term.CLEAR_EOL) - self.cleared = 1 diff --git a/indra/lib/python/uuid.py b/indra/lib/python/uuid.py deleted file mode 100755 index e956383cca..0000000000 --- a/indra/lib/python/uuid.py +++ /dev/null @@ -1,508 +0,0 @@ -#!/usr/bin/python -## $LicenseInfo:firstyear=2011&license=viewerlgpl$ -## Second Life Viewer Source Code -## Copyright (C) 2011, Linden Research, Inc. -## -## This library is free software; you can redistribute it and/or -## modify it under the terms of the GNU Lesser General Public -## License as published by the Free Software Foundation; -## version 2.1 of the License only. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public -## License along with this library; if not, write to the Free Software -## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -## -## Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -## $/LicenseInfo$ -r"""UUID objects (universally unique identifiers) according to RFC 4122. - -This module provides immutable UUID objects (class UUID) and the functions -uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5 -UUIDs as specified in RFC 4122. - -If all you want is a unique ID, you should probably call uuid1() or uuid4(). -Note that uuid1() may compromise privacy since it creates a UUID containing -the computer's network address. uuid4() creates a random UUID. - -Typical usage: - - >>> import uuid - - # make a UUID based on the host ID and current time - >>> uuid.uuid1() - UUID('a8098c1a-f86e-11da-bd1a-00112444be1e') - - # make a UUID using an MD5 hash of a namespace UUID and a name - >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org') - UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e') - - # make a random UUID - >>> uuid.uuid4() - UUID('16fd2706-8baf-433b-82eb-8c7fada847da') - - # make a UUID using a SHA-1 hash of a namespace UUID and a name - >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org') - UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d') - - # make a UUID from a string of hex digits (braces and hyphens ignored) - >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}') - - # convert a UUID to a string of hex digits in standard form - >>> str(x) - '00010203-0405-0607-0809-0a0b0c0d0e0f' - - # get the raw 16 bytes of the UUID - >>> x.bytes - '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f' - - # make a UUID from a 16-byte string - >>> uuid.UUID(bytes=x.bytes) - UUID('00010203-0405-0607-0809-0a0b0c0d0e0f') - -This module works with Python 2.3 or higher.""" - -__author__ = 'Ka-Ping Yee <ping@zesty.ca>' -__date__ = '$Date: 2006/06/12 23:15:40 $'.split()[1].replace('/', '-') -__version__ = '$Revision: 1.30 $'.split()[1] - -RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [ - 'reserved for NCS compatibility', 'specified in RFC 4122', - 'reserved for Microsoft compatibility', 'reserved for future definition'] - -class UUID(object): - """Instances of the UUID class represent UUIDs as specified in RFC 4122. - UUID objects are immutable, hashable, and usable as dictionary keys. - Converting a UUID to a string with str() yields something in the form - '12345678-1234-1234-1234-123456789abc'. The UUID constructor accepts - four possible forms: a similar string of hexadecimal digits, or a - string of 16 raw bytes as an argument named 'bytes', or a tuple of - six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and - 48-bit values respectively) as an argument named 'fields', or a single - 128-bit integer as an argument named 'int'. - - UUIDs have these read-only attributes: - - bytes the UUID as a 16-byte string - - fields a tuple of the six integer fields of the UUID, - which are also available as six individual attributes - and two derived attributes: - - time_low the first 32 bits of the UUID - time_mid the next 16 bits of the UUID - time_hi_version the next 16 bits of the UUID - clock_seq_hi_variant the next 8 bits of the UUID - clock_seq_low the next 8 bits of the UUID - node the last 48 bits of the UUID - - time the 60-bit timestamp - clock_seq the 14-bit sequence number - - hex the UUID as a 32-character hexadecimal string - - int the UUID as a 128-bit integer - - urn the UUID as a URN as specified in RFC 4122 - - variant the UUID variant (one of the constants RESERVED_NCS, - RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE) - - version the UUID version number (1 through 5, meaningful only - when the variant is RFC_4122) - """ - - def __init__(self, hex=None, bytes=None, fields=None, int=None, - version=None): - r"""Create a UUID from either a string of 32 hexadecimal digits, - a string of 16 bytes as the 'bytes' argument, a tuple of six - integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version, - 8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as - the 'fields' argument, or a single 128-bit integer as the 'int' - argument. When a string of hex digits is given, curly braces, - hyphens, and a URN prefix are all optional. For example, these - expressions all yield the same UUID: - - UUID('{12345678-1234-5678-1234-567812345678}') - UUID('12345678123456781234567812345678') - UUID('urn:uuid:12345678-1234-5678-1234-567812345678') - UUID(bytes='\x12\x34\x56\x78'*4) - UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678)) - UUID(int=0x12345678123456781234567812345678) - - Exactly one of 'hex', 'bytes', 'fields', or 'int' must be given. - The 'version' argument is optional; if given, the resulting UUID - will have its variant and version number set according to RFC 4122, - overriding bits in the given 'hex', 'bytes', 'fields', or 'int'. - """ - - if [hex, bytes, fields, int].count(None) != 3: - raise TypeError('need just one of hex, bytes, fields, or int') - if hex is not None: - hex = hex.replace('urn:', '').replace('uuid:', '') - hex = hex.strip('{}').replace('-', '') - if len(hex) != 32: - raise ValueError('badly formed hexadecimal UUID string') - int = long(hex, 16) - if bytes is not None: - if len(bytes) != 16: - raise ValueError('bytes is not a 16-char string') - int = long(('%02x'*16) % tuple(map(ord, bytes)), 16) - if fields is not None: - if len(fields) != 6: - raise ValueError('fields is not a 6-tuple') - (time_low, time_mid, time_hi_version, - clock_seq_hi_variant, clock_seq_low, node) = fields - if not 0 <= time_low < 1<<32L: - raise ValueError('field 1 out of range (need a 32-bit value)') - if not 0 <= time_mid < 1<<16L: - raise ValueError('field 2 out of range (need a 16-bit value)') - if not 0 <= time_hi_version < 1<<16L: - raise ValueError('field 3 out of range (need a 16-bit value)') - if not 0 <= clock_seq_hi_variant < 1<<8L: - raise ValueError('field 4 out of range (need an 8-bit value)') - if not 0 <= clock_seq_low < 1<<8L: - raise ValueError('field 5 out of range (need an 8-bit value)') - if not 0 <= node < 1<<48L: - raise ValueError('field 6 out of range (need a 48-bit value)') - clock_seq = (clock_seq_hi_variant << 8L) | clock_seq_low - int = ((time_low << 96L) | (time_mid << 80L) | - (time_hi_version << 64L) | (clock_seq << 48L) | node) - if int is not None: - if not 0 <= int < 1<<128L: - raise ValueError('int is out of range (need a 128-bit value)') - if version is not None: - if not 1 <= version <= 5: - raise ValueError('illegal version number') - # Set the variant to RFC 4122. - int &= ~(0xc000 << 48L) - int |= 0x8000 << 48L - # Set the version number. - int &= ~(0xf000 << 64L) - int |= version << 76L - self.__dict__['int'] = int - - def __cmp__(self, other): - if isinstance(other, UUID): - return cmp(self.int, other.int) - return NotImplemented - - def __hash__(self): - return hash(self.int) - - def __int__(self): - return self.int - - def __repr__(self): - return 'UUID(%r)' % str(self) - - def __setattr__(self, name, value): - raise TypeError('UUID objects are immutable') - - def __str__(self): - hex = '%032x' % self.int - return '%s-%s-%s-%s-%s' % ( - hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:]) - - def get_bytes(self): - bytes = '' - for shift in range(0, 128, 8): - bytes = chr((self.int >> shift) & 0xff) + bytes - return bytes - - bytes = property(get_bytes) - - def get_fields(self): - return (self.time_low, self.time_mid, self.time_hi_version, - self.clock_seq_hi_variant, self.clock_seq_low, self.node) - - fields = property(get_fields) - - def get_time_low(self): - return self.int >> 96L - - time_low = property(get_time_low) - - def get_time_mid(self): - return (self.int >> 80L) & 0xffff - - time_mid = property(get_time_mid) - - def get_time_hi_version(self): - return (self.int >> 64L) & 0xffff - - time_hi_version = property(get_time_hi_version) - - def get_clock_seq_hi_variant(self): - return (self.int >> 56L) & 0xff - - clock_seq_hi_variant = property(get_clock_seq_hi_variant) - - def get_clock_seq_low(self): - return (self.int >> 48L) & 0xff - - clock_seq_low = property(get_clock_seq_low) - - def get_time(self): - return (((self.time_hi_version & 0x0fffL) << 48L) | - (self.time_mid << 32L) | self.time_low) - - time = property(get_time) - - def get_clock_seq(self): - return (((self.clock_seq_hi_variant & 0x3fL) << 8L) | - self.clock_seq_low) - - clock_seq = property(get_clock_seq) - - def get_node(self): - return self.int & 0xffffffffffff - - node = property(get_node) - - def get_hex(self): - return '%032x' % self.int - - hex = property(get_hex) - - def get_urn(self): - return 'urn:uuid:' + str(self) - - urn = property(get_urn) - - def get_variant(self): - if not self.int & (0x8000 << 48L): - return RESERVED_NCS - elif not self.int & (0x4000 << 48L): - return RFC_4122 - elif not self.int & (0x2000 << 48L): - return RESERVED_MICROSOFT - else: - return RESERVED_FUTURE - - variant = property(get_variant) - - def get_version(self): - # The version bits are only meaningful for RFC 4122 UUIDs. - if self.variant == RFC_4122: - return int((self.int >> 76L) & 0xf) - - version = property(get_version) - -def _ifconfig_getnode(): - """Get the hardware address on Unix by running ifconfig.""" - import os - for dir in ['', '/sbin/', '/usr/sbin']: - try: - path = os.path.join(dir, 'ifconfig') - if os.path.exists(path): - pipe = os.popen(path) - else: - continue - except IOError: - continue - for line in pipe: - words = line.lower().split() - for i in range(len(words)): - if words[i] in ['hwaddr', 'ether']: - return int(words[i + 1].replace(':', ''), 16) - -def _ipconfig_getnode(): - """Get the hardware address on Windows by running ipconfig.exe.""" - import os, re - dirs = ['', r'c:\windows\system32', r'c:\winnt\system32'] - try: - import ctypes - buffer = ctypes.create_string_buffer(300) - ctypes.windll.kernel32.GetSystemDirectoryA(buffer, 300) - dirs.insert(0, buffer.value.decode('mbcs')) - except: - pass - for dir in dirs: - try: - pipe = os.popen(os.path.join(dir, 'ipconfig') + ' /all') - except IOError: - continue - for line in pipe: - value = line.split(':')[-1].strip().lower() - if re.match('([0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value): - return int(value.replace('-', ''), 16) - -def _netbios_getnode(): - """Get the hardware address on Windows using NetBIOS calls. - See http://support.microsoft.com/kb/118623 for details.""" - import win32wnet, netbios - ncb = netbios.NCB() - ncb.Command = netbios.NCBENUM - ncb.Buffer = adapters = netbios.LANA_ENUM() - adapters._pack() - if win32wnet.Netbios(ncb) != 0: - return - adapters._unpack() - for i in range(adapters.length): - ncb.Reset() - ncb.Command = netbios.NCBRESET - ncb.Lana_num = ord(adapters.lana[i]) - if win32wnet.Netbios(ncb) != 0: - continue - ncb.Reset() - ncb.Command = netbios.NCBASTAT - ncb.Lana_num = ord(adapters.lana[i]) - ncb.Callname = '*'.ljust(16) - ncb.Buffer = status = netbios.ADAPTER_STATUS() - if win32wnet.Netbios(ncb) != 0: - continue - status._unpack() - bytes = map(ord, status.adapter_address) - return ((bytes[0]<<40L) + (bytes[1]<<32L) + (bytes[2]<<24L) + - (bytes[3]<<16L) + (bytes[4]<<8L) + bytes[5]) - -# Thanks to Thomas Heller for ctypes and for his help with its use here. - -# If ctypes is available, use it to find system routines for UUID generation. -_uuid_generate_random = _uuid_generate_time = _UuidCreate = None -try: - import ctypes, ctypes.util - _buffer = ctypes.create_string_buffer(16) - - # The uuid_generate_* routines are provided by libuuid on at least - # Linux and FreeBSD, and provided by libc on Mac OS X. - for libname in ['uuid', 'c']: - try: - lib = ctypes.CDLL(ctypes.util.find_library(libname)) - except: - continue - if hasattr(lib, 'uuid_generate_random'): - _uuid_generate_random = lib.uuid_generate_random - if hasattr(lib, 'uuid_generate_time'): - _uuid_generate_time = lib.uuid_generate_time - - # On Windows prior to 2000, UuidCreate gives a UUID containing the - # hardware address. On Windows 2000 and later, UuidCreate makes a - # random UUID and UuidCreateSequential gives a UUID containing the - # hardware address. These routines are provided by the RPC runtime. - try: - lib = ctypes.windll.rpcrt4 - except: - lib = None - _UuidCreate = getattr(lib, 'UuidCreateSequential', - getattr(lib, 'UuidCreate', None)) -except: - pass - -def _unixdll_getnode(): - """Get the hardware address on Unix using ctypes.""" - _uuid_generate_time(_buffer) - return UUID(bytes=_buffer.raw).node - -def _windll_getnode(): - """Get the hardware address on Windows using ctypes.""" - if _UuidCreate(_buffer) == 0: - return UUID(bytes=_buffer.raw).node - -def _random_getnode(): - """Get a random node ID, with eighth bit set as suggested by RFC 4122.""" - import random - return random.randrange(0, 1<<48L) | 0x010000000000L - -_node = None - -def getnode(): - """Get the hardware address as a 48-bit integer. The first time this - runs, it may launch a separate program, which could be quite slow. If - all attempts to obtain the hardware address fail, we choose a random - 48-bit number with its eighth bit set to 1 as recommended in RFC 4122.""" - - global _node - if _node is not None: - return _node - - import sys - if sys.platform == 'win32': - getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] - else: - getters = [_unixdll_getnode, _ifconfig_getnode] - - for getter in getters + [_random_getnode]: - try: - _node = getter() - except: - continue - if _node is not None: - return _node - -def uuid1(node=None, clock_seq=None): - """Generate a UUID from a host ID, sequence number, and the current time. - If 'node' is not given, getnode() is used to obtain the hardware - address. If 'clock_seq' is given, it is used as the sequence number; - otherwise a random 14-bit sequence number is chosen.""" - - # When the system provides a version-1 UUID generator, use it (but don't - # use UuidCreate here because its UUIDs don't conform to RFC 4122). - if _uuid_generate_time and node is clock_seq is None: - _uuid_generate_time(_buffer) - return UUID(bytes=_buffer.raw) - - import time - nanoseconds = int(time.time() * 1e9) - # 0x01b21dd213814000 is the number of 100-ns intervals between the - # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. - timestamp = int(nanoseconds/100) + 0x01b21dd213814000L - if clock_seq is None: - import random - clock_seq = random.randrange(1<<14L) # instead of stable storage - time_low = timestamp & 0xffffffffL - time_mid = (timestamp >> 32L) & 0xffffL - time_hi_version = (timestamp >> 48L) & 0x0fffL - clock_seq_low = clock_seq & 0xffL - clock_seq_hi_variant = (clock_seq >> 8L) & 0x3fL - if node is None: - node = getnode() - return UUID(fields=(time_low, time_mid, time_hi_version, - clock_seq_hi_variant, clock_seq_low, node), version=1) - -def uuid3(namespace, name): - """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" - try: - # Python 2.6 - from hashlib import md5 - except ImportError: - # Python 2.5 and earlier - from md5 import new as md5 - - hash = md5(namespace.bytes + name).digest() - return UUID(bytes=hash[:16], version=3) - -def uuid4(): - """Generate a random UUID.""" - - # When the system provides a version-4 UUID generator, use it. - if _uuid_generate_random: - _uuid_generate_random(_buffer) - return UUID(bytes=_buffer.raw) - - # Otherwise, get randomness from urandom or the 'random' module. - try: - import os - return UUID(bytes=os.urandom(16), version=4) - except: - import random - bytes = [chr(random.randrange(256)) for i in range(16)] - return UUID(bytes=bytes, version=4) - -def uuid5(namespace, name): - """Generate a UUID from the SHA-1 hash of a namespace UUID and a name.""" - import sha - hash = sha.sha(namespace.bytes + name).digest() - return UUID(bytes=hash[:16], version=5) - -# The following standard UUIDs are for use with uuid3() or uuid5(). - -NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8') -NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8') -NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8') -NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8') diff --git a/indra/linux_crash_logger/linux_crash_logger.cpp b/indra/linux_crash_logger/linux_crash_logger.cpp index 9d5ec33fed..63e5409876 100644 --- a/indra/linux_crash_logger/linux_crash_logger.cpp +++ b/indra/linux_crash_logger/linux_crash_logger.cpp @@ -51,7 +51,7 @@ int main(int argc, char **argv) return 1; } - app.mainLoop(); + app.frame(); app.cleanup(); LL_INFOS() << "Crash reporter finished normally." << LL_ENDL; return 0; diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp index e2d2e7ff26..4092d43fc5 100644 --- a/indra/linux_crash_logger/llcrashloggerlinux.cpp +++ b/indra/linux_crash_logger/llcrashloggerlinux.cpp @@ -114,7 +114,7 @@ void LLCrashLoggerLinux::gatherPlatformSpecificFiles() { } -bool LLCrashLoggerLinux::mainLoop() +bool LLCrashLoggerLinux::frame() { bool send_logs = true; if(CRASH_BEHAVIOR_ASK == getCrashBehavior()) diff --git a/indra/linux_crash_logger/llcrashloggerlinux.h b/indra/linux_crash_logger/llcrashloggerlinux.h index dae6c46651..789f6f03f5 100644 --- a/indra/linux_crash_logger/llcrashloggerlinux.h +++ b/indra/linux_crash_logger/llcrashloggerlinux.h @@ -36,7 +36,7 @@ class LLCrashLoggerLinux : public LLCrashLogger public: LLCrashLoggerLinux(void); ~LLCrashLoggerLinux(void); - virtual bool mainLoop(); + virtual bool frame(); virtual void updateApplication(const std::string& = LLStringUtil::null); virtual void gatherPlatformSpecificFiles(); virtual bool cleanup(); diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index cd201a65b4..e786dfff86 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -2149,7 +2149,7 @@ void LLKeyframeMotion::onLoadComplete(LLVFS *vfs, LLCharacter* character = *char_iter; // look for an existing instance of this motion - LLKeyframeMotion* motionp = (LLKeyframeMotion*) character->findMotion(asset_uuid); + LLKeyframeMotion* motionp = dynamic_cast<LLKeyframeMotion*> (character->findMotion(asset_uuid)); if (motionp) { if (0 == status) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 907dbab8f8..410a5819b3 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -58,6 +58,7 @@ set(llcommon_SOURCE_FILES lleventfilter.cpp llevents.cpp lleventtimer.cpp + llexception.cpp llfasttimer.cpp llfile.cpp llfindlocale.cpp @@ -157,6 +158,7 @@ set(llcommon_HEADER_FILES lleventfilter.h llevents.h lleventemitter.h + llexception.h llfasttimer.h llfile.h llfindlocale.h @@ -315,7 +317,7 @@ if (LL_TESTS) LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}") - LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}") + LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lltrace "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}") @@ -328,6 +330,11 @@ if (LL_TESTS) LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}") +## llexception_test.cpp isn't a regression test, and doesn't need to be run +## every build. It's to help a developer make implementation choices about +## throwing and catching exceptions. +##LL_ADD_INTEGRATION_TEST(llexception "" "${test_libs}") + # *TODO - reenable these once tcmalloc libs no longer break the build. #ADD_BUILD_TEST(llallocator llcommon) #ADD_BUILD_TEST(llallocator_heap_profile llcommon) diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index d9933b3d36..ff9a92b45f 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -172,12 +172,12 @@ public: virtual bool cleanup() = 0; // Override to do application cleanup // - // mainLoop() + // frame() // - // Runs the application main loop. It's assumed that when you exit - // this method, the application is in one of the cleanup states, either QUITTING or ERROR + // Pass control to the application for a single frame. Returns 'done' + // flag: if frame() returns false, it expects to be called again. // - virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit. + virtual bool frame() = 0; // Override for application body logic // // Crash logging diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp index a548c96002..86f407cdb0 100644 --- a/indra/llcommon/llapr.cpp +++ b/indra/llcommon/llapr.cpp @@ -294,9 +294,11 @@ void LLScopedLock::unlock() bool ll_apr_warn_status(apr_status_t status) { if(APR_SUCCESS == status) return false; +#if !LL_LINUX char buf[MAX_STRING]; /* Flawfinder: ignore */ apr_strerror(status, buf, sizeof(buf)); LL_WARNS("APR") << "APR: " << buf << LL_ENDL; +#endif return true; } diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index d16bf0160b..8e516d8beb 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -38,6 +38,7 @@ #include "llevents.h" #include "llerror.h" #include "stringize.h" +#include "llexception.h" // do nothing, when we need nothing done void LLCoros::no_cleanup(CoroData*) {} @@ -131,9 +132,9 @@ bool LLCoros::cleanup(const LLSD&) if ((previousCount < 5) || !(previousCount % 50)) { if (previousCount < 5) - LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; + LL_DEBUGS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; else - LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << LL_ENDL; + LL_DEBUGS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << LL_ENDL; } // The erase() call will invalidate its passed iterator value -- @@ -185,9 +186,9 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const if ((previousCount < 5) || !(previousCount % 50)) { if (previousCount < 5) - LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; + LL_DEBUGS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; else - LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL; + LL_DEBUGS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL; } @@ -223,7 +224,7 @@ std::string LLCoros::getName() const void LLCoros::setStackSize(S32 stacksize) { - LL_INFOS("LLCoros") << "Setting coroutine stack size to " << stacksize << LL_ENDL; + LL_DEBUGS("LLCoros") << "Setting coroutine stack size to " << stacksize << LL_ENDL; mStackSize = stacksize; } @@ -235,7 +236,23 @@ void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& calla // capture the 'self' param in CoroData data->mSelf = &self; // run the code the caller actually wants in the coroutine - callable(); + try + { + callable(); + } + catch (const LLContinueError&) + { + // Any uncaught exception derived from LLContinueError will be caught + // here and logged. This coroutine will terminate but the rest of the + // viewer will carry on. + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName)); + } + catch (...) + { + // Any OTHER kind of uncaught exception will cause the viewer to + // crash, hopefully informatively. + CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName)); + } // This cleanup isn't perfectly symmetrical with the way we initially set // data->mPrev, but this is our last chance to reset mCurrentCoro. sCurrentCoro.reset(data->mPrev); diff --git a/indra/llcommon/lldependencies.cpp b/indra/llcommon/lldependencies.cpp index 0e72c175cb..0d5757effd 100644 --- a/indra/llcommon/lldependencies.cpp +++ b/indra/llcommon/lldependencies.cpp @@ -40,6 +40,7 @@ #include <boost/graph/topological_sort.hpp> #include <boost/graph/exception.hpp> // other Linden headers +#include "llexception.h" LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const EdgeList& edges) const { @@ -76,7 +77,7 @@ LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const // Omit independent nodes: display only those that might contribute to // the cycle. describe(out, false); - throw Cycle(out.str()); + LLTHROW(Cycle(out.str())); } // A peculiarity of boost::topological_sort() is that it emits results in // REVERSE topological order: to get the result you want, you must diff --git a/indra/llcommon/lldependencies.h b/indra/llcommon/lldependencies.h index e0294e271b..125bd6a835 100644 --- a/indra/llcommon/lldependencies.h +++ b/indra/llcommon/lldependencies.h @@ -34,13 +34,13 @@ #include <vector> #include <set> #include <map> -#include <stdexcept> #include <iosfwd> #include <boost/iterator/transform_iterator.hpp> #include <boost/iterator/indirect_iterator.hpp> #include <boost/range/iterator_range.hpp> #include <boost/function.hpp> #include <boost/bind.hpp> +#include "llexception.h" /***************************************************************************** * Utilities @@ -106,9 +106,9 @@ public: /** * Exception thrown by sort() if there's a cycle */ - struct Cycle: public std::runtime_error + struct Cycle: public LLException { - Cycle(const std::string& what): std::runtime_error(what) {} + Cycle(const std::string& what): LLException(what) {} }; /** diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 3beef65723..7cbe4334b3 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -174,7 +174,8 @@ namespace LLError // not really a level // used to indicate that no messages should be logged }; - + // If you change ELevel, please update llvlog() macro below. + /* Macro support The classes CallSite and Log are used by the logging macros below. They are not intended for general use. @@ -305,24 +306,38 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; ///////////////////////////////// // Error Logging Macros -// See top of file for common usage. +// See top of file for common usage. ///////////////////////////////// -// this macro uses a one-shot do statement to avoid parsing errors when writing control flow statements -// without braces: -// if (condition) LL_INFOS() << "True" << LL_ENDL; else LL_INFOS()() << "False" << LL_ENDL - -#define lllog(level, once, ...) \ - do { \ - const char* tags[] = {"", ##__VA_ARGS__}; \ - ::size_t tag_count = LL_ARRAY_SIZE(tags) - 1; \ - static LLError::CallSite _site( \ - level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, once, &tags[1], tag_count);\ - if (LL_UNLIKELY(_site.shouldLog())) \ - { \ - std::ostringstream* _out = LLError::Log::out(); \ +// Instead of using LL_DEBUGS(), LL_INFOS() et al., it may be tempting to +// directly code the lllog() macro so you can pass in the LLError::ELevel as a +// variable. DON'T DO IT! The reason is that the first time control passes +// through lllog(), it initializes a local static LLError::CallSite with that +// *first* ELevel value. All subsequent visits will decide whether or not to +// emit output based on the *first* ELevel value bound into that static +// CallSite instance. Use LL_VLOGS() instead. lllog() assumes its ELevel +// argument never varies. + +// this macro uses a one-shot do statement to avoid parsing errors when +// writing control flow statements without braces: +// if (condition) LL_INFOS() << "True" << LL_ENDL; else LL_INFOS()() << "False" << LL_ENDL; + +#define lllog(level, once, ...) \ + do { \ + const char* tags[] = {"", ##__VA_ARGS__}; \ + static LLError::CallSite _site(lllog_site_args_(level, once, tags)); \ + lllog_test_() + +#define lllog_test_() \ + if (LL_UNLIKELY(_site.shouldLog())) \ + { \ + std::ostringstream* _out = LLError::Log::out(); \ (*_out) +#define lllog_site_args_(level, once, tags) \ + level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), \ + __FUNCTION__, once, &tags[1], LL_ARRAY_SIZE(tags)-1 + //Use this construct if you need to do computation in the middle of a //message: // @@ -363,4 +378,46 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; #define LL_INFOS_ONCE(...) lllog(LLError::LEVEL_INFO, true, ##__VA_ARGS__) #define LL_WARNS_ONCE(...) lllog(LLError::LEVEL_WARN, true, ##__VA_ARGS__) +// Use this if you need to pass LLError::ELevel as a variable. +#define LL_VLOGS(level, ...) llvlog(level, false, ##__VA_ARGS__) +#define LL_VLOGS_ONCE(level, ...) llvlog(level, true, ##__VA_ARGS__) + +// The problem with using lllog() with a variable level is that the first time +// through, it initializes a static CallSite instance with whatever level you +// pass. That first level is bound into the CallSite; the level parameter is +// never again examined. One approach to variable level would be to +// dynamically construct a CallSite instance every call -- which could get +// expensive, depending on context. So instead, initialize a static CallSite +// for each level value we support, then dynamically select the CallSite +// instance for the passed level value. +// Compare implementation to lllog() above. +#define llvlog(level, once, ...) \ + do { \ + const char* tags[] = {"", ##__VA_ARGS__}; \ + /* Need a static CallSite instance per expected ELevel value. */ \ + /* Since we intend to index this array with the ELevel, */ \ + /* _sites[0] should be ELevel(0), and so on -- avoid using */ \ + /* ELevel symbolic names when initializing -- except for */ \ + /* the last entry, which handles anything beyond the end. */ \ + /* (Commented ELevel value names are from 2016-09-01.) */ \ + /* Passing an ELevel past the end of this array is itself */ \ + /* a fatal error, so ensure the last is LEVEL_ERROR. */ \ + static LLError::CallSite _sites[] = \ + { \ + /* LEVEL_DEBUG */ \ + LLError::CallSite(lllog_site_args_(LLError::ELevel(0), once, tags)), \ + /* LEVEL_INFO */ \ + LLError::CallSite(lllog_site_args_(LLError::ELevel(1), once, tags)), \ + /* LEVEL_WARN */ \ + LLError::CallSite(lllog_site_args_(LLError::ELevel(2), once, tags)), \ + /* LEVEL_ERROR */ \ + LLError::CallSite(lllog_site_args_(LLError::LEVEL_ERROR, once, tags)) \ + }; \ + /* Clamp the passed 'level' to at most last entry */ \ + std::size_t which((std::size_t(level) >= LL_ARRAY_SIZE(_sites)) ? \ + (LL_ARRAY_SIZE(_sites) - 1) : std::size_t(level)); \ + /* selected CallSite *must* be named _site for LL_ENDL */ \ + LLError::CallSite& _site(_sites[which]); \ + lllog_test_() + #endif // LL_LLERROR_H diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 2d5f964deb..56367b8f54 100644 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -39,6 +39,7 @@ #include "llerror.h" #include "llcoros.h" #include "llmake.h" +#include "llexception.h" #include "lleventfilter.h" @@ -351,7 +352,7 @@ LLSD errorException(const LLEventWithID& result, const std::string& desc) // returning it, deliver it via exception. if (result.second) { - throw LLErrorEvent(desc, result.first); + LLTHROW(LLErrorEvent(desc, result.first)); } // That way, our caller knows a simple return must be from the reply // pump (pump 0). diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index 87926c692d..84827aab4a 100644 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -31,10 +31,10 @@ #include <boost/optional.hpp> #include <string> -#include <stdexcept> #include <utility> // std::pair #include "llevents.h" #include "llerror.h" +#include "llexception.h" /** * Like LLListenerOrPumpName, this is a class intended for parameter lists: @@ -234,11 +234,11 @@ LLSD errorException(const LLEventWithID& result, const std::string& desc); * because it's not an error in event processing: rather, this exception * announces an event that bears error information (for some other API). */ -class LL_COMMON_API LLErrorEvent: public std::runtime_error +class LL_COMMON_API LLErrorEvent: public LLException { public: LLErrorEvent(const std::string& what, const LLSD& data): - std::runtime_error(what), + LLException(what), mData(data) {} virtual ~LLErrorEvent() throw() {} diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 645c29d770..97270e4931 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -57,6 +57,7 @@ #include "stringize.h" #include "llerror.h" #include "llsdutil.h" +#include "llexception.h" #if LL_MSVC #pragma warning (disable : 4702) #endif @@ -174,7 +175,7 @@ std::string LLEventPumps::registerNew(const LLEventPump& pump, const std::string // Unless we're permitted to tweak it, that's Bad. if (! tweak) { - throw LLEventPump::DupPumpName(std::string("Duplicate LLEventPump name '") + name + "'"); + LLTHROW(LLEventPump::DupPumpName("Duplicate LLEventPump name '" + name + "'")); } // The passed name isn't unique, but we're permitted to tweak it. Find the // first decimal-integer suffix not already taken. The insert() attempt @@ -275,6 +276,8 @@ LLEventPumps::~LLEventPumps() #pragma warning (push) #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally #endif +const std::string LLEventPump::ANONYMOUS = std::string(); + LLEventPump::LLEventPump(const std::string& name, bool tweak): // Register every new instance with LLEventPumps @@ -313,145 +316,162 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL const NameList& after, const NameList& before) { - // Check for duplicate name before connecting listener to mSignal - ConnectionMap::const_iterator found = mConnections.find(name); - // In some cases the user might disconnect a connection explicitly -- or - // might use LLEventTrackable to disconnect implicitly. Either way, we can - // end up retaining in mConnections a zombie connection object that's - // already been disconnected. Such a connection object can't be - // reconnected -- nor, in the case of LLEventTrackable, would we want to - // try, since disconnection happens with the destruction of the listener - // object. That means it's safe to overwrite a disconnected connection - // object with the new one we're attempting. The case we want to prevent - // is only when the existing connection object is still connected. - if (found != mConnections.end() && found->second.connected()) - { - throw DupListenerName(std::string("Attempt to register duplicate listener name '") + name + - "' on " + typeid(*this).name() + " '" + getName() + "'"); - } - // Okay, name is unique, try to reconcile its dependencies. Specify a new - // "node" value that we never use for an mSignal placement; we'll fix it - // later. - DependencyMap::node_type& newNode = mDeps.add(name, -1.0, after, before); - // What if this listener has been added, removed and re-added? In that - // case newNode already has a non-negative value because we never remove a - // listener from mDeps. But keep processing uniformly anyway in case the - // listener was added back with different dependencies. Then mDeps.sort() - // would put it in a different position, and the old newNode placement - // value would be wrong, so we'd have to reassign it anyway. Trust that - // re-adding a listener with the same dependencies is the trivial case for - // mDeps.sort(): it can just replay its cache. - DependencyMap::sorted_range sorted_range; - try - { - // Can we pick an order that works including this new entry? - sorted_range = mDeps.sort(); - } - catch (const DependencyMap::Cycle& e) - { - // No: the new node's after/before dependencies have made mDeps - // unsortable. If we leave the new node in mDeps, it will continue - // to screw up all future attempts to sort()! Pull it out. - mDeps.remove(name); - throw Cycle(std::string("New listener '") + name + "' on " + typeid(*this).name() + - " '" + getName() + "' would cause cycle: " + e.what()); - } - // Walk the list to verify that we haven't changed the order. - float previous = 0.0, myprev = 0.0; - DependencyMap::sorted_iterator mydmi = sorted_range.end(); // need this visible after loop - for (DependencyMap::sorted_iterator dmi = sorted_range.begin(); - dmi != sorted_range.end(); ++dmi) + float nodePosition = 1.0; + + // if the supplied name is empty we are not interested in the ordering mechanism + // and can bypass attempting to find the optimal location to insert the new + // listener. We'll just tack it on to the end. + if (!name.empty()) // should be the same as testing against ANONYMOUS { - // Since we've added the new entry with an invalid placement, - // recognize it and skip it. - if (dmi->first == name) + // Check for duplicate name before connecting listener to mSignal + ConnectionMap::const_iterator found = mConnections.find(name); + // In some cases the user might disconnect a connection explicitly -- or + // might use LLEventTrackable to disconnect implicitly. Either way, we can + // end up retaining in mConnections a zombie connection object that's + // already been disconnected. Such a connection object can't be + // reconnected -- nor, in the case of LLEventTrackable, would we want to + // try, since disconnection happens with the destruction of the listener + // object. That means it's safe to overwrite a disconnected connection + // object with the new one we're attempting. The case we want to prevent + // is only when the existing connection object is still connected. + if (found != mConnections.end() && found->second.connected()) { - // Remember the iterator belonging to our new node, and which - // placement value was 'previous' at that point. - mydmi = dmi; - myprev = previous; - continue; + LLTHROW(DupListenerName("Attempt to register duplicate listener name '" + name + + "' on " + typeid(*this).name() + " '" + getName() + "'")); } - // If the new node has rearranged the existing nodes, we'll find - // that their placement values are no longer in increasing order. - if (dmi->second < previous) + // Okay, name is unique, try to reconcile its dependencies. Specify a new + // "node" value that we never use for an mSignal placement; we'll fix it + // later. + DependencyMap::node_type& newNode = mDeps.add(name, -1.0, after, before); + // What if this listener has been added, removed and re-added? In that + // case newNode already has a non-negative value because we never remove a + // listener from mDeps. But keep processing uniformly anyway in case the + // listener was added back with different dependencies. Then mDeps.sort() + // would put it in a different position, and the old newNode placement + // value would be wrong, so we'd have to reassign it anyway. Trust that + // re-adding a listener with the same dependencies is the trivial case for + // mDeps.sort(): it can just replay its cache. + DependencyMap::sorted_range sorted_range; + try { - // This is another scenario in which we'd better back out the - // newly-added node from mDeps -- but don't do it yet, we want to - // traverse the existing mDeps to report on it! - // Describe the change to the order of our listeners. Copy - // everything but the newest listener to a vector we can sort to - // obtain the old order. - typedef std::vector< std::pair<float, std::string> > SortNameList; - SortNameList sortnames; - for (DependencyMap::sorted_iterator cdmi(sorted_range.begin()), cdmend(sorted_range.end()); - cdmi != cdmend; ++cdmi) + // Can we pick an order that works including this new entry? + sorted_range = mDeps.sort(); + } + catch (const DependencyMap::Cycle& e) + { + // No: the new node's after/before dependencies have made mDeps + // unsortable. If we leave the new node in mDeps, it will continue + // to screw up all future attempts to sort()! Pull it out. + mDeps.remove(name); + LLTHROW(Cycle("New listener '" + name + "' on " + typeid(*this).name() + + " '" + getName() + "' would cause cycle: " + e.what())); + } + // Walk the list to verify that we haven't changed the order. + float previous = 0.0, myprev = 0.0; + DependencyMap::sorted_iterator mydmi = sorted_range.end(); // need this visible after loop + for (DependencyMap::sorted_iterator dmi = sorted_range.begin(); + dmi != sorted_range.end(); ++dmi) + { + // Since we've added the new entry with an invalid placement, + // recognize it and skip it. + if (dmi->first == name) { - if (cdmi->first != name) - { - sortnames.push_back(SortNameList::value_type(cdmi->second, cdmi->first)); - } + // Remember the iterator belonging to our new node, and which + // placement value was 'previous' at that point. + mydmi = dmi; + myprev = previous; + continue; } - std::sort(sortnames.begin(), sortnames.end()); - std::ostringstream out; - out << "New listener '" << name << "' on " << typeid(*this).name() << " '" << getName() - << "' would move previous listener '" << dmi->first << "'\nwas: "; - SortNameList::const_iterator sni(sortnames.begin()), snend(sortnames.end()); - if (sni != snend) + // If the new node has rearranged the existing nodes, we'll find + // that their placement values are no longer in increasing order. + if (dmi->second < previous) { - out << sni->second; - while (++sni != snend) + // This is another scenario in which we'd better back out the + // newly-added node from mDeps -- but don't do it yet, we want to + // traverse the existing mDeps to report on it! + // Describe the change to the order of our listeners. Copy + // everything but the newest listener to a vector we can sort to + // obtain the old order. + typedef std::vector< std::pair<float, std::string> > SortNameList; + SortNameList sortnames; + for (DependencyMap::sorted_iterator cdmi(sorted_range.begin()), cdmend(sorted_range.end()); + cdmi != cdmend; ++cdmi) { - out << ", " << sni->second; + if (cdmi->first != name) + { + sortnames.push_back(SortNameList::value_type(cdmi->second, cdmi->first)); + } } - } - out << "\nnow: "; - DependencyMap::sorted_iterator ddmi(sorted_range.begin()), ddmend(sorted_range.end()); - if (ddmi != ddmend) - { - out << ddmi->first; - while (++ddmi != ddmend) + std::sort(sortnames.begin(), sortnames.end()); + std::ostringstream out; + out << "New listener '" << name << "' on " << typeid(*this).name() << " '" << getName() + << "' would move previous listener '" << dmi->first << "'\nwas: "; + SortNameList::const_iterator sni(sortnames.begin()), snend(sortnames.end()); + if (sni != snend) { - out << ", " << ddmi->first; + out << sni->second; + while (++sni != snend) + { + out << ", " << sni->second; + } } + out << "\nnow: "; + DependencyMap::sorted_iterator ddmi(sorted_range.begin()), ddmend(sorted_range.end()); + if (ddmi != ddmend) + { + out << ddmi->first; + while (++ddmi != ddmend) + { + out << ", " << ddmi->first; + } + } + // NOW remove the offending listener node. + mDeps.remove(name); + // Having constructed a description of the order change, inform caller. + LLTHROW(OrderChange(out.str())); } - // NOW remove the offending listener node. - mDeps.remove(name); - // Having constructed a description of the order change, inform caller. - throw OrderChange(out.str()); + // This node becomes the previous one. + previous = dmi->second; } - // This node becomes the previous one. - previous = dmi->second; - } - // We just got done with a successful mDeps.add(name, ...) call. We'd - // better have found 'name' somewhere in that sorted list! - assert(mydmi != sorted_range.end()); - // Four cases: - // 0. name is the only entry: placement 1.0 - // 1. name is the first of several entries: placement (next placement)/2 - // 2. name is between two other entries: placement (myprev + (next placement))/2 - // 3. name is the last entry: placement ceil(myprev) + 1.0 - // Since we've cleverly arranged for myprev to be 0.0 if name is the - // first entry, this folds down to two cases. Case 1 is subsumed by - // case 2, and case 0 is subsumed by case 3. So we need only handle - // cases 2 and 3, which means we need only detect whether name is the - // last entry. Increment mydmi to see if there's anything beyond. - if (++mydmi != sorted_range.end()) - { - // The new node isn't last. Place it between the previous node and - // the successor. - newNode = (myprev + mydmi->second)/2.f; - } - else - { - // The new node is last. Bump myprev up to the next integer, add - // 1.0 and use that. - newNode = std::ceil(myprev) + 1.f; + // We just got done with a successful mDeps.add(name, ...) call. We'd + // better have found 'name' somewhere in that sorted list! + assert(mydmi != sorted_range.end()); + // Four cases: + // 0. name is the only entry: placement 1.0 + // 1. name is the first of several entries: placement (next placement)/2 + // 2. name is between two other entries: placement (myprev + (next placement))/2 + // 3. name is the last entry: placement ceil(myprev) + 1.0 + // Since we've cleverly arranged for myprev to be 0.0 if name is the + // first entry, this folds down to two cases. Case 1 is subsumed by + // case 2, and case 0 is subsumed by case 3. So we need only handle + // cases 2 and 3, which means we need only detect whether name is the + // last entry. Increment mydmi to see if there's anything beyond. + if (++mydmi != sorted_range.end()) + { + // The new node isn't last. Place it between the previous node and + // the successor. + newNode = (myprev + mydmi->second) / 2.f; + } + else + { + // The new node is last. Bump myprev up to the next integer, add + // 1.0 and use that. + newNode = std::ceil(myprev) + 1.f; + } + + nodePosition = newNode; } // Now that newNode has a value that places it appropriately in mSignal, // connect it. - LLBoundListener bound = mSignal->connect(newNode, listener); - mConnections[name] = bound; + LLBoundListener bound = mSignal->connect(nodePosition, listener); + + if (!name.empty()) + { // note that we are not tracking anonymous listeners here either. + // This means that it is the caller's responsibility to either assign + // to a TempBoundListerer (scoped_connection) or manually disconnect + // when done. + mConnections[name] = bound; + } return bound; } @@ -608,7 +628,7 @@ bool LLListenerOrPumpName::operator()(const LLSD& event) const { if (! mListener) { - throw Empty("attempting to call uninitialized"); + LLTHROW(Empty("attempting to call uninitialized")); } return (*mListener)(event); } diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index ba4fcd766e..a3b9ec02e0 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -37,7 +37,6 @@ #include <set> #include <vector> #include <deque> -#include <stdexcept> #if LL_WINDOWS #pragma warning (push) #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch @@ -62,6 +61,7 @@ #include "llsingleton.h" #include "lldependencies.h" #include "llstl.h" +#include "llexception.h" /*==========================================================================*| // override this to allow binding free functions with more parameters @@ -95,12 +95,32 @@ struct LLStopWhenHandled result_type operator()(InputIterator first, InputIterator last) const { for (InputIterator si = first; si != last; ++si) - { - if (*si) - { - return true; - } - } + { + try + { + if (*si) + { + return true; + } + } + catch (const LLContinueError&) + { + // We catch LLContinueError here because an LLContinueError- + // based exception means the viewer as a whole should carry on + // to the best of our ability. Therefore subsequent listeners + // on the same LLEventPump should still receive this event. + + // The iterator passed to a boost::signals2 Combiner is very + // clever, but provides no contextual information. We would + // very much like to be able to log the name of the LLEventPump + // plus the name of this particular listener, but alas. + LOG_UNHANDLED_EXCEPTION("LLEventPump"); + } + // We do NOT catch (...) here because we might as well let it + // propagate out to the generic handler. If we were able to log + // context information here, that would be great, but we can't, so + // there's no point. + } return false; } }; @@ -188,10 +208,10 @@ public: bool operator()(const LLSD& event) const; /// exception if you try to call when empty - struct Empty: public std::runtime_error + struct Empty: public LLException { Empty(const std::string& what): - std::runtime_error(std::string("LLListenerOrPumpName::Empty: ") + what) {} + LLException(std::string("LLListenerOrPumpName::Empty: ") + what) {} }; private: @@ -365,16 +385,18 @@ typedef boost::signals2::trackable LLEventTrackable; class LL_COMMON_API LLEventPump: public LLEventTrackable { public: + static const std::string ANONYMOUS; // constant for anonymous listeners. + /** * Exception thrown by LLEventPump(). You are trying to instantiate an * LLEventPump (subclass) using the same name as some other instance, and * you didn't pass <tt>tweak=true</tt> to permit it to generate a unique * variant. */ - struct DupPumpName: public std::runtime_error + struct DupPumpName: public LLException { DupPumpName(const std::string& what): - std::runtime_error(std::string("DupPumpName: ") + what) {} + LLException(std::string("DupPumpName: ") + what) {} }; /** @@ -399,9 +421,9 @@ public: /// group exceptions thrown by listen(). We use exceptions because these /// particular errors are likely to be coding errors, found and fixed by /// the developer even before preliminary checkin. - struct ListenError: public std::runtime_error + struct ListenError: public LLException { - ListenError(const std::string& what): std::runtime_error(what) {} + ListenError(const std::string& what): LLException(what) {} }; /** * exception thrown by listen(). You are attempting to register a @@ -476,6 +498,12 @@ public: * instantiate your listener, then passing the same name on each listen() * call, allows us to optimize away the second and subsequent dependency * sorts. + * + * If name is set to LLEventPump::ANONYMOUS listen will bypass the entire + * dependency and ordering calculation. In this case, it is critical that + * the result be assigned to a LLTempBoundListener or the listener is + * manually disconnected when no longer needed since there will be no + * way to later find and disconnect this listener manually. * * If (as is typical) you pass a <tt>boost::bind()</tt> expression as @a * listener, listen() will inspect the components of that expression. If a diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp new file mode 100644 index 0000000000..b32ec2c9c9 --- /dev/null +++ b/indra/llcommon/llexception.cpp @@ -0,0 +1,55 @@ +/** + * @file llexception.cpp + * @author Nat Goodspeed + * @date 2016-08-12 + * @brief Implementation for llexception. + * + * $LicenseInfo:firstyear=2016&license=viewerlgpl$ + * Copyright (c) 2016, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llexception.h" +// STL headers +// std headers +#include <typeinfo> +// external library headers +#include <boost/exception/diagnostic_information.hpp> +// other Linden headers +#include "llerror.h" +#include "llerrorcontrol.h" + +namespace { +// used by crash_on_unhandled_exception_() and log_unhandled_exception_() +void log_unhandled_exception_(LLError::ELevel level, + const char* file, int line, const char* pretty_function, + const std::string& context) +{ + // log same message but allow caller-specified severity level + LL_VLOGS(level, "LLException") << LLError::abbreviateFile(file) + << "(" << line << "): Unhandled exception caught in " << pretty_function; + if (! context.empty()) + { + LL_CONT << ": " << context; + } + LL_CONT << ":\n" << boost::current_exception_diagnostic_information() << LL_ENDL; +} +} + +void crash_on_unhandled_exception_(const char* file, int line, const char* pretty_function, + const std::string& context) +{ + // LL_ERRS() terminates and propagates message into crash dump. + log_unhandled_exception_(LLError::LEVEL_ERROR, file, line, pretty_function, context); +} + +void log_unhandled_exception_(const char* file, int line, const char* pretty_function, + const std::string& context) +{ + // Use LL_WARNS() because we seriously do not expect this to happen + // routinely, but we DO expect to return from this function. + log_unhandled_exception_(LLError::LEVEL_WARN, file, line, pretty_function, context); +} diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h new file mode 100644 index 0000000000..dfcb7c192f --- /dev/null +++ b/indra/llcommon/llexception.h @@ -0,0 +1,85 @@ +/** + * @file llexception.h + * @author Nat Goodspeed + * @date 2016-06-29 + * @brief Types needed for generic exception handling + * + * $LicenseInfo:firstyear=2016&license=viewerlgpl$ + * Copyright (c) 2016, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLEXCEPTION_H) +#define LL_LLEXCEPTION_H + +#include <stdexcept> +#include <boost/exception/exception.hpp> +#include <boost/throw_exception.hpp> +#include <boost/current_function.hpp> + +// "Found someone who can comfort me +// But there are always exceptions..." +// - Empty Pages, Traffic, from John Barleycorn (1970) +// https://www.youtube.com/watch?v=dRH0CGVK7ic + +/** + * LLException is intended as the common base class from which all + * viewer-specific exceptions are derived. Rationale for why it's derived from + * both std::exception and boost::exception is explained in + * tests/llexception_test.cpp. + * + * boost::current_exception_diagnostic_information() is quite wonderful: if + * all we need to do with an exception is log it, in most places we should + * catch (...) and log boost::current_exception_diagnostic_information(). + * See CRASH_ON_UNHANDLED_EXCEPTION() and LOG_UNHANDLED_EXCEPTION() below. + * + * There may be circumstances in which it would be valuable to distinguish an + * exception explicitly thrown by viewer code from an exception thrown by + * (say) a third-party library. Catching (const LLException&) supports such + * usage. However, most of the value of this base class is in the + * diagnostic_information() available via Boost.Exception. + */ +struct LLException: + public std::runtime_error, + public boost::exception +{ + LLException(const std::string& what): + std::runtime_error(what) + {} +}; + +/** + * The point of LLContinueError is to distinguish exceptions that need not + * terminate the whole viewer session. In general, an uncaught exception will + * be logged and will crash the viewer. However, though an uncaught exception + * derived from LLContinueError will still be logged, the viewer will attempt + * to continue processing. + */ +struct LLContinueError: public LLException +{ + LLContinueError(const std::string& what): + LLException(what) + {} +}; + +/** + * Please use LLTHROW() to throw viewer exceptions whenever possible. This + * enriches the exception's diagnostic_information() with the source file, + * line and containing function of the LLTHROW() macro. + */ +// Currently we implement that using BOOST_THROW_EXCEPTION(). Wrap it in +// LLTHROW() in case we ever want to revisit that implementation decision. +#define LLTHROW(x) BOOST_THROW_EXCEPTION(x) + +/// Call this macro from a catch (...) clause +#define CRASH_ON_UNHANDLED_EXCEPTION(CONTEXT) \ + crash_on_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT) +void crash_on_unhandled_exception_(const char*, int, const char*, const std::string&); + +/// Call this from a catch (const LLContinueError&) clause, or from a catch +/// (...) clause in which you do NOT want the viewer to crash. +#define LOG_UNHANDLED_EXCEPTION(CONTEXT) \ + log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT) +void log_unhandled_exception_(const char*, int, const char*, const std::string&); + +#endif /* ! defined(LL_LLEXCEPTION_H) */ diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 2370253078..f56e5596f5 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -296,7 +296,16 @@ LL_FORCE_INLINE BlockTimer::BlockTimer(BlockTimerStatHandle& timer) { #if LL_FAST_TIMER_ON BlockTimerStackRecord* cur_timer_data = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance(); - if (!cur_timer_data) return; + if (!cur_timer_data) + { + // How likely is it that + // LLThreadLocalSingletonPointer<T>::getInstance() will return NULL? + // Even without researching, what we can say is that if we exit + // without setting mStartTime at all, gcc 4.7 produces (fatal) + // warnings about a possibly-uninitialized data member. + mStartTime = 0; + return; + } TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator(); accumulator.mActiveCount++; // keep current parent as long as it is active when we are diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h index 401e4d759a..feb5f41848 100644 --- a/indra/llcommon/llhandle.h +++ b/indra/llcommon/llhandle.h @@ -28,8 +28,11 @@ #define LLHANDLE_H #include "llpointer.h" +#include "llexception.h" +#include <stdexcept> #include <boost/type_traits/is_convertible.hpp> #include <boost/utility/enable_if.hpp> +#include <boost/throw_exception.hpp> /** * Helper object for LLHandle. Don't instantiate these directly, used @@ -213,4 +216,82 @@ private: mutable LLRootHandle<T> mHandle; }; + + +class LLCheckedHandleBase +{ +public: + class Stale : public LLException + { + public: + Stale() : + LLException("Attempt to access stale handle.") + {} + }; + +protected: + LLCheckedHandleBase() { } + +}; + +/** + * This is a simple wrapper for Handles, allowing direct calls to the underlying + * pointer. The checked handle will throw a Stale if an attempt + * is made to access the object referenced by the handle and that object has + * been destroyed. + **/ +template <typename T> +class LLCheckedHandle: public LLCheckedHandleBase +{ +public: + + LLCheckedHandle(LLHandle<T> handle): + mHandle(handle) + { } + + /** + * Test the underlying handle. If it is no longer valid, throw a Stale exception. + */ + void check() const + { + T* ptr = mHandle.get(); + if (!ptr) + BOOST_THROW_EXCEPTION(Stale()); + } + + /** + * Cast back to an appropriate handle + */ + operator LLHandle<T>() const + { + return mHandle; + } + + /** + * Converts the LLCheckedHandle to a bool. Allows for if (chkdHandle) {} + * Does not throw. + */ + /*explicit*/ operator bool() const // explicit conversion operator not available with Linux compiler + { + return (mHandle.get() != NULL); + } + + /** + * Attempt to call a method or access a member in the structure referenced + * by the handle. If the handle no longer points to a valid structure + * throw a Stale. + */ + T* operator ->() const + { + T* ptr = mHandle.get(); + if (!ptr) + BOOST_THROW_EXCEPTION(Stale()); + return ptr; + } + +private: + + LLHandle<T> mHandle; +}; + #endif diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index 84d2a12f65..c87d2a3e58 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -33,6 +33,7 @@ #include "lltimer.h" #include "lluuid.h" #include "llleaplistener.h" +#include "llexception.h" #if LL_MSVC #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally @@ -69,7 +70,7 @@ public: // Rule out empty vector if (plugin.empty()) { - throw Error("no plugin command"); + LLTHROW(Error("no plugin command")); } // Don't leave desc empty either, but in this case, if we weren't @@ -112,7 +113,7 @@ public: // If that didn't work, no point in keeping this LLLeap object. if (! mChild) { - throw Error(STRINGIZE("failed to run " << mDesc)); + LLTHROW(Error(STRINGIZE("failed to run " << mDesc))); } // Okay, launch apparently worked. Change our mDonePump listener. diff --git a/indra/llcommon/llleap.h b/indra/llcommon/llleap.h index e33f25e530..8aac8a64c5 100644 --- a/indra/llcommon/llleap.h +++ b/indra/llcommon/llleap.h @@ -13,9 +13,9 @@ #define LL_LLLEAP_H #include "llinstancetracker.h" +#include "llexception.h" #include <string> #include <vector> -#include <stdexcept> /** * LLSD Event API Plugin class. Because instances are managed by @@ -67,9 +67,9 @@ public: * string(s) passed to create() might come from an external source. This * way the caller can catch LLLeap::Error and try to recover. */ - struct Error: public std::runtime_error + struct Error: public LLException { - Error(const std::string& what): std::runtime_error(what) {} + Error(const std::string& what): LLException(what) {} }; virtual ~LLLeap(); diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index 0fb257aab1..575edddc43 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -110,11 +110,15 @@ template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address) #if defined(LL_WINDOWS) return _aligned_malloc(size, align); #else + char* aligned = NULL; void* mem = malloc( size + (align - 1) + sizeof(void*) ); - char* aligned = ((char*)mem) + sizeof(void*); - aligned += align - ((uintptr_t)aligned & (align - 1)); + if (mem) + { + aligned = ((char*)mem) + sizeof(void*); + aligned += align - ((uintptr_t)aligned & (align - 1)); - ((void**)aligned)[-1] = mem; + ((void**)aligned)[-1] = mem; + } return aligned; #endif } diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index 44f56daf2d..8c321d06b9 100644 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -34,6 +34,7 @@ #include "llapr.h" #include "apr_signal.h" #include "llevents.h" +#include "llexception.h" #include <boost/foreach.hpp> #include <boost/bind.hpp> @@ -472,9 +473,9 @@ private: *****************************************************************************/ /// Need an exception to avoid constructing an invalid LLProcess object, but /// internal use only -struct LLProcessError: public std::runtime_error +struct LLProcessError: public LLException { - LLProcessError(const std::string& msg): std::runtime_error(msg) {} + LLProcessError(const std::string& msg): LLException(msg) {} }; LLProcessPtr LLProcess::create(const LLSDOrParams& params) @@ -530,8 +531,8 @@ LLProcess::LLProcess(const LLSDOrParams& params): if (! params.validateBlock(true)) { - throw LLProcessError(STRINGIZE("not launched: failed parameter validation\n" - << LLSDNotationStreamer(params))); + LLTHROW(LLProcessError(STRINGIZE("not launched: failed parameter validation\n" + << LLSDNotationStreamer(params)))); } mPostend = params.postend; @@ -596,10 +597,10 @@ LLProcess::LLProcess(const LLSDOrParams& params): } else { - throw LLProcessError(STRINGIZE("For " << params.executable() - << ": unsupported FileParam for " << which - << ": type='" << fparam.type() - << "', name='" << fparam.name() << "'")); + LLTHROW(LLProcessError(STRINGIZE("For " << params.executable() + << ": unsupported FileParam for " << which + << ": type='" << fparam.type() + << "', name='" << fparam.name() << "'"))); } } // By default, pass APR_NO_PIPE for unspecified slots. @@ -678,7 +679,7 @@ LLProcess::LLProcess(const LLSDOrParams& params): if (ll_apr_warn_status(apr_proc_create(&mProcess, argv[0], &argv[0], NULL, procattr, gAPRPoolp))) { - throw LLProcessError(STRINGIZE(params << " failed")); + LLTHROW(LLProcessError(STRINGIZE(params << " failed"))); } // arrange to call status_callback() @@ -1063,7 +1064,7 @@ PIPETYPE& LLProcess::getPipe(FILESLOT slot) PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot); if (! wp) { - throw NoPipe(error); + LLTHROW(NoPipe(error)); } return *wp; } diff --git a/indra/llcommon/llprocess.h b/indra/llcommon/llprocess.h index 43ccadc412..bfac4567a5 100644 --- a/indra/llcommon/llprocess.h +++ b/indra/llcommon/llprocess.h @@ -30,13 +30,13 @@ #include "llinitparam.h" #include "llsdparam.h" #include "llwin32headerslean.h" +#include "llexception.h" #include "apr_thread_proc.h" #include <boost/shared_ptr.hpp> #include <boost/ptr_container/ptr_vector.hpp> #include <boost/optional.hpp> #include <boost/noncopyable.hpp> #include <iosfwd> // std::ostream -#include <stdexcept> #if LL_WINDOWS #include "llwin32headerslean.h" // for HANDLE @@ -479,9 +479,9 @@ public: /// Exception thrown by getWritePipe(), getReadPipe() if you didn't ask to /// create a pipe at the corresponding FILESLOT. - struct NoPipe: public std::runtime_error + struct NoPipe: public LLException { - NoPipe(const std::string& what): std::runtime_error(what) {} + NoPipe(const std::string& what): LLException(what) {} }; /** diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp index 185f0d63fb..491f920c0f 100644 --- a/indra/llcommon/llthreadsafequeue.cpp +++ b/indra/llcommon/llthreadsafequeue.cpp @@ -27,6 +27,7 @@ #include <apr_pools.h> #include <apr_queue.h> #include "llthreadsafequeue.h" +#include "llexception.h" @@ -41,13 +42,13 @@ LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(apr_pool_t * po { if(mOwnsPool) { apr_status_t status = apr_pool_create(&mPool, 0); - if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate pool"); + if(status != APR_SUCCESS) LLTHROW(LLThreadSafeQueueError("failed to allocate pool")); } else { ; // No op. } apr_status_t status = apr_queue_create(&mQueue, capacity, mPool); - if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate queue"); + if(status != APR_SUCCESS) LLTHROW(LLThreadSafeQueueError("failed to allocate queue")); } @@ -68,9 +69,9 @@ void LLThreadSafeQueueImplementation::pushFront(void * element) apr_status_t status = apr_queue_push(mQueue, element); if(status == APR_EINTR) { - throw LLThreadSafeQueueInterrupt(); + LLTHROW(LLThreadSafeQueueInterrupt()); } else if(status != APR_SUCCESS) { - throw LLThreadSafeQueueError("push failed"); + LLTHROW(LLThreadSafeQueueError("push failed")); } else { ; // Success. } @@ -88,9 +89,9 @@ void * LLThreadSafeQueueImplementation::popBack(void) apr_status_t status = apr_queue_pop(mQueue, &element); if(status == APR_EINTR) { - throw LLThreadSafeQueueInterrupt(); + LLTHROW(LLThreadSafeQueueInterrupt()); } else if(status != APR_SUCCESS) { - throw LLThreadSafeQueueError("pop failed"); + LLTHROW(LLThreadSafeQueueError("pop failed")); } else { return element; } diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h index 58cac38769..45289ef0b4 100644 --- a/indra/llcommon/llthreadsafequeue.h +++ b/indra/llcommon/llthreadsafequeue.h @@ -27,9 +27,8 @@ #ifndef LL_LLTHREADSAFEQUEUE_H #define LL_LLTHREADSAFEQUEUE_H - +#include "llexception.h" #include <string> -#include <stdexcept> struct apr_pool_t; // From apr_pools.h @@ -40,11 +39,11 @@ class LLThreadSafeQueueImplementation; // See below. // A general queue exception. // class LL_COMMON_API LLThreadSafeQueueError: -public std::runtime_error + public LLException { public: LLThreadSafeQueueError(std::string const & message): - std::runtime_error(message) + LLException(message) { ; // No op. } diff --git a/indra/llcommon/lluriparser.cpp b/indra/llcommon/lluriparser.cpp index 82d0dc8b4b..c275b90120 100644 --- a/indra/llcommon/lluriparser.cpp +++ b/indra/llcommon/lluriparser.cpp @@ -205,9 +205,9 @@ void LLUriParser::glue(std::string& uri) const uri = first_part + second_part; } -void LLUriParser::glueFirst(std::string& uri) const +void LLUriParser::glueFirst(std::string& uri, bool use_scheme) const { - if (mScheme.size()) + if (use_scheme && mScheme.size()) { uri = mScheme; uri += "://"; diff --git a/indra/llcommon/lluriparser.h b/indra/llcommon/lluriparser.h index 2df8085ae6..cfbf54f3c8 100644 --- a/indra/llcommon/lluriparser.h +++ b/indra/llcommon/lluriparser.h @@ -60,7 +60,7 @@ public: void extractParts(); void glue(std::string& uri) const; - void glueFirst(std::string& uri) const; + void glueFirst(std::string& uri, bool use_scheme = true) const; void glueSecond(std::string& uri) const; bool test() const; S32 normalize(); diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp index e3671047b4..d4af2c6b01 100644 --- a/indra/llcommon/lluuid.cpp +++ b/indra/llcommon/lluuid.cpp @@ -83,7 +83,7 @@ unsigned int decode( char const * fiveChars ) throw( bad_input_data ) unsigned int ret = 0; for( int ix = 0; ix < 5; ++ix ) { char * s = strchr( encodeTable, fiveChars[ ix ] ); -if( s == 0 ) throw bad_input_data(); +if( s == 0 ) LLTHROW(bad_input_data()); ret = ret * 85 + (s-encodeTable); } return ret; diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index 364d2d13dd..20de205454 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -237,8 +237,21 @@ namespace tut void ErrorTestObject::test<4>() // file abbreviation { - std::string thisFile = __FILE__; - std::string abbreviateFile = LLError::abbreviateFile(thisFile); + std::string prev, abbreviateFile = __FILE__; + do + { + prev = abbreviateFile; + abbreviateFile = LLError::abbreviateFile(abbreviateFile); + // __FILE__ is assumed to end with + // indra/llcommon/tests/llerror_test.cpp. This test used to call + // abbreviateFile() exactly once, then check below whether it + // still contained the string 'indra'. That fails if the FIRST + // part of the pathname also contains indra! Certain developer + // machine images put local directory trees under + // /ngi-persist/indra, which is where we observe the problem. So + // now, keep calling abbreviateFile() until it returns its + // argument unchanged, THEN check. + } while (abbreviateFile != prev); ensure_ends_with("file name abbreviation", abbreviateFile, diff --git a/indra/llcommon/tests/llexception_test.cpp b/indra/llcommon/tests/llexception_test.cpp new file mode 100644 index 0000000000..6bee1943c2 --- /dev/null +++ b/indra/llcommon/tests/llexception_test.cpp @@ -0,0 +1,308 @@ +/** + * @file llexception_test.cpp + * @author Nat Goodspeed + * @date 2016-08-12 + * @brief Tests for throwing exceptions. + * + * This isn't a regression test: it doesn't need to be run every build, which + * is why the corresponding line in llcommon/CMakeLists.txt is commented out. + * Rather it's a head-to-head test of what kind of exception information we + * can collect from various combinations of exception base classes, type of + * throw verb and sequences of catch clauses. + * + * This "test" makes no ensure() calls: its output goes to stdout for human + * examination. + * + * As of 2016-08-12 with Boost 1.57, we come to the following conclusions. + * These should probably be re-examined from time to time as we update Boost. + * + * - It is indisputably beneficial to use BOOST_THROW_EXCEPTION() rather than + * plain throw. The macro annotates the exception object with the filename, + * line number and function name from which the exception was thrown. + * + * - That being the case, deriving only from boost::exception isn't an option. + * Every exception object passed to BOOST_THROW_EXCEPTION() must be derived + * directly or indirectly from std::exception. The only question is whether + * to also derive from boost::exception. We decided to derive LLException + * from both, as it makes message output slightly cleaner, but this is a + * trivial reason: if a strong reason emerges to prefer single inheritance, + * dropping the boost::exception base class shouldn't be a problem. + * + * - (As you will have guessed, ridiculous things like a char* or int or a + * class derived from neither boost::exception nor std::exception can only + * be caught by that specific type or (...), and + * boost::current_exception_diagnostic_information() simply throws up its + * hands and confesses utter ignorance. Stay away from such nonsense.) + * + * - But if you derive from std::exception, to nat's surprise, + * boost::current_exception_diagnostic_information() gives as much + * information about exceptions in a catch (...) clause as you can get from + * a specific catch (const std::exception&) clause, notably the concrete + * exception class and the what() string. So instead of a sequence like + * + * try { ... } + * catch (const boost::exception& e) { ... boost-flavored logging ... } + * catch (const std::exception& e) { ... std::exception logging ... } + * catch (...) { ... generic logging ... } + * + * we should be able to get away with only a catch (...) clause that logs + * boost::current_exception_diagnostic_information(). + * + * - Going further: boost::current_exception_diagnostic_information() provides + * just as much information even within a std::set_terminate() handler. So + * it might not even be strictly necessary to include a catch (...) clause + * since the viewer does use std::set_terminate(). + * + * - (We might consider adding a catch (int) clause because Kakadu internally + * throws ints, and who knows if one of those might leak out. If it does, + * boost::current_exception_diagnostic_information() can do nothing with it. + * A catch (int) clause could at least log the value and rethrow.) + * + * $LicenseInfo:firstyear=2016&license=viewerlgpl$ + * Copyright (c) 2016, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llexception.h" +// STL headers +// std headers +#include <typeinfo> +// external library headers +#include <boost/throw_exception.hpp> +// other Linden headers +#include "../test/lltut.h" + +// helper for display output +// usage: std::cout << center(some string value, fill char, width) << std::endl; +// (assumes it's the only thing on that particular line) +struct center +{ + center(const std::string& label, char fill, std::size_t width): + mLabel(label), + mFill(fill), + mWidth(width) + {} + + // Use friend declaration not because we need to grant access, but because + // it lets us declare a free operator like a member function. + friend std::ostream& operator<<(std::ostream& out, const center& ctr) + { + std::size_t padded = ctr.mLabel.length() + 2; + std::size_t left = (ctr.mWidth - padded) / 2; + std::size_t right = ctr.mWidth - left - padded; + return out << std::string(left, ctr.mFill) << ' ' << ctr.mLabel << ' ' + << std::string(right, ctr.mFill); + } + + std::string mLabel; + char mFill; + std::size_t mWidth; +}; + +/***************************************************************************** +* Four kinds of exceptions: derived from boost::exception, from +* std::exception, from both, from neither +*****************************************************************************/ +// Interestingly, we can't use this variant with BOOST_THROW_EXCEPTION() +// (which we want) -- we reach a failure topped by this comment: +// //All boost exceptions are required to derive from std::exception, +// //to ensure compatibility with BOOST_NO_EXCEPTIONS. +struct FromBoost: public boost::exception +{ + FromBoost(const std::string& what): mWhat(what) {} + ~FromBoost() throw() {} + std::string what() const { return mWhat; } + std::string mWhat; +}; + +struct FromStd: public std::runtime_error +{ + FromStd(const std::string& what): std::runtime_error(what) {} +}; + +struct FromBoth: public boost::exception, public std::runtime_error +{ + FromBoth(const std::string& what): std::runtime_error(what) {} +}; + +// Same deal with FromNeither: can't use with BOOST_THROW_EXCEPTION(). +struct FromNeither +{ + FromNeither(const std::string& what): mWhat(what) {} + std::string what() const { return mWhat; } + std::string mWhat; +}; + +/***************************************************************************** +* Two kinds of throws: plain throw and BOOST_THROW_EXCEPTION() +*****************************************************************************/ +template <typename EXC> +void plain_throw(const std::string& what) +{ + throw EXC(what); +} + +template <typename EXC> +void boost_throw(const std::string& what) +{ + BOOST_THROW_EXCEPTION(EXC(what)); +} + +// Okay, for completeness, functions that throw non-class values. We wouldn't +// even deign to consider these if we hadn't found examples in our own source +// code! (Note that Kakadu's internal exception support is still based on +// throwing ints.) +void throw_char_ptr(const std::string& what) +{ + throw what.c_str(); // umm... +} + +void throw_int(const std::string& what) +{ + throw int(what.length()); +} + +/***************************************************************************** +* Three sequences of catch clauses: +* boost::exception then ..., +* std::exception then ..., +* or just ... +*****************************************************************************/ +void catch_boost_dotdotdot(void (*thrower)(const std::string&), const std::string& what) +{ + try + { + thrower(what); + } + catch (const boost::exception& e) + { + std::cout << "catch (const boost::exception& e)" << std::endl; + std::cout << "e is " << typeid(e).name() << std::endl; + std::cout << "boost::diagnostic_information(e):\n'" + << boost::diagnostic_information(e) << "'" << std::endl; + // no way to report e.what() + } + catch (...) + { + std::cout << "catch (...)" << std::endl; + std::cout << "boost::current_exception_diagnostic_information():\n'" + << boost::current_exception_diagnostic_information() << "'" + << std::endl; + } +} + +void catch_std_dotdotdot(void (*thrower)(const std::string&), const std::string& what) +{ + try + { + thrower(what); + } + catch (const std::exception& e) + { + std::cout << "catch (const std::exception& e)" << std::endl; + std::cout << "e is " << typeid(e).name() << std::endl; + std::cout << "boost::diagnostic_information(e):\n'" + << boost::diagnostic_information(e) << "'" << std::endl; + std::cout << "e.what: '" + << e.what() << "'" << std::endl; + } + catch (...) + { + std::cout << "catch (...)" << std::endl; + std::cout << "boost::current_exception_diagnostic_information():\n'" + << boost::current_exception_diagnostic_information() << "'" + << std::endl; + } +} + +void catch_dotdotdot(void (*thrower)(const std::string&), const std::string& what) +{ + try + { + thrower(what); + } + catch (...) + { + std::cout << "catch (...)" << std::endl; + std::cout << "boost::current_exception_diagnostic_information():\n'" + << boost::current_exception_diagnostic_information() << "'" + << std::endl; + } +} + +/***************************************************************************** +* Try a particular kind of throw against each of three catch sequences +*****************************************************************************/ +void catch_several(void (*thrower)(const std::string&), const std::string& what) +{ + std::cout << std::string(20, '-') << "catch_boost_dotdotdot(" << what << ")" << std::endl; + catch_boost_dotdotdot(thrower, "catch_boost_dotdotdot(" + what + ")"); + + std::cout << std::string(20, '-') << "catch_std_dotdotdot(" << what << ")" << std::endl; + catch_std_dotdotdot(thrower, "catch_std_dotdotdot(" + what + ")"); + + std::cout << std::string(20, '-') << "catch_dotdotdot(" << what << ")" << std::endl; + catch_dotdotdot(thrower, "catch_dotdotdot(" + what + ")"); +} + +/***************************************************************************** +* For a particular kind of exception, try both kinds of throw against all +* three catch sequences +*****************************************************************************/ +template <typename EXC> +void catch_both_several(const std::string& what) +{ + std::cout << std::string(20, '*') << "plain_throw<" << what << ">" << std::endl; + catch_several(plain_throw<EXC>, "plain_throw<" + what + ">"); + + std::cout << std::string(20, '*') << "boost_throw<" << what << ">" << std::endl; + catch_several(boost_throw<EXC>, "boost_throw<" + what + ">"); +} + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct llexception_data + { + }; + typedef test_group<llexception_data> llexception_group; + typedef llexception_group::object object; + llexception_group llexceptiongrp("llexception"); + + template<> template<> + void object::test<1>() + { + set_test_name("throwing exceptions"); + + // For each kind of exception, try both kinds of throw against all + // three catch sequences + std::size_t margin = 72; + std::cout << center("FromStd", '=', margin) << std::endl; + catch_both_several<FromStd>("FromStd"); + + std::cout << center("FromBoth", '=', margin) << std::endl; + catch_both_several<FromBoth>("FromBoth"); + + std::cout << center("FromBoost", '=', margin) << std::endl; + // can't throw with BOOST_THROW_EXCEPTION(), just use catch_several() + catch_several(plain_throw<FromBoost>, "plain_throw<FromBoost>"); + + std::cout << center("FromNeither", '=', margin) << std::endl; + // can't throw this with BOOST_THROW_EXCEPTION() either + catch_several(plain_throw<FromNeither>, "plain_throw<FromNeither>"); + + std::cout << center("const char*", '=', margin) << std::endl; + // We don't expect BOOST_THROW_EXCEPTION() to throw anything so daft + // as a const char* or an int, so don't bother with + // catch_both_several() -- just catch_several(). + catch_several(throw_char_ptr, "throw_char_ptr"); + + std::cout << center("int", '=', margin) << std::endl; + catch_several(throw_int, "throw_int"); + } +} // namespace tut diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index d342dece84..c387da6c48 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -110,10 +110,7 @@ namespace tut // finding indra/lib/python. Use our __FILE__, with // raw-string syntax to deal with Windows pathnames. "mydir = os.path.dirname(r'" << __FILE__ << "')\n" - // We expect mydir to be .../indra/llcommon/tests. - "sys.path.insert(0,\n" - " os.path.join(mydir, os.pardir, os.pardir, 'lib', 'python'))\n" - "from indra.base import llsd\n" + "from llbase import llsd\n" "\n" "class ProtocolError(Exception):\n" " def __init__(self, msg, data):\n" diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index 6fbb9abfc0..81b930e1e2 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -1518,10 +1518,7 @@ namespace tut // scanner. import_llsd("import os.path\n" "import sys\n" - "sys.path.insert(0,\n" - " os.path.join(os.path.dirname(r'" __FILE__ "'),\n" - " os.pardir, os.pardir, 'lib', 'python'))\n" - "from indra.base import llsd\n") + "from llbase import llsd\n") {} ~TestPythonCompatible() {} diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index 785197ba11..9a4bbbd630 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -35,13 +35,13 @@ #include <tut/tut.hpp> #include "llerrorcontrol.h" +#include "llexception.h" #include "stringize.h" #include <boost/bind.hpp> #include <boost/noncopyable.hpp> #include <boost/shared_ptr.hpp> #include <list> #include <string> -#include <stdexcept> // statically reference the function in test.cpp... it's short, we could // replicate, but better to reuse @@ -67,9 +67,9 @@ struct WrapLLErrs LLError::restoreSettings(mPriorErrorSettings); } - struct FatalException: public std::runtime_error + struct FatalException: public LLException { - FatalException(const std::string& what): std::runtime_error(what) {} + FatalException(const std::string& what): LLException(what) {} }; void operator()(const std::string& message) @@ -78,7 +78,7 @@ struct WrapLLErrs error = message; // Also throw an appropriate exception since calling code is likely to // assume that control won't continue beyond LL_ERRS. - throw FatalException(message); + LLTHROW(FatalException(message)); } std::string error; diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 04cde651c4..6c5f37d407 100755 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -42,10 +42,8 @@ except ImportError: from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from SocketServer import ThreadingMixIn -mydir = os.path.dirname(__file__) # expected to be .../indra/llcorehttp/tests/ -sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) -from indra.util.fastest_elementtree import parse as xml_parse -from indra.base import llsd +from llbase.fastest_elementtree import parse as xml_parse +from llbase import llsd from testrunner import freeport, run, debug, VERBOSE class TestHTTPRequestHandler(BaseHTTPRequestHandler): diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h index 8b4afae24a..56e26c23ba 100644 --- a/indra/llcrashlogger/llcrashlogger.h +++ b/indra/llcrashlogger/llcrashlogger.h @@ -57,7 +57,7 @@ public: LLSD constructPostData(); virtual void updateApplication(const std::string& message = LLStringUtil::null); virtual bool init(); - virtual bool mainLoop() = 0; + virtual bool frame() = 0; virtual bool cleanup() = 0; void commonCleanup(); void setUserText(const std::string& text) { mCrashInfo["UserNotes"] = text; } diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index feb97ec2ab..f71607096c 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -1426,7 +1426,12 @@ void LLImageRaw::copyScaled( LLImageRaw* src ) bool LLImageRaw::scale( S32 new_width, S32 new_height, bool scale_image_data ) { - llassert((1 == getComponents()) || (3 == getComponents()) || (4 == getComponents()) ); + S32 components = getComponents(); + if (! ((1 == components) || (3 == components) || (4 == components) )) + { + LL_WARNS() << "Invalid getComponents value (" << components << ")" << LL_ENDL; + return false; + } if (isBufferInvalid()) { @@ -1446,67 +1451,53 @@ bool LLImageRaw::scale( S32 new_width, S32 new_height, bool scale_image_data ) if (scale_image_data) { - /* - S32 temp_data_size = old_width * new_height * getComponents(); - llassert_always(temp_data_size > 0); - std::vector<U8> temp_buffer(temp_data_size); - - // Vertical - for( S32 col = 0; col < old_width; col++ ) - { - copyLineScaled( getData() + (getComponents() * col), &temp_buffer[0] + (getComponents() * col), old_height, new_height, old_width, old_width ); - } - - deleteData(); + S32 new_data_size = new_width * new_height * components; - U8* new_buffer = allocateDataSize(new_width, new_height, getComponents()); + if (new_data_size > 0) + { + U8 *new_data = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), new_data_size); + if(NULL == new_data) + { + return false; + } - // Horizontal - for( S32 row = 0; row < new_height; row++ ) - { - copyLineScaled( &temp_buffer[0] + (getComponents() * old_width * row), new_buffer + (getComponents() * new_width * row), old_width, new_width, 1, 1 ); - } - */ - - S32 new_data_size = new_width * new_height * getComponents(); - llassert_always(new_data_size > 0); - - U8 *new_data = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), new_data_size); - if(NULL == new_data) - { - return false; + bilinear_scale(getData(), old_width, old_height, components, old_width*components, new_data, new_width, new_height, components, new_width*components); + setDataAndSize(new_data, new_width, new_height, components); } - - bilinear_scale(getData(), old_width, old_height, getComponents(), old_width*getComponents(), new_data, new_width, new_height, getComponents(), new_width*getComponents()); - setDataAndSize(new_data, new_width, new_height, getComponents()); } else { // copy out existing image data - S32 temp_data_size = old_width * old_height * getComponents(); + S32 temp_data_size = old_width * old_height * components; std::vector<U8> temp_buffer(temp_data_size); memcpy(&temp_buffer[0], getData(), temp_data_size); // allocate new image data, will delete old data - U8* new_buffer = allocateDataSize(new_width, new_height, getComponents()); - - for( S32 row = 0; row < new_height; row++ ) - { - if (row < old_height) - { - memcpy(new_buffer + (new_width * row * getComponents()), &temp_buffer[0] + (old_width * row * getComponents()), getComponents() * llmin(old_width, new_width)); - if (old_width < new_width) - { - // pad out rest of row with black - memset(new_buffer + (getComponents() * ((new_width * row) + old_width)), 0, getComponents() * (new_width - old_width)); - } - } - else - { - // pad remaining rows with black - memset(new_buffer + (new_width * row * getComponents()), 0, new_width * getComponents()); - } - } + U8* new_buffer = allocateDataSize(new_width, new_height, components); + + if (!new_buffer) + { + LL_WARNS() << "Failed to allocate new image data buffer" << LL_ENDL; + return false; + } + + for( S32 row = 0; row < new_height; row++ ) + { + if (row < old_height) + { + memcpy(new_buffer + (new_width * row * components), &temp_buffer[0] + (old_width * row * components), components * llmin(old_width, new_width)); + if (old_width < new_width) + { + // pad out rest of row with black + memset(new_buffer + (components * ((new_width * row) + old_width)), 0, components * (new_width - old_width)); + } + } + else + { + // pad remaining rows with black + memset(new_buffer + (new_width * row * components), 0, new_width * components); + } + } } return true ; diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp index aad139f570..da289ea889 100644 --- a/indra/llimage/llpngwrapper.cpp +++ b/indra/llimage/llpngwrapper.cpp @@ -31,6 +31,16 @@ #include "llimage.h" #include "llpngwrapper.h" +#include "llexception.h" + +namespace { +// Failure to load an image shouldn't crash the whole viewer. +struct PngError: public LLContinueError +{ + PngError(png_const_charp msg): LLContinueError(msg) {} +}; +} // anonymous namespace + // --------------------------------------------------------------------------- // LLPngWrapper // --------------------------------------------------------------------------- @@ -75,11 +85,10 @@ BOOL LLPngWrapper::isValidPng(U8* src) } // Called by the libpng library when a fatal encoding or decoding error -// occurs. We simply throw the error message and let our try/catch -// block clean up. +// occurs. We throw PngError and let our try/catch block clean up. void LLPngWrapper::errorHandler(png_structp png_ptr, png_const_charp msg) { - throw msg; + LLTHROW(PngError(msg)); } // Called by the libpng library when reading (decoding) the PNG file. We @@ -129,7 +138,7 @@ BOOL LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInf this, &errorHandler, NULL); if (mReadPngPtr == NULL) { - throw "Problem creating png read structure"; + LLTHROW(PngError("Problem creating png read structure")); } // Allocate/initialize the memory for image information. @@ -187,9 +196,9 @@ BOOL LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInf mFinalSize = dataPtr.mOffset; } - catch (png_const_charp msg) + catch (const PngError& msg) { - mErrorMessage = msg; + mErrorMessage = msg.what(); releaseResources(); return (FALSE); } @@ -288,14 +297,14 @@ BOOL LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest) if (mColorType == -1) { - throw "Unsupported image: unexpected number of channels"; + LLTHROW(PngError("Unsupported image: unexpected number of channels")); } mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, &errorHandler, NULL); if (!mWritePngPtr) { - throw "Problem creating png write structure"; + LLTHROW(PngError("Problem creating png write structure")); } mWriteInfoPtr = png_create_info_struct(mWritePngPtr); @@ -339,9 +348,9 @@ BOOL LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest) png_write_end(mWritePngPtr, mWriteInfoPtr); mFinalSize = dataPtr.mOffset; } - catch (png_const_charp msg) + catch (const PngError& msg) { - mErrorMessage = msg; + mErrorMessage = msg.what(); releaseResources(); return (FALSE); } diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 025c77b85e..aa405362e8 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -1,4 +1,4 @@ - /** +/** * @file llimagej2ckdu.cpp * @brief This is an implementation of JPEG2000 encode/decode using Kakadu * @@ -35,16 +35,51 @@ #include "kdu_block_coding.h" -#include <stdexcept> -#include <iostream> +#include "llexception.h" +#include <boost/exception/diagnostic_information.hpp> +#include <sstream> +#include <iomanip> namespace { -// exception used to keep KDU from terminating entire program -- see comments -// in LLKDUMessageError::flush() -struct KDUError: public std::runtime_error +// Failure to load an image shouldn't crash the whole viewer. +struct KDUError: public LLContinueError { - KDUError(const std::string& msg): std::runtime_error(msg) {} + KDUError(const std::string& msg): LLContinueError(msg) {} }; + +// KDU defines int error codes as hex values, so we should log them in hex +// so we can grep KDU headers for the hex. However those hex values +// generally "happen" to encode big-endian multibyte character sequences, +// e.g. KDU_ERROR_EXCEPTION is 0x6b647545: 'kduE' +// But beware because KDU_NULL_EXCEPTION is simply 0 -- which doesn't +// preclude somebody from throwing it. +std::string report_kdu_exception(kdu_exception mb) +{ + std::ostringstream out; + // always report mb in hex + out << "kdu_exception " << std::hex << mb; + + // Also display as many chars as are encoded in the kdu_exception + // value. Make a char array; reserve 1 extra byte for nul terminator. + char bytes[sizeof(kdu_exception) + 1]; + // Back up through 'bytes' + char *bptr = bytes + sizeof(bytes); + *(--bptr) = '\0'; + while (mb) + { + // store low-order byte of mb in next-left char + *(--bptr) = char(mb & 0xFF); + // then shift mb right by one byte + mb >>= 8; + } + // did that produce any characters? + if (*bptr) + { + out << " (" << bptr << ')'; + } + + return out.str(); +} } // anonymous namespace // stream kdu_dims to std::ostream @@ -195,7 +230,7 @@ struct LLKDUMessageError : public LLKDUMessage // shutdown will NOT engage the behavior described above. if (end_of_message) { - throw KDUError("LLKDUMessageError::flush()"); + LLTHROW(KDUError("LLKDUMessageError::flush()")); } } }; @@ -309,10 +344,10 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECod { // This method is only called from methods that catch KDUError. // We want to fail the image load, not crash the viewer. - throw KDUError(STRINGIZE("Component " << idx << " dimensions " + LLTHROW(KDUError(STRINGIZE("Component " << idx << " dimensions " << other_dims << " do not match component 0 dimensions " - << dims << "!")); + << dims << "!"))); } } @@ -439,9 +474,19 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco base.setLastError(msg.what()); return false; } + catch (kdu_exception kdu_value) + { + // KDU internally throws kdu_exception. It's possible that such an + // exception might leak out into our code. Catch kdu_exception + // specially because boost::current_exception_diagnostic_information() + // could do nothing with it. + base.setLastError(report_kdu_exception(kdu_value)); + return false; + } catch (...) { - base.setLastError("Unknown J2C error"); + base.setLastError("Unknown J2C error: " + + boost::current_exception_diagnostic_information()); return false; } @@ -537,9 +582,21 @@ bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco cleanupCodeStream(); return true; // done } + catch (kdu_exception kdu_value) + { + // KDU internally throws kdu_exception. It's possible that such an + // exception might leak out into our code. Catch kdu_exception + // specially because boost::current_exception_diagnostic_information() + // could do nothing with it. + base.setLastError(report_kdu_exception(kdu_value)); + base.decodeFailed(); + cleanupCodeStream(); + return true; // done + } catch (...) { - base.setLastError( "Unknown J2C error" ); + base.setLastError("Unknown J2C error: " + + boost::current_exception_diagnostic_information()); base.decodeFailed(); cleanupCodeStream(); return true; // done @@ -728,9 +785,19 @@ bool LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co base.setLastError(msg.what()); return false; } + catch (kdu_exception kdu_value) + { + // KDU internally throws kdu_exception. It's possible that such an + // exception might leak out into our code. Catch kdu_exception + // specially because boost::current_exception_diagnostic_information() + // could do nothing with it. + base.setLastError(report_kdu_exception(kdu_value)); + return false; + } catch( ... ) { - base.setLastError( "Unknown J2C error" ); + base.setLastError("Unknown J2C error: " + + boost::current_exception_diagnostic_information()); return false; } @@ -752,9 +819,19 @@ bool LLImageJ2CKDU::getMetadata(LLImageJ2C &base) base.setLastError(msg.what()); return false; } + catch (kdu_exception kdu_value) + { + // KDU internally throws kdu_exception. It's possible that such an + // exception might leak out into our code. Catch kdu_exception + // specially because boost::current_exception_diagnostic_information() + // could do nothing with it. + base.setLastError(report_kdu_exception(kdu_value)); + return false; + } catch (...) { - base.setLastError( "Unknown J2C error" ); + base.setLastError("Unknown J2C error: " + + boost::current_exception_diagnostic_information()); return false; } } diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index d932eb53a0..567ad9a414 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -2143,19 +2143,22 @@ BOOL LLVolume::generate() F32 profile_detail = mDetail; F32 path_detail = mDetail; - - U8 path_type = mParams.getPathParams().getCurveType(); - U8 profile_type = mParams.getProfileParams().getCurveType(); - - if (path_type == LL_PCODE_PATH_LINE && profile_type == LL_PCODE_PROFILE_CIRCLE) - { //cylinders don't care about Z-Axis - mLODScaleBias.setVec(0.6f, 0.6f, 0.0f); - } - else if (path_type == LL_PCODE_PATH_CIRCLE) - { - mLODScaleBias.setVec(0.6f, 0.6f, 0.6f); + + if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH) + { + U8 path_type = mParams.getPathParams().getCurveType(); + U8 profile_type = mParams.getProfileParams().getCurveType(); + if (path_type == LL_PCODE_PATH_LINE && profile_type == LL_PCODE_PROFILE_CIRCLE) + { + //cylinders don't care about Z-Axis + mLODScaleBias.setVec(0.6f, 0.6f, 0.0f); + } + else if (path_type == LL_PCODE_PATH_CIRCLE) + { + mLODScaleBias.setVec(0.6f, 0.6f, 0.6f); + } } - + BOOL regenPath = mPathp->generate(mParams.getPathParams(), path_detail, split); BOOL regenProf = mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(),profile_detail, split); diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 1ca5f58ae2..004db546b7 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -43,6 +43,8 @@ #include "llcoros.h" #include "lleventcoro.h" #include "llcorehttputil.h" +#include "llexception.h" +#include "stringize.h" #include <map> #include <set> @@ -231,13 +233,12 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLU LLAvatarNameCache::handleAvNameCacheSuccess(results, httpResults); } - catch (std::exception e) - { - LL_WARNS() << "Caught exception '" << e.what() << "'" << LL_ENDL; - } catch (...) { - LL_WARNS() << "Caught unknown exception." << LL_ENDL; + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName() + << "('" << url << "', " << agentIds.size() + << " Agent Ids)")); + throw; } } diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index d4c0788b7d..74cdff2b00 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -27,6 +27,8 @@ #include "linden_common.h" #include "llcoproceduremanager.h" +#include "llexception.h" +#include "stringize.h" #include <boost/assign.hpp> //========================================================================= @@ -388,14 +390,14 @@ void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdap { coproc->mProc(httpAdapter, coproc->mId); } - catch (std::exception &e) - { - LL_WARNS() << "Coprocedure(" << coproc->mName << ") id=" << coproc->mId.asString() << - " threw an exception! Message=\"" << e.what() << "\"" << LL_ENDL; - } catch (...) { - LL_WARNS() << "A non std::exception was thrown from " << coproc->mName << " with id=" << coproc->mId << "." << " in pool \"" << mPoolName << "\"" << LL_ENDL; + LOG_UNHANDLED_EXCEPTION(STRINGIZE("Coprocedure('" << coproc->mName + << "', id=" << coproc->mId.asString() + << ") in pool '" << mPoolName << "'")); + // must NOT omit this or we deplete the pool + mActiveCoprocs.erase(itActive); + throw; } LL_INFOS() << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL; diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index 7742cbc182..9bf38fb336 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -642,7 +642,7 @@ HttpRequestPumper::HttpRequestPumper(const LLCore::HttpRequest::ptr_t &request) mHttpRequest(request) { mBoundListener = LLEventPumps::instance().obtain("mainloop"). - listen(LLEventPump::inventName(), boost::bind(&HttpRequestPumper::pollRequest, this, _1)); + listen(LLEventPump::ANONYMOUS, boost::bind(&HttpRequestPumper::pollRequest, this, _1)); } HttpRequestPumper::~HttpRequestPumper() diff --git a/indra/llmessage/llhttpnode.cpp b/indra/llmessage/llhttpnode.cpp index f235965879..04b34a296c 100644 --- a/indra/llmessage/llhttpnode.cpp +++ b/indra/llmessage/llhttpnode.cpp @@ -31,6 +31,7 @@ #include "llstl.h" #include "llhttpconstants.h" +#include "llexception.h" const std::string CONTEXT_HEADERS("headers"); const std::string CONTEXT_PATH("path"); @@ -92,27 +93,28 @@ LLHTTPNode::~LLHTTPNode() namespace { - class NotImplemented + struct NotImplemented: public LLException { + NotImplemented(): LLException("LLHTTPNode::NotImplemented") {} }; } // virtual LLSD LLHTTPNode::simpleGet() const { - throw NotImplemented(); + LLTHROW(NotImplemented()); } // virtual LLSD LLHTTPNode::simplePut(const LLSD& input) const { - throw NotImplemented(); + LLTHROW(NotImplemented()); } // virtual LLSD LLHTTPNode::simplePost(const LLSD& input) const { - throw NotImplemented(); + LLTHROW(NotImplemented()); } @@ -172,7 +174,7 @@ void LLHTTPNode::del(LLHTTPNode::ResponsePtr response, const LLSD& context) cons // virtual LLSD LLHTTPNode::simpleDel(const LLSD&) const { - throw NotImplemented(); + LLTHROW(NotImplemented()); } // virtual diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index cecb2021e7..290b67feb3 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -4007,7 +4007,7 @@ void LLMessageSystem::sendUntrustedSimulatorMessageCoro(std::string url, std::st { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t - httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("groupMembersRequest", httpPolicy)); + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("untrustedSimulatorMessage", httpPolicy)); LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); diff --git a/indra/llmessage/tests/commtest.h b/indra/llmessage/tests/commtest.h index 0d149b5258..7c8f27bbd2 100644 --- a/indra/llmessage/tests/commtest.h +++ b/indra/llmessage/tests/commtest.h @@ -33,15 +33,15 @@ #include "llevents.h" #include "llsd.h" #include "llhost.h" +#include "llexception.h" #include "stringize.h" #include <map> #include <string> -#include <stdexcept> #include <boost/lexical_cast.hpp> -struct CommtestError: public std::runtime_error +struct CommtestError: public LLException { - CommtestError(const std::string& what): std::runtime_error(what) {} + CommtestError(const std::string& what): LLException(what) {} }; static bool query_verbose() @@ -68,7 +68,7 @@ static int query_port(const std::string& var) const char* cport = getenv(var.c_str()); if (! cport) { - throw CommtestError(STRINGIZE("missing environment variable" << var)); + LLTHROW(CommtestError(STRINGIZE("missing environment variable" << var))); } // This will throw, too, if the value of PORT isn't numeric. int port(boost::lexical_cast<int>(cport)); diff --git a/indra/llmessage/tests/networkio.h b/indra/llmessage/tests/networkio.h index 2aff90ca1e..5eb739393f 100644 --- a/indra/llmessage/tests/networkio.h +++ b/indra/llmessage/tests/networkio.h @@ -34,6 +34,7 @@ #include "llares.h" #include "llpumpio.h" #include "llhttpclient.h" +#include "llexception.h" /***************************************************************************** * NetworkIO @@ -51,7 +52,7 @@ public: ll_init_apr(); if (! gAPRPoolp) { - throw std::runtime_error("Can't initialize APR"); + LLTHROW(LLException("Can't initialize APR")); } // Create IO Pump to use for HTTP Requests. @@ -59,7 +60,7 @@ public: LLHTTPClient::setPump(*mServicePump); if (ll_init_ares() == NULL || !gAres->isInitialized()) { - throw std::runtime_error("Can't start DNS resolver"); + LLTHROW(LLException("Can't start DNS resolver")); } // You can interrupt pump() without waiting the full timeout duration diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index e45249b1cb..bac18fa374 100755 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -34,10 +34,8 @@ import sys from threading import Thread from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler -mydir = os.path.dirname(__file__) # expected to be .../indra/llmessage/tests/ -sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) -from indra.util.fastest_elementtree import parse as xml_parse -from indra.base import llsd +from llbase.fastest_elementtree import parse as xml_parse +from llbase import llsd from testrunner import freeport, run, debug, VERBOSE import time diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 3d173d0459..d672650658 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -116,7 +116,7 @@ void LLPluginClassMedia::reset() mMediaHeight = 0; mDirtyRect = LLRect::null; mAutoScaleMedia = false; - mRequestedVolume = 1.0f; + mRequestedVolume = 0.0f; mPriority = PRIORITY_NORMAL; mLowPrioritySizeLimit = LOW_PRIORITY_TEXTURE_SIZE_DEFAULT; mAllowDownsample = false; diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index 00bde8dbc3..8071d716da 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -1071,7 +1071,7 @@ bool LLDAELoader::OpenFile(const std::string& filename) std::string LLDAELoader::preprocessDAE(std::string filename) { // Open a DAE file for some preprocessing (like removing space characters in IDs), see MAINT-5678 - std::ifstream inFile; + llifstream inFile; inFile.open(filename.c_str(), std::ios_base::in); std::stringstream strStream; strStream << inFile.rdbuf(); diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 0e2946632a..cf0a117567 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -364,12 +364,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons if (right_x) { - F32 cr_x = (cur_x - origin.mV[VX]) / sScaleX; - if (*right_x < cr_x) - { - // rightmost edge of previously drawn text, don't draw over previous text - *right_x = cr_x; - } + *right_x = (cur_x - origin.mV[VX]) / sScaleX; } //FIXME: add underline as glyph? diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index b30bc1aed6..58c1186a3e 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -349,8 +349,8 @@ void LLGLSLShader::unloadInternal() for (GLsizei i = 0; i < count; i++) { glDetachObjectARB(mProgramObject, obj[i]); - glDeleteObjectARB(obj[i]); - } + glDeleteObjectARB(obj[i]); + } glDeleteObjectARB(mProgramObject); diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index ebed454271..81a5537f78 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -487,14 +487,15 @@ bool LLImageGL::checkSize(S32 width, S32 height) return check_power_of_two(width) && check_power_of_two(height); } -void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents, S32 discard_level) +bool LLImageGL::setSize(S32 width, S32 height, S32 ncomponents, S32 discard_level) { if (width != mWidth || height != mHeight || ncomponents != mComponents) { // Check if dimensions are a power of two! if (!checkSize(width,height)) { - LL_ERRS() << llformat("Texture has non power of two dimension: %dx%d",width,height) << LL_ENDL; + LL_WARNS() << llformat("Texture has non power of two dimension: %dx%d",width,height) << LL_ENDL; + return false; } if (mTexName) @@ -529,6 +530,8 @@ void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents, S32 discard_leve mMaxDiscardLevel = MAX_DISCARD_LEVEL; } } + + return true; } //---------------------------------------------------------------------------- @@ -909,7 +912,11 @@ BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image) S32 h = raw_image->getHeight() << discard_level; // setSize may call destroyGLTexture if the size does not match - setSize(w, h, raw_image->getComponents(), discard_level); + if (!setSize(w, h, raw_image->getComponents(), discard_level)) + { + LL_WARNS() << "Trying to create a texture with incorrect dimensions!" << LL_ENDL; + return FALSE; + } if( !mHasExplicitFormat ) { @@ -1273,7 +1280,11 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S S32 h = raw_h << discard_level; // setSize may call destroyGLTexture if the size does not match - setSize(w, h, imageraw->getComponents(), discard_level); + if (!setSize(w, h, imageraw->getComponents(), discard_level)) + { + LL_WARNS() << "Trying to create a texture with incorrect dimensions!" << LL_ENDL; + return FALSE; + } if( !mHasExplicitFormat ) { diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 21982eab1d..ad2aea9067 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -94,7 +94,7 @@ protected: public: virtual void dump(); // debugging info to LL_INFOS() - void setSize(S32 width, S32 height, S32 ncomponents, S32 discard_level = -1); + bool setSize(S32 width, S32 height, S32 ncomponents, S32 discard_level = -1); void setComponents(S32 ncomponents) { mComponents = (S8)ncomponents ;} void setAllowCompression(bool allow) { mAllowCompression = allow; } diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index b297223c2e..55f0791174 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -520,7 +520,7 @@ void LLShaderMgr::dumpObjectLog(GLhandleARB ret, BOOL warns, const std::string& if (!filename.empty()) { LL_CONT << "From " << filename << ":\n"; - } + } LL_CONT << log << LL_ENDL; } } diff --git a/indra/llui/llclipboard.cpp b/indra/llui/llclipboard.cpp index 1d18cb2bb0..06fac190ed 100644 --- a/indra/llui/llclipboard.cpp +++ b/indra/llui/llclipboard.cpp @@ -123,7 +123,15 @@ bool LLClipboard::copyToClipboard(const LLWString &src, S32 pos, S32 len, bool u // Concatenate the input string to the LL and the system clipboard bool LLClipboard::addToClipboard(const LLWString &src, S32 pos, S32 len, bool use_primary) { - mString = src.substr(pos, len); + try + { + mString = src.substr(pos, len); + } + catch (const std::exception& e) + { + LL_WARNS() << "Can't add the substring to clipboard: " << e.what() << LL_ENDL; + return false; + } return (use_primary ? LLView::getWindow()->copyTextToPrimary(mString) : LLView::getWindow()->copyTextToClipboard(mString)); } diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index 8166ef6a07..f9664e0658 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -684,6 +684,13 @@ void LLFolderView::draw() } } + if (mRenameItem && mRenamer && mRenamer->getVisible() && !getVisibleRect().overlaps(mRenamer->getRect())) + { + // renamer is not connected to the item we are renaming in any form so manage it manually + // TODO: consider stopping on any scroll action instead of when out of visible area + finishRenamingItem(); + } + // skip over LLFolderViewFolder::draw since we don't want the folder icon, label, // and arrow for the root folder LLView::draw(); diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 5eb5ca4f82..3d618548c4 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -127,6 +127,8 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mIsSelected( FALSE ), mIsCurSelection( FALSE ), mSelectPending(FALSE), + mIsItemCut(false), + mCutGeneration(0), mLabelStyle( LLFontGL::NORMAL ), mHasVisibleChildren(FALSE), mIsFolderComplete(true), @@ -694,6 +696,19 @@ void LLFolderViewItem::drawOpenFolderArrow(const Params& default_params, const L return mIsCurSelection; } +/*virtual*/ bool LLFolderViewItem::isFadeItem() +{ + LLClipboard& clipboard = LLClipboard::instance(); + if (mCutGeneration != clipboard.getGeneration()) + { + mCutGeneration = clipboard.getGeneration(); + mIsItemCut = clipboard.isCutMode() + && ((getParentFolder() && getParentFolder()->isFadeItem()) + || getViewModelItem()->isCutToClipboard()); + } + return mIsItemCut; +} + void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, const LLUIColor &focusOutlineColor, const LLUIColor &mouseOverColor) { @@ -875,6 +890,12 @@ void LLFolderViewItem::draw() } LLColor4 color = (mIsSelected && filled) ? mFontHighlightColor : mFontColor; + + if (isFadeItem()) + { + // Fade out item color to indicate it's being cut + color.mV[VALPHA] *= 0.5f; + } drawLabel(font, text_left, y, color, right_x); //--------------------------------------------------------------------------------// @@ -882,7 +903,7 @@ void LLFolderViewItem::draw() // if (!mLabelSuffix.empty()) { - font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor, + font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &right_x, FALSE ); } diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index 0322c8836d..61c39e0175 100644 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -121,8 +121,11 @@ protected: mIsMouseOverTitle, mAllowWear, mAllowDrop, - mSelectPending; - + mSelectPending, + mIsItemCut; + + S32 mCutGeneration; + LLUIColor mFontColor; LLUIColor mFontHighlightColor; @@ -145,6 +148,7 @@ protected: virtual void addFolder(LLFolderViewFolder*) { } virtual bool isHighlightAllowed(); virtual bool isHighlightActive(); + virtual bool isFadeItem(); virtual bool isFlashing() { return false; } virtual void setFlashState(bool) { } diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index a395af537a..641241a88c 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -173,6 +173,7 @@ public: virtual BOOL isItemCopyable() const = 0; virtual BOOL copyToClipboard() const = 0; virtual BOOL cutToClipboard() = 0; + virtual bool isCutToClipboard() { return false; }; virtual BOOL isClipboardPasteable() const = 0; virtual void pasteFromClipboard() = 0; diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 492c9315d1..c89e1dac1d 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -2636,10 +2636,17 @@ void LLLineEditor::showContextMenu(S32 x, S32 y) void LLLineEditor::setContextMenu(LLContextMenu* new_context_menu) { - if (new_context_menu) - mContextMenuHandle = new_context_menu->getHandle(); - else - mContextMenuHandle.markDead(); + LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get()); + if (menu) + { + menu->die(); + mContextMenuHandle.markDead(); + } + + if (new_context_menu) + { + mContextMenuHandle = new_context_menu->getHandle(); + } } void LLLineEditor::setFont(const LLFontGL* font) diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index c6d472f59b..ccbd305a16 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -272,7 +272,7 @@ public: void setReplaceNewlinesWithSpaces(BOOL replace); - void setContextMenu(LLContextMenu* new_context_menu); + void resetContextMenu() { setContextMenu(NULL); }; private: // private helper methods @@ -308,6 +308,8 @@ private: virtual S32 getPreeditFontSize() const; virtual LLWString getPreeditString() const { return getWText(); } + void setContextMenu(LLContextMenu* new_context_menu); + protected: LLUIString mText; // The string being edited. std::string mPrevText; // Saved string for 'ESC' revert diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index c7d7535f87..20be739286 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -181,7 +181,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) mMaxTextByteLength( p.max_text_length ), mFont(p.font), mFontShadow(p.font_shadow), - mPopupMenu(NULL), + mPopupMenuHandle(), mReadOnly(p.read_only), mSpellCheck(p.spellcheck), mSpellCheckStart(-1), @@ -1263,9 +1263,10 @@ void LLTextBase::setReadOnlyColor(const LLColor4 &c) //virtual void LLTextBase::onVisibilityChange( BOOL new_visibility ) { - if(!new_visibility && mPopupMenu) + LLContextMenu* menu = static_cast<LLContextMenu*>(mPopupMenuHandle.get()); + if(!new_visibility && menu) { - mPopupMenu->hide(); + menu->hide(); } LLUICtrl::onVisibilityChange(new_visibility); } @@ -1956,41 +1957,48 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url) registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url)); // create and return the context menu from the XUI file - delete mPopupMenu; + + LLContextMenu* menu = static_cast<LLContextMenu*>(mPopupMenuHandle.get()); + if (menu) + { + menu->die(); + mPopupMenuHandle.markDead(); + } llassert(LLMenuGL::sMenuContainer != NULL); - mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(xui_file, LLMenuGL::sMenuContainer, - LLMenuHolderGL::child_registry_t::instance()); - if (mIsFriendSignal) - { - bool isFriend = *(*mIsFriendSignal)(LLUUID(LLUrlAction::getUserID(url))); - LLView* addFriendButton = mPopupMenu->getChild<LLView>("add_friend"); - LLView* removeFriendButton = mPopupMenu->getChild<LLView>("remove_friend"); + menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(xui_file, LLMenuGL::sMenuContainer, + LLMenuHolderGL::child_registry_t::instance()); + if (menu) + { + mPopupMenuHandle = menu->getHandle(); - if (addFriendButton && removeFriendButton) - { - addFriendButton->setEnabled(!isFriend); - removeFriendButton->setEnabled(isFriend); - } - } + if (mIsFriendSignal) + { + bool isFriend = *(*mIsFriendSignal)(LLUUID(LLUrlAction::getUserID(url))); + LLView* addFriendButton = menu->getChild<LLView>("add_friend"); + LLView* removeFriendButton = menu->getChild<LLView>("remove_friend"); - if (mIsObjectBlockedSignal) - { - bool is_blocked = *(*mIsObjectBlockedSignal)(LLUUID(LLUrlAction::getObjectId(url)), LLUrlAction::getObjectName(url)); - LLView* blockButton = mPopupMenu->getChild<LLView>("block_object"); - LLView* unblockButton = mPopupMenu->getChild<LLView>("unblock_object"); + if (addFriendButton && removeFriendButton) + { + addFriendButton->setEnabled(!isFriend); + removeFriendButton->setEnabled(isFriend); + } + } - if (blockButton && unblockButton) - { - blockButton->setVisible(!is_blocked); - unblockButton->setVisible(is_blocked); - } - } - - if (mPopupMenu) - { - mPopupMenu->show(x, y); - LLMenuGL::showPopup(this, mPopupMenu, x, y); - } + if (mIsObjectBlockedSignal) + { + bool is_blocked = *(*mIsObjectBlockedSignal)(LLUUID(LLUrlAction::getObjectId(url)), LLUrlAction::getObjectName(url)); + LLView* blockButton = menu->getChild<LLView>("block_object"); + LLView* unblockButton = menu->getChild<LLView>("unblock_object"); + + if (blockButton && unblockButton) + { + blockButton->setVisible(!is_blocked); + unblockButton->setVisible(is_blocked); + } + } + menu->show(x, y); + LLMenuGL::showPopup(this, menu, x, y); + } } void LLTextBase::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params) diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 85641fd899..3d3a6ca869 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -673,7 +673,7 @@ protected: S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes // support widgets - LLContextMenu* mPopupMenu; + LLHandle<LLContextMenu> mPopupMenuHandle; LLView* mDocumentView; LLScrollContainer* mScroller; diff --git a/indra/llui/lltextvalidate.cpp b/indra/llui/lltextvalidate.cpp index 324ceb7fba..bfe0a5bb5d 100644 --- a/indra/llui/lltextvalidate.cpp +++ b/indra/llui/lltextvalidate.cpp @@ -328,6 +328,15 @@ namespace LLTextValidate return rv; } + bool validateASCIINoLeadingSpace(const LLWString &str) + { + if (LLStringOps::isSpace(str[0])) + { + return FALSE; + } + return validateASCII(str); + } + // Used for multiline text stored on the server. // Example is landmark description in Places SP. bool validateASCIIWithNewLine(const LLWString &str) diff --git a/indra/llui/lltextvalidate.h b/indra/llui/lltextvalidate.h index 5c830d7db3..e2b6c313d6 100644 --- a/indra/llui/lltextvalidate.h +++ b/indra/llui/lltextvalidate.h @@ -52,6 +52,7 @@ namespace LLTextValidate bool validateASCIIPrintableNoPipe(const LLWString &str); bool validateASCIIPrintableNoSpace(const LLWString &str); bool validateASCII(const LLWString &str); + bool validateASCIINoLeadingSpace(const LLWString &str); bool validateASCIIWithNewLine(const LLWString &str); } diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index aabc7ed2e4..f790d8e005 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -522,7 +522,7 @@ const LLView* LLUI::resolvePath(const LLView* context, const std::string& path) else { std::string part(ti->begin(), ti->end()); - context = context->findChildView(part, recurse); + context = context->findChildView(LLURI::unescape(part), recurse); recurse = false; } } diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index e4848362a7..27a2456deb 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -183,8 +183,9 @@ bool LLUrlEntryBase::isLinkDisabled() const bool LLUrlEntryBase::isWikiLinkCorrect(std::string url) { - std::string label = getLabelFromWikiLink(url); - return (LLUrlRegistry::instance().hasUrl(label)) ? false : true; + LLWString label = utf8str_to_wstring(getLabelFromWikiLink(url)); + label.erase(std::remove(label.begin(), label.end(), L'\u200B'), label.end()); + return (LLUrlRegistry::instance().hasUrl(wstring_to_utf8str(label))) ? false : true; } std::string LLUrlEntryBase::urlToLabelWithGreyQuery(const std::string &url) const @@ -205,9 +206,15 @@ std::string LLUrlEntryBase::urlToGreyQuery(const std::string &url) const std::string label; up.extractParts(); - up.glueFirst(label); - std::string query = url.substr(label.size()); - return query; + up.glueFirst(label, false); + + size_t pos = url.find(label); + if (pos == std::string::npos) + { + return ""; + } + pos += label.size(); + return url.substr(pos); } diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 62c3f401bf..9604e5ce10 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -391,7 +391,27 @@ static void buildPathname(std::ostream& out, const LLView* view) buildPathname(out, view->getParent()); // Build pathname into ostream on the way back from recursion. - out << '/' << view->getName(); + out << '/'; + + // substitute all '/' in name with appropriate code + std::string name = view->getName(); + std::size_t found = name.find('/'); + std::size_t start = 0; + while (found != std::string::npos) + { + std::size_t sub_len = found - start; + if (sub_len > 0) + { + out << name.substr(start, sub_len); + } + out << "%2F"; + start = found + 1; + found = name.find('/', start); + } + if (start < name.size()) + { + out << name.substr(start, name.size() - start); + } } std::string LLView::getPathname() const @@ -812,7 +832,7 @@ LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask) LLView* LLView::childFromPoint(S32 x, S32 y, bool recur) { if (!getVisible()) - return false; + return NULL; BOOST_FOREACH(LLView* viewp, mChildList) { diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 5f4fb8f4a0..86a15f2ef2 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -531,6 +531,13 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd case LL_PATH_PER_ACCOUNT_CHAT_LOGS: prefix = getPerAccountChatLogsDir(); + if (prefix.empty()) + { + // potentially directory was not set yet + // intentionally return a blank string to the caller + LL_DEBUGS("LLDir") << "Conversation log directory is not yet set" << LL_ENDL; + return std::string(); + } break; case LL_PATH_LOGS: diff --git a/indra/llwindow/llappdelegate-objc.h b/indra/llwindow/llappdelegate-objc.h index faa5d3abb7..6daf1ac55b 100644 --- a/indra/llwindow/llappdelegate-objc.h +++ b/indra/llwindow/llappdelegate-objc.h @@ -41,7 +41,7 @@ @property (retain) NSString *currentInputLanguage; -- (void) mainLoop; +- (void) oneFrame; - (void) showInputWindow:(bool)show withEvent:(NSEvent*)textEvent; - (void) languageUpdated; - (bool) romanScript; diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index 0a30f4c807..a05ba8cbba 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -166,6 +166,8 @@ public: // Provide native key event data virtual LLSD getNativeKeyData() { return LLSD::emptyMap(); } + // Get system UI size based on DPI (for 96 DPI UI size should be 1.0) + virtual F32 getSystemUISize() { return 1.0; } protected: LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags); virtual ~LLWindow(); diff --git a/indra/llwindow/llwindowcallbacks.cpp b/indra/llwindow/llwindowcallbacks.cpp index d2afb3f91b..474953d3a4 100644 --- a/indra/llwindow/llwindowcallbacks.cpp +++ b/indra/llwindow/llwindowcallbacks.cpp @@ -175,6 +175,11 @@ BOOL LLWindowCallbacks::handleDeviceChange(LLWindow *window) return FALSE; } +void LLWindowCallbacks::handleDPIChanged(LLWindow *window, F32 ui_scale_factor, S32 window_width, S32 window_height) +{ + +} + void LLWindowCallbacks::handlePingWatchdog(LLWindow *window, const char * msg) { diff --git a/indra/llwindow/llwindowcallbacks.h b/indra/llwindow/llwindowcallbacks.h index 6a7137e593..de789a71d9 100644 --- a/indra/llwindow/llwindowcallbacks.h +++ b/indra/llwindow/llwindowcallbacks.h @@ -65,6 +65,7 @@ public: virtual void handleDataCopy(LLWindow *window, S32 data_type, void *data); virtual BOOL handleTimerEvent(LLWindow *window); virtual BOOL handleDeviceChange(LLWindow *window); + virtual void handleDPIChanged(LLWindow *window, F32 ui_scale_factor, S32 window_width, S32 window_height); enum DragNDropAction { DNDA_START_TRACKING = 0,// Start tracking an incoming drag diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h index c22f3382fb..b06cd2c184 100644 --- a/indra/llwindow/llwindowmacosx-objc.h +++ b/indra/llwindow/llwindowmacosx-objc.h @@ -69,7 +69,7 @@ typedef const NativeKeyEventData * NSKeyEventRef; // These are defined in llappviewermacosx.cpp. bool initViewer(); void handleQuit(); -bool runMainLoop(); +bool pumpMainLoop(); void initMainLoop(); void cleanupViewer(); void handleUrl(const char* url); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 875ffe4cd4..4086db8e52 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -60,24 +60,50 @@ #include <dinput.h> #include <Dbt.h.> -// culled from winuser.h -#ifndef WM_MOUSEWHEEL /* Added to be compatible with later SDK's */ -const S32 WM_MOUSEWHEEL = 0x020A; -#endif -#ifndef WHEEL_DELTA /* Added to be compatible with later SDK's */ -const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */ -#endif const S32 MAX_MESSAGE_PER_UPDATE = 20; const S32 BITS_PER_PIXEL = 32; const S32 MAX_NUM_RESOLUTIONS = 32; const F32 ICON_FLASH_TIME = 0.5f; +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif + extern BOOL gDebugWindowProc; LPWSTR gIconResource = IDI_APPLICATION; LLW32MsgCallback gAsyncMsgCallback = NULL; +#ifndef DPI_ENUMS_DECLARED + +typedef enum PROCESS_DPI_AWARENESS { + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 +} PROCESS_DPI_AWARENESS; + +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; + +#endif + +typedef HRESULT(STDAPICALLTYPE *SetProcessDpiAwarenessType)(_In_ PROCESS_DPI_AWARENESS value); + +typedef HRESULT(STDAPICALLTYPE *GetProcessDpiAwarenessType)( + _In_ HANDLE hprocess, + _Out_ PROCESS_DPI_AWARENESS *value); + +typedef HRESULT(STDAPICALLTYPE *GetDpiForMonitorType)( + _In_ HMONITOR hmonitor, + _In_ MONITOR_DPI_TYPE dpiType, + _Out_ UINT *dpiX, + _Out_ UINT *dpiY); + // // LLWindowWin32 // @@ -2593,6 +2619,24 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ return 0; } + + case WM_DPICHANGED: + { + LPRECT lprc_new_scale; + F32 new_scale = LOWORD(w_param) / USER_DEFAULT_SCREEN_DPI; + lprc_new_scale = (LPRECT)l_param; + S32 new_width = lprc_new_scale->right - lprc_new_scale->left; + S32 new_height = lprc_new_scale->bottom - lprc_new_scale->top; + window_imp->mCallbacks->handleDPIChanged(window_imp, new_scale, new_width, new_height); + SetWindowPos(h_wnd, + HWND_TOP, + lprc_new_scale->left, + lprc_new_scale->top, + new_width, + new_height, + SWP_NOZORDER | SWP_NOACTIVATE); + return 0; + } case WM_SETFOCUS: window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_SETFOCUS"); @@ -3879,6 +3923,92 @@ BOOL LLWindowWin32::handleImeRequests(U32 request, U32 param, LRESULT *result) } //static +void LLWindowWin32::setDPIAwareness() +{ + HMODULE hShcore = LoadLibrary(L"shcore.dll"); + if (hShcore != NULL) + { + SetProcessDpiAwarenessType pSPDA; + pSPDA = (SetProcessDpiAwarenessType)GetProcAddress(hShcore, "SetProcessDpiAwareness"); + if (pSPDA) + { + + HRESULT hr = pSPDA(PROCESS_PER_MONITOR_DPI_AWARE); + if (hr != S_OK) + { + LL_WARNS() << "SetProcessDpiAwareness() function returned an error. Will use legacy DPI awareness API of Win XP/7" << LL_ENDL; + } + } + FreeLibrary(hShcore); + } + else + { + LL_WARNS() << "Could not load shcore.dll library (included by <ShellScalingAPI.h> from Win 8.1 SDK. Will use legacy DPI awareness API of Win XP/7" << LL_ENDL; + } +} + +F32 LLWindowWin32::getSystemUISize() +{ + float scale_value = 0; + HWND hWnd = (HWND)getPlatformWindow(); + HDC hdc = GetDC(hWnd); + HMONITOR hMonitor; + HANDLE hProcess = GetCurrentProcess(); + PROCESS_DPI_AWARENESS dpi_awareness; + + HMODULE hShcore = LoadLibrary(L"shcore.dll"); + + if (hShcore != NULL) + { + GetProcessDpiAwarenessType pGPDA; + pGPDA = (GetProcessDpiAwarenessType)GetProcAddress(hShcore, "GetProcessDpiAwareness"); + GetDpiForMonitorType pGDFM; + pGDFM = (GetDpiForMonitorType)GetProcAddress(hShcore, "GetDpiForMonitor"); + if (pGPDA != NULL && pGDFM != NULL) + { + pGPDA(hProcess, &dpi_awareness); + if (dpi_awareness == PROCESS_PER_MONITOR_DPI_AWARE) + { + POINT pt; + UINT dpix = 0, dpiy = 0; + HRESULT hr = E_FAIL; + RECT rect; + + GetWindowRect(hWnd, &rect); + // Get the DPI for the monitor, on which the center of window is displayed and set the scaling factor + pt.x = (rect.left + rect.right) / 2; + pt.y = (rect.top + rect.bottom) / 2; + hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + hr = pGDFM(hMonitor, MDT_EFFECTIVE_DPI, &dpix, &dpiy); + if (hr == S_OK) + { + scale_value = dpix / USER_DEFAULT_SCREEN_DPI; + } + else + { + LL_WARNS() << "Could not determine DPI for monitor. Setting scale to default 100 %" << LL_ENDL; + scale_value = 1.0f; + } + } + else + { + LL_WARNS() << "Process is not per-monitor DPI-aware. Setting scale to default 100 %" << LL_ENDL; + scale_value = 1.0f; + } + } + FreeLibrary(hShcore); + } + else + { + LL_WARNS() << "Could not load shcore.dll library (included by <ShellScalingAPI.h> from Win 8.1 SDK). Using legacy DPI awareness API of Win XP/7" << LL_ENDL; + scale_value = GetDeviceCaps(hdc, LOGPIXELSX) / USER_DEFAULT_SCREEN_DPI; + } + + ReleaseDC(hWnd, hdc); + return scale_value; +} + +//static std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList() { // Fonts previously in getFontListSans() have moved to fonts.xml. diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index 1a775eadaf..39ef9b31a4 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -110,10 +110,12 @@ public: /*virtual*/ void interruptLanguageTextInput(); /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async); + /*virtual*/ F32 getSystemUISize(); + LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url ); static std::vector<std::string> getDynamicFallbackFontList(); - + static void setDPIAwareness(); protected: LLWindowWin32(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, diff --git a/indra/mac_crash_logger/llcrashloggermac.cpp b/indra/mac_crash_logger/llcrashloggermac.cpp index 3149fad6e8..ec3616e26a 100644 --- a/indra/mac_crash_logger/llcrashloggermac.cpp +++ b/indra/mac_crash_logger/llcrashloggermac.cpp @@ -64,7 +64,7 @@ void LLCrashLoggerMac::gatherPlatformSpecificFiles() { } -bool LLCrashLoggerMac::mainLoop() +bool LLCrashLoggerMac::frame() { if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND) diff --git a/indra/mac_crash_logger/llcrashloggermac.h b/indra/mac_crash_logger/llcrashloggermac.h index 6d8f63ecac..05ef8c9f53 100644 --- a/indra/mac_crash_logger/llcrashloggermac.h +++ b/indra/mac_crash_logger/llcrashloggermac.h @@ -37,7 +37,7 @@ public: LLCrashLoggerMac(void); ~LLCrashLoggerMac(void); virtual bool init(); - virtual bool mainLoop(); + virtual bool frame(); virtual bool cleanup(); virtual void gatherPlatformSpecificFiles(); }; diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp index 95d4e65207..54e41a1954 100644 --- a/indra/mac_crash_logger/mac_crash_logger.cpp +++ b/indra/mac_crash_logger/mac_crash_logger.cpp @@ -49,7 +49,7 @@ int main(int argc, char **argv) { // return NSApplicationMain(argc, (const char **)argv); } - app.mainLoop(); + app.frame(); app.cleanup(); LL_INFOS() << "Crash reporter finished normally." << LL_ENDL; diff --git a/indra/media_plugins/CMakeLists.txt b/indra/media_plugins/CMakeLists.txt index 24eb3947b4..9055e0111a 100644 --- a/indra/media_plugins/CMakeLists.txt +++ b/indra/media_plugins/CMakeLists.txt @@ -4,15 +4,18 @@ add_subdirectory(base) if (LINUX) add_subdirectory(gstreamer010) + add_subdirectory(libvlc) endif (LINUX) -if (WINDOWS OR DARWIN) +if (DARWIN) add_subdirectory(quicktime) add_subdirectory(cef) -endif (WINDOWS OR DARWIN) +endif (DARWIN) if (WINDOWS) + add_subdirectory(cef) add_subdirectory(winmmshim) + add_subdirectory(libvlc) endif (WINDOWS) ### add_subdirectory(example) diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp index 8d9d1dd975..28a8a5886a 100644 --- a/indra/media_plugins/cef/media_plugin_cef.cpp +++ b/indra/media_plugins/cef/media_plugin_cef.cpp @@ -100,6 +100,12 @@ private: LLCEFLib* mLLCEFLib; VolumeCatcher mVolumeCatcher; + + U8 *mPopupBuffer; + U32 mPopupW; + U32 mPopupH; + U32 mPopupX; + U32 mPopupY; }; //////////////////////////////////////////////////////////////////////////////// @@ -127,12 +133,19 @@ MediaPluginBase(host_send_func, host_user_data) mCookiePath = ""; mPickedFile = ""; mLLCEFLib = new LLCEFLib(); + + mPopupBuffer = NULL; + mPopupW = 0; + mPopupH = 0; + mPopupX = 0; + mPopupY = 0; } //////////////////////////////////////////////////////////////////////////////// // MediaPluginCEF::~MediaPluginCEF() { + delete[] mPopupBuffer; } //////////////////////////////////////////////////////////////////////////////// @@ -155,20 +168,28 @@ void MediaPluginCEF::postDebugMessage(const std::string& msg) // void MediaPluginCEF::onPageChangedCallback(unsigned char* pixels, int x, int y, int width, int height, bool is_popup) { - if (mPixels && pixels) + if( is_popup ) + { + delete mPopupBuffer; + mPopupBuffer = NULL; + mPopupH = 0; + mPopupW = 0; + mPopupX = 0; + mPopupY = 0; + } + + if( mPixels && pixels ) { if (is_popup) { - for (int line = 0; line < height; ++line) + if( width > 0 && height> 0 ) { - int inverted_y = mHeight - y - height; - int src = line * width * mDepth; - int dst = (inverted_y + line) * mWidth * mDepth + x * mDepth; - - if (dst + width * mDepth < mWidth * mHeight * mDepth) - { - memcpy(mPixels + dst, pixels + src, width * mDepth); - } + mPopupBuffer = new U8[ width * height * mDepth ]; + memcpy( mPopupBuffer, pixels, width * height * mDepth ); + mPopupH = height; + mPopupW = width; + mPopupX = x; + mPopupY = mHeight - y - height; } } else @@ -177,6 +198,23 @@ void MediaPluginCEF::onPageChangedCallback(unsigned char* pixels, int x, int y, { memcpy(mPixels, pixels, mWidth * mHeight * mDepth); } + if( mPopupBuffer && mPopupH && mPopupW ) + { + U32 bufferSize = mWidth * mHeight * mDepth; + U32 popupStride = mPopupW * mDepth; + U32 bufferStride = mWidth * mDepth; + int dstY = mPopupY; + + int src = 0; + int dst = dstY * mWidth * mDepth + mPopupX * mDepth; + + for( int line = 0; dst + popupStride < bufferSize && line < mPopupH; ++line ) + { + memcpy( mPixels + dst, mPopupBuffer + src, popupStride ); + src += popupStride; + dst += bufferStride; + } + } } setDirty(0, 0, mWidth, mHeight); @@ -559,6 +597,8 @@ void MediaPluginCEF::receiveMessage(const char* message_string) S32 x = message_in.getValueS32("x"); S32 y = message_in.getValueS32("y"); + y = mHeight - y; + // only even send left mouse button events to LLCEFLib // (partially prompted by crash in OS X CEF when sending right button events) // we catch the right click in viewer and display our own context menu anyway diff --git a/indra/media_plugins/libvlc/CMakeLists.txt b/indra/media_plugins/libvlc/CMakeLists.txt new file mode 100644 index 0000000000..535d29125b --- /dev/null +++ b/indra/media_plugins/libvlc/CMakeLists.txt @@ -0,0 +1,86 @@ +# -*- cmake -*- + +project(media_plugin_libvlc) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLPlugin) +include(LLMath) +include(LLRender) +include(LLWindow) +include(Linking) +include(PluginAPI) +include(MediaPluginBase) +include(OpenGL) + +include(LibVLCPlugin) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${LLRENDER_INCLUDE_DIRS} + ${LLWINDOW_INCLUDE_DIRS} + ${VLC_INCLUDE_DIR} +) +include_directories(SYSTEM + ${LLCOMMON_SYSTEM_INCLUDE_DIRS} + ) + + +### media_plugin_libvlc + +if(NOT WORD_SIZE EQUAL 32) + if(WINDOWS) + add_definitions(/FIXED:NO) + else(WINDOWS) # not windows therefore gcc LINUX and DARWIN + add_definitions(-fPIC) + endif(WINDOWS) +endif(NOT WORD_SIZE EQUAL 32) + +set(media_plugin_libvlc_SOURCE_FILES + media_plugin_libvlc.cpp + ) + +add_library(media_plugin_libvlc + SHARED + ${media_plugin_libvlc_SOURCE_FILES} +) + +target_link_libraries(media_plugin_libvlc + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${VLC_PLUGIN_LIBRARIES} + ${PLUGIN_API_WINDOWS_LIBRARIES} +) + +add_dependencies(media_plugin_libvlc + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} +) + +if (WINDOWS) + set_target_properties( + media_plugin_libvlc + PROPERTIES + LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /LTCG /NODEFAULTLIB:LIBCMT" + ) +endif (WINDOWS) + +if (DARWIN) + # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name + set_target_properties( + media_plugin_libvlc + PROPERTIES + PREFIX "" + BUILD_WITH_INSTALL_RPATH 1 + INSTALL_NAME_DIR "@executable_path" + LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp" + ) + +endif (DARWIN) diff --git a/indra/media_plugins/libvlc/media_plugin_libvlc.cpp b/indra/media_plugins/libvlc/media_plugin_libvlc.cpp new file mode 100644 index 0000000000..0bd323eb58 --- /dev/null +++ b/indra/media_plugins/libvlc/media_plugin_libvlc.cpp @@ -0,0 +1,618 @@ +/** +* @file media_plugin_libvlc.cpp +* @brief LibVLC plugin for LLMedia API plugin system +* +* @cond +* $LicenseInfo:firstyear=2008&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2010, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +* @endcond +*/ + +#include "linden_common.h" + +#include "llgl.h" +#include "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" +#include "media_plugin_base.h" + +#include "vlc/vlc.h" +#include "vlc/libvlc_version.h" + +//////////////////////////////////////////////////////////////////////////////// +// +class MediaPluginLibVLC : + public MediaPluginBase +{ +public: + MediaPluginLibVLC(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + ~MediaPluginLibVLC(); + + /*virtual*/ void receiveMessage(const char* message_string); + +private: + bool init(); + + void initVLC(); + void playMedia(); + void resetVLC(); + void setVolume(const F64 volume); + void updateTitle(const char* title); + + static void* lock(void* data, void** p_pixels); + static void unlock(void* data, void* id, void* const* raw_pixels); + static void display(void* data, void* id); + + /*virtual*/ void setDirty(int left, int top, int right, int bottom) /* override, but that is not supported in gcc 4.6 */; + + static void eventCallbacks(const libvlc_event_t* event, void* ptr); + + libvlc_instance_t* mLibVLC; + libvlc_media_t* mLibVLCMedia; + libvlc_media_player_t* mLibVLCMediaPlayer; + + struct mLibVLCContext + { + unsigned char* texture_pixels; + libvlc_media_player_t* mp; + MediaPluginLibVLC* parent; + }; + struct mLibVLCContext mLibVLCCallbackContext; + + std::string mURL; + F64 mCurVolume; + + bool mIsLooping; + + float mCurTime; + float mDuration; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +MediaPluginLibVLC::MediaPluginLibVLC(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) : +MediaPluginBase(host_send_func, host_user_data) +{ + mTextureWidth = 0; + mTextureHeight = 0; + mWidth = 0; + mHeight = 0; + mDepth = 4; + mPixels = 0; + + mLibVLC = 0; + mLibVLCMedia = 0; + mLibVLCMediaPlayer = 0; + + mCurVolume = 0.0; + + mIsLooping = false; + + mCurTime = 0.0; + mDuration = 0.0; + + mURL = std::string(); + + setStatus(STATUS_NONE); +} + +//////////////////////////////////////////////////////////////////////////////// +// +MediaPluginLibVLC::~MediaPluginLibVLC() +{ +} + +///////////////////////////////////////////////////////////////////////////////// +// +void* MediaPluginLibVLC::lock(void* data, void** p_pixels) +{ + struct mLibVLCContext* context = (mLibVLCContext*)data; + + *p_pixels = context->texture_pixels; + + return NULL; +} + +///////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::unlock(void* data, void* id, void* const* raw_pixels) +{ + // nothing to do here for the moment + // we *could* modify pixels here to, for example, Y flip, but this is done with + // a VLC video filter transform. +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::display(void* data, void* id) +{ + struct mLibVLCContext* context = (mLibVLCContext*)data; + + context->parent->setDirty(0, 0, context->parent->mWidth, context->parent->mHeight); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::initVLC() +{ + char const* vlc_argv[] = + { + "--no-xlib", + "--video-filter=transform{type=vflip}", // MAINT-6578 Y flip textures in plugin vs client + }; + + int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv); + mLibVLC = libvlc_new(vlc_argc, vlc_argv); + + if (!mLibVLC) + { + // for the moment, if this fails, the plugin will fail and + // the media sub-system will tell the viewer something went wrong. + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::resetVLC() +{ + libvlc_media_player_stop(mLibVLCMediaPlayer); + libvlc_media_player_release(mLibVLCMediaPlayer); + libvlc_release(mLibVLC); +} + +//////////////////////////////////////////////////////////////////////////////// +// *virtual* +void MediaPluginLibVLC::setDirty(int left, int top, int right, int bottom) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated"); + + message.setValueS32("left", left); + message.setValueS32("top", top); + message.setValueS32("right", right); + message.setValueS32("bottom", bottom); + + message.setValueReal("current_time", mCurTime); + message.setValueReal("duration", mDuration); + message.setValueReal("current_rate", 1.0f); + + sendMessage(message); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr) +{ + MediaPluginLibVLC* parent = (MediaPluginLibVLC*)ptr; + if (parent == 0) + { + return; + } + + switch (event->type) + { + case libvlc_MediaPlayerOpening: + parent->setStatus(STATUS_LOADING); + break; + + case libvlc_MediaPlayerPlaying: + parent->mDuration = (float)(libvlc_media_get_duration(parent->mLibVLCMedia)) / 1000.0f; + parent->setStatus(STATUS_PLAYING); + break; + + case libvlc_MediaPlayerPaused: + parent->setStatus(STATUS_PAUSED); + break; + + case libvlc_MediaPlayerStopped: + parent->setStatus(STATUS_DONE); + break; + + case libvlc_MediaPlayerEndReached: + parent->setStatus(STATUS_DONE); + break; + + case libvlc_MediaPlayerEncounteredError: + parent->setStatus(STATUS_ERROR); + break; + + case libvlc_MediaPlayerTimeChanged: + parent->mCurTime = (float)libvlc_media_player_get_time(parent->mLibVLCMediaPlayer) / 1000.0f; + break; + + case libvlc_MediaPlayerPositionChanged: + break; + + case libvlc_MediaPlayerLengthChanged: + parent->mDuration = (float)libvlc_media_get_duration(parent->mLibVLCMedia) / 1000.0f; + break; + + case libvlc_MediaPlayerTitleChanged: + { + char* title = libvlc_media_get_meta(parent->mLibVLCMedia, libvlc_meta_Title); + if (title) + { + parent->updateTitle(title); + } + } + break; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::playMedia() +{ + if (mURL.length() == 0) + { + return; + } + + if (mLibVLCMediaPlayer) + { + // stop listening to events while we reset things + libvlc_event_manager_t* em = libvlc_media_player_event_manager(mLibVLCMediaPlayer); + if (em) + { + libvlc_event_detach(em, libvlc_MediaPlayerOpening, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerPlaying, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerPaused, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerStopped, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerEndReached, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerEncounteredError, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerTimeChanged, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerPositionChanged, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerLengthChanged, eventCallbacks, NULL); + libvlc_event_detach(em, libvlc_MediaPlayerTitleChanged, eventCallbacks, NULL); + }; + + libvlc_media_player_stop(mLibVLCMediaPlayer); + libvlc_media_player_release(mLibVLCMediaPlayer); + + mLibVLCMediaPlayer = 0; + } + + if (mLibVLCMedia) + { + libvlc_media_release(mLibVLCMedia); + + mLibVLCMedia = 0; + } + + mLibVLCMedia = libvlc_media_new_location(mLibVLC, mURL.c_str()); + if (!mLibVLCMedia) + { + mLibVLCMediaPlayer = 0; + setStatus(STATUS_ERROR); + return; + } + + mLibVLCMediaPlayer = libvlc_media_player_new_from_media(mLibVLCMedia); + if (!mLibVLCMediaPlayer) + { + setStatus(STATUS_ERROR); + return; + } + + // listen to events + libvlc_event_manager_t* em = libvlc_media_player_event_manager(mLibVLCMediaPlayer); + if (em) + { + libvlc_event_attach(em, libvlc_MediaPlayerOpening, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerPlaying, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerPaused, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerStopped, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerEndReached, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerEncounteredError, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerTimeChanged, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerPositionChanged, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerLengthChanged, eventCallbacks, this); + libvlc_event_attach(em, libvlc_MediaPlayerTitleChanged, eventCallbacks, this); + } + + mLibVLCCallbackContext.parent = this; + mLibVLCCallbackContext.texture_pixels = mPixels; + mLibVLCCallbackContext.mp = mLibVLCMediaPlayer; + + // Send a "navigate begin" event. + // This is really a browser message but the QuickTime plugin did it and + // the media system relies on this message to update internal state so we must send it too + // Note: see "navigate_complete" message below too + // https://jira.secondlife.com/browse/MAINT-6528 + LLPluginMessage message_begin(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin"); + message_begin.setValue("uri", mURL); + message_begin.setValueBoolean("history_back_available", false); + message_begin.setValueBoolean("history_forward_available", false); + sendMessage(message_begin); + + // volume level gets set before VLC is initialized (thanks media system) so we have to record + // it in mCurVolume and set it again here so that volume levels are correctly initialized + setVolume(mCurVolume); + + setStatus(STATUS_LOADED); + + libvlc_video_set_callbacks(mLibVLCMediaPlayer, lock, unlock, display, &mLibVLCCallbackContext); + libvlc_video_set_format(mLibVLCMediaPlayer, "RV32", mWidth, mHeight, mWidth * mDepth); + + // note this relies on the "set_loop" message arriving before the "start" (play) one + // but that appears to always be the case + if (mIsLooping) + { + libvlc_media_add_option(mLibVLCMedia, "input-repeat=-1"); + } + + libvlc_media_player_play(mLibVLCMediaPlayer); + + // send a "location_changed" message - this informs the media system + // that a new URL is the 'current' one and is used extensively. + // Again, this is really a browser message but we will use it here. + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "location_changed"); + message.setValue("uri", mURL); + sendMessage(message); + + // Send a "navigate complete" event. + // This is really a browser message but the QuickTime plugin did it and + // the media system relies on this message to update internal state so we must send it too + // Note: see "navigate_begin" message above too + // https://jira.secondlife.com/browse/MAINT-6528 + LLPluginMessage message_complete(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete"); + message_complete.setValue("uri", mURL); + message_complete.setValueS32("result_code", 200); + message_complete.setValue("result_string", "OK"); + sendMessage(message_complete); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::updateTitle(const char* title) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); + message.setValue("name", title); + sendMessage(message); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::setVolume(const F64 volume) +{ + mCurVolume = volume; + + if (mLibVLCMediaPlayer) + { + int result = libvlc_audio_set_volume(mLibVLCMediaPlayer, (int)(volume * 100)); + if (result != 0) + { + // volume wasn't set but not much to be done here + } + } + else + { + // volume change was requested but VLC wasn't ready. + // that's okay thought because we saved the value in mCurVolume and + // the next volume change after the VLC system is initilzied will set it + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginLibVLC::receiveMessage(const char* message_string) +{ + LLPluginMessage message_in; + + if (message_in.parse(message_string) >= 0) + { + std::string message_class = message_in.getClass(); + std::string message_name = message_in.getName(); + if (message_class == LLPLUGIN_MESSAGE_CLASS_BASE) + { + if (message_name == "init") + { + initVLC(); + + LLPluginMessage message("base", "init_response"); + LLSD versions = LLSD::emptyMap(); + versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION; + message.setValueLLSD("versions", versions); + + std::ostringstream s; + s << "LibVLC plugin "; + s << LIBVLC_VERSION_MAJOR; + s << "."; + s << LIBVLC_VERSION_MINOR; + s << "."; + s << LIBVLC_VERSION_REVISION; + + message.setValue("plugin_version", s.str()); + sendMessage(message); + } + else if (message_name == "idle") + { + } + else if (message_name == "cleanup") + { + resetVLC(); + } + else if (message_name == "shm_added") + { + SharedSegmentInfo info; + info.mAddress = message_in.getValuePointer("address"); + info.mSize = (size_t)message_in.getValueS32("size"); + std::string name = message_in.getValue("name"); + + mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); + + } + else if (message_name == "shm_remove") + { + std::string name = message_in.getValue("name"); + + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if (iter != mSharedSegments.end()) + { + if (mPixels == iter->second.mAddress) + { + libvlc_media_player_stop(mLibVLCMediaPlayer); + libvlc_media_player_release(mLibVLCMediaPlayer); + mLibVLCMediaPlayer = 0; + + mPixels = NULL; + mTextureSegmentName.clear(); + } + mSharedSegments.erase(iter); + } + else + { + //std::cerr << "MediaPluginWebKit::receiveMessage: unknown shared memory region!" << std::endl; + } + + // Send the response so it can be cleaned up. + LLPluginMessage message("base", "shm_remove_response"); + message.setValue("name", name); + sendMessage(message); + } + else + { + //std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl; + } + } + else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) + { + if (message_name == "init") + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); + message.setValueS32("default_width", 1024); + message.setValueS32("default_height", 1024); + message.setValueS32("depth", mDepth); + message.setValueU32("internalformat", GL_RGB); + message.setValueU32("format", GL_BGRA_EXT); + message.setValueU32("type", GL_UNSIGNED_BYTE); + message.setValueBoolean("coords_opengl", true); + sendMessage(message); + } + else if (message_name == "size_change") + { + std::string name = message_in.getValue("name"); + S32 width = message_in.getValueS32("width"); + S32 height = message_in.getValueS32("height"); + S32 texture_width = message_in.getValueS32("texture_width"); + S32 texture_height = message_in.getValueS32("texture_height"); + + if (!name.empty()) + { + // Find the shared memory region with this name + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if (iter != mSharedSegments.end()) + { + mPixels = (unsigned char*)iter->second.mAddress; + mWidth = width; + mHeight = height; + mTextureWidth = texture_width; + mTextureHeight = texture_height; + + playMedia(); + }; + }; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); + message.setValue("name", name); + message.setValueS32("width", width); + message.setValueS32("height", height); + message.setValueS32("texture_width", texture_width); + message.setValueS32("texture_height", texture_height); + sendMessage(message); + } + else if (message_name == "load_uri") + { + mURL = message_in.getValue("uri"); + playMedia(); + } + } + else + if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) + { + if (message_name == "stop") + { + if (mLibVLCMediaPlayer) + { + libvlc_media_player_stop(mLibVLCMediaPlayer); + } + } + else if (message_name == "start") + { + if (mLibVLCMediaPlayer) + { + libvlc_media_player_play(mLibVLCMediaPlayer); + } + } + else if (message_name == "pause") + { + if (mLibVLCMediaPlayer) + { + libvlc_media_player_pause(mLibVLCMediaPlayer); + } + } + else if (message_name == "seek") + { + if (mDuration > 0) + { + F64 normalized_offset = message_in.getValueReal("time") / mDuration; + libvlc_media_player_set_position(mLibVLCMediaPlayer, normalized_offset); + } + } + else if (message_name == "set_loop") + { + mIsLooping = true; + } + else if (message_name == "set_volume") + { + // volume comes in 0 -> 1.0 + F64 volume = message_in.getValueReal("volume"); + setVolume(volume); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool MediaPluginLibVLC::init() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); + message.setValue("name", "LibVLC Plugin"); + sendMessage(message); + + return true; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, + void* host_user_data, + LLPluginInstance::sendMessageFunction *plugin_send_func, + void **plugin_user_data) +{ + MediaPluginLibVLC* self = new MediaPluginLibVLC(host_send_func, host_user_data); + *plugin_send_func = MediaPluginLibVLC::staticReceiveMessage; + *plugin_user_data = (void*)self; + + return 0; +} diff --git a/indra/media_plugins/quicktime/CMakeLists.txt b/indra/media_plugins/quicktime/CMakeLists.txt index c5615145be..58391007ff 100644..100755 --- a/indra/media_plugins/quicktime/CMakeLists.txt +++ b/indra/media_plugins/quicktime/CMakeLists.txt @@ -14,7 +14,6 @@ include(PluginAPI) include(MediaPluginBase) include(OpenGL) include(QuickTimePlugin) -include(Boost) include_directories( ${LLPLUGIN_INCLUDE_DIRS} @@ -54,12 +53,17 @@ target_link_libraries(media_plugin_quicktime ${PLUGIN_API_WINDOWS_LIBRARIES} ) +add_dependencies(media_plugin_quicktime + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} +) + if (WINDOWS) set_target_properties( media_plugin_quicktime PROPERTIES - LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /NODEFAULTLIB:LIBCMT" - LINK_FLAGS_DEBUG "/MANIFEST:NO /SAFESEH:NO /NODEFAULTLIB:LIBCMTD" + LINK_FLAGS "/MANIFEST:NO" ) endif (WINDOWS) diff --git a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp index 7ef5a0fe44..b43598e4a5 100644..100755 --- a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp +++ b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp @@ -837,9 +837,7 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string) else if(message_name == "cleanup") { // TODO: clean up here - LLPluginMessage message("base", "goodbye"); - sendMessage(message); - } + } else if(message_name == "shm_added") { SharedSegmentInfo info; @@ -921,6 +919,9 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string) #endif message.setValueS32("depth", mDepth); message.setValueU32("internalformat", GL_RGB); + + // note this apparently only has an effect when media is opened in 2D browser. + // see https://jira.secondlife.com/browse/BUG-18252 - media flipped in 2D so flipping it back. message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left. message.setValueBoolean("allow_downsample", true); sendMessage(message); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 195363fb75..16edd39ecc 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1755,6 +1755,7 @@ if (WINDOWS) SLPlugin media_plugin_quicktime media_plugin_cef + media_plugin_libvlc winmm_shim windows-crash-logger ) @@ -1970,6 +1971,7 @@ if (LINUX) linux-crash-logger SLPlugin media_plugin_gstreamer010 + media_plugin_libvlc llcommon ) @@ -2077,7 +2079,7 @@ if (DARWIN) ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py ) - add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_cef mac-crash-logger) + add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_libvlc media_plugin_cef mac-crash-logger) add_dependencies(${VIEWER_BINARY_NAME} mac-crash-logger) if (ENABLE_SIGNING) diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 7919852fe1..4d0dcda01c 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -4.0.9 +4.1.2 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2d3c885522..460c0809a6 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4515,28 +4515,6 @@ <key>Value</key> <string>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]&channel=[CHANNEL]&version=[VERSION]&major=[VERSION_MAJOR]&minor=[VERSION_MINOR]&patch=[VERSION_PATCH]&build=[VERSION_BUILD]</string> </map> - <key>WebProfileURL</key> - <map> - <key>Comment</key> - <string>URL for Web Profiles</string> - <key>Persist</key> - <integer>0</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>https://my.secondlife.com/[AGENT_NAME]</string> - </map> - <key>WebProfileNonProductionURL</key> - <map> - <key>Comment</key> - <string>URL for Web Profiles on Non-Production grids</string> - <key>Persist</key> - <integer>0</integer> - <key>Type</key> - <string>String</string> - <key>Value</key> - <string>https://my-demo.secondlife.com/[AGENT_NAME]</string> - </map> <key>HighResSnapshot</key> <map> <key>Comment</key> @@ -4881,7 +4859,7 @@ <key>InventoryTrashMaxCapacity</key> <map> <key>Comment</key> - <string>Maximum capacity of the Trash folder. User will ve offered to clean it up when exceeded.</string> + <string>Maximum capacity of the Trash folder. User will be offered to clean it up when exceeded.</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -7951,6 +7929,17 @@ <key>Value</key> <integer>100000</integer> </map> + <key>PrimTextMaxDrawDistance</key> + <map> + <key>Comment</key> + <string>Maximum draw distance beyond which PRIM_TEXT won't be rendered</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>64.0</real> + </map> <key>ProbeHardwareOnStartup</key> <map> <key>Comment</key> @@ -10103,6 +10092,17 @@ <key>Value</key> <integer>10</integer> </map> + <key>ComplexityChangesPopUpDelay</key> + <map> + <key>Comment</key> + <string>Delay before viewer will show avatar complexity notice again</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>300</integer> + </map> <key>RenderAvatarMaxComplexity</key> <map> <key>Comment</key> @@ -10115,6 +10115,50 @@ <key>Value</key> <integer>0</integer> </map> + <key>RenderHUDObjectsWarning</key> + <map> + <key>Comment</key> + <string>Viewer will warn user about HUD containing to many objects if objects count is above this value</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>1000</integer> + </map> + <key>RenderHUDTexturesWarning</key> + <map> + <key>Comment</key> + <string>Viewer will warn user about HUD containing to many textures if texture count is above this value</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>200</integer> + </map> + <key>RenderHUDOversizedTexturesWarning</key> + <map> + <key>Comment</key> + <string>How many textures with size 1024 * 1024 or bigger HUD can contain before notifying user</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>6</integer> + </map> + <key>RenderHUDTexturesMemoryWarning</key> + <map> + <key>Comment</key> + <string>Viewer will warn user about HUD textures using memory above this value (in bytes)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>32000000</integer> + </map> <key>RenderAutoMuteSurfaceAreaLimit</key> <map> <key>Comment</key> @@ -10229,6 +10273,17 @@ <key>Value</key> <real>1.0</real> </map> + <key>RenderRiggedFactorMultiplier</key> + <map> + <key>Comment</key> + <string>Affects level of detail for worn rigged meshes</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>6.5</real> + </map> <key>RenderWater</key> <map> <key>Comment</key> @@ -12803,6 +12858,17 @@ <key>Value</key> <real>1.0</real> </map> + <key>LastSystemUIScaleFactor</key> + <map> + <key>Comment</key> + <string>Size of system UI during last run. On Windows 100% (96 DPI) system setting is 1.0 UI size</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> <key>UIScrollbarSize</key> <map> <key>Comment</key> @@ -13298,6 +13364,17 @@ <key>Value</key> <string>1</string> </map> + <key>UpdaterShowReleaseNotes</key> + <map> + <key>Comment</key> + <string>Enables displaying of the Release notes in a web floater after update.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>UploadBakedTexOld</key> <map> <key>Comment</key> diff --git a/indra/newview/llaccountingcostmanager.cpp b/indra/newview/llaccountingcostmanager.cpp index 92a5413adb..1dddf52961 100644 --- a/indra/newview/llaccountingcostmanager.cpp +++ b/indra/newview/llaccountingcostmanager.cpp @@ -31,6 +31,8 @@ #include "llcoros.h" #include "lleventcoro.h" #include "llcorehttputil.h" +#include "llexception.h" +#include "stringize.h" #include <algorithm> #include <iterator> @@ -154,13 +156,11 @@ void LLAccountingCostManager::accountingCostCoro(std::string url, } while (false); } - catch (std::exception e) - { - LL_WARNS() << "Caught exception '" << e.what() << "'" << LL_ENDL; - } catch (...) { - LL_WARNS() << "Caught unknown exception." << LL_ENDL; + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName() + << "('" << url << "')")); + throw; } mPendingObjectQuota.clear(); diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index d933537d2e..6a1215c3af 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -3191,7 +3191,7 @@ void LLAgent::initOriginGlobal(const LLVector3d &origin_global) } BOOL LLAgent::leftButtonGrabbed() const -{ +{ const BOOL camera_mouse_look = gAgentCamera.cameraMouselook(); return (!camera_mouse_look && mControlsTakenCount[CONTROL_LBUTTON_DOWN_INDEX] > 0) || (camera_mouse_look && mControlsTakenCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0) @@ -3199,13 +3199,6 @@ BOOL LLAgent::leftButtonGrabbed() const || (camera_mouse_look && mControlsTakenPassedOnCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0); } -BOOL LLAgent::leftButtonBlocked() const -{ - const BOOL camera_mouse_look = gAgentCamera.cameraMouselook(); - return (!camera_mouse_look && mControlsTakenCount[CONTROL_LBUTTON_DOWN_INDEX] > 0) - || (camera_mouse_look && mControlsTakenCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0); -} - BOOL LLAgent::rotateGrabbed() const { return (mControlsTakenCount[CONTROL_YAW_POS_INDEX] > 0) @@ -3663,14 +3656,7 @@ BOOL LLAgent::anyControlGrabbed() const BOOL LLAgent::isControlGrabbed(S32 control_index) const { - if (gAgent.mControlsTakenCount[control_index] > 0) - return TRUE; - return gAgent.mControlsTakenPassedOnCount[control_index] > 0; -} - -BOOL LLAgent::isControlBlocked(S32 control_index) const -{ - return mControlsTakenCount[control_index] > 0; + return mControlsTakenCount[control_index] > 0; } void LLAgent::forceReleaseControls() @@ -3895,11 +3881,17 @@ void LLAgent::handleTeleportFinished() mIsMaturityRatingChangingDuringTeleport = false; } - // Init SLM Marketplace connection so we know which UI should be used for the user as a merchant - // Note: Eventually, all merchant will be migrated to the new SLM system and there will be no reason to show the old UI at all. - // Note: Some regions will not support the SLM cap for a while so we need to do that check for each teleport. - // *TODO : Suppress that line from here once the whole grid migrated to SLM and move it to idle_startup() (llstartup.cpp) - check_merchant_status(); + if (mRegionp) + { + if (mRegionp->capabilitiesReceived()) + { + onCapabilitiesReceivedAfterTeleport(); + } + else + { + mRegionp->setCapabilitiesReceivedCallback(boost::bind(&LLAgent::onCapabilitiesReceivedAfterTeleport)); + } + } } void LLAgent::handleTeleportFailed() @@ -3931,6 +3923,14 @@ void LLAgent::handleTeleportFailed() } } +/*static*/ +void LLAgent::onCapabilitiesReceivedAfterTeleport() +{ + + check_merchant_status(); +} + + void LLAgent::teleportRequest( const U64& region_handle, const LLVector3& pos_local, diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 3a533c2cba..b5da5e9062 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -444,8 +444,7 @@ private: // Grab //-------------------------------------------------------------------- public: - BOOL leftButtonGrabbed() const; - BOOL leftButtonBlocked() const; + BOOL leftButtonGrabbed() const; BOOL rotateGrabbed() const; BOOL forwardGrabbed() const; BOOL backwardGrabbed() const; @@ -462,9 +461,8 @@ public: BOOL controlFlagsDirty() const; void enableControlFlagReset(); void resetControlFlags(); - BOOL anyControlGrabbed() const; // True if a script has taken over any control - BOOL isControlGrabbed(S32 control_index) const; // True if a script has taken over a control - BOOL isControlBlocked(S32 control_index) const; // Control should be ignored or won't be passed + BOOL anyControlGrabbed() const; // True iff a script has taken over a control + BOOL isControlGrabbed(S32 control_index) const; // Send message to simulator to force grabbed controls to be // released, in case of a poorly written script. void forceReleaseControls(); @@ -676,6 +674,8 @@ private: void handleTeleportFinished(); void handleTeleportFailed(); + static void onCapabilitiesReceivedAfterTeleport(); + //-------------------------------------------------------------------- // Teleport State //-------------------------------------------------------------------- diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index b76a66ab39..170e4063a1 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -62,23 +62,37 @@ using namespace LLAvatarAppearanceDefines; /////////////////////////////////////////////////////////////////////////////// -// Callback to wear and start editing an item that has just been created. -void wear_and_edit_cb(const LLUUID& inv_item) +void set_default_permissions(LLViewerInventoryItem* item) { - if (inv_item.isNull()) return; - - LLViewerInventoryItem* item = gInventory.getItem(inv_item); - if (!item) return; - - LLPermissions perm = item->getPermissions(); + llassert(item); + LLPermissions perm = item->getPermissions(); + if (perm.getMaskNextOwner() != LLFloaterPerms::getNextOwnerPerms("Wearables") + || perm.getMaskEveryone() != LLFloaterPerms::getEveryonePerms("Wearables") + || perm.getMaskGroup() != LLFloaterPerms::getGroupPerms("Wearables")) + { perm.setMaskNext(LLFloaterPerms::getNextOwnerPerms("Wearables")); perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms("Wearables")); perm.setMaskGroup(LLFloaterPerms::getGroupPerms("Wearables")); + item->setPermissions(perm); item->updateServer(FALSE); - gInventory.updateItem(item); - gInventory.notifyObservers(); + } +} + +// Callback to wear and start editing an item that has just been created. +void wear_and_edit_cb(const LLUUID& inv_item) +{ + if (inv_item.isNull()) return; + + LLViewerInventoryItem* item = gInventory.getItem(inv_item); + if (!item) return; + + set_default_permissions(item); + + // item was just created, update even if permissions did not changed + gInventory.updateItem(item); + gInventory.notifyObservers(); // Request editing the item after it gets worn. gAgentWearables.requestEditingWearable(inv_item); @@ -94,13 +108,8 @@ void wear_cb(const LLUUID& inv_item) LLViewerInventoryItem* item = gInventory.getItem(inv_item); if (item) { - LLPermissions perm = item->getPermissions(); - perm.setMaskNext(LLFloaterPerms::getNextOwnerPerms("Wearables")); - perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms("Wearables")); - perm.setMaskGroup(LLFloaterPerms::getGroupPerms("Wearables")); - item->setPermissions(perm); + set_default_permissions(item); - item->updateServer(FALSE); gInventory.updateItem(item); gInventory.notifyObservers(); } @@ -253,6 +262,7 @@ void LLAgentWearables::AddWearableToAgentInventoryCallback::fire(const LLUUID& i { LLAppearanceMgr::instance().addCOFItemLink(inv_item, new LLUpdateAppearanceAndEditWearableOnDestroy(inv_item), mDescription); + editWearable(inv_item); } } @@ -423,7 +433,7 @@ void LLAgentWearables::saveWearableAs(const LLWearableType::EType type, // old_wearable may still be referred to by other inventory items. Revert // unsaved changes so other inventory items aren't affected by the changes // that were just saved. - old_wearable->revertValues(); + old_wearable->revertValuesWithoutUpdate(); } void LLAgentWearables::revertWearable(const LLWearableType::EType type, const U32 index) @@ -1364,6 +1374,30 @@ void LLAgentWearables::findAttachmentsAddRemoveInfo(LLInventoryModel::item_array // LL_INFOS() << "remove " << remove_count << " add " << add_count << LL_ENDL; } +std::vector<LLViewerObject*> LLAgentWearables::getTempAttachments() +{ + llvo_vec_t temp_attachs; + if (isAgentAvatarValid()) + { + for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); iter != gAgentAvatarp->mAttachmentPoints.end();) + { + LLVOAvatar::attachment_map_t::iterator curiter = iter++; + LLViewerJointAttachment* attachment = curiter->second; + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { + LLViewerObject *objectp = (*attachment_iter); + if (objectp && objectp->isTempAttachment()) + { + temp_attachs.push_back(objectp); + } + } + } + } + return temp_attachs; +} + void LLAgentWearables::userRemoveMultipleAttachments(llvo_vec_t& objects_to_remove) { if (!isAgentAvatarValid()) return; diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 1004482020..b27698fd8f 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -185,6 +185,8 @@ public: static void userRemoveMultipleAttachments(llvo_vec_t& llvo_array); static void userAttachMultipleAttachments(LLInventoryModel::item_array_t& obj_item_array); + static llvo_vec_t getTempAttachments(); + //-------------------------------------------------------------------- // Signals //-------------------------------------------------------------------- diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 49291ea564..c1f898284a 100644 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -30,6 +30,8 @@ #include "llappviewer.h" #include "llviewercontrol.h" +#include "llexception.h" +#include "stringize.h" #include <openssl/x509_vfy.h> #include <openssl/ssl.h> @@ -534,7 +536,7 @@ LLCore::HttpStatus LLAppCoreHttp::sslVerify(const std::string &url, // somewhat clumsy, as we may run into errors that do not map directly to curl // error codes. Should be refactored with login refactoring, perhaps. result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_CACERT); - result.setMessage(cert_exception.getMessage()); + result.setMessage(cert_exception.what()); LLPointer<LLCertificate> cert = cert_exception.getCert(); cert->ref(); // adding an extra ref here result.setErrorData(cert.get()); @@ -544,13 +546,14 @@ LLCore::HttpStatus LLAppCoreHttp::sslVerify(const std::string &url, catch (LLCertException &cert_exception) { result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_PEER_CERTIFICATE); - result.setMessage(cert_exception.getMessage()); + result.setMessage(cert_exception.what()); LLPointer<LLCertificate> cert = cert_exception.getCert(); cert->ref(); // adding an extra ref here result.setErrorData(cert.get()); } catch (...) { + LOG_UNHANDLED_EXCEPTION(STRINGIZE("('" << url << "')")); // any other odd error, we just handle as a connect error. result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_CONNECT_ERROR); } diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm index 549df80fa1..be8877328d 100644 --- a/indra/newview/llappdelegate-objc.mm +++ b/indra/newview/llappdelegate-objc.mm @@ -48,16 +48,19 @@ - (void) applicationDidFinishLaunching:(NSNotification *)notification { frameTimer = nil; - + [self languageUpdated]; - + if (initViewer()) { - frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(mainLoop) userInfo:nil repeats:YES]; + // Set up recurring calls to oneFrame (repeating timer with timeout 0) + // until applicationShouldTerminate. + frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self + selector:@selector(oneFrame) userInfo:nil repeats:YES]; } else { handleQuit(); } - + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageUpdated) name:@"NSTextInputContextKeyboardSelectionDidChangeNotification" object:nil]; // [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; @@ -96,22 +99,29 @@ - (NSApplicationDelegateReply) applicationShouldTerminate:(NSApplication *)sender { - if (!runMainLoop()) + // run one frame to assess state + if (!pumpMainLoop()) { + // pumpMainLoop() returns true when done, false if it wants to be + // called again. Since it returned false, do not yet cancel + // frameTimer. handleQuit(); return NSTerminateCancel; } else { + // pumpMainLoop() returned true: it's done. Okay, done with frameTimer. [frameTimer release]; cleanupViewer(); return NSTerminateNow; } } -- (void) mainLoop +- (void) oneFrame { - bool appExiting = runMainLoop(); + bool appExiting = pumpMainLoop(); if (appExiting) { + // Once pumpMainLoop() reports that we're done, cancel frameTimer: + // stop the repetitive calls. [frameTimer release]; [[NSApplication sharedApplication] terminate:self]; } diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index ff5439d610..92e3cd0279 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -1875,15 +1875,15 @@ bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id) return false; } - // Check whether the outfit contains any wearables we aren't wearing already (STORM-702). + // Check whether the outfit contains any wearables LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; - LLFindWearablesEx is_worn(/*is_worn=*/ false, /*include_body_parts=*/ true); + LLFindWearables is_wearable; gInventory.collectDescendentsIf(outfit_cat_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, - is_worn); + is_wearable); return items.size() > 0; } @@ -3477,13 +3477,13 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd } else { - if (cofVersion < lastRcv) + if (cofVersion <= lastRcv) { LL_WARNS("Avatar") << "Have already received update for cof version " << lastRcv << " but requesting for " << cofVersion << LL_ENDL; return; } - if (lastReq > cofVersion) + if (lastReq >= cofVersion) { LL_WARNS("Avatar") << "Request already in flight for cof version " << lastReq << " but requesting for " << cofVersion << LL_ENDL; @@ -3503,7 +3503,7 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd LL_WARNS("Avatar") << "Forcing version failure on COF Baking" << LL_ENDL; } - LL_INFOS() << "Requesting bake for COF version " << cofVersion << LL_ENDL; + LL_INFOS("Avatar") << "Requesting bake for COF version " << cofVersion << LL_ENDL; LLSD postData; if (gSavedSettings.getBOOL("DebugAvatarExperimentalServerAppearanceUpdate")) @@ -3969,6 +3969,10 @@ void LLAppearanceMgr::setAttachmentInvLinkEnable(bool val) LL_DEBUGS("Avatar") << "setAttachmentInvLinkEnable => " << (int) val << LL_ENDL; mAttachmentInvLinkEnabled = val; } +boost::signals2::connection LLAppearanceMgr::setAttachmentsChangedCallback(attachments_changed_callback_t cb) +{ + return mAttachmentsChangeSignal.connect(cb); +} void dumpAttachmentSet(const std::set<LLUUID>& atts, const std::string& msg) { @@ -3995,6 +3999,8 @@ void LLAppearanceMgr::registerAttachment(const LLUUID& item_id) gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); LLAttachmentsMgr::instance().onAttachmentArrived(item_id); + + mAttachmentsChangeSignal(); } void LLAppearanceMgr::unregisterAttachment(const LLUUID& item_id) @@ -4015,6 +4021,8 @@ void LLAppearanceMgr::unregisterAttachment(const LLUUID& item_id) { //LL_INFOS() << "no link changes, inv link not enabled" << LL_ENDL; } + + mAttachmentsChangeSignal(); } BOOL LLAppearanceMgr::getIsInCOF(const LLUUID& obj_id) const diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index 2e570b9188..f0d3f80f59 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -229,6 +229,10 @@ public: void setAppearanceServiceURL(const std::string& url) { mAppearanceServiceURL = url; } std::string getAppearanceServiceURL() const; + typedef boost::function<void ()> attachments_changed_callback_t; + typedef boost::signals2::signal<void ()> attachments_changed_signal_t; + boost::signals2::connection setAttachmentsChangedCallback(attachments_changed_callback_t cb); + private: @@ -272,6 +276,8 @@ private: LLTimer mInFlightTimer; static bool mActive; + attachments_changed_signal_t mAttachmentsChangeSignal; + LLUUID mCOFImageID; std::auto_ptr<LLOutfitUnLockTimer> mUnlockOutfitTimer; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 721a7cc00b..b0829a3da1 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -122,15 +122,20 @@ #include "llleap.h" #include "stringize.h" #include "llcoros.h" +#include "llexception.h" #if !LL_LINUX #include "cef/llceflib.h" -#endif +#if LL_WINDOWS +#include "vlc/libvlc_version.h" +#endif // LL_WINDOWS +#endif // LL_LINUX // Third party library includes #include <boost/bind.hpp> #include <boost/foreach.hpp> #include <boost/algorithm/string.hpp> #include <boost/regex.hpp> +#include <boost/throw_exception.hpp> #if LL_WINDOWS # include <share.h> // For _SH_DENYWR in processMarkerFiles @@ -232,7 +237,6 @@ #include "llcoproceduremanager.h" #include "llviewereventrecorder.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 @@ -588,6 +592,7 @@ static void settings_to_globals() LLImageGL::sGlobalUseAnisotropic = gSavedSettings.getBOOL("RenderAnisotropic"); LLImageGL::sCompressTextures = gSavedSettings.getBOOL("RenderCompressTextures"); LLVOVolume::sLODFactor = gSavedSettings.getF32("RenderVolumeLODFactor"); + LLVOVolume::sRiggedFactorMultiplier = gSavedSettings.getF32("RenderRiggedFactorMultiplier"); LLVOVolume::sDistanceFactor = 1.f-LLVOVolume::sLODFactor * 0.1f; LLVolumeImplFlexible::sUpdateFactor = gSavedSettings.getF32("RenderFlexTimeFactor"); LLVOTree::sTreeFactor = gSavedSettings.getF32("RenderTreeLODFactor"); @@ -697,7 +702,8 @@ LLAppViewer::LLAppViewer() mPeriodicSlowFrame(LLCachedControl<bool>(gSavedSettings,"Periodic Slow Frame", FALSE)), mFastTimerLogThread(NULL), mUpdater(new LLUpdaterService()), - mSettingsLocationList(NULL) + mSettingsLocationList(NULL), + mIsFirstRun(false) { if(NULL != sInstance) { @@ -770,9 +776,6 @@ bool LLAppViewer::init() // // Start of the application // -#ifdef LL_DARWIN - mMainLoopInitialized = false; -#endif // initialize LLWearableType translation bridge. // Memory will be cleaned up in ::cleanupClass() @@ -926,7 +929,7 @@ bool LLAppViewer::init() // Provide the text fields with callbacks for opening Urls LLUrlAction::setOpenURLCallback(boost::bind(&LLWeb::loadURL, _1, LLStringUtil::null, LLStringUtil::null)); - LLUrlAction::setOpenURLInternalCallback(boost::bind(&LLWeb::loadURLInternal, _1, LLStringUtil::null, LLStringUtil::null)); + LLUrlAction::setOpenURLInternalCallback(boost::bind(&LLWeb::loadURLInternal, _1, LLStringUtil::null, LLStringUtil::null, false)); LLUrlAction::setOpenURLExternalCallback(boost::bind(&LLWeb::loadURLExternal, _1, true, LLStringUtil::null)); LLUrlAction::setExecuteSLURLCallback(&LLURLDispatcher::dispatchFromTextEditor); @@ -1123,17 +1126,23 @@ bool LLAppViewer::init() #if LL_WINDOWS if (gGLManager.mGLVersion < LLFeatureManager::getInstance()->getExpectedGLVersion()) { + std::string url; if (gGLManager.mIsIntel) { - LLNotificationsUtil::add("IntelOldDriver"); + url = LLTrans::getString("IntelDriverPage"); } else if (gGLManager.mIsNVIDIA) { - LLNotificationsUtil::add("NVIDIAOldDriver"); + url = LLTrans::getString("NvidiaDriverPage"); } else if (gGLManager.mIsATI) { - LLNotificationsUtil::add("AMDOldDriver"); + url = LLTrans::getString("AMDDriverPage"); + } + + if (!url.empty()) + { + LLNotificationsUtil::add("OldGPUDriver", LLSD().with("URL", url)); } } #endif @@ -1221,6 +1230,25 @@ bool LLAppViewer::init() boost::bind(&LLControlGroup::getU32, boost::ref(gSavedSettings), _1), boost::bind(&LLControlGroup::declareU32, boost::ref(gSavedSettings), _1, _2, _3, LLControlVariable::PERSIST_ALWAYS)); + showReleaseNotesIfRequired(); + + /*----------------------------------------------------------------------*/ + // nat 2016-06-29 moved the following here from the former mainLoop(). + mMainloopTimeout = new LLWatchdogTimeout(); + + // Create IO Pump to use for HTTP Requests. + gServicePump = new LLPumpIO(gAPRPoolp); + + // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated. + + LLVoiceChannel::initClass(); + LLVoiceClient::getInstance()->init(gServicePump); + LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true); + + joystick = LLViewerJoystick::getInstance(); + joystick->setNeedsReset(true); + /*----------------------------------------------------------------------*/ + return true; } @@ -1295,300 +1323,266 @@ static LLTrace::BlockTimerStatHandle FTM_AGENT_UPDATE("Update"); // externally visible timers LLTrace::BlockTimerStatHandle FTM_FRAME("Frame"); -bool LLAppViewer::mainLoop() +bool LLAppViewer::frame() { -#ifdef LL_DARWIN - if (!mMainLoopInitialized) -#endif - { - LL_INFOS() << "Entering main_loop" << LL_ENDL; - mMainloopTimeout = new LLWatchdogTimeout(); - - //------------------------------------------- - // Run main loop until time to quit - //------------------------------------------- - - // Create IO Pump to use for HTTP Requests. - gServicePump = new LLPumpIO(gAPRPoolp); - - // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated. - - LLVoiceChannel::initClass(); - LLVoiceClient::getInstance()->init(gServicePump); - LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true); - - joystick = LLViewerJoystick::getInstance(); - joystick->setNeedsReset(true); - -#ifdef LL_DARWIN - // Ensure that this section of code never gets called again on OS X. - mMainLoopInitialized = true; -#endif - } - // As we do not (yet) send data on the mainloop LLEventPump that varies - // with each frame, no need to instantiate a new LLSD event object each - // time. Obviously, if that changes, just instantiate the LLSD at the - // point of posting. - LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); - - LLSD newFrame; - + LLSD newFrame; + LLTimer frameTimer,idleTimer; LLTimer debugTime; - + //LLPrivateMemoryPoolTester::getInstance()->run(false) ; //LLPrivateMemoryPoolTester::getInstance()->run(true) ; //LLPrivateMemoryPoolTester::destroy() ; - // Handle messages -#ifdef LL_DARWIN - if (!LLApp::isExiting()) -#else - while (!LLApp::isExiting()) -#endif - { - LL_RECORD_BLOCK_TIME(FTM_FRAME); - LLTrace::BlockTimer::processTimes(); - LLTrace::get_frame_recording().nextPeriod(); - LLTrace::BlockTimer::logStats(); + LL_RECORD_BLOCK_TIME(FTM_FRAME); + LLTrace::BlockTimer::processTimes(); + LLTrace::get_frame_recording().nextPeriod(); + LLTrace::BlockTimer::logStats(); - LLTrace::get_thread_recorder()->pullFromChildren(); + LLTrace::get_thread_recorder()->pullFromChildren(); - //clear call stack records - LL_CLEAR_CALLSTACKS(); + //clear call stack records + LL_CLEAR_CALLSTACKS(); - //check memory availability information - checkMemory() ; - - try + //check memory availability information + checkMemory() ; + + try + { + pingMainloopTimeout("Main:MiscNativeWindowEvents"); + + if (gViewerWindow) { - pingMainloopTimeout("Main:MiscNativeWindowEvents"); + LL_RECORD_BLOCK_TIME(FTM_MESSAGES); + gViewerWindow->getWindow()->processMiscNativeEvents(); + } - if (gViewerWindow) + pingMainloopTimeout("Main:GatherInput"); + + if (gViewerWindow) + { + LL_RECORD_BLOCK_TIME(FTM_MESSAGES); + if (!restoreErrorTrap()) { - LL_RECORD_BLOCK_TIME(FTM_MESSAGES); - gViewerWindow->getWindow()->processMiscNativeEvents(); + LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL; } - - pingMainloopTimeout("Main:GatherInput"); - - if (gViewerWindow) - { - LL_RECORD_BLOCK_TIME(FTM_MESSAGES); - if (!restoreErrorTrap()) - { - LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL; - } - gViewerWindow->getWindow()->gatherInput(); - } + gViewerWindow->getWindow()->gatherInput(); + } #if 1 && !LL_RELEASE_FOR_DOWNLOAD - // once per second debug info - if (debugTime.getElapsedTimeF32() > 1.f) - { - debugTime.reset(); - } - + // once per second debug info + if (debugTime.getElapsedTimeF32() > 1.f) + { + debugTime.reset(); + } + #endif - //memory leaking simulation - LLFloaterMemLeak* mem_leak_instance = - LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); - if(mem_leak_instance) - { - mem_leak_instance->idle() ; - } + //memory leaking simulation + LLFloaterMemLeak* mem_leak_instance = + LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); + if(mem_leak_instance) + { + mem_leak_instance->idle() ; + } - // canonical per-frame event - mainloop.post(newFrame); + // canonical per-frame event + mainloop.post(newFrame); - if (!LLApp::isExiting()) + if (!LLApp::isExiting()) + { + pingMainloopTimeout("Main:JoystickKeyboard"); + + // Scan keyboard for movement keys. Command keys and typing + // are handled by windows callbacks. Don't do this until we're + // done initializing. JC + if ((gHeadlessClient || gViewerWindow->getWindow()->getVisible()) + && gViewerWindow->getActive() + && !gViewerWindow->getWindow()->getMinimized() + && LLStartUp::getStartupState() == STATE_STARTED + && (gHeadlessClient || !gViewerWindow->getShowProgress()) + && !gFocusMgr.focusLocked()) { - pingMainloopTimeout("Main:JoystickKeyboard"); + joystick->scanJoystick(); + gKeyboard->scanKeyboard(); + } + + // Update state based on messages, user input, object idle. + { + pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds! - // Scan keyboard for movement keys. Command keys and typing - // are handled by windows callbacks. Don't do this until we're - // done initializing. JC - if ((gHeadlessClient || gViewerWindow->getWindow()->getVisible()) - && gViewerWindow->getActive() - && !gViewerWindow->getWindow()->getMinimized() - && LLStartUp::getStartupState() == STATE_STARTED - && (gHeadlessClient || !gViewerWindow->getShowProgress()) - && !gFocusMgr.focusLocked()) - { - joystick->scanJoystick(); - gKeyboard->scanKeyboard(); - } + LL_RECORD_BLOCK_TIME(FTM_IDLE); + idle(); - // Update state based on messages, user input, object idle. - { - pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds! - - LL_RECORD_BLOCK_TIME(FTM_IDLE); - idle(); + resumeMainloopTimeout(); + } - resumeMainloopTimeout(); - } - - if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED)) - { - pauseMainloopTimeout(); - saveFinalSnapshot(); - disconnectViewer(); - resumeMainloopTimeout(); - } + if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED)) + { + pauseMainloopTimeout(); + saveFinalSnapshot(); + disconnectViewer(); + resumeMainloopTimeout(); + } - // Render scene. - // *TODO: Should we run display() even during gHeadlessClient? DK 2011-02-18 - if (!LLApp::isExiting() && !gHeadlessClient) - { - pingMainloopTimeout("Main:Display"); - gGLActive = TRUE; - display(); - pingMainloopTimeout("Main:Snapshot"); - LLFloaterSnapshot::update(); // take snapshots + // Render scene. + // *TODO: Should we run display() even during gHeadlessClient? DK 2011-02-18 + if (!LLApp::isExiting() && !gHeadlessClient) + { + pingMainloopTimeout("Main:Display"); + gGLActive = TRUE; + display(); + pingMainloopTimeout("Main:Snapshot"); + LLFloaterSnapshot::update(); // take snapshots LLFloaterOutfitSnapshot::update(); - gGLActive = FALSE; - } + gGLActive = FALSE; } + } + + pingMainloopTimeout("Main:Sleep"); - pingMainloopTimeout("Main:Sleep"); + pauseMainloopTimeout(); + + // Sleep and run background threads + { + LL_RECORD_BLOCK_TIME(FTM_SLEEP); - pauseMainloopTimeout(); + // yield some time to the os based on command line option + if(mYieldTime >= 0) + { + LL_RECORD_BLOCK_TIME(FTM_YIELD); + ms_sleep(mYieldTime); + } - // Sleep and run background threads + // yield cooperatively when not running as foreground window + if ( (gViewerWindow && !gViewerWindow->getWindow()->getVisible()) + || !gFocusMgr.getAppHasFocus()) { - LL_RECORD_BLOCK_TIME(FTM_SLEEP); - - // yield some time to the os based on command line option - if(mYieldTime >= 0) + // Sleep if we're not rendering, or the window is minimized. + S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 0, 1000); + // don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads + // of equal priority on Windows + if (milliseconds_to_sleep > 0) { - LL_RECORD_BLOCK_TIME(FTM_YIELD); - ms_sleep(mYieldTime); + ms_sleep(milliseconds_to_sleep); + // also pause worker threads during this wait period + LLAppViewer::getTextureCache()->pause(); + LLAppViewer::getImageDecodeThread()->pause(); } + } + + if (mRandomizeFramerate) + { + ms_sleep(rand() % 200); + } + + if (mPeriodicSlowFrame + && (gFrameCount % 10 == 0)) + { + LL_INFOS() << "Periodic slow frame - sleeping 500 ms" << LL_ENDL; + ms_sleep(500); + } + + const F64Milliseconds max_idle_time = llmin(.005f*10.f*(F32Milliseconds)gFrameTimeSeconds, F32Milliseconds(5)); // 5 ms a second + idleTimer.reset(); + S32 total_work_pending = 0; + S32 total_io_pending = 0; + while(1) + { + S32 work_pending = 0; + S32 io_pending = 0; + F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f); + + work_pending += updateTextureThreads(max_time); - // yield cooperatively when not running as foreground window - if ( (gViewerWindow && !gViewerWindow->getWindow()->getVisible()) - || !gFocusMgr.getAppHasFocus()) { - // Sleep if we're not rendering, or the window is minimized. - S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 0, 1000); - // don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads - // of equal priority on Windows - if (milliseconds_to_sleep > 0) - { - ms_sleep(milliseconds_to_sleep); - // also pause worker threads during this wait period - LLAppViewer::getTextureCache()->pause(); - LLAppViewer::getImageDecodeThread()->pause(); - } + LL_RECORD_BLOCK_TIME(FTM_VFS); + io_pending += LLVFSThread::updateClass(1); } - - if (mRandomizeFramerate) { - ms_sleep(rand() % 200); + LL_RECORD_BLOCK_TIME(FTM_LFS); + io_pending += LLLFSThread::updateClass(1); } - if (mPeriodicSlowFrame - && (gFrameCount % 10 == 0)) + if (io_pending > 1000) { - LL_INFOS() << "Periodic slow frame - sleeping 500 ms" << LL_ENDL; - ms_sleep(500); + ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up } - const F64Milliseconds max_idle_time = llmin(.005f*10.f*(F32Milliseconds)gFrameTimeSeconds, F32Milliseconds(5)); // 5 ms a second - idleTimer.reset(); - S32 total_work_pending = 0; - S32 total_io_pending = 0; - while(1) - { - S32 work_pending = 0; - S32 io_pending = 0; - F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f); - - work_pending += updateTextureThreads(max_time); - - { - LL_RECORD_BLOCK_TIME(FTM_VFS); - io_pending += LLVFSThread::updateClass(1); - } - { - LL_RECORD_BLOCK_TIME(FTM_LFS); - io_pending += LLLFSThread::updateClass(1); - } - - if (io_pending > 1000) - { - ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up - } - - total_work_pending += work_pending ; - total_io_pending += io_pending ; - - if (!work_pending || idleTimer.getElapsedTimeF64() >= max_idle_time) - { - break; - } - } - gMeshRepo.update() ; + total_work_pending += work_pending ; + total_io_pending += io_pending ; - if(!total_work_pending) //pause texture fetching threads if nothing to process. + if (!work_pending || idleTimer.getElapsedTimeF64() >= max_idle_time) { - LLAppViewer::getTextureCache()->pause(); - LLAppViewer::getImageDecodeThread()->pause(); - LLAppViewer::getTextureFetch()->pause(); + break; } - if(!total_io_pending) //pause file threads if nothing to process. - { - LLVFSThread::sLocal->pause(); - LLLFSThread::sLocal->pause(); - } + } + gMeshRepo.update() ; + + if(!total_work_pending) //pause texture fetching threads if nothing to process. + { + LLAppViewer::getTextureCache()->pause(); + LLAppViewer::getImageDecodeThread()->pause(); + LLAppViewer::getTextureFetch()->pause(); + } + if(!total_io_pending) //pause file threads if nothing to process. + { + LLVFSThread::sLocal->pause(); + LLLFSThread::sLocal->pause(); + } - //texture fetching debugger - if(LLTextureFetchDebugger::isEnabled()) + //texture fetching debugger + if(LLTextureFetchDebugger::isEnabled()) + { + LLFloaterTextureFetchDebugger* tex_fetch_debugger_instance = + LLFloaterReg::findTypedInstance<LLFloaterTextureFetchDebugger>("tex_fetch_debugger"); + if(tex_fetch_debugger_instance) { - LLFloaterTextureFetchDebugger* tex_fetch_debugger_instance = - LLFloaterReg::findTypedInstance<LLFloaterTextureFetchDebugger>("tex_fetch_debugger"); - if(tex_fetch_debugger_instance) - { - tex_fetch_debugger_instance->idle() ; - } + tex_fetch_debugger_instance->idle() ; } + } - if ((LLStartUp::getStartupState() >= STATE_CLEANUP) && - (frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD)) - { - gFrameStalls++; - } - frameTimer.reset(); + if ((LLStartUp::getStartupState() >= STATE_CLEANUP) && + (frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD)) + { + gFrameStalls++; + } + frameTimer.reset(); - resumeMainloopTimeout(); - - pingMainloopTimeout("Main:End"); - } + resumeMainloopTimeout(); + + pingMainloopTimeout("Main:End"); } - catch(std::bad_alloc) - { - LLMemory::logMemoryInfo(TRUE) ; + } + catch (const LLContinueError&) + { + LOG_UNHANDLED_EXCEPTION(""); + } + catch(std::bad_alloc) + { + LLMemory::logMemoryInfo(TRUE) ; - //stop memory leaking simulation - LLFloaterMemLeak* mem_leak_instance = - LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); - if(mem_leak_instance) - { - mem_leak_instance->stop() ; - LL_WARNS() << "Bad memory allocation in LLAppViewer::mainLoop()!" << LL_ENDL ; - } - else - { - //output possible call stacks to log file. - LLError::LLCallStacks::print() ; + //stop memory leaking simulation + LLFloaterMemLeak* mem_leak_instance = + LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); + if(mem_leak_instance) + { + mem_leak_instance->stop() ; + LL_WARNS() << "Bad memory allocation in LLAppViewer::frame()!" << LL_ENDL ; + } + else + { + //output possible call stacks to log file. + LLError::LLCallStacks::print() ; - LL_ERRS() << "Bad memory allocation in LLAppViewer::mainLoop()!" << LL_ENDL ; - } + LL_ERRS() << "Bad memory allocation in LLAppViewer::frame()!" << LL_ENDL ; } } + catch (...) + { + CRASH_ON_UNHANDLED_EXCEPTION(""); + } if (LLApp::isExiting()) { @@ -1602,7 +1596,7 @@ bool LLAppViewer::mainLoop() catch(std::bad_alloc) { LL_WARNS() << "Bad memory allocation when saveFinalSnapshot() is called!" << LL_ENDL ; - + //stop memory leaking simulation LLFloaterMemLeak* mem_leak_instance = LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking"); @@ -1611,16 +1605,20 @@ bool LLAppViewer::mainLoop() mem_leak_instance->stop() ; } } + catch (...) + { + CRASH_ON_UNHANDLED_EXCEPTION("saveFinalSnapshot()"); + } } - + delete gServicePump; - + destroyMainloopTimeout(); - + LL_INFOS() << "Exiting main_loop" << LL_ENDL; } - return LLApp::isExiting(); + return ! LLApp::isRunning(); } S32 LLAppViewer::updateTextureThreads(F32 max_time) @@ -2485,7 +2483,10 @@ bool LLAppViewer::initConfiguration() if (gSavedSettings.getBOOL("FirstRunThisInstall")) { - // Note that the "FirstRunThisInstall" settings is currently unused. + // Set firstrun flag to indicate that some further init actiona should be taken + // like determining screen DPI value and so on + mIsFirstRun = true; + gSavedSettings.setBOOL("FirstRunThisInstall", FALSE); } @@ -3142,7 +3143,8 @@ bool LLAppViewer::initWindow() .min_width(gSavedSettings.getU32("MinWindowWidth")) .min_height(gSavedSettings.getU32("MinWindowHeight")) .fullscreen(gSavedSettings.getBOOL("FullScreen")) - .ignore_pixel_depth(ignorePixelDepth); + .ignore_pixel_depth(ignorePixelDepth) + .first_run(mIsFirstRun); gViewerWindow = new LLViewerWindow(window_params); @@ -3344,6 +3346,19 @@ LLSD LLAppViewer::getViewerInfo() const info["LLCEFLIB_VERSION"] = LLCEFLIB_VERSION; #else info["LLCEFLIB_VERSION"] = "Undefined"; + +#endif + +#if LL_WINDOWS + std::ostringstream ver_codec; + ver_codec << LIBVLC_VERSION_MAJOR; + ver_codec << "."; + ver_codec << LIBVLC_VERSION_MINOR; + ver_codec << "."; + ver_codec << LIBVLC_VERSION_REVISION; + info["LIBVLC_VERSION"] = ver_codec.str(); +#else + info["LIBVLC_VERSION"] = "Undefined"; #endif S32 packets_in = LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_IN); @@ -5548,8 +5563,7 @@ void LLAppViewer::forceErrorInfiniteLoop() void LLAppViewer::forceErrorSoftwareException() { LL_WARNS() << "Forcing a deliberate exception" << LL_ENDL; - // *FIX: Any way to insure it won't be handled? - throw; + LLTHROW(LLException("User selected Force Software Exception")); } void LLAppViewer::forceErrorDriverCrash() @@ -5801,6 +5815,20 @@ void LLAppViewer::launchUpdater() // LLAppViewer::instance()->forceQuit(); } +/** +* Check if user is running a new version of the viewer. +* Display the Release Notes if it's not overriden by the "UpdaterShowReleaseNotes" setting. +*/ +void LLAppViewer::showReleaseNotesIfRequired() +{ + if (LLVersionInfo::getChannelAndVersion() != gLastRunVersion + && gSavedSettings.getBOOL("UpdaterShowReleaseNotes") + && !gSavedSettings.getBOOL("FirstLoginThisInstall")) + { + LLSD info(getViewerInfo()); + LLWeb::loadURLInternal(info["VIEWER_RELEASE_NOTES_URL"]); + } +} //virtual void LLAppViewer::setMasterSystemAudioMute(bool mute) diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index b5e674bd7b..948d316009 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -79,7 +79,7 @@ public: // virtual bool init(); // Override to do application initialization virtual bool cleanup(); // Override to do application cleanup - virtual bool mainLoop(); // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit. + virtual bool frame(); // Override for application body logic // Application control void flushVFSIO(); // waits for vfs transfers to complete @@ -254,6 +254,8 @@ private: void sendLogoutRequest(); void disconnectViewer(); + + void showReleaseNotesIfRequired(); // *FIX: the app viewer class should be some sort of singleton, no? // Perhaps its child class is the singleton and this should be an abstract base. @@ -283,7 +285,6 @@ private: std::string mSerialNumber; bool mPurgeCache; bool mPurgeOnExit; - bool mMainLoopInitialized; LLViewerJoystick* joystick; bool mSavedFinalSnapshot; @@ -316,6 +317,7 @@ private: // llcorehttp library init/shutdown helper LLAppCoreHttp mAppCoreHttp; + bool mIsFirstRun; //--------------------------------------------- //*NOTE: Mani - legacy updater stuff // Still useable? diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index f5742b29cf..6f32aab851 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -95,10 +95,8 @@ int main( int argc, char **argv ) } // Run the application main loop - if(!LLApp::isQuitting()) - { - viewer_app_ptr->mainLoop(); - } + while (! viewer_app_ptr->frame()) + {} if (!LLApp::isError()) { diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index ca219fda59..4fe1e31668 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -117,12 +117,17 @@ void handleQuit() LLAppViewer::instance()->userQuit(); } -bool runMainLoop() +// This function is called pumpMainLoop() rather than runMainLoop() because +// it passes control to the viewer's main-loop logic for a single frame. Like +// LLAppViewer::frame(), it returns 'true' when it's done. Until then, it +// expects to be called again by the timer in LLAppDelegate +// (llappdelegate-objc.mm). +bool pumpMainLoop() { bool ret = LLApp::isQuitting(); if (!ret && gViewerAppPtr != NULL) { - ret = gViewerAppPtr->mainLoop(); + ret = gViewerAppPtr->frame(); } else { ret = true; } diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 4786f83bfd..5107030476 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -231,6 +231,8 @@ int APIENTRY WINMAIN(HINSTANCE hInstance, DWORD heap_enable_lfh_error[MAX_HEAPS]; S32 num_heaps = 0; + LLWindowWin32::setDPIAwareness(); + #if WINDOWS_CRT_MEM_CHECKS && !INCLUDE_VLD _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); // dump memory leaks on exit #elif 0 @@ -317,10 +319,8 @@ int APIENTRY WINMAIN(HINSTANCE hInstance, } // Run the application main loop - if(!LLApp::isQuitting()) - { - viewer_app_ptr->mainLoop(); - } + while (! viewer_app_ptr->frame()) + {} if (!LLApp::isError()) { @@ -330,33 +330,33 @@ int APIENTRY WINMAIN(HINSTANCE hInstance, // app cleanup if there was a problem. // #if WINDOWS_CRT_MEM_CHECKS - LL_INFOS() << "CRT Checking memory:" << LL_ENDL; - if (!_CrtCheckMemory()) - { - LL_WARNS() << "_CrtCheckMemory() failed at prior to cleanup!" << LL_ENDL; - } - else - { - LL_INFOS() << " No corruption detected." << LL_ENDL; - } + LL_INFOS() << "CRT Checking memory:" << LL_ENDL; + if (!_CrtCheckMemory()) + { + LL_WARNS() << "_CrtCheckMemory() failed at prior to cleanup!" << LL_ENDL; + } + else + { + LL_INFOS() << " No corruption detected." << LL_ENDL; + } #endif - - gGLActive = TRUE; - viewer_app_ptr->cleanup(); - + gGLActive = TRUE; + + viewer_app_ptr->cleanup(); + #if WINDOWS_CRT_MEM_CHECKS - LL_INFOS() << "CRT Checking memory:" << LL_ENDL; - if (!_CrtCheckMemory()) - { - LL_WARNS() << "_CrtCheckMemory() failed after cleanup!" << LL_ENDL; - } - else - { - LL_INFOS() << " No corruption detected." << LL_ENDL; - } + LL_INFOS() << "CRT Checking memory:" << LL_ENDL; + if (!_CrtCheckMemory()) + { + LL_WARNS() << "_CrtCheckMemory() failed after cleanup!" << LL_ENDL; + } + else + { + LL_INFOS() << " No corruption detected." << LL_ENDL; + } #endif - + } delete viewer_app_ptr; viewer_app_ptr = NULL; @@ -569,7 +569,7 @@ bool LLAppViewerWin32::initHardwareTest() // Do driver verification and initialization based on DirectX // hardware polling and driver versions // - if (FALSE == gSavedSettings.getBOOL("NoHardwareProbe")) + if (TRUE == gSavedSettings.getBOOL("ProbeHardwareOnStartup") && FALSE == gSavedSettings.getBOOL("NoHardwareProbe")) { // per DEV-11631 - disable hardware probing for everything // but vram. diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index a6e745448a..7b8c630837 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -950,15 +950,22 @@ bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NUL const std::set<LLFolderViewItem*> inventory_selected = root_folder->getSelectionList(); if (inventory_selected.empty()) return false; // nothing selected + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); bool can_share = true; std::set<LLFolderViewItem*>::const_iterator it = inventory_selected.begin(); const std::set<LLFolderViewItem*>::const_iterator it_end = inventory_selected.end(); for (; it != it_end; ++it) { - LLViewerInventoryCategory* inv_cat = gInventory.getCategory(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); - // any category can be offered. + LLUUID cat_id = static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID(); + LLViewerInventoryCategory* inv_cat = gInventory.getCategory(cat_id); + // any category can be offered if it's not in trash. if (inv_cat) { + if ((cat_id == trash_id) || gInventory.isObjectDescendentOf(cat_id, trash_id)) + { + can_share = false; + break; + } continue; } diff --git a/indra/newview/llavatarrendernotifier.cpp b/indra/newview/llavatarrendernotifier.cpp index 24934fdb73..94584a623b 100644 --- a/indra/newview/llavatarrendernotifier.cpp +++ b/indra/newview/llavatarrendernotifier.cpp @@ -38,6 +38,7 @@ #include "llnotifications.h" #include "llnotificationsutil.h" #include "llnotificationtemplate.h" +#include "llslurl.h" #include "lltimer.h" #include "llvoavatarself.h" #include "llviewercontrol.h" @@ -51,6 +52,11 @@ static const F32 RENDER_ALLOWED_CHANGE_PCT = 0.1; // wait seconds before processing over limit updates after last complexity change static const U32 OVER_LIMIT_UPDATE_DELAY = 70; +static const U32 WARN_HUD_OBJECTS_LIMIT = 1000; +static const U32 WARN_HUD_TEXTURES_LIMIT = 200; +static const U32 WARN_HUD_OVERSIZED_TEXTURES_LIMIT = 6; +static const U32 WARN_HUD_TEXTURE_MEMORY_LIMIT = 32000000; // in bytes + LLAvatarRenderNotifier::LLAvatarRenderNotifier() : mAgentsCount(0), @@ -264,3 +270,220 @@ void LLAvatarRenderNotifier::updateNotificationAgent(U32 agentComplexity) } } +// LLHUDRenderNotifier + +static const char* e_hud_messages[] = +{ + "hud_render_textures_warning", + "hud_render_cramped_warning", + "hud_render_heavy_textures_warning", + "hud_render_cost_warning", + "hud_render_memory_warning", +}; + +LLHUDRenderNotifier::LLHUDRenderNotifier() : +mReportedHUDWarning(WARN_NONE) +{ +} + +LLHUDRenderNotifier::~LLHUDRenderNotifier() +{ +} + +void LLHUDRenderNotifier::updateNotificationHUD(hud_complexity_list_t complexity) +{ + if (!isAgentAvatarValid() || !gAgentWearables.areWearablesLoaded()) + { + // data not ready. + return; + } + + // TODO: + // Find a way to show message with list of issues, but without making it too large + // and intrusive. + + LLHUDComplexity new_total_complexity; + LLHUDComplexity report_complexity; + + hud_complexity_list_t::iterator iter = complexity.begin(); + hud_complexity_list_t::iterator end = complexity.end(); + EWarnLevel warning_level = WARN_NONE; + for (; iter != end; ++iter) + { + LLHUDComplexity object_complexity = *iter; + EWarnLevel object_level = getWarningType(object_complexity, report_complexity); + if (object_level >= 0) + { + warning_level = object_level; + report_complexity = object_complexity; + } + new_total_complexity.objectsCost += object_complexity.objectsCost; + new_total_complexity.objectsCount += object_complexity.objectsCount; + new_total_complexity.texturesCost += object_complexity.texturesCost; + new_total_complexity.texturesCount += object_complexity.texturesCount; + new_total_complexity.largeTexturesCount += object_complexity.largeTexturesCount; + new_total_complexity.texturesMemoryTotal += object_complexity.texturesMemoryTotal; + } + + if (mHUDPopUpDelayTimer.hasExpired() || isNotificationVisible()) + { + if (warning_level >= 0) + { + // Display info about most complex HUD object + // make sure it shown only once unless object's complexity or object itself changed + if (mReportedHUDComplexity.objectId != report_complexity.objectId + || mReportedHUDWarning != warning_level) + { + displayHUDNotification(warning_level, report_complexity.objectId, report_complexity.objectName, report_complexity.jointName); + mReportedHUDComplexity = report_complexity; + mReportedHUDWarning = warning_level; + } + } + else + { + // Check if total complexity is above threshold and above previous warning + // Show warning with highest importance (5m delay between warnings by default) + if (!mReportedHUDComplexity.objectId.isNull()) + { + mReportedHUDComplexity.reset(); + mReportedHUDWarning = WARN_NONE; + } + + warning_level = getWarningType(new_total_complexity, mReportedHUDComplexity); + if (warning_level >= 0 && mReportedHUDWarning != warning_level) + { + displayHUDNotification(warning_level); + } + mReportedHUDComplexity = new_total_complexity; + mReportedHUDWarning = warning_level; + } + } + else if (warning_level >= 0) + { + LL_DEBUGS("HUDdetail") << "HUD individual warning postponed" << LL_ENDL; + } + + if (mLatestHUDComplexity.objectsCost != new_total_complexity.objectsCost + || mLatestHUDComplexity.objectsCount != new_total_complexity.objectsCount + || mLatestHUDComplexity.texturesCost != new_total_complexity.texturesCost + || mLatestHUDComplexity.texturesCount != new_total_complexity.texturesCount + || mLatestHUDComplexity.largeTexturesCount != new_total_complexity.largeTexturesCount + || mLatestHUDComplexity.texturesMemoryTotal != new_total_complexity.texturesMemoryTotal) + { + LL_INFOS("HUDdetail") << "HUD textures count: " << new_total_complexity.texturesCount + << " HUD textures cost: " << new_total_complexity.texturesCost + << " Large textures: " << new_total_complexity.largeTexturesCount + << " HUD objects cost: " << new_total_complexity.objectsCost + << " HUD objects count: " << new_total_complexity.objectsCount << LL_ENDL; + + mLatestHUDComplexity = new_total_complexity; + } +} + +bool LLHUDRenderNotifier::isNotificationVisible() +{ + return mHUDNotificationPtr != NULL && mHUDNotificationPtr->isActive(); +} + +// private static +LLHUDRenderNotifier::EWarnLevel LLHUDRenderNotifier::getWarningType(LLHUDComplexity object_complexity, LLHUDComplexity cmp_complexity) +{ + static LLCachedControl<U32> max_render_cost(gSavedSettings, "RenderAvatarMaxComplexity", 0U); // ties max HUD cost to avatar cost + static LLCachedControl<U32> max_objects_count(gSavedSettings, "RenderHUDObjectsWarning", WARN_HUD_OBJECTS_LIMIT); + static LLCachedControl<U32> max_textures_count(gSavedSettings, "RenderHUDTexturesWarning", WARN_HUD_TEXTURES_LIMIT); + static LLCachedControl<U32> max_oversized_count(gSavedSettings, "RenderHUDOversizedTexturesWarning", WARN_HUD_OVERSIZED_TEXTURES_LIMIT); + static LLCachedControl<U32> max_texture_memory(gSavedSettings, "RenderHUDTexturesMemoryWarning", WARN_HUD_TEXTURE_MEMORY_LIMIT); + + if (cmp_complexity.texturesMemoryTotal < object_complexity.texturesMemoryTotal + && object_complexity.texturesMemoryTotal > (F64Bytes)max_texture_memory) + { + // Note: Memory might not be accurate since texture is still loading or discard level changes + + LL_DEBUGS("HUDdetail") << "HUD " << object_complexity.objectName << " memory usage over limit, " + << " was " << cmp_complexity.texturesMemoryTotal + << " is " << object_complexity.texturesMemoryTotal << LL_ENDL; + + return WARN_MEMORY; + } + else if ((cmp_complexity.objectsCost < object_complexity.objectsCost + || cmp_complexity.texturesCost < object_complexity.texturesCost) + && max_render_cost > 0 + && object_complexity.objectsCost + object_complexity.texturesCost > max_render_cost) + { + LL_DEBUGS("HUDdetail") << "HUD " << object_complexity.objectName << " complexity over limit," + << " HUD textures cost: " << object_complexity.texturesCost + << " HUD objects cost: " << object_complexity.objectsCost << LL_ENDL; + + return WARN_COST; + } + else if (cmp_complexity.largeTexturesCount < object_complexity.largeTexturesCount + && object_complexity.largeTexturesCount > max_oversized_count) + { + LL_DEBUGS("HUDdetail") << "HUD " << object_complexity.objectName << " contains to many large textures: " + << object_complexity.largeTexturesCount << LL_ENDL; + + return WARN_HEAVY; + } + else if (cmp_complexity.texturesCount < object_complexity.texturesCount + && object_complexity.texturesCount > max_textures_count) + { + LL_DEBUGS("HUDdetail") << "HUD " << object_complexity.objectName << " contains too many textures: " + << object_complexity.texturesCount << LL_ENDL; + + return WARN_CRAMPED; + } + else if (cmp_complexity.objectsCount < object_complexity.objectsCount + && object_complexity.objectsCount > max_objects_count) + { + LL_DEBUGS("HUDdetail") << "HUD " << object_complexity.objectName << " contains too many objects: " + << object_complexity.objectsCount << LL_ENDL; + + return WARN_TEXTURES; + } + return WARN_NONE; +} + +void LLHUDRenderNotifier::displayHUDNotification(EWarnLevel warn_type, LLUUID obj_id, std::string obj_name, std::string joint_name) +{ + static LLCachedControl<U32> pop_up_delay(gSavedSettings, "ComplexityChangesPopUpDelay", 300); + static LLCachedControl<U32> expire_delay(gSavedSettings, "ShowMyComplexityChanges", 20); + LLDate expire_date(LLDate::now().secondsSinceEpoch() + expire_delay); + + // Since we need working "ignoretext" there is no other way but to + // use single notification while constructing it from multiple pieces + LLSD reason_args; + if (obj_id.isNull()) + { + reason_args["HUD_DETAILS"] = LLTrans::getString("hud_description_total"); + } + else + { + if (obj_name.empty()) + { + LL_WARNS("HUDdetail") << "Object name not assigned" << LL_ENDL; + } + if (joint_name.empty()) + { + std::string verb = "select?name=" + LLURI::escape(obj_name); + reason_args["HUD_DETAILS"] = LLSLURL("inventory", obj_id, verb.c_str()).getSLURLString(); + } + else + { + LLSD object_args; + std::string verb = "select?name=" + LLURI::escape(obj_name); + object_args["OBJ_NAME"] = LLSLURL("inventory", obj_id, verb.c_str()).getSLURLString(); + object_args["JNT_NAME"] = LLTrans::getString(joint_name); + reason_args["HUD_DETAILS"] = LLTrans::getString("hud_name_with_joint", object_args); + } + } + + LLSD msg_args; + msg_args["HUD_REASON"] = LLTrans::getString(e_hud_messages[warn_type], reason_args); + + mHUDNotificationPtr = LLNotifications::instance().add(LLNotification::Params() + .name("HUDComplexityWarning") + .expiry(expire_date) + .substitutions(msg_args)); + mHUDPopUpDelayTimer.resetWithExpiry(pop_up_delay); +} + diff --git a/indra/newview/llavatarrendernotifier.h b/indra/newview/llavatarrendernotifier.h index 2a2704de28..a169baef40 100644 --- a/indra/newview/llavatarrendernotifier.h +++ b/indra/newview/llavatarrendernotifier.h @@ -33,6 +33,36 @@ class LLViewerRegion; +struct LLHUDComplexity +{ + LLHUDComplexity() + { + reset(); + } + void reset() + { + objectId = LLUUID::null; + objectName = ""; + objectsCost = 0; + objectsCount = 0; + texturesCost = 0; + texturesCount = 0; + largeTexturesCount = 0; + texturesMemoryTotal = (F64Bytes)0; + } + LLUUID objectId; + std::string objectName; + std::string jointName; + U32 objectsCost; + U32 objectsCount; + U32 texturesCost; + U32 texturesCount; + U32 largeTexturesCount; + F64Bytes texturesMemoryTotal; +}; + +typedef std::list<LLHUDComplexity> hud_complexity_list_t; + // Class to notify user about drastic changes in agent's render weights or if other agents // reported that user's agent is too 'heavy' for their settings class LLAvatarRenderNotifier : public LLSingleton<LLAvatarRenderNotifier> @@ -81,4 +111,36 @@ private: S32 mLastOutfitRezStatus; }; +// Class to notify user about heavy set of HUD +class LLHUDRenderNotifier : public LLSingleton<LLHUDRenderNotifier> +{ +public: + LLHUDRenderNotifier(); + ~LLHUDRenderNotifier(); + + void updateNotificationHUD(hud_complexity_list_t complexity); + bool isNotificationVisible(); + +private: + enum EWarnLevel + { + WARN_NONE = -1, + WARN_TEXTURES = 0, // least important + WARN_CRAMPED, + WARN_HEAVY, + WARN_COST, + WARN_MEMORY, //most important + }; + + LLNotificationPtr mHUDNotificationPtr; + + static EWarnLevel getWarningType(LLHUDComplexity object_complexity, LLHUDComplexity cmp_complexity); + void displayHUDNotification(EWarnLevel warn_type, LLUUID obj_id = LLUUID::null, std::string object_name = "", std::string joint_name = ""); + + LLHUDComplexity mReportedHUDComplexity; + EWarnLevel mReportedHUDWarning; + LLHUDComplexity mLatestHUDComplexity; + LLFrameTimer mHUDPopUpDelayTimer; +}; + #endif /* ! defined(LL_llavatarrendernotifier_H) */ diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index 00fa6dd979..54c6c985d6 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -311,7 +311,8 @@ LLWString LLChatBar::stripChannelNumber(const LLWString &mesg, S32* channel) } else if (mesg[0] == '/' && mesg[1] - && LLStringOps::isDigit(mesg[1])) + && (LLStringOps::isDigit(mesg[1]) + || (mesg[1] == '-' && mesg[2] && LLStringOps::isDigit(mesg[2])))) { // This a special "/20" speak on a channel S32 pos = 0; @@ -325,7 +326,7 @@ LLWString LLChatBar::stripChannelNumber(const LLWString &mesg, S32* channel) channel_string.push_back(c); pos++; } - while(c && pos < 64 && LLStringOps::isDigit(c)); + while(c && pos < 64 && (LLStringOps::isDigit(c) || (pos == 1 && c == '-'))); // Move the pointer forward to the first non-whitespace char // Check isspace before looping, so we can handle "/33foo" diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp index 1819fc74ee..90a5483dc9 100644 --- a/indra/newview/llcommandlineparser.cpp +++ b/indra/newview/llcommandlineparser.cpp @@ -26,6 +26,7 @@ #include "llviewerprecompiledheaders.h" #include "llcommandlineparser.h" +#include "llexception.h" // *NOTE: The boost::lexical_cast generates // the warning C4701(local used with out assignment) in VC7.1. @@ -50,6 +51,7 @@ #include "llsdserialize.h" #include "llerror.h" #include "stringize.h" +#include "llexception.h" #include <string> #include <set> #include <iostream> @@ -98,14 +100,14 @@ namespace bool gPastLastOption = false; } -class LLCLPError : public std::logic_error { +class LLCLPError : public LLException { public: - LLCLPError(const std::string& what) : std::logic_error(what) {} + LLCLPError(const std::string& what) : LLException(what) {} }; -class LLCLPLastOption : public std::logic_error { +class LLCLPLastOption : public LLException { public: - LLCLPLastOption(const std::string& what) : std::logic_error(what) {} + LLCLPLastOption(const std::string& what) : LLException(what) {} }; class LLCLPValue : public po::value_semantic_codecvt_helper<char> @@ -202,17 +204,17 @@ protected: { if(gPastLastOption) { - throw(LLCLPLastOption("Don't parse no more!")); + LLTHROW(LLCLPLastOption("Don't parse no more!")); } // Error checks. Needed? if (!value_store.empty() && !is_composing()) { - throw(LLCLPError("Non composing value with multiple occurences.")); + LLTHROW(LLCLPError("Non composing value with multiple occurences.")); } if (new_tokens.size() < min_tokens() || new_tokens.size() > max_tokens()) { - throw(LLCLPError("Illegal number of tokens specified.")); + LLTHROW(LLCLPError("Illegal number of tokens specified.")); } if(value_store.empty()) @@ -466,7 +468,7 @@ onevalue(const std::string& option, { // What does it mean when the user specifies a command-line switch // that requires a value, but omits the value? Complain. - throw LLCLPError(STRINGIZE("No value specified for --" << option << "!")); + LLTHROW(LLCLPError(STRINGIZE("No value specified for --" << option << "!"))); } else if (value.size() > 1) { @@ -484,9 +486,9 @@ void badvalue(const std::string& option, // If the user passes an unusable value for a command-line switch, it // seems like a really bad idea to just ignore it, even with a log // warning. - throw LLCLPError(STRINGIZE("Invalid value specified by command-line switch '" << option - << "' for variable '" << varname << "' of type " << type - << ": '" << value << "'")); + LLTHROW(LLCLPError(STRINGIZE("Invalid value specified by command-line switch '" << option + << "' for variable '" << varname << "' of type " << type + << ": '" << value << "'"))); } template <typename T> diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index 7721e67290..76e16f5a1f 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -67,7 +67,9 @@ namespace const std::string QUEUE_EVENTPUMP_NAME("ScriptActionQueue"); - + // ObjectIventoryFetcher is an adapter between the LLVOInventoryListener::inventoryChanged + // callback mechanism and the LLEventPump coroutine architecture allowing the + // coroutine to wait for the inventory event. class ObjectInventoryFetcher: public LLVOInventoryListener { public: @@ -144,7 +146,7 @@ public: queue->getChild<LLScrollListCtrl>("queue output")->addSimpleElement(message, ADD_BOTTOM); } - return LLSD().with("success", LLSD::Boolean(true)); + return LLSDMap("success", LLSD::Boolean(true)); } @@ -254,7 +256,6 @@ LLFloaterCompileQueue::LLFloaterCompileQueue(const LLSD& key) setTitle(LLTrans::getString("CompileQueueTitle")); setStartString(LLTrans::getString("CompileQueueStart")); -// mUploadQueue = new LLAssetUploadQueue(new LLCompileFloaterUploadQueueSupplier(key.asUUID())); } LLFloaterCompileQueue::~LLFloaterCompileQueue() @@ -267,7 +268,6 @@ void LLFloaterCompileQueue::experienceIdsReceived( const LLSD& content ) { mExperienceIds.insert(it->asUUID()); } -// nextObject(); } BOOL LLFloaterCompileQueue::hasExperience( const LLUUID& id ) const @@ -277,11 +277,6 @@ BOOL LLFloaterCompileQueue::hasExperience( const LLUUID& id ) const // //Attempt to record this asset ID. If it can not be inserted into the set // //then it has already been processed so return false. -// bool LLFloaterCompileQueue::checkAssetId(const LLUUID &assetId) -// { -// std::pair<uuid_list_t::iterator, bool> result = mAssetIds.insert(assetId); -// return result.second; -// } void LLFloaterCompileQueue::handleHTTPResponse(std::string pumpName, const LLSD &expresult) { @@ -331,8 +326,10 @@ void LLFloaterCompileQueue::processExperienceIdResults(LLSD result, LLUUID paren queue->experienceIdsReceived(result["experience_ids"]); + // getDerived handle gets a handle that can be resolved to a parent class of the derived object. LLHandle<LLFloaterScriptQueue> hFloater(queue->getDerivedHandle<LLFloaterScriptQueue>()); + // note subtle difference here: getDerivedHandle in this case is for an LLFloaterCompileQueue fnQueueAction_t fn = boost::bind(LLFloaterCompileQueue::processScript, queue->getDerivedHandle<LLFloaterCompileQueue>(), _1, _2, _3); @@ -345,37 +342,35 @@ void LLFloaterCompileQueue::processExperienceIdResults(LLSD result, LLUUID paren } +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloater, const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) { LLSD result; - LLFloaterCompileQueue *that = hfloater.get(); - bool monocompile = that->mMono; + LLCheckedHandle<LLFloaterCompileQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // which is caught in objectScriptProcessingQueueCoro + bool monocompile = floater->mMono; F32 fetch_timeout = gSavedSettings.getF32("QueueInventoryFetchTimeout"); - if (!that) - return false; // Initial test to see if we can (or should) attempt to compile the script. LLInventoryItem *item = dynamic_cast<LLInventoryItem *>(inventory); - { - if (!item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID()) || - !item->getPermissions().allowCopyBy(gAgent.getID(), gAgent.getGroupID())) - { - std::string buffer = "Skipping: " + item->getName() + "(Permissions)"; - that->addStringMessage(buffer); - return true; - } + if (!item) + { + LL_WARNS("SCRIPTQ") << "item retrieved is not an LLInventoryItem." << LL_ENDL; + return true; + } -// if (!that->checkAssetId(item->getAssetUUID())) -// { -// std::string buffer = "Skipping: " + item->getName() + "(Repeat)"; -// that->addStringMessage(buffer); -// return true; -// } + if (!item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID()) || + !item->getPermissions().allowCopyBy(gAgent.getID(), gAgent.getGroupID())) + { + std::string buffer = "Skipping: " + item->getName() + "(Permissions)"; + floater->addStringMessage(buffer); + return true; } - that = NULL; // Attempt to retrieve the experience LLUUID experienceId; @@ -384,37 +379,30 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat boost::bind(&LLFloaterCompileQueue::handleHTTPResponse, pump.getName(), _1)); result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, - LLSD().with("timeout", LLSD::Boolean(true))); + LLSDMap("timeout", LLSD::Boolean(true))); - that = hfloater.get(); - if (!that) - { - return false; - } - - if (result.has("timeout") && result["timeout"].asBoolean()) - { + if (result.has("timeout")) + { // A timeout filed in the result will always be true if present. LLStringUtil::format_map_t args; args["[OBJECT_NAME]"] = inventory->getName(); - std::string buffer = that->getString("Timeout", args); - that->addStringMessage(buffer); + std::string buffer = floater->getString("Timeout", args); + floater->addStringMessage(buffer); return true; } if (result.has(LLExperienceCache::EXPERIENCE_ID)) { experienceId = result[LLExperienceCache::EXPERIENCE_ID].asUUID(); - if (!that->hasExperience(experienceId)) + if (!floater->hasExperience(experienceId)) { - that->addProcessingMessage("CompileNoExperiencePerm", LLSD() - .with("SCRIPT", inventory->getName()) - .with("EXPERIENCE", result[LLExperienceCache::NAME].asString())); + floater->addProcessingMessage("CompileNoExperiencePerm", + LLSDMap("SCRIPT", inventory->getName()) + ("EXPERIENCE", result[LLExperienceCache::NAME].asString())); return true; } } } - that = NULL; { HandleScriptUserData userData(pump.getName()); @@ -433,32 +421,23 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat &userData); result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, - LLSD().with("timeout", LLSD::Boolean(true))); - } - - that = hfloater.get(); - if (!that) - { - return false; + LLSDMap("timeout", LLSD::Boolean(true))); } if (result.has("timeout")) - { - if (result.has("timeout") && result["timeout"].asBoolean()) - { - LLStringUtil::format_map_t args; - args["[OBJECT_NAME]"] = inventory->getName(); - std::string buffer = that->getString("Timeout", args); - that->addStringMessage(buffer); - return true; - } + { // A timeout filed in the result will always be true if present. + LLStringUtil::format_map_t args; + args["[OBJECT_NAME]"] = inventory->getName(); + std::string buffer = floater->getString("Timeout", args); + floater->addStringMessage(buffer); + return true; } if (result.has("error")) { LL_WARNS("SCRIPTQ") << "Inventory fetch returned with error. Code: " << result["error"].asString() << LL_ENDL; std::string buffer = result["message"].asString() + " " + inventory->getName(); - that->addStringMessage(buffer); + floater->addStringMessage(buffer); if (result.has("alert")) { @@ -470,12 +449,9 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat } LLUUID assetId = result["asset_id"]; - that = NULL; - std::string url = object->getRegion()->getCapability("UpdateScriptTask"); - { LLResourceUploadInfo::ptr_t uploadInfo(new LLQueuedScriptAssetUpload(object->getID(), inventory->getUUID(), @@ -490,24 +466,15 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); } - result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, LLSD().with("timeout", LLSD::Boolean(true))); - - that = hfloater.get(); - if (!that) - { - return false; - } + result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, LLSDMap("timeout", LLSD::Boolean(true))); if (result.has("timeout")) - { - if (result.has("timeout") && result["timeout"].asBoolean()) - { - LLStringUtil::format_map_t args; - args["[OBJECT_NAME]"] = inventory->getName(); - std::string buffer = that->getString("Timeout", args); - that->addStringMessage(buffer); - return true; - } + { // A timeout filed in the result will always be true if present. + LLStringUtil::format_map_t args; + args["[OBJECT_NAME]"] = inventory->getName(); + std::string buffer = floater->getString("Timeout", args); + floater->addStringMessage(buffer); + return true; } // Bytecode save completed @@ -515,21 +482,21 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat { std::string buffer = std::string("Compilation of \"") + inventory->getName() + std::string("\" succeeded"); - that->addStringMessage(buffer); + floater->addStringMessage(buffer); LL_INFOS() << buffer << LL_ENDL; } else { LLSD compile_errors = result["errors"]; std::string buffer = std::string("Compilation of \"") + inventory->getName() + std::string("\" failed:"); - that->addStringMessage(buffer); + floater->addStringMessage(buffer); for (LLSD::array_const_iterator line = compile_errors.beginArray(); line < compile_errors.endArray(); line++) { std::string str = line->asString(); str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); - that->addStringMessage(str); + floater->addStringMessage(str); } LL_INFOS() << result["errors"] << LL_ENDL; } @@ -576,16 +543,18 @@ LLFloaterResetQueue::~LLFloaterResetQueue() { } -bool LLFloaterResetQueue::resetObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. +bool LLFloaterResetQueue::resetObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) { - LLFloaterScriptQueue *that = hfloater.get(); - if (that) - { - std::string buffer; - buffer = that->getString("Resetting") + (": ") + inventory->getName(); - that->addStringMessage(buffer); - } + LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // which is caught in objectScriptProcessingQueueCoro + + std::string buffer; + buffer = floater->getString("Resetting") + (": ") + inventory->getName(); + floater->addStringMessage(buffer); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_ScriptReset); @@ -602,6 +571,8 @@ bool LLFloaterResetQueue::resetObjectScripts(LLHandle<LLFloaterScriptQueue> hflo bool LLFloaterResetQueue::startQueue() { + // Bind the resetObjectScripts method into a QueueAction function and pass it + // into the object queue processing coroutine. fnQueueAction_t fn = boost::bind(LLFloaterResetQueue::resetObjectScripts, getDerivedHandle<LLFloaterScriptQueue>(), _1, _2, _3); @@ -629,16 +600,18 @@ LLFloaterRunQueue::~LLFloaterRunQueue() { } -bool LLFloaterRunQueue::runObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. +bool LLFloaterRunQueue::runObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) { - LLFloaterScriptQueue *that = hfloater.get(); - if (that) - { - std::string buffer; - buffer = that->getString("Running") + (": ") + inventory->getName(); - that->addStringMessage(buffer); - } + LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // which is caught in objectScriptProcessingQueueCoro + + std::string buffer; + buffer = floater->getString("Running") + (": ") + inventory->getName(); + floater->addStringMessage(buffer); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_SetScriptRunning); @@ -684,16 +657,18 @@ LLFloaterNotRunQueue::~LLFloaterNotRunQueue() { } +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. bool LLFloaterNotRunQueue::stopObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater, const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump) { - LLFloaterScriptQueue *that = hfloater.get(); - if (that) - { - std::string buffer; - buffer = that->getString("NotRunning") + (": ") + inventory->getName(); - that->addStringMessage(buffer); - } + LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // which is caught in objectScriptProcessingQueueCoro + + std::string buffer; + buffer = floater->getString("NotRunning") + (": ") + inventory->getName(); + floater->addStringMessage(buffer); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_SetScriptRunning); @@ -732,7 +707,7 @@ void ObjectInventoryFetcher::inventoryChanged(LLViewerObject* object, mInventoryList.clear(); mInventoryList.assign(inventory->begin(), inventory->end()); - mPump.post(LLSD().with("changed", LLSD::Boolean(true))); + mPump.post(LLSDMap("changed", LLSD::Boolean(true))); } @@ -740,115 +715,97 @@ void LLFloaterScriptQueue::objectScriptProcessingQueueCoro(std::string action, L object_data_list_t objectList, fnQueueAction_t func) { LLCoros::set_consuming(true); - LLFloaterScriptQueue * floater(NULL); + LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); + // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. + // This is expected if the dialog closes. LLEventMailDrop maildrop(QUEUE_EVENTPUMP_NAME, true); F32 fetch_timeout = gSavedSettings.getF32("QueueInventoryFetchTimeout"); -// floater = hfloater.get(); -// floater->addProcessingMessage("Starting", -// LLSD() -// .with("[START]", action) -// .with("[COUNT]", LLSD::Integer(objectList.size()))); -// floater = NULL; - for (object_data_list_t::iterator itObj(objectList.begin()); (itObj != objectList.end()); ++itObj) + try { - bool firstForObject = true; - LLUUID object_id = (*itObj).mObjectId; - LL_INFOS("SCRIPTQ") << "Next object in queue with ID=" << object_id.asString() << LL_ENDL; - - LLPointer<LLViewerObject> obj = gObjectList.findObject(object_id); - LLInventoryObject::object_list_t inventory; - if (obj) + for (object_data_list_t::iterator itObj(objectList.begin()); (itObj != objectList.end()); ++itObj) { - ObjectInventoryFetcher::ptr_t fetcher(new ObjectInventoryFetcher(maildrop, obj, NULL)); + bool firstForObject = true; + LLUUID object_id = (*itObj).mObjectId; + LL_INFOS("SCRIPTQ") << "Next object in queue with ID=" << object_id.asString() << LL_ENDL; - fetcher->fetchInventory(); - - floater = hfloater.get(); - if (floater) + LLPointer<LLViewerObject> obj = gObjectList.findObject(object_id); + LLInventoryObject::object_list_t inventory; + if (obj) { + ObjectInventoryFetcher::ptr_t fetcher(new ObjectInventoryFetcher(maildrop, obj, NULL)); + + fetcher->fetchInventory(); + LLStringUtil::format_map_t args; args["[OBJECT_NAME]"] = (*itObj).mObjectName; floater->addStringMessage(floater->getString("LoadingObjInv", args)); - } - LLSD result = llcoro::suspendUntilEventOnWithTimeout(maildrop, fetch_timeout, - LLSD().with("timeout", LLSD::Boolean(true))); + LLSD result = llcoro::suspendUntilEventOnWithTimeout(maildrop, fetch_timeout, + LLSDMap("timeout", LLSD::Boolean(true))); - if (result.has("timeout") && result["timeout"].asBoolean()) - { - LL_WARNS("SCRIPTQ") << "Unable to retrieve inventory for object " << object_id.asString() << - ". Skipping to next object." << LL_ENDL; + if (result.has("timeout")) + { // A timeout filed in the result will always be true if present. + LL_WARNS("SCRIPTQ") << "Unable to retrieve inventory for object " << object_id.asString() << + ". Skipping to next object." << LL_ENDL; - // floater could have been closed - floater = hfloater.get(); - if (floater) - { LLStringUtil::format_map_t args; args["[OBJECT_NAME]"] = (*itObj).mObjectName; floater->addStringMessage(floater->getString("Timeout", args)); + + continue; } + inventory.assign(fetcher->getInventoryList().begin(), fetcher->getInventoryList().end()); + } + else + { + LL_WARNS("SCRIPTQ") << "Unable to retrieve object with ID of " << object_id << + ". Skipping to next." << LL_ENDL; continue; } - inventory.assign(fetcher->getInventoryList().begin(), fetcher->getInventoryList().end()); - } - else - { - LL_WARNS("SCRIPTQ") << "Unable to retrieve object with ID of " << object_id << - ". Skipping to next." << LL_ENDL; - continue; - } + // TODO: Get the name of the object we are looking at here so that we can display it below. + //std::string objName = (dynamic_cast<LLInventoryObject *>(obj.get()))->getName(); + LL_DEBUGS("SCRIPTQ") << "Object has " << inventory.size() << " items." << LL_ENDL; - // TODO: Get the name of the object we are looking at here so that we can display it below. - //std::string objName = (dynamic_cast<LLInventoryObject *>(obj.get()))->getName(); - LL_DEBUGS("SCRIPTQ") << "Object has " << inventory.size() << " items." << LL_ENDL; - - for (LLInventoryObject::object_list_t::iterator itInv = inventory.begin(); - itInv != inventory.end(); ++itInv) - { - floater = hfloater.get(); - if (!floater) + for (LLInventoryObject::object_list_t::iterator itInv = inventory.begin(); + itInv != inventory.end(); ++itInv) { - LL_WARNS("SCRIPTQ") << "Script Queue floater closed! Canceling remaining ops" << LL_ENDL; - break; - } + floater.check(); - // note, we have a smart pointer to the obj above... but if we didn't we'd check that - // it still exists here. + // note, we have a smart pointer to the obj above... but if we didn't we'd check that + // it still exists here. - if (((*itInv)->getType() == LLAssetType::AT_LSL_TEXT)) - { - LL_DEBUGS("SCRIPTQ") << "Inventory item " << (*itInv)->getUUID().asString() << "\"" << (*itInv)->getName() << "\"" << LL_ENDL; - if (firstForObject) + if (((*itInv)->getType() == LLAssetType::AT_LSL_TEXT)) { - //floater->addStringMessage(objName + ":"); - firstForObject = false; + LL_DEBUGS("SCRIPTQ") << "Inventory item " << (*itInv)->getUUID().asString() << "\"" << (*itInv)->getName() << "\"" << LL_ENDL; + if (firstForObject) + { + //floater->addStringMessage(objName + ":"); + firstForObject = false; + } + + if (!func(obj, (*itInv), maildrop)) + { + continue; + } } - if (!func(obj, (*itInv), maildrop)) - { - continue; - } + // no other explicit suspension point in this loop. func(...) MIGHT suspend + // but offers no guarantee of doing so. + llcoro::suspend(); } - - llcoro::suspend(); - } - // Just test to be sure the floater is still present before calling the func - if (!hfloater.get()) - { - LL_WARNS("SCRIPTQ") << "Script Queue floater dismissed." << LL_ENDL; - break; } - } - - floater = hfloater.get(); - if (floater) - { floater->addStringMessage("Done"); floater->getChildView("close")->setEnabled(TRUE); } + catch (LLCheckedHandleBase::Stale &) + { + // This is expected. It means that floater has been closed before + // processing was completed. + LL_DEBUGS("SCRIPTQ") << "LLExeceptionStaleHandle caught! Floater has most likely been closed." << LL_ENDL; + } } diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp index 05c7e6caa5..4e69896b69 100644 --- a/indra/newview/llconversationlog.cpp +++ b/indra/newview/llconversationlog.cpp @@ -448,7 +448,12 @@ bool LLConversationLog::moveLog(const std::string &originDirectory, const std::s std::string LLConversationLog::getFileName() { std::string filename = "conversation"; - return gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS, filename) + ".log"; + std::string log_address = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS, filename); + if (!log_address.empty()) + { + log_address += ".log"; + } + return log_address; } bool LLConversationLog::saveToFile(const std::string& filename) diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index bababca652..c2d0d9f06b 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -108,6 +108,7 @@ private: static const std::string sCheckUpdateListenerName; static void startFetchServerReleaseNotes(); + static void fetchServerReleaseNotesCoro(const std::string& cap_url); static void handleServerReleaseNotes(LLSD results); }; @@ -224,35 +225,62 @@ void LLFloaterAbout::startFetchServerReleaseNotes() // an URL suitable for external browsers in the "Location:" HTTP header. std::string cap_url = region->getCapability("ServerReleaseNotes"); - LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(cap_url, - &LLFloaterAbout::handleServerReleaseNotes, &LLFloaterAbout::handleServerReleaseNotes); + LLCoros::instance().launch("fetchServerReleaseNotesCoro", boost::bind(&LLFloaterAbout::fetchServerReleaseNotesCoro, cap_url)); } /*static*/ +void LLFloaterAbout::fetchServerReleaseNotesCoro(const std::string& cap_url) +{ + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("fetchServerReleaseNotesCoro", LLCore::HttpRequest::DEFAULT_POLICY_ID)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + httpOpts->setFollowRedirects(false); + + LLSD result = httpAdapter->getAndSuspend(httpRequest, cap_url, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + handleServerReleaseNotes(httpResults); + } + else + { + handleServerReleaseNotes(result); + } +} + +/*static*/ void LLFloaterAbout::handleServerReleaseNotes(LLSD results) { -// LLFloaterAbout* floater_about = LLFloaterReg::getTypedInstance<LLFloaterAbout>("sl_about"); -// if (floater_about) -// { - LLSD http_headers; - if (results.has(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS)) - { - LLSD http_results = results[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; - http_headers = http_results[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; - } - else - { - http_headers = results[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; - } - - std::string location = http_headers[HTTP_IN_HEADER_LOCATION].asString(); - if (location.empty()) - { - location = LLTrans::getString("ErrorFetchingServerReleaseNotesURL"); - } - LLAppViewer::instance()->setServerReleaseNotesURL(location); -// } + LLSD http_headers; + if (results.has(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS)) + { + LLSD http_results = results[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + http_headers = http_results[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; + } + else + { + http_headers = results[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS]; + } + + std::string location = http_headers[HTTP_IN_HEADER_LOCATION].asString(); + if (location.empty()) + { + location = LLTrans::getString("ErrorFetchingServerReleaseNotesURL"); + } + LLAppViewer::instance()->setServerReleaseNotesURL(location); + + LLFloaterAbout* floater_about = LLFloaterReg::findTypedInstance<LLFloaterAbout>("sl_about"); + if (floater_about) + { + floater_about->setSupportText(location); + } } class LLFloaterAboutListener: public LLEventAPI diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp index 72892b47a4..aa7bfbfdb7 100644 --- a/indra/newview/llfloateravatarpicker.cpp +++ b/indra/newview/llfloateravatarpicker.cpp @@ -55,6 +55,8 @@ //#include "llsdserialize.h" +static const U32 AVATAR_PICKER_SEARCH_TIMEOUT = 180U; + //put it back as a member once the legacy path is out? static std::map<LLUUID, LLAvatarName> sAvatarNameMap; @@ -463,10 +465,13 @@ void LLFloaterAvatarPicker::findCoro(std::string url, LLUUID queryID, std::strin LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericPostCoro", httpPolicy)); LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); LL_INFOS("HttpCoroutineAdapter", "genericPostCoro") << "Generic POST for " << url << LL_ENDL; - LLSD result = httpAdapter->getAndSuspend(httpRequest, url); + httpOpts->setTimeout(AVATAR_PICKER_SEARCH_TIMEOUT); + + LLSD result = httpAdapter->getAndSuspend(httpRequest, url, httpOpts); LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); diff --git a/indra/newview/llfloaterbuycurrency.cpp b/indra/newview/llfloaterbuycurrency.cpp index e21a8594bc..91436e52fe 100644 --- a/indra/newview/llfloaterbuycurrency.cpp +++ b/indra/newview/llfloaterbuycurrency.cpp @@ -280,7 +280,7 @@ void LLFloaterBuyCurrencyUI::onClickCancel() void LLFloaterBuyCurrencyUI::onClickErrorWeb() { - LLWeb::loadURLExternal(mManager.errorURI()); + LLWeb::loadURL(mManager.errorURI()); closeFloater(); // Update L$ balance LLStatusBar::sendMoneyBalanceRequest(); diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp index d842106146..b840d37c4d 100644 --- a/indra/newview/llfloatergesture.cpp +++ b/indra/newview/llfloatergesture.cpp @@ -328,7 +328,7 @@ void LLFloaterGesture::addGesture(const LLUUID& item_id , LLMultiGesture* gestur element["columns"][0]["font"]["name"] = "SANSSERIF"; element["columns"][0]["font"]["style"] = font_style; - std::string key_string = LLKeyboard::stringFromKey(gesture->mKey); + std::string key_string; std::string buffer; if (gesture->mKey == KEY_NONE) @@ -338,6 +338,7 @@ void LLFloaterGesture::addGesture(const LLUUID& item_id , LLMultiGesture* gestur } else { + key_string = LLKeyboard::stringFromKey(gesture->mKey); buffer = LLKeyboard::stringFromAccelerator(gesture->mMask, gesture->mKey); } diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index 7a989806a1..257b39a7dd 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -798,7 +798,8 @@ LLWString LLFloaterIMNearbyChat::stripChannelNumber(const LLWString &mesg, S32* } else if (mesg[0] == '/' && mesg[1] - && LLStringOps::isDigit(mesg[1])) + && (LLStringOps::isDigit(mesg[1]) + || (mesg[1] == '-' && mesg[2] && LLStringOps::isDigit(mesg[2])))) { // This a special "/20" speak on a channel S32 pos = 0; @@ -812,7 +813,7 @@ LLWString LLFloaterIMNearbyChat::stripChannelNumber(const LLWString &mesg, S32* channel_string.push_back(c); pos++; } - while(c && pos < 64 && LLStringOps::isDigit(c)); + while(c && pos < 64 && (LLStringOps::isDigit(c) || (pos==1 && c =='-'))); // Move the pointer forward to the first non-whitespace char // Check isspace before looping, so we can handle "/33foo" @@ -837,19 +838,36 @@ LLWString LLFloaterIMNearbyChat::stripChannelNumber(const LLWString &mesg, S32* void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) { - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ChatFromViewer); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_ChatData); - msg->addStringFast(_PREHASH_Message, utf8_out_text); - msg->addU8Fast(_PREHASH_Type, type); - msg->addS32("Channel", channel); - - gAgent.sendReliableMessage(); - - add(LLStatViewer::CHAT_COUNT, 1); + LLMessageSystem* msg = gMessageSystem; + + if (channel >= 0) + { + msg->newMessageFast(_PREHASH_ChatFromViewer); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_ChatData); + msg->addStringFast(_PREHASH_Message, utf8_out_text); + msg->addU8Fast(_PREHASH_Type, type); + msg->addS32("Channel", channel); + + } + else + { + // Hack: ChatFromViewer doesn't allow negative channels + msg->newMessage("ScriptDialogReply"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgentID); + msg->addUUID("SessionID", gAgentSessionID); + msg->nextBlock("Data"); + msg->addUUID("ObjectID", gAgentID); + msg->addS32("ChatChannel", channel); + msg->addS32("ButtonIndex", 0); + msg->addString("ButtonLabel", utf8_out_text); + } + + gAgent.sendReliableMessage(); + add(LLStatViewer::CHAT_COUNT, 1); } class LLChatCommandHandler : public LLCommandHandler diff --git a/indra/newview/llfloaterpay.cpp b/indra/newview/llfloaterpay.cpp index 31245db344..dfe462c8d1 100644 --- a/indra/newview/llfloaterpay.cpp +++ b/indra/newview/llfloaterpay.cpp @@ -72,6 +72,8 @@ struct LLGiveMoneyInfo mFloater(floater), mAmount(amount){} }; +typedef boost::shared_ptr<LLGiveMoneyInfo> give_money_ptr; + ///---------------------------------------------------------------------------- /// Class LLFloaterPay ///---------------------------------------------------------------------------- @@ -94,18 +96,18 @@ public: bool is_group); static bool payConfirmationCallback(const LLSD& notification, const LLSD& response, - LLGiveMoneyInfo* info); + give_money_ptr info); private: static void onCancel(void* data); static void onKeystroke(LLLineEditor* editor, void* data); - static void onGive(void* data); + static void onGive(give_money_ptr info); void give(S32 amount); static void processPayPriceReply(LLMessageSystem* msg, void **userdata); void finishPayUI(const LLUUID& target_id, BOOL is_group); protected: - std::vector<LLGiveMoneyInfo*> mCallbackData; + std::vector<give_money_ptr> mCallbackData; money_callback mCallback; LLTextBox* mObjectNameText; LLUUID mTargetUUID; @@ -113,7 +115,7 @@ protected: BOOL mHaveName; LLButton* mQuickPayButton[MAX_PAY_BUTTONS]; - LLGiveMoneyInfo* mQuickPayInfo[MAX_PAY_BUTTONS]; + give_money_ptr mQuickPayInfo[MAX_PAY_BUTTONS]; LLSafeHandle<LLObjectSelection> mObjectSelection; }; @@ -136,7 +138,11 @@ LLFloaterPay::LLFloaterPay(const LLSD& key) // Destroys the object LLFloaterPay::~LLFloaterPay() { - std::for_each(mCallbackData.begin(), mCallbackData.end(), DeletePointer()); + std::vector<give_money_ptr>::iterator iter; + for (iter = mCallbackData.begin(); iter != mCallbackData.end(); ++iter) + { + (*iter)->mFloater = NULL; + } mCallbackData.clear(); // Name callbacks will be automatically disconnected since LLFloater is trackable @@ -148,40 +154,40 @@ BOOL LLFloaterPay::postBuild() { S32 i = 0; - LLGiveMoneyInfo* info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_0); + give_money_ptr info = give_money_ptr(new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_0)); mCallbackData.push_back(info); - childSetAction("fastpay 1",&LLFloaterPay::onGive,info); + childSetAction("fastpay 1", boost::bind(LLFloaterPay::onGive, info)); getChildView("fastpay 1")->setVisible(FALSE); mQuickPayButton[i] = getChild<LLButton>("fastpay 1"); mQuickPayInfo[i] = info; ++i; - info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_1); + info = give_money_ptr(new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_1)); mCallbackData.push_back(info); - childSetAction("fastpay 5",&LLFloaterPay::onGive,info); + childSetAction("fastpay 5", boost::bind(LLFloaterPay::onGive, info)); getChildView("fastpay 5")->setVisible(FALSE); mQuickPayButton[i] = getChild<LLButton>("fastpay 5"); mQuickPayInfo[i] = info; ++i; - info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_2); + info = give_money_ptr(new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_2)); mCallbackData.push_back(info); - childSetAction("fastpay 10",&LLFloaterPay::onGive,info); + childSetAction("fastpay 10", boost::bind(LLFloaterPay::onGive, info)); getChildView("fastpay 10")->setVisible(FALSE); mQuickPayButton[i] = getChild<LLButton>("fastpay 10"); mQuickPayInfo[i] = info; ++i; - info = new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_3); + info = give_money_ptr(new LLGiveMoneyInfo(this, PAY_BUTTON_DEFAULT_3)); mCallbackData.push_back(info); - childSetAction("fastpay 20",&LLFloaterPay::onGive,info); + childSetAction("fastpay 20", boost::bind(LLFloaterPay::onGive, info)); getChildView("fastpay 20")->setVisible(FALSE); mQuickPayButton[i] = getChild<LLButton>("fastpay 20"); @@ -195,10 +201,10 @@ BOOL LLFloaterPay::postBuild() getChild<LLLineEditor>("amount")->setKeystrokeCallback(&LLFloaterPay::onKeystroke, this); getChild<LLLineEditor>("amount")->setPrevalidate(LLTextValidate::validateNonNegativeS32); - info = new LLGiveMoneyInfo(this, 0); + info = give_money_ptr(new LLGiveMoneyInfo(this, 0)); mCallbackData.push_back(info); - childSetAction("pay btn",&LLFloaterPay::onGive,info); + childSetAction("pay btn", boost::bind(LLFloaterPay::onGive, info)); setDefaultBtn("pay btn"); getChildView("pay btn")->setVisible(FALSE); getChildView("pay btn")->setEnabled(FALSE); @@ -415,9 +421,9 @@ void LLFloaterPay::payDirectly(money_callback callback, floater->finishPayUI(target_id, is_group); } -bool LLFloaterPay::payConfirmationCallback(const LLSD& notification, const LLSD& response, LLGiveMoneyInfo* info) +bool LLFloaterPay::payConfirmationCallback(const LLSD& notification, const LLSD& response, give_money_ptr info) { - if (!info || !info->mFloater) + if (!info.get() || !info->mFloater) { return false; } @@ -479,54 +485,61 @@ void LLFloaterPay::onKeystroke(LLLineEditor*, void* data) } // static -void LLFloaterPay::onGive(void* data) +void LLFloaterPay::onGive(give_money_ptr info) { - LLGiveMoneyInfo* info = reinterpret_cast<LLGiveMoneyInfo*>(data); - LLFloaterPay* floater = info->mFloater; - if(info && floater) - { - S32 amount = info->mAmount; - if(amount == 0) - { - amount = atoi(floater->getChild<LLUICtrl>("amount")->getValue().asString().c_str()); - } - if (amount > PAY_AMOUNT_NOTIFICATION && gStatusBar && gStatusBar->getBalance() > amount) - { - LLUUID payee_id = LLUUID::null; - BOOL is_group = false; - if (floater->mObjectSelection.notNull()) - { - LLSelectNode* node = floater->mObjectSelection->getFirstRootNode(); - if (node) - { - node->mPermissions->getOwnership(payee_id, is_group); - } - else - { - // object no longer exists - LLNotificationsUtil::add("PayObjectFailed"); - floater->closeFloater(); - return; - } - } - else - { - is_group = floater->mTargetIsGroup; - payee_id = floater->mTargetUUID; - } - - LLSD args; - args["TARGET"] = LLSLURL( is_group ? "group" : "agent", payee_id, "completename").getSLURLString(); - args["AMOUNT"] = amount; - - LLNotificationsUtil::add("PayConfirmation", args, LLSD(), boost::bind(&LLFloaterPay::payConfirmationCallback, _1, _2, info)); - } - else - { - floater->give(amount); - floater->closeFloater(); - } - } + if (!info.get() || !info->mFloater) + { + return; + } + + LLFloaterPay* floater = info->mFloater; + S32 amount = info->mAmount; + if (amount == 0) + { + LLUICtrl* text_field = floater->getChild<LLUICtrl>("amount"); + if (!text_field) + { + return; + } + amount = atoi(text_field->getValue().asString().c_str()); + } + + if (amount > PAY_AMOUNT_NOTIFICATION && gStatusBar && gStatusBar->getBalance() > amount) + { + LLUUID payee_id = LLUUID::null; + BOOL is_group = false; + if (floater->mObjectSelection.notNull()) + { + LLSelectNode* node = floater->mObjectSelection->getFirstRootNode(); + if (node) + { + node->mPermissions->getOwnership(payee_id, is_group); + } + else + { + // object no longer exists + LLNotificationsUtil::add("PayObjectFailed"); + floater->closeFloater(); + return; + } + } + else + { + is_group = floater->mTargetIsGroup; + payee_id = floater->mTargetUUID; + } + + LLSD args; + args["TARGET"] = LLSLURL(is_group ? "group" : "agent", payee_id, "completename").getSLURLString(); + args["AMOUNT"] = amount; + + LLNotificationsUtil::add("PayConfirmation", args, LLSD(), boost::bind(&LLFloaterPay::payConfirmationCallback, _1, _2, info)); + } + else + { + floater->give(amount); + floater->closeFloater(); + } } void LLFloaterPay::give(S32 amount) diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 36bdcf4d89..75f5e87a2b 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -1232,6 +1232,9 @@ void LLFloaterPreference::refreshEnabledState() (ctrl_wind_light->get()) ? TRUE : FALSE; ctrl_deferred->setEnabled(enabled); + + // Cannot have floater active until caps have been received + getChild<LLButton>("default_creation_permissions")->setEnabled(LLStartUp::getStartupState() < STATE_STARTED ? false : true); } void LLFloaterPreferenceGraphicsAdvanced::refreshEnabledState() @@ -1369,9 +1372,6 @@ void LLFloaterPreferenceGraphicsAdvanced::refreshEnabledState() disableUnavailableSettings(); getChildView("block_list")->setEnabled(LLLoginInstance::getInstance()->authSuccess()); - - // Cannot have floater active until caps have been received - getChild<LLButton>("default_creation_permissions")->setEnabled(LLStartUp::getStartupState() < STATE_STARTED ? false : true); } // static @@ -1411,7 +1411,7 @@ void LLAvatarComplexityControls::setIndirectMaxArc() else { // This is the inverse of the calculation in updateMaxComplexity - indirect_max_arc = (U32)((log(max_arc) - MIN_ARC_LOG) / ARC_LIMIT_MAP_SCALE) + MIN_INDIRECT_ARC_LIMIT; + indirect_max_arc = (U32)ll_round(((log(F32(max_arc)) - MIN_ARC_LOG) / ARC_LIMIT_MAP_SCALE)) + MIN_INDIRECT_ARC_LIMIT; } gSavedSettings.setU32("IndirectMaxComplexity", indirect_max_arc); } @@ -1930,7 +1930,7 @@ void LLAvatarComplexityControls::updateMax(LLSliderCtrl* slider, LLTextBox* valu { // if this is changed, the inverse calculation in setIndirectMaxArc // must be changed to match - max_arc = (U32)exp(MIN_ARC_LOG + (ARC_LIMIT_MAP_SCALE * (indirect_value - MIN_INDIRECT_ARC_LIMIT))); + max_arc = (U32)ll_round(exp(MIN_ARC_LOG + (ARC_LIMIT_MAP_SCALE * (indirect_value - MIN_INDIRECT_ARC_LIMIT)))); } gSavedSettings.setU32("RenderAvatarMaxComplexity", (U32)max_arc); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index ed6f4ede9f..a6ce0ba678 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -192,7 +192,6 @@ BOOL LLFloaterReporter::postBuild() mOwnerName = LLStringUtil::null; getChild<LLUICtrl>("summary_edit")->setFocus(TRUE); - getChild<LLCheckBoxCtrl>("screen_check")->set(TRUE); mDefaultSummary = getChild<LLUICtrl>("details_edit")->getValue().asString(); @@ -246,8 +245,6 @@ LLFloaterReporter::~LLFloaterReporter() // virtual void LLFloaterReporter::draw() { - getChildView("screen_check")->setEnabled(TRUE ); - LLFloater::draw(); } @@ -255,7 +252,6 @@ void LLFloaterReporter::enableControls(BOOL enable) { getChildView("category_combo")->setEnabled(enable); getChildView("chat_check")->setEnabled(enable); - getChildView("screen_check")->setEnabled(enable); getChildView("screenshot")->setEnabled(FALSE); getChildView("pick_btn")->setEnabled(enable); getChildView("summary_edit")->setEnabled(enable); @@ -448,23 +444,15 @@ void LLFloaterReporter::onClickSend(void *userdata) if(!url.empty() || !sshot_url.empty()) { self->sendReportViaCaps(url, sshot_url, self->gatherReport()); + LLNotificationsUtil::add("HelpReportAbuseConfirm"); self->closeFloater(); } else { - if(self->getChild<LLUICtrl>("screen_check")->getValue()) - { - self->getChildView("send_btn")->setEnabled(FALSE); - self->getChildView("cancel_btn")->setEnabled(FALSE); - // the callback from uploading the image calls sendReportViaLegacy() - self->uploadImage(); - } - else - { - self->sendReportViaLegacy(self->gatherReport()); - LLUploadDialog::modalUploadFinished(); - self->closeFloater(); - } + self->getChildView("send_btn")->setEnabled(FALSE); + self->getChildView("cancel_btn")->setEnabled(FALSE); + // the callback from uploading the image calls sendReportViaLegacy() + self->uploadImage(); } } } @@ -713,10 +701,7 @@ LLSD LLFloaterReporter::gatherReport() // only send a screenshot ID if we're asked to and the email is // going to LL - Estate Owners cannot see the screenshot asset LLUUID screenshot_id = LLUUID::null; - if (getChild<LLUICtrl>("screen_check")->getValue()) - { - screenshot_id = getChild<LLUICtrl>("screenshot")->getValue(); - }; + screenshot_id = getChild<LLUICtrl>("screenshot")->getValue(); LLSD report = LLSD::emptyMap(); report["report-type"] = (U8) mReportType; @@ -770,7 +755,7 @@ void LLFloaterReporter::finishedARPost(const LLSD &) void LLFloaterReporter::sendReportViaCaps(std::string url, std::string sshot_url, const LLSD& report) { - if(getChild<LLUICtrl>("screen_check")->getValue().asBoolean() && !sshot_url.empty()) + if(!sshot_url.empty()) { // try to upload screenshot LLResourceUploadInfo::ptr_t uploadInfo(new LLARScreenShotUploader(report, mResourceDatap->mAssetInfo.mUuid, mResourceDatap->mAssetInfo.mType)); @@ -885,6 +870,7 @@ void LLFloaterReporter::uploadDoneCallback(const LLUUID &uuid, void *user_data, self->mScreenID = uuid; LL_INFOS() << "Got screen shot " << uuid << LL_ENDL; self->sendReportViaLegacy(self->gatherReport()); + LLNotificationsUtil::add("HelpReportAbuseConfirm"); self->closeFloater(); } } diff --git a/indra/newview/llfloaterscriptlimits.cpp b/indra/newview/llfloaterscriptlimits.cpp index 7b8fc5b35b..eae16b9f03 100644 --- a/indra/newview/llfloaterscriptlimits.cpp +++ b/indra/newview/llfloaterscriptlimits.cpp @@ -660,6 +660,8 @@ BOOL LLPanelScriptLimitsRegionMemory::postBuild() { return FALSE; } + list->setCommitCallback(boost::bind(&LLPanelScriptLimitsRegionMemory::checkButtonsEnabled, this)); + checkButtonsEnabled(); //set all columns to resizable mode even if some columns will be empty for(S32 column = 0; column < list->getNumColumns(); column++) @@ -750,6 +752,14 @@ void LLPanelScriptLimitsRegionMemory::clearList() getChild<LLUICtrl>("parcels_listed")->setValue(LLSD(msg_empty_string)); mObjectListItems.clear(); + checkButtonsEnabled(); +} + +void LLPanelScriptLimitsRegionMemory::checkButtonsEnabled() +{ + LLScrollListCtrl* list = getChild<LLScrollListCtrl>("scripts_list"); + getChild<LLButton>("highlight_btn")->setEnabled(list->getNumSelected() > 0); + getChild<LLButton>("return_btn")->setEnabled(list->getNumSelected() > 0); } // static diff --git a/indra/newview/llfloaterscriptlimits.h b/indra/newview/llfloaterscriptlimits.h index e3cbbd185f..2ac3862b4f 100644 --- a/indra/newview/llfloaterscriptlimits.h +++ b/indra/newview/llfloaterscriptlimits.h @@ -113,6 +113,7 @@ public: void showBeacon(); void returnObjectsFromParcel(S32 local_id); void returnObjects(); + void checkButtonsEnabled(); private: void onNameCache(const LLUUID& id, diff --git a/indra/newview/llfloatersellland.cpp b/indra/newview/llfloatersellland.cpp index 0cb37dabe7..b139e5daf5 100644 --- a/indra/newview/llfloatersellland.cpp +++ b/indra/newview/llfloatersellland.cpp @@ -257,7 +257,6 @@ void LLFloaterSellLandUI::setBadge(const char* id, Badge badge) static std::string badgeOK("badge_ok.j2c"); static std::string badgeNote("badge_note.j2c"); static std::string badgeWarn("badge_warn.j2c"); - static std::string badgeError("badge_error.j2c"); std::string badgeName; switch (badge) @@ -266,7 +265,7 @@ void LLFloaterSellLandUI::setBadge(const char* id, Badge badge) case BADGE_OK: badgeName = badgeOK; break; case BADGE_NOTE: badgeName = badgeNote; break; case BADGE_WARN: badgeName = badgeWarn; break; - case BADGE_ERROR: badgeName = badgeError; break; + case BADGE_ERROR: badgeName = badgeWarn; break; } getChild<LLUICtrl>(id)->setValue(badgeName); diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index ff7594a531..b5ba64716d 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -1375,7 +1375,7 @@ const LLVector3d& LLFloaterSnapshotBase::getPosTakenGlobal() // static void LLFloaterSnapshot::setAgentEmail(const std::string& email) { - LLFloaterSnapshot* instance = getInstance(); + LLFloaterSnapshot* instance = findInstance(); if (instance) { LLSideTrayPanelContainer* panel_container = instance->getChild<LLSideTrayPanelContainer>("panel_container"); diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp index 024e315632..dece3fc1ea 100644 --- a/indra/newview/llfloaterwebcontent.cpp +++ b/indra/newview/llfloaterwebcontent.cpp @@ -55,7 +55,8 @@ LLFloaterWebContent::_Params::_Params() preferred_media_size("preferred_media_size"), trusted_content("trusted_content", false), show_page_title("show_page_title", true), - clean_browser("clean_browser", false) + clean_browser("clean_browser", false), + dev_mode("dev_mode", false) {} LLFloaterWebContent::LLFloaterWebContent( const Params& params ) @@ -74,14 +75,16 @@ LLFloaterWebContent::LLFloaterWebContent( const Params& params ) mShowPageTitle(params.show_page_title), mAllowNavigation(true), mCurrentURL(""), - mDisplayURL("") + mDisplayURL(""), + mDevelopMode(params.dev_mode) // if called from "Develop" Menu, set a flag and change things to be more useful for devs { mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this )); mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this )); mCommitCallbackRegistrar.add( "WebContent.Reload", boost::bind( &LLFloaterWebContent::onClickReload, this )); mCommitCallbackRegistrar.add( "WebContent.Stop", boost::bind( &LLFloaterWebContent::onClickStop, this )); mCommitCallbackRegistrar.add( "WebContent.EnterAddress", boost::bind( &LLFloaterWebContent::onEnterAddress, this )); - mCommitCallbackRegistrar.add( "WebContent.PopExternal", boost::bind( &LLFloaterWebContent::onPopExternal, this )); + mCommitCallbackRegistrar.add( "WebContent.PopExternal", boost::bind(&LLFloaterWebContent::onPopExternal, this)); + mCommitCallbackRegistrar.add( "WebContent.TestURL", boost::bind(&LLFloaterWebContent::onTestURL, this, _2)); } BOOL LLFloaterWebContent::postBuild() @@ -195,8 +198,6 @@ void LLFloaterWebContent::geometryChanged(S32 x, S32 y, S32 width, S32 height) width + getRect().getWidth() - browser_rect.getWidth(), height + getRect().getHeight() - browser_rect.getHeight()); - LL_DEBUGS() << "geometry change: " << geom << LL_ENDL; - LLRect new_rect; getParent()->screenRectToLocal(geom, &new_rect); setShape(new_rect); @@ -205,8 +206,6 @@ void LLFloaterWebContent::geometryChanged(S32 x, S32 y, S32 width, S32 height) // static void LLFloaterWebContent::preCreate(LLFloaterWebContent::Params& p) { - LL_DEBUGS() << "url = " << p.url() << ", target = " << p.target() << ", uuid = " << p.id() << LL_ENDL; - if (!p.id.isProvided()) { p.id = LLUUID::generateNewID().asString(); @@ -224,12 +223,6 @@ void LLFloaterWebContent::preCreate(LLFloaterWebContent::Params& p) // and close the least recently opened one if this will put us over the limit. LLFloaterReg::const_instance_list_t &instances = LLFloaterReg::getFloaterList(p.window_class); - LL_DEBUGS() << "total instance count is " << instances.size() << LL_ENDL; - - for(LLFloaterReg::const_instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); iter++) - { - LL_DEBUGS() << " " << (*iter)->getKey()["target"] << LL_ENDL; - } if(instances.size() >= (size_t)browser_window_limit) { @@ -241,16 +234,19 @@ void LLFloaterWebContent::preCreate(LLFloaterWebContent::Params& p) void LLFloaterWebContent::open_media(const Params& p) { - // Specifying a mime type of text/html here causes the plugin system to skip the MIME type probe and just open a browser plugin. LLViewerMedia::proxyWindowOpened(p.target(), p.id()); - mWebBrowser->setHomePageUrl(p.url, HTTP_CONTENT_TEXT_HTML); + mWebBrowser->setHomePageUrl(p.url); mWebBrowser->setTarget(p.target); - mWebBrowser->navigateTo(p.url, HTTP_CONTENT_TEXT_HTML, p.clean_browser); + mWebBrowser->navigateTo(p.url); set_current_url(p.url); getChild<LLLayoutPanel>("status_bar")->setVisible(p.show_chrome); getChild<LLLayoutPanel>("nav_controls")->setVisible(p.show_chrome); + + // turn additional debug controls on but only for Develop mode (Develop menu open) + getChild<LLLayoutPanel>("debug_controls")->setVisible(mDevelopMode); + bool address_entry_enabled = p.allow_address_entry && !p.trusted_content; mAllowNavigation = p.allow_back_forward_navigation; getChildView("address")->setEnabled(address_entry_enabled); @@ -499,7 +495,7 @@ void LLFloaterWebContent::onEnterAddress() LLStringUtil::trim(url); if ( url.length() > 0 ) { - mWebBrowser->navigateTo(url, HTTP_CONTENT_TEXT_HTML); + mWebBrowser->navigateTo(url); }; } @@ -508,9 +504,18 @@ void LLFloaterWebContent::onPopExternal() // make sure there is at least something there. // (perhaps this test should be for minimum length of a URL) std::string url = mAddressCombo->getValue().asString(); - LLStringUtil::trim(url); - if ( url.length() > 0 ) + LLStringUtil::trim(url); + if (url.length() > 0) + { + LLWeb::loadURLExternal(url); + }; +} + +void LLFloaterWebContent::onTestURL(std::string url) +{ + LLStringUtil::trim(url); + if (url.length() > 0) { - LLWeb::loadURLExternal( url ); + mWebBrowser->navigateTo(url); }; } diff --git a/indra/newview/llfloaterwebcontent.h b/indra/newview/llfloaterwebcontent.h index 4291fd9f2c..0bf93504c2 100644 --- a/indra/newview/llfloaterwebcontent.h +++ b/indra/newview/llfloaterwebcontent.h @@ -58,7 +58,8 @@ public: allow_back_forward_navigation, trusted_content, show_page_title, - clean_browser; + clean_browser, + dev_mode; Optional<LLRect> preferred_media_size; _Params(); @@ -92,6 +93,7 @@ protected: void onClickStop(); void onEnterAddress(); void onPopExternal(); + void onTestURL(std::string url); static void preCreate(Params& p); void open_media(const Params& ); @@ -113,6 +115,7 @@ protected: std::string mUUID; bool mShowPageTitle; bool mAllowNavigation; + bool mDevelopMode; }; #endif // LL_LLFLOATERWEBCONTENT_H diff --git a/indra/newview/llgroupiconctrl.cpp b/indra/newview/llgroupiconctrl.cpp index 271dd44c1f..7c2a3cad43 100644 --- a/indra/newview/llgroupiconctrl.cpp +++ b/indra/newview/llgroupiconctrl.cpp @@ -74,9 +74,16 @@ LLGroupIconCtrl::~LLGroupIconCtrl() LLGroupMgr::getInstance()->removeObserver(this); } -void LLGroupIconCtrl::setIconId(const LLSD& value) +void LLGroupIconCtrl::setIconId(const LLUUID& icon_id) { - LLIconCtrl::setValue(value); + if (icon_id.notNull()) + { + LLIconCtrl::setValue(icon_id); + } + else + { + LLIconCtrl::setValue(mDefaultIconName, LLViewerFetchedTexture::BOOST_UI); + } } void LLGroupIconCtrl::setValue(const LLSD& value) @@ -122,14 +129,7 @@ bool LLGroupIconCtrl::updateFromCache() LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(mGroupId); if (!group_data) return false; - if (group_data->mInsigniaID.notNull()) - { - LLIconCtrl::setValue(group_data->mInsigniaID); - } - else - { - LLIconCtrl::setValue(mDefaultIconName, LLViewerFetchedTexture::BOOST_UI); - } + setIconId(group_data->mInsigniaID); if (mDrawTooltip && !group_data->mName.empty()) { diff --git a/indra/newview/llgroupiconctrl.h b/indra/newview/llgroupiconctrl.h index f8b22cf581..43e384d3e2 100644 --- a/indra/newview/llgroupiconctrl.h +++ b/indra/newview/llgroupiconctrl.h @@ -66,7 +66,13 @@ public: */ virtual void setValue(const LLSD& value); - void setIconId(const LLSD& value); + /** + * Sets icon_id as icon value directly. Avoids LLGroupMgr cache checks for group id + * Uses default icon in case id is null. + * + * @params icon_id - it is processed as icon id, default image will be used in case id is null. + */ + void setIconId(const LLUUID& icon_id); // LLGroupMgrObserver observer trigger virtual void changed(LLGroupChange gc); diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 6126db2988..62414d3bbb 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -379,10 +379,7 @@ void LLGroupListItem::setGroupID(const LLUUID& group_id) void LLGroupListItem::setGroupIconID(const LLUUID& group_icon_id) { - if (group_icon_id.notNull()) - { - mGroupIcon->setIconId(group_icon_id); - } + mGroupIcon->setIconId(group_icon_id); } void LLGroupListItem::setGroupIconVisible(bool visible) diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp index 52e83fe412..6b1e196182 100644 --- a/indra/newview/llhudtext.cpp +++ b/indra/newview/llhudtext.cpp @@ -53,7 +53,7 @@ const F32 VERTICAL_PADDING = 12.f; const F32 BUFFER_SIZE = 2.f; const F32 HUD_TEXT_MAX_WIDTH = 190.f; const F32 HUD_TEXT_MAX_WIDTH_NO_BUBBLE = 1000.f; -const F32 MAX_DRAW_DISTANCE = 64.f; +const F32 MAX_DRAW_DISTANCE = 300.f; std::set<LLPointer<LLHUDText> > LLHUDText::sTextObjects; std::vector<LLPointer<LLHUDText> > LLHUDText::sVisibleTextObjects; @@ -394,7 +394,20 @@ void LLHUDText::updateVisibility() LLVector3 pos_agent_center = gAgent.getPosAgentFromGlobal(mPositionGlobal) - dir_from_camera; F32 last_distance_center = (pos_agent_center - LLViewerCamera::getInstance()->getOrigin()).magVec(); - if(last_distance_center > MAX_DRAW_DISTANCE) + F32 max_draw_distance = gSavedSettings.getF32("PrimTextMaxDrawDistance"); + + if(max_draw_distance < 0) + { + max_draw_distance = 0; + gSavedSettings.setF32("PrimTextMaxDrawDistance", max_draw_distance); + } + else if(max_draw_distance > MAX_DRAW_DISTANCE) + { + max_draw_distance = MAX_DRAW_DISTANCE; + gSavedSettings.setF32("PrimTextMaxDrawDistance", MAX_DRAW_DISTANCE); + } + + if(last_distance_center > max_draw_distance) { mVisible = FALSE; return; diff --git a/indra/newview/llinspectgroup.cpp b/indra/newview/llinspectgroup.cpp index 8e91af321e..a4fce36783 100644 --- a/indra/newview/llinspectgroup.cpp +++ b/indra/newview/llinspectgroup.cpp @@ -41,6 +41,7 @@ #include "lltooltip.h" // positionViewNearMouse() #include "lltrans.h" #include "lluictrl.h" +#include "llgroupiconctrl.h" ////////////////////////////////////////////////////////////////////////////// // LLInspectGroup @@ -233,7 +234,7 @@ void LLInspectGroup::processGroupData() getChild<LLUICtrl>("group_details")->setValue( LLSD(data->mCharter) ); - getChild<LLUICtrl>("group_icon")->setValue( LLSD(data->mInsigniaID) ); + getChild<LLGroupIconCtrl>("group_icon")->setIconId(data->mInsigniaID); std::string cost; bool is_member = LLGroupActions::isInGroup(mGroupID); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 9f0b35fc8c..eebb6a0384 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -286,6 +286,16 @@ BOOL LLInvFVBridge::cutToClipboard() return FALSE; } +// virtual +bool LLInvFVBridge::isCutToClipboard() +{ + if (LLClipboard::instance().isCutMode()) + { + return LLClipboard::instance().isOnClipboard(mUUID); + } + return false; +} + // Callback for cutToClipboard if DAMA required... BOOL LLInvFVBridge::callback_cutToClipboard(const LLSD& notification, const LLSD& response) { @@ -307,9 +317,7 @@ BOOL LLInvFVBridge::perform_cutToClipboard() if (obj && isItemMovable() && isItemRemovable()) { LLClipboard::instance().setCutMode(true); - BOOL added_to_clipboard = LLClipboard::instance().addToClipboard(mUUID); - removeObject(&gInventory, mUUID); // Always perform the remove even if the object couldn't make it to the clipboard - return added_to_clipboard; + return LLClipboard::instance().addToClipboard(mUUID); } return FALSE; } @@ -1390,6 +1398,12 @@ bool LLInvFVBridge::canShare() const // Categories can be given. can_share = (model->getCategory(mUUID) != NULL); } + + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + if ((mUUID == trash_id) || gInventory.isObjectDescendentOf(mUUID, trash_id)) + { + can_share = false; + } } } @@ -1925,13 +1939,15 @@ BOOL LLItemBridge::removeItem() } // move it to the trash - LLPreview::hide(mUUID, TRUE); LLInventoryModel* model = getInventoryModel(); if(!model) return FALSE; const LLUUID& trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); LLViewerInventoryItem* item = getItem(); if (!item) return FALSE; - + if (item->getType() != LLAssetType::AT_LSL_TEXT) + { + LLPreview::hide(mUUID, TRUE); + } // Already in trash if (model->isObjectDescendentOf(mUUID, trash_id)) return FALSE; diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 9053c61171..df25e01688 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -116,6 +116,7 @@ public: virtual BOOL isItemCopyable() const { return FALSE; } virtual BOOL copyToClipboard() const; virtual BOOL cutToClipboard(); + virtual bool isCutToClipboard(); virtual BOOL isClipboardPasteable() const; virtual BOOL isClipboardPasteableAsLink() const; virtual void pasteFromClipboard() {} diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 003bbcafed..e995c138b4 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -84,21 +84,18 @@ LLInventoryFilter::LLInventoryFilter(const Params& p) bool LLInventoryFilter::check(const LLFolderViewModelItem* item) { const LLFolderViewModelItemInventory* listener = dynamic_cast<const LLFolderViewModelItemInventory*>(item); - // Clipboard cut items are *always* filtered so we need this value upfront - const BOOL passed_clipboard = (listener ? checkAgainstClipboard(listener->getUUID()) : TRUE); // If it's a folder and we're showing all folders, return automatically. const BOOL is_folder = listener->getInventoryType() == LLInventoryType::IT_CATEGORY; if (is_folder && (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS)) { - return passed_clipboard; + return true; } bool passed = (mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) != std::string::npos : true); passed = passed && checkAgainstFilterType(listener); passed = passed && checkAgainstPermissions(listener); passed = passed && checkAgainstFilterLinks(listener); - passed = passed && passed_clipboard; return passed; } @@ -108,9 +105,8 @@ bool LLInventoryFilter::check(const LLInventoryItem* item) const bool passed_string = (mFilterSubString.size() ? item->getName().find(mFilterSubString) != std::string::npos : true); const bool passed_filtertype = checkAgainstFilterType(item); const bool passed_permissions = checkAgainstPermissions(item); - const bool passed_clipboard = checkAgainstClipboard(item->getUUID()); - return passed_filtertype && passed_permissions && passed_clipboard && passed_string; + return passed_filtertype && passed_permissions && passed_string; } bool LLInventoryFilter::checkFolder(const LLFolderViewModelItem* item) const @@ -129,13 +125,10 @@ bool LLInventoryFilter::checkFolder(const LLFolderViewModelItem* item) const bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const { - // Always check against the clipboard - const BOOL passed_clipboard = checkAgainstClipboard(folder_id); - // we're showing all folders, overriding filter if (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS) { - return passed_clipboard; + return true; } // when applying a filter, matching folders get their contents downloaded first @@ -201,7 +194,7 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const LLViewerInventoryItem* item = gInventory.getItem(folder_id); if (item && item->getActualType() == LLAssetType::AT_LINK_FOLDER) { - return passed_clipboard; + return true; } if (mFilterOps.mFilterTypes & FILTERTYPE_CATEGORY) @@ -216,7 +209,7 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const return false; } - return passed_clipboard; + return true; } bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewModelItemInventory* listener) const diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 9a33e210ff..503fa28a33 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -582,7 +582,7 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, // Add the category to the internal representation LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID()); - cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL); + cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1); // accountForUpdate() will icrease version by 1 cat->setDescendentCount(0); LLCategoryUpdate update(cat->getParentUUID(), 1); accountForUpdate(update); @@ -640,7 +640,7 @@ void LLInventoryModel::createNewCategoryCoro(std::string url, LLSD postData, inv result["parent_id"].asUUID(), (LLFolderType::EType)result["type"].asInteger(), result["name"].asString(), gAgent.getID()); - cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL); + cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1); // accountForUpdate() will icrease version by 1 cat->setDescendentCount(0); LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); @@ -914,8 +914,11 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask) item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, category_id); if( item_array ) { + LLInventoryModel::LLCategoryUpdate update(category_id, 1); + gInventory.accountForUpdate(update); + // *FIX: bit of a hack to call update server from here... - new_item->updateServer(TRUE); + new_item->updateParentOnServer(FALSE); item_array->push_back(new_item); } else @@ -956,9 +959,11 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask) item_array = get_ptr_in_map(mParentChildItemTree, parent_id); if(item_array) { + LLInventoryModel::LLCategoryUpdate update(parent_id, 1); + gInventory.accountForUpdate(update); // *FIX: bit of a hack to call update server from // here... - new_item->updateServer(TRUE); + new_item->updateParentOnServer(FALSE); item_array->push_back(new_item); } else @@ -1045,7 +1050,6 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 if(old_cat) { // We already have an old category, modify its values - U32 mask = LLInventoryObserver::NONE; LLUUID old_parent_id = old_cat->getParentUUID(); LLUUID new_parent_id = cat->getParentUUID(); if(old_parent_id != new_parent_id) @@ -1100,7 +1104,8 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 item_array_t* itemsp = new item_array_t; mParentChildCategoryTree[new_cat->getUUID()] = catsp; mParentChildItemTree[new_cat->getUUID()] = itemsp; - addChangedMask(LLInventoryObserver::ADD, cat->getUUID()); + mask |= LLInventoryObserver::ADD; + addChangedMask(mask, cat->getUUID()); } } @@ -1390,7 +1395,11 @@ void LLInventoryModel::onObjectDeletedFromServer(const LLUUID& object_id, bool f } // From purgeObject() - LLPreview::hide(object_id); + LLViewerInventoryItem *item = getItem(object_id); + if (item && (item->getType() != LLAssetType::AT_LSL_TEXT)) + { + LLPreview::hide(object_id, TRUE); + } deleteObject(object_id, fix_broken_links, do_notify_observers); } } diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp index aa934f95a1..a55938f334 100644 --- a/indra/newview/lllocalbitmaps.cpp +++ b/indra/newview/lllocalbitmaps.cpp @@ -181,7 +181,12 @@ bool LLLocalBitmap::updateSelf(EUpdateType optional_firstupdate) if (gDirUtilp->fileExists(mFilename)) { // verifying that the file has indeed been modified + +#ifndef LL_WINDOWS const std::time_t temp_time = boost::filesystem::last_write_time(boost::filesystem::path(mFilename)); +#else + const std::time_t temp_time = boost::filesystem::last_write_time(boost::filesystem::path(utf8str_to_utf16str(mFilename))); +#endif LLSD new_last_modified = asctime(localtime(&temp_time)); if (mLastModified.asString() != new_last_modified.asString()) diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 53b2ca2b74..11bc1425f9 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -243,7 +243,7 @@ LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p) params.commit_on_focus_lost(false); params.follows.flags(FOLLOWS_ALL); mTextEntry = LLUICtrlFactory::create<LLURLLineEditor>(params); - mTextEntry->setContextMenu(NULL); + mTextEntry->resetContextMenu(); addChild(mTextEntry); // LLLineEditor is replaced with LLLocationLineEditor diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 639641d1c2..485d4677b1 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -244,7 +244,10 @@ std::string LLLogChat::makeLogFileName(std::string filename) filename = cleanFileName(filename); filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS, filename); - filename += '.' + LL_TRANSCRIPT_FILE_EXTENSION; + if (!filename.empty()) + { + filename += '.' + LL_TRANSCRIPT_FILE_EXTENSION; + } return filename; } diff --git a/indra/newview/llmanip.h b/indra/newview/llmanip.h index 1fb05e047a..69881e8589 100644 --- a/indra/newview/llmanip.h +++ b/indra/newview/llmanip.h @@ -1,4 +1,4 @@ -/** +/** * @file llmanip.h * @brief LLManip class definition * @@ -37,7 +37,7 @@ class LLToolComposite; class LLVector3; class LLObjectSelection; -const S32 MIN_DIVISION_PIXEL_WIDTH = 9; +const S32 MIN_DIVISION_PIXEL_WIDTH = 3; class LLManip : public LLTool { diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp index b4259a456c..3975d3980b 100644 --- a/indra/newview/llmaniptranslate.cpp +++ b/indra/newview/llmaniptranslate.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llmaniptranslate.cpp * @brief LLManipTranslate class implementation * @@ -548,12 +548,7 @@ BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask) if (off_axis_magnitude > mSnapOffsetMeters) { mInSnapRegime = TRUE; - LLVector3 mouse_down_offset(mDragCursorStartGlobal - mDragSelectionStartGlobal); LLVector3 cursor_snap_agent = gAgent.getPosAgentFromGlobal(cursor_point_snap_line); - if (!gSavedSettings.getBOOL("SnapToMouseCursor")) - { - cursor_snap_agent -= mouse_down_offset; - } F32 cursor_grid_dist = (cursor_snap_agent - mGridOrigin) * axis_f; diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index 6cc7a0fc99..54f95520db 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -773,7 +773,9 @@ void LLMarketplaceData::getMerchantStatusCoro() std::string url = getSLMConnectURL("/merchant"); if (url.empty()) { - LL_INFOS("Marketplace") << "No marketplace capability on Sim" << LL_ENDL; + LL_WARNS("Marketplace") << "No marketplace capability on Sim" << LL_ENDL; + setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE); + return; } LLSD result = httpAdapter->getAndSuspend(httpRequest, url, httpOpts); diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 9cf3249983..00043d1e72 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -1007,7 +1007,11 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) std::string uuid = self->getClickUUID(); LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << target << "\", uri is " << url << LL_ENDL; - LLWeb::loadURL(url, target, std::string()); + // try as slurl first + if (!LLURLDispatcher::dispatch(url, "clicked", NULL, mTrusted)) + { + LLWeb::loadURL(url, target, std::string()); + } // CP: removing this code because we no longer support popups so this breaks the flow. // replaced with a bare call to LLWeb::LoadURL(...) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 54f8fb93d0..e42647739f 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1753,6 +1753,11 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat bool LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size) { + if (data == NULL || data_size == 0) + { + return false; + } + LLPointer<LLVolume> volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod)); std::string mesh_string((char*) data, data_size); std::istringstream stream(mesh_string); @@ -3010,12 +3015,23 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b } else if (data && data_size > 0) { - // header was successfully retrieved from sim, cache in vfs - LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id]; + // header was successfully retrieved from sim and parsed, cache in vfs + S32 header_bytes = 0; + LLSD header; - S32 version = header["version"].asInteger(); + gMeshRepo.mThread->mHeaderMutex->lock(); + LLMeshRepoThread::mesh_header_map::iterator iter = gMeshRepo.mThread->mMeshHeader.find(mesh_id); + if (iter != gMeshRepo.mThread->mMeshHeader.end()) + { + header_bytes = (S32)gMeshRepo.mThread->mMeshHeaderSize[mesh_id]; + header = iter->second; + } + gMeshRepo.mThread->mHeaderMutex->unlock(); - if (version <= MAX_MESH_VERSION) + if (header_bytes > 0 + && !header.has("404") + && header.has("version") + && header["version"].asInteger() <= MAX_MESH_VERSION) { std::stringstream str; @@ -3064,6 +3080,17 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b } } } + else + { + LL_WARNS(LOG_MESH) << "Trying to cache nonexistent mesh, mesh id: " << mesh_id << LL_ENDL; + + // headerReceived() parsed header, but header's data is invalid so none of the LODs will be available + LLMutexLock lock(gMeshRepo.mThread->mMutex); + for (int i(0); i < 4; ++i) + { + gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i)); + } + } } } @@ -4115,7 +4142,7 @@ F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32 } } - F32 max_area = 102932.f; //area of circle that encompasses region + F32 max_area = 102944.f; //area of circle that encompasses region (see MAINT-6559) F32 min_area = 1.f; F32 high_area = llmin(F_PI*dmid*dmid, max_area); diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp index 70035bcc74..c3dd08c327 100644 --- a/indra/newview/llmoveview.cpp +++ b/indra/newview/llmoveview.cpp @@ -252,7 +252,7 @@ void LLFloaterMove::setSittingMode(BOOL bSitting) LLPanelStandStopFlying::setStandStopFlyingMode(LLPanelStandStopFlying::SSFM_STOP_FLYING); } } - enableInstance(!bSitting); + enableInstance(); } // protected @@ -459,7 +459,7 @@ void LLFloaterMove::showModeButtons(BOOL bShow) } //static -void LLFloaterMove::enableInstance(BOOL bEnable) +void LLFloaterMove::enableInstance() { LLFloaterMove* instance = LLFloaterReg::findTypedInstance<LLFloaterMove>("moveview"); if (instance) @@ -470,7 +470,7 @@ void LLFloaterMove::enableInstance(BOOL bEnable) } else { - instance->showModeButtons(bEnable); + instance->showModeButtons(isAgentAvatarValid() && !gAgentAvatarp->isSitting()); } } } @@ -566,7 +566,7 @@ BOOL LLPanelStandStopFlying::postBuild() { mStandButton = getChild<LLButton>("stand_btn"); mStandButton->setCommitCallback(boost::bind(&LLPanelStandStopFlying::onStandButtonClick, this)); - mStandButton->setCommitCallback(boost::bind(&LLFloaterMove::enableInstance, TRUE)); + mStandButton->setCommitCallback(boost::bind(&LLFloaterMove::enableInstance)); mStandButton->setVisible(FALSE); LLHints::registerHintTarget("stand_btn", mStandButton->getHandle()); @@ -685,8 +685,7 @@ void LLPanelStandStopFlying::onStandButtonClick() LLSelectMgr::getInstance()->deselectAllForStandingUp(); gAgent.setControlFlags(AGENT_CONTROL_STAND_UP); - setFocus(FALSE); // EXT-482 - mStandButton->setVisible(FALSE); // force visibility changing to avoid seeing Stand & Move buttons at once. + setFocus(FALSE); } void LLPanelStandStopFlying::onStopFlyingButtonClick() @@ -694,7 +693,6 @@ void LLPanelStandStopFlying::onStopFlyingButtonClick() gAgent.setFlying(FALSE); setFocus(FALSE); // EXT-482 - mStopFlyingButton->setVisible(FALSE); } /** diff --git a/indra/newview/llmoveview.h b/indra/newview/llmoveview.h index c525d9dfdb..4a31f2a814 100644 --- a/indra/newview/llmoveview.h +++ b/indra/newview/llmoveview.h @@ -56,7 +56,7 @@ public: static void setAlwaysRunMode(bool run); void setAlwaysRunModeImpl(bool run); static void setSittingMode(BOOL bSitting); - static void enableInstance(BOOL bEnable); + static void enableInstance(); /*virtual*/ void onOpen(const LLSD& key); static void sUpdateFlyingStatus(); diff --git a/indra/newview/llnotificationofferhandler.cpp b/indra/newview/llnotificationofferhandler.cpp index 2657b84ef3..63ab88da42 100644 --- a/indra/newview/llnotificationofferhandler.cpp +++ b/indra/newview/llnotificationofferhandler.cpp @@ -96,17 +96,21 @@ bool LLOfferHandler::processNotification(const LLNotificationPtr& notification) LLUUID from_id = notification->getPayload()["from_id"]; - //Will not play a notification sound for inventory and teleport offer based upon chat preference - bool playSound = (!notification->isDND() - && ((notification->getName() == "UserGiveItem" - && gSavedSettings.getBOOL("PlaySoundInventoryOffer")) - || (notification->getName() == "TeleportOffered" - && gSavedSettings.getBOOL("PlaySoundTeleportOffer")))); - - if(playSound) - { - notification->playSound(); - } + if (!notification->isDND()) + { + //Will not play a notification sound for inventory and teleport offer based upon chat preference + bool playSound = (notification->getName() == "UserGiveItem" + && gSavedSettings.getBOOL("PlaySoundInventoryOffer")) + || ((notification->getName() == "TeleportOffered" + || notification->getName() == "TeleportOffered_MaturityExceeded" + || notification->getName() == "TeleportOffered_MaturityBlocked") + && gSavedSettings.getBOOL("PlaySoundTeleportOffer")); + + if (playSound) + { + notification->playSound(); + } + } LLHandlerUtil::spawnIMSession(name, from_id); LLHandlerUtil::addNotifPanelToIM(notification); diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index 65fd3f95ab..de6a36ce2f 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -999,7 +999,7 @@ void LLOutfitGallery::refreshOutfit(const LLUUID& category_id) } } - if (mGalleryCreated) + if (mGalleryCreated && !LLApp::isQuitting()) { reArrangeRows(); } diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index b2164c1f21..d17f5494a0 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -199,7 +199,7 @@ void LLPanelGroupGeneral::setupCtrls(LLPanel* panel_group) mGroupNameEditor = panel_group->getChild<LLLineEditor>("group_name_editor"); - mGroupNameEditor->setPrevalidate( LLTextValidate::validateASCII ); + mGroupNameEditor->setPrevalidate( LLTextValidate::validateASCIINoLeadingSpace ); } diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 8331c152e2..8b9941c0ca 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -1059,9 +1059,6 @@ void LLPanelOutfitEdit::filterWearablesBySelectedItem(void) case LLAssetType::AT_BODYPART: applyListViewFilter(LVIT_BODYPART); break; - case LLAssetType::AT_GESTURE: - applyListViewFilter(LVIT_GESTURES); - break; case LLAssetType::AT_CLOTHING: default: applyListViewFilter(LVIT_CLOTHING); diff --git a/indra/newview/llpaneloutfitedit.h b/indra/newview/llpaneloutfitedit.h index 841bb4337a..30870daf40 100644 --- a/indra/newview/llpaneloutfitedit.h +++ b/indra/newview/llpaneloutfitedit.h @@ -80,7 +80,6 @@ public: { LVIT_ALL = 0, LVIT_CLOTHING, - LVIT_GESTURES, LVIT_BODYPART, LVIT_ATTACHMENT, LVIT_SHAPE, diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 763657ebad..0bcd8a9e63 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -95,7 +95,8 @@ LLPanelPrimMediaControls::LLPanelPrimMediaControls() : mVolumeSliderVisible(0), mWindowShade(NULL), mHideImmediately(false), - mSecureURL(false) + mSecureURL(false), + mMediaPlaySliderCtrlMouseDownValue(0.0) { mCommitCallbackRegistrar.add("MediaCtrl.Close", boost::bind(&LLPanelPrimMediaControls::onClickClose, this)); mCommitCallbackRegistrar.add("MediaCtrl.Back", boost::bind(&LLPanelPrimMediaControls::onClickBack, this)); @@ -109,7 +110,8 @@ LLPanelPrimMediaControls::LLPanelPrimMediaControls() : mCommitCallbackRegistrar.add("MediaCtrl.Open", boost::bind(&LLPanelPrimMediaControls::onClickOpen, this)); mCommitCallbackRegistrar.add("MediaCtrl.Zoom", boost::bind(&LLPanelPrimMediaControls::onClickZoom, this)); mCommitCallbackRegistrar.add("MediaCtrl.CommitURL", boost::bind(&LLPanelPrimMediaControls::onCommitURL, this)); - mCommitCallbackRegistrar.add("MediaCtrl.JumpProgress", boost::bind(&LLPanelPrimMediaControls::onCommitSlider, this)); + mCommitCallbackRegistrar.add("MediaCtrl.MouseDown", boost::bind(&LLPanelPrimMediaControls::onMediaPlaySliderCtrlMouseDown, this)); + mCommitCallbackRegistrar.add("MediaCtrl.MouseUp", boost::bind(&LLPanelPrimMediaControls::onMediaPlaySliderCtrlMouseUp, this)); mCommitCallbackRegistrar.add("MediaCtrl.CommitVolumeUp", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeUp, this)); mCommitCallbackRegistrar.add("MediaCtrl.CommitVolumeDown", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeDown, this)); mCommitCallbackRegistrar.add("MediaCtrl.Volume", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeSlider, this)); @@ -1246,26 +1248,38 @@ void LLPanelPrimMediaControls::setCurrentURL() #endif // USE_COMBO_BOX_FOR_MEDIA_URL } -void LLPanelPrimMediaControls::onCommitSlider() + +void LLPanelPrimMediaControls::onMediaPlaySliderCtrlMouseDown() { - focusOnTarget(); + mMediaPlaySliderCtrlMouseDownValue = mMediaPlaySliderCtrl->getValue().asReal(); - LLViewerMediaImpl* media_impl = getTargetMediaImpl(); - if (media_impl) + mUpdateSlider = false; +} + +void LLPanelPrimMediaControls::onMediaPlaySliderCtrlMouseUp() +{ + F64 cur_value = mMediaPlaySliderCtrl->getValue().asReal(); + + if (mMediaPlaySliderCtrlMouseDownValue != cur_value) { - // get slider value - F64 slider_value = mMediaPlaySliderCtrl->getValue().asReal(); - if(slider_value <= 0.0) - { - media_impl->stop(); - } - else + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) { - media_impl->seek(slider_value*mMovieDuration); - //mUpdateSlider= false; + if (cur_value <= 0.0) + { + media_impl->stop(); + } + else + { + media_impl->seek(cur_value * mMovieDuration); + } } + + mUpdateSlider = true; } -} +} void LLPanelPrimMediaControls::onCommitVolumeUp() { diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h index 6d2eb3430e..21d5236074 100644 --- a/indra/newview/llpanelprimmediacontrols.h +++ b/indra/newview/llpanelprimmediacontrols.h @@ -107,8 +107,10 @@ private: void updateZoom(); void setCurrentURL(); - void onCommitSlider(); - + + void onMediaPlaySliderCtrlMouseDown(); + void onMediaPlaySliderCtrlMouseUp(); + void onCommitVolumeUp(); void onCommitVolumeDown(); void onCommitVolumeSlider(); @@ -219,6 +221,8 @@ private: S32 mVolumeSliderVisible; LLNotificationPtr mActiveNotification; + + F64 mMediaPlaySliderCtrlMouseDownValue; }; #endif // LL_PANELPRIMMEDIACONTROLS_H diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 184238c40c..8afa35efa0 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -44,19 +44,11 @@ static const std::string PANEL_PICKS = "panel_picks"; std::string getProfileURL(const std::string& agent_name) { - std::string url; - - if (LLGridManager::getInstance()->isInProductionGrid()) - { - url = gSavedSettings.getString("WebProfileURL"); - } - else - { - url = gSavedSettings.getString("WebProfileNonProductionURL"); - } + std::string url = "[WEB_PROFILE_URL][AGENT_NAME]"; LLSD subs; + subs["WEB_PROFILE_URL"] = LLGridManager::getInstance()->getWebProfileURL(); subs["AGENT_NAME"] = agent_name; - url = LLWeb::expandURLSubstitutions(url,subs); + url = LLWeb::expandURLSubstitutions(url, subs); LLStringUtil::toLower(url); return url; } diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp index d0353259a5..796372ba04 100644 --- a/indra/newview/llpanelwearing.cpp +++ b/indra/newview/llpanelwearing.cpp @@ -30,13 +30,19 @@ #include "lltoggleablemenu.h" +#include "llagent.h" +#include "llaccordionctrl.h" +#include "llaccordionctrltab.h" #include "llappearancemgr.h" #include "llfloatersidepanelcontainer.h" #include "llinventoryfunctions.h" +#include "llinventoryicon.h" #include "llinventorymodel.h" #include "llinventoryobserver.h" #include "llmenubutton.h" +#include "llscrolllistctrl.h" #include "llviewermenu.h" +#include "llviewerregion.h" #include "llwearableitemslist.h" #include "llsdserialize.h" #include "llclipboard.h" @@ -146,11 +152,47 @@ protected: menu->setItemVisible("detach", allow_detach); menu->setItemVisible("edit_outfit_separator", allow_take_off || allow_detach); menu->setItemVisible("show_original", mUUIDs.size() == 1); + menu->setItemVisible("edit_item", FALSE); } }; ////////////////////////////////////////////////////////////////////////// +class LLTempAttachmentsContextMenu : public LLListContextMenu +{ +public: + LLTempAttachmentsContextMenu(LLPanelWearing* panel_wearing) + : mPanelWearing(panel_wearing) + {} +protected: + /* virtual */ LLContextMenu* createMenu() + { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + + registrar.add("Wearing.EditItem", boost::bind(&LLPanelWearing::onEditAttachment, mPanelWearing)); + registrar.add("Wearing.Detach", boost::bind(&LLPanelWearing::onRemoveAttachment, mPanelWearing)); + LLContextMenu* menu = createFromFile("menu_wearing_tab.xml"); + + updateMenuItemsVisibility(menu); + + return menu; + } + + void updateMenuItemsVisibility(LLContextMenu* menu) + { + menu->setItemVisible("take_off", FALSE); + menu->setItemVisible("detach", TRUE); + menu->setItemVisible("edit_outfit_separator", TRUE); + menu->setItemVisible("show_original", FALSE); + menu->setItemVisible("edit_item", TRUE); + menu->setItemVisible("edit", FALSE); + } + + LLPanelWearing* mPanelWearing; +}; + +////////////////////////////////////////////////////////////////////////// + std::string LLPanelAppearanceTab::sFilterSubString = LLStringUtil::null; static LLPanelInjector<LLPanelWearing> t_panel_wearing("panel_wearing"); @@ -159,30 +201,47 @@ LLPanelWearing::LLPanelWearing() : LLPanelAppearanceTab() , mCOFItemsList(NULL) , mIsInitialized(false) + , mAttachmentsChangedConnection() { mCategoriesObserver = new LLInventoryCategoriesObserver(); mGearMenu = new LLWearingGearMenu(this); mContextMenu = new LLWearingContextMenu(); + mAttachmentsMenu = new LLTempAttachmentsContextMenu(this); } LLPanelWearing::~LLPanelWearing() { delete mGearMenu; delete mContextMenu; + delete mAttachmentsMenu; if (gInventory.containsObserver(mCategoriesObserver)) { gInventory.removeObserver(mCategoriesObserver); } delete mCategoriesObserver; + + if (mAttachmentsChangedConnection.connected()) + { + mAttachmentsChangedConnection.disconnect(); + } } BOOL LLPanelWearing::postBuild() { + mAccordionCtrl = getChild<LLAccordionCtrl>("wearables_accordion"); + mWearablesTab = getChild<LLAccordionCtrlTab>("tab_wearables"); + mAttachmentsTab = getChild<LLAccordionCtrlTab>("tab_temp_attachments"); + mAttachmentsTab->setDropDownStateChangedCallback(boost::bind(&LLPanelWearing::onAccordionTabStateChanged, this)); + mCOFItemsList = getChild<LLWearableItemsList>("cof_items_list"); mCOFItemsList->setRightMouseDownCallback(boost::bind(&LLPanelWearing::onWearableItemsListRightClick, this, _1, _2, _3)); + mTempItemsList = getChild<LLScrollListCtrl>("temp_attachments_list"); + mTempItemsList->setFgUnselectedColor(LLColor4::white); + mTempItemsList->setRightMouseDownCallback(boost::bind(&LLPanelWearing::onTempAttachmentsListRightClick, this, _1, _2, _3)); + LLMenuButton* menu_gear_btn = getChild<LLMenuButton>("options_gear_btn"); menu_gear_btn->setMenu(mGearMenu->getMenu()); @@ -223,6 +282,44 @@ void LLPanelWearing::onOpen(const LLSD& /*info*/) } } +void LLPanelWearing::draw() +{ + if (mUpdateTimer.getStarted() && (mUpdateTimer.getElapsedTimeF32() > 0.1)) + { + mUpdateTimer.stop(); + updateAttachmentsList(); + } + LLPanel::draw(); +} + +void LLPanelWearing::onAccordionTabStateChanged() +{ + if(mAttachmentsTab->isExpanded()) + { + startUpdateTimer(); + mAttachmentsChangedConnection = LLAppearanceMgr::instance().setAttachmentsChangedCallback(boost::bind(&LLPanelWearing::startUpdateTimer, this)); + } + else + { + if (mAttachmentsChangedConnection.connected()) + { + mAttachmentsChangedConnection.disconnect(); + } + } +} + +void LLPanelWearing::startUpdateTimer() +{ + if (!mUpdateTimer.getStarted()) + { + mUpdateTimer.start(); + } + else + { + mUpdateTimer.reset(); + } +} + // virtual void LLPanelWearing::setFilterSubString(const std::string& string) { @@ -251,6 +348,124 @@ bool LLPanelWearing::isActionEnabled(const LLSD& userdata) return false; } +void LLPanelWearing::updateAttachmentsList() +{ + std::vector<LLViewerObject*> attachs = LLAgentWearables::getTempAttachments(); + mTempItemsList->deleteAllItems(); + mAttachmentsMap.clear(); + if(!attachs.empty()) + { + if(!populateAttachmentsList()) + { + requestAttachmentDetails(); + } + } + else + { + std::string no_attachments = getString("no_attachments"); + LLSD row; + row["columns"][0]["column"] = "text"; + row["columns"][0]["value"] = no_attachments; + row["columns"][0]["font"] = "SansSerifBold"; + mTempItemsList->addElement(row); + } +} + +bool LLPanelWearing::populateAttachmentsList(bool update) +{ + bool populated = true; + if(mTempItemsList) + { + mTempItemsList->deleteAllItems(); + mAttachmentsMap.clear(); + std::vector<LLViewerObject*> attachs = LLAgentWearables::getTempAttachments(); + + std::string icon_name = LLInventoryIcon::getIconName(LLAssetType::AT_OBJECT, LLInventoryType::IT_OBJECT); + for (std::vector<LLViewerObject*>::iterator iter = attachs.begin(); + iter != attachs.end(); ++iter) + { + LLViewerObject *attachment = *iter; + LLSD row; + row["id"] = attachment->getID(); + row["columns"][0]["column"] = "icon"; + row["columns"][0]["type"] = "icon"; + row["columns"][0]["value"] = icon_name; + row["columns"][1]["column"] = "text"; + if(mObjectNames.count(attachment->getID()) && !mObjectNames[attachment->getID()].empty()) + { + row["columns"][1]["value"] = mObjectNames[attachment->getID()]; + } + else if(update) + { + row["columns"][1]["value"] = attachment->getID(); + populated = false; + } + else + { + row["columns"][1]["value"] = "Loading..."; + populated = false; + } + mTempItemsList->addElement(row); + mAttachmentsMap[attachment->getID()] = attachment; + } + } + return populated; +} + +void LLPanelWearing::requestAttachmentDetails() +{ + LLSD body; + std::string url = gAgent.getRegion()->getCapability("AttachmentResources"); + if (!url.empty()) + { + LLCoros::instance().launch("LLPanelWearing::getAttachmentLimitsCoro", + boost::bind(&LLPanelWearing::getAttachmentLimitsCoro, this, url)); + } +} + +void LLPanelWearing::getAttachmentLimitsCoro(std::string url) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("getAttachmentLimitsCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + + LLSD result = httpAdapter->getAndSuspend(httpRequest, url); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS() << "Unable to retrieve attachment limits." << LL_ENDL; + return; + } + + result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); + setAttachmentDetails(result); +} + + +void LLPanelWearing::setAttachmentDetails(LLSD content) +{ + mObjectNames.clear(); + S32 number_attachments = content["attachments"].size(); + for(int i = 0; i < number_attachments; i++) + { + S32 number_objects = content["attachments"][i]["objects"].size(); + for(int j = 0; j < number_objects; j++) + { + LLUUID task_id = content["attachments"][i]["objects"][j]["id"].asUUID(); + std::string name = content["attachments"][i]["objects"][j]["name"].asString(); + mObjectNames[task_id] = name; + } + } + if(!mObjectNames.empty()) + { + populateAttachmentsList(true); + } +} + boost::signals2::connection LLPanelWearing::setSelectionChangeCallback(commit_callback_t cb) { if (!mCOFItemsList) return boost::signals2::connection(); @@ -270,6 +485,20 @@ void LLPanelWearing::onWearableItemsListRightClick(LLUICtrl* ctrl, S32 x, S32 y) mContextMenu->show(ctrl, selected_uuids, x, y); } +void LLPanelWearing::onTempAttachmentsListRightClick(LLUICtrl* ctrl, S32 x, S32 y) +{ + LLScrollListCtrl* list = dynamic_cast<LLScrollListCtrl*>(ctrl); + if (!list) return; + list->selectItemAt(x, y, MASK_NONE); + uuid_vec_t selected_uuids; + + if(list->getCurrentID().notNull()) + { + selected_uuids.push_back(list->getCurrentID()); + mAttachmentsMenu->show(ctrl, selected_uuids, x, y); + } +} + bool LLPanelWearing::hasItemSelected() { return mCOFItemsList->getSelectedItem() != NULL; @@ -280,6 +509,28 @@ void LLPanelWearing::getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const mCOFItemsList->getSelectedUUIDs(selected_uuids); } +void LLPanelWearing::onEditAttachment() +{ + LLScrollListItem* item = mTempItemsList->getFirstSelected(); + if (item) + { + LLSelectMgr::getInstance()->deselectAll(); + LLSelectMgr::getInstance()->selectObjectAndFamily(mAttachmentsMap[item->getUUID()]); + handle_object_edit(); + } +} + +void LLPanelWearing::onRemoveAttachment() +{ + LLScrollListItem* item = mTempItemsList->getFirstSelected(); + if (item) + { + LLSelectMgr::getInstance()->deselectAll(); + LLSelectMgr::getInstance()->selectObjectAndFamily(mAttachmentsMap[item->getUUID()]); + LLSelectMgr::getInstance()->sendDropAttachment(); + } +} + void LLPanelWearing::copyToClipboard() { std::string text; diff --git a/indra/newview/llpanelwearing.h b/indra/newview/llpanelwearing.h index 9a212b3cca..c5cb79092a 100644 --- a/indra/newview/llpanelwearing.h +++ b/indra/newview/llpanelwearing.h @@ -31,9 +31,14 @@ // newview #include "llpanelappearancetab.h" +#include "llselectmgr.h" +#include "lltimer.h" +class LLAccordionCtrl; +class LLAccordionCtrlTab; class LLInventoryCategoriesObserver; class LLListContextMenu; +class LLScrollListCtrl; class LLWearableItemsList; class LLWearingGearMenu; @@ -52,6 +57,8 @@ public: /*virtual*/ BOOL postBuild(); + /*virtual*/ void draw(); + /*virtual*/ void onOpen(const LLSD& info); /*virtual*/ void setFilterSubString(const std::string& string); @@ -62,17 +69,43 @@ public: /*virtual*/ void copyToClipboard(); + void startUpdateTimer(); + void updateAttachmentsList(); + boost::signals2::connection setSelectionChangeCallback(commit_callback_t cb); bool hasItemSelected(); + bool populateAttachmentsList(bool update = false); + void onAccordionTabStateChanged(); + void setAttachmentDetails(LLSD content); + void requestAttachmentDetails(); + void onEditAttachment(); + void onRemoveAttachment(); + private: void onWearableItemsListRightClick(LLUICtrl* ctrl, S32 x, S32 y); + void onTempAttachmentsListRightClick(LLUICtrl* ctrl, S32 x, S32 y); + + void getAttachmentLimitsCoro(std::string url); LLInventoryCategoriesObserver* mCategoriesObserver; LLWearableItemsList* mCOFItemsList; + LLScrollListCtrl* mTempItemsList; LLWearingGearMenu* mGearMenu; LLListContextMenu* mContextMenu; + LLListContextMenu* mAttachmentsMenu; + + LLAccordionCtrlTab* mWearablesTab; + LLAccordionCtrlTab* mAttachmentsTab; + LLAccordionCtrl* mAccordionCtrl; + + std::map<LLUUID, LLViewerObject*> mAttachmentsMap; + + std::map<LLUUID, std::string> mObjectNames; + + boost::signals2::connection mAttachmentsChangedConnection; + LLFrameTimer mUpdateTimer; bool mIsInitialized; }; diff --git a/indra/newview/llpathfindinglinksetlist.cpp b/indra/newview/llpathfindinglinksetlist.cpp index b886e46765..eb7b95552e 100644 --- a/indra/newview/llpathfindinglinksetlist.cpp +++ b/indra/newview/llpathfindinglinksetlist.cpp @@ -204,7 +204,10 @@ void LLPathfindingLinksetList::parseLinksetListData(const LLSD& pLinksetListData { const std::string& uuid(linksetDataIter->first); const LLSD& linksetData = linksetDataIter->second; - LLPathfindingObjectPtr linksetPtr(new LLPathfindingLinkset(uuid, linksetData)); - objectMap.insert(std::pair<std::string, LLPathfindingObjectPtr>(uuid, linksetPtr)); + if(linksetData.size() != 0) + { + LLPathfindingObjectPtr linksetPtr(new LLPathfindingLinkset(uuid, linksetData)); + objectMap.insert(std::pair<std::string, LLPathfindingObjectPtr>(uuid, linksetPtr)); + } } } diff --git a/indra/newview/llpresetsmanager.cpp b/indra/newview/llpresetsmanager.cpp index 9957039f72..836f63bffa 100644 --- a/indra/newview/llpresetsmanager.cpp +++ b/indra/newview/llpresetsmanager.cpp @@ -104,8 +104,7 @@ void LLPresetsManager::loadPresetNamesFromDir(const std::string& dir, preset_nam if (found) { std::string path = gDirUtilp->add(dir, file); - std::string name = gDirUtilp->getBaseFileName(LLURI::unescape(path), /*strip_exten = */ true); - + std::string name = LLURI::unescape(gDirUtilp->getBaseFileName(path, /*strip_exten = */ true)); LL_DEBUGS() << " Found preset '" << name << "'" << LL_ENDL; if (PRESETS_DEFAULT != name) diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index ba9845ef04..510d91839d 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -437,6 +437,23 @@ void LLPreviewNotecard::finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, } } +void LLPreviewNotecard::finishTaskUpload(LLUUID itemId, LLUUID newAssetId, LLUUID taskId) +{ + + LLSD floater_key; + floater_key["taskid"] = taskId; + floater_key["itemid"] = itemId; + LLPreviewNotecard* nc = LLFloaterReg::findTypedInstance<LLPreviewNotecard>("preview_notecard", floater_key); + if (nc) + { + if (nc->hasEmbeddedInventory()) + { + gVFS->removeFile(newAssetId, LLAssetType::AT_NOTECARD); + } + nc->setAssetId(newAssetId); + nc->refreshFromInventory(); + } +} bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem) { @@ -485,7 +502,7 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem) else if (!mObjectUUID.isNull() && !task_url.empty()) { uploadInfo = LLResourceUploadInfo::ptr_t(new LLBufferedAssetUploadInfo(mObjectUUID, mItemUUID, LLAssetType::AT_NOTECARD, buffer, - boost::bind(&LLPreviewNotecard::finishInventoryUpload, _1, _3, LLUUID::null))); + boost::bind(&LLPreviewNotecard::finishTaskUpload, _1, _3, mObjectUUID))); url = task_url; } diff --git a/indra/newview/llpreviewnotecard.h b/indra/newview/llpreviewnotecard.h index ba571995f6..017c4485ba 100644 --- a/indra/newview/llpreviewnotecard.h +++ b/indra/newview/llpreviewnotecard.h @@ -96,6 +96,7 @@ protected: bool handleConfirmDeleteDialog(const LLSD& notification, const LLSD& response); static void finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, LLUUID newItemId); + static void finishTaskUpload(LLUUID itemId, LLUUID newAssetId, LLUUID taskId); protected: LLViewerTextEditor* mEditor; diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 5b1b356597..f28ffce602 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -375,7 +375,8 @@ LLScriptEdCore::LLScriptEdCore( mLiveFile(NULL), mLive(live), mContainer(container), - mHasScriptData(FALSE) + mHasScriptData(FALSE), + mScriptRemoved(FALSE) { setFollowsAll(); setBorderVisible(FALSE); @@ -666,7 +667,7 @@ bool LLScriptEdCore::hasChanged() void LLScriptEdCore::draw() { BOOL script_changed = hasChanged(); - getChildView("Save_btn")->setEnabled(script_changed); + getChildView("Save_btn")->setEnabled(script_changed && !mScriptRemoved); if( mEditor->hasFocus() ) { @@ -840,7 +841,7 @@ void LLScriptEdCore::addHelpItemToHistory(const std::string& help_string) BOOL LLScriptEdCore::canClose() { - if(mForceClose || !hasChanged()) + if(mForceClose || !hasChanged() || mScriptRemoved) { return TRUE; } @@ -1511,6 +1512,17 @@ BOOL LLPreviewLSL::postBuild() return LLPreview::postBuild(); } +void LLPreviewLSL::draw() +{ + const LLInventoryItem* item = getItem(); + if(!item) + { + setTitle(LLTrans::getString("ScriptWasDeleted")); + mScriptEd->setItemRemoved(TRUE); + } + + LLPreview::draw(); +} // virtual void LLPreviewLSL::callbackLSLCompileSucceeded() { diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index a8c6a6eeeb..6b31125641 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -119,6 +119,8 @@ public: void setScriptName(const std::string& name){mScriptName = name;}; + void setItemRemoved(bool script_removed){mScriptRemoved = script_removed;}; + private: void onBtnHelp(); void onBtnDynamicHelp(); @@ -163,6 +165,7 @@ private: BOOL mHasScriptData; LLLiveLSLFile* mLiveFile; LLUUID mAssociatedExperience; + BOOL mScriptRemoved; LLScriptEdContainer* mContainer; // parent view @@ -198,6 +201,7 @@ public: /*virtual*/ BOOL postBuild(); protected: + virtual void draw(); virtual BOOL canClose(); void closeIfNeeded(); diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp index 4f9f83b6f2..72d7cf1e45 100644 --- a/indra/newview/llsecapi.cpp +++ b/indra/newview/llsecapi.cpp @@ -29,6 +29,8 @@ #include "llviewerprecompiledheaders.h" #include "llsecapi.h" #include "llsechandler_basic.h" +#include "llexception.h" +#include "stringize.h" #include <openssl/evp.h> #include <openssl/err.h> #include <map> @@ -64,12 +66,12 @@ void initializeSecHandler() } catch (LLProtectedDataException e) { - exception_msg = e.getMessage(); + exception_msg = e.what(); } } if (!exception_msg.empty()) // an exception was thrown. { - throw LLProtectedDataException(exception_msg.c_str()); + LLTHROW(LLProtectedDataException(exception_msg)); } } @@ -101,6 +103,7 @@ std::ostream& operator <<(std::ostream& s, const LLCredential& cred) LLSD LLCredential::getLoginParams() { LLSD result = LLSD::emptyMap(); + std::string username; try { if (mIdentifier["type"].asString() == "agent") @@ -109,17 +112,19 @@ LLSD LLCredential::getLoginParams() result["passwd"] = "$1$" + mAuthenticator["secret"].asString(); result["first"] = mIdentifier["first_name"]; result["last"] = mIdentifier["last_name"]; - + username = result["first"].asString() + " " + result["last"].asString(); } else if (mIdentifier["type"].asString() == "account") { result["username"] = mIdentifier["account_name"]; result["passwd"] = mAuthenticator["secret"]; - + username = result["username"].asString(); } } catch (...) { + // nat 2016-08-18: not clear what exceptions the above COULD throw?! + LOG_UNHANDLED_EXCEPTION(STRINGIZE("for '" << username << "'")); // we could have corrupt data, so simply return a null login param if so LL_WARNS("AppInit") << "Invalid credential" << LL_ENDL; } diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h index 6fe3ee31cf..6af5a28fa5 100644 --- a/indra/newview/llsecapi.h +++ b/indra/newview/llsecapi.h @@ -32,6 +32,7 @@ #include <openssl/x509.h> #include <ostream> #include "llpointer.h" +#include "llexception.h" #ifdef LL_WINDOWS #pragma warning(disable:4250) @@ -116,17 +117,13 @@ -class LLProtectedDataException +struct LLProtectedDataException: public LLException { -public: - LLProtectedDataException(const char *msg) + LLProtectedDataException(const std::string& msg): + LLException(msg) { - LL_WARNS("SECAPI") << "Protected Data Error: " << (std::string)msg << LL_ENDL; - mMsg = (std::string)msg; + LL_WARNS("SECAPI") << "Protected Data Error: " << msg << LL_ENDL; } - std::string getMessage() { return mMsg; } -protected: - std::string mMsg; }; // class LLCertificate @@ -334,22 +331,21 @@ std::ostream& operator <<(std::ostream& s, const LLCredential& cred); // All error handling is via exceptions. -class LLCertException +class LLCertException: public LLException { public: - LLCertException(LLPointer<LLCertificate> cert, const char* msg) + LLCertException(LLPointer<LLCertificate> cert, const std::string& msg): + LLException(msg) { mCert = cert; - LL_WARNS("SECAPI") << "Certificate Error: " << (std::string)msg << LL_ENDL; - mMsg = (std::string)msg; + LL_WARNS("SECAPI") << "Certificate Error: " << msg << LL_ENDL; } - LLPointer<LLCertificate> getCert() { return mCert; } - std::string getMessage() { return mMsg; } + virtual ~LLCertException() throw() {} + LLPointer<LLCertificate> getCert() const { return mCert; } protected: LLPointer<LLCertificate> mCert; - std::string mMsg; }; class LLInvalidCertificate : public LLCertException @@ -358,6 +354,7 @@ public: LLInvalidCertificate(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalid") { } + virtual ~LLInvalidCertificate() throw() {} protected: }; @@ -367,6 +364,7 @@ public: LLCertValidationTrustException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertUntrusted") { } + virtual ~LLCertValidationTrustException() throw() {} protected: }; @@ -378,7 +376,7 @@ public: { mHostname = hostname; } - + virtual ~LLCertValidationHostnameException() throw() {} std::string getHostname() { return mHostname; } protected: std::string mHostname; @@ -392,6 +390,7 @@ public: { mTime = current_time; } + virtual ~LLCertValidationExpirationException() throw() {} LLDate GetTime() { return mTime; } protected: LLDate mTime; @@ -403,6 +402,7 @@ public: LLCertKeyUsageValidationException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertKeyUsage") { } + virtual ~LLCertKeyUsageValidationException() throw() {} protected: }; @@ -412,6 +412,7 @@ public: LLCertBasicConstraintsValidationException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertBasicConstraints") { } + virtual ~LLCertBasicConstraintsValidationException() throw() {} protected: }; @@ -421,6 +422,7 @@ public: LLCertValidationInvalidSignatureException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalidSignature") { } + virtual ~LLCertValidationInvalidSignatureException() throw() {} protected: }; diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 40516f9bbb..d6fb801cc0 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -35,6 +35,8 @@ #include "llfile.h" #include "lldir.h" #include "llviewercontrol.h" +#include "llexception.h" +#include "stringize.h" #include <vector> #include <ios> #include <openssl/ossl_typ.h> @@ -72,14 +74,14 @@ LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert) if(pem_bio == NULL) { LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; - throw LLInvalidCertificate(this); + LLTHROW(LLInvalidCertificate(this)); } mCert = NULL; PEM_read_bio_X509(pem_bio, &mCert, 0, NULL); BIO_free(pem_bio); if (!mCert) { - throw LLInvalidCertificate(this); + LLTHROW(LLInvalidCertificate(this)); } } @@ -88,7 +90,7 @@ LLBasicCertificate::LLBasicCertificate(X509* pCert) { if (!pCert || !pCert->cert_info) { - throw LLInvalidCertificate(this); + LLTHROW(LLInvalidCertificate(this)); } mCert = X509_dup(pCert); } @@ -617,7 +619,7 @@ void LLBasicCertificateStore::load_from_file(const std::string& filename) } catch (...) { - LL_WARNS("SECAPI") << "Failure creating certificate from the certificate store file." << LL_ENDL; + LOG_UNHANDLED_EXCEPTION("creating certificate from the certificate store file"); } X509_free(cert_x509); cert_x509 = NULL; @@ -873,22 +875,22 @@ void _validateCert(int validation_policy, // 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"); + LLTHROW(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"); + LLTHROW(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"); + LLTHROW(LLCertException(cert, "Cert doesn't have an expiration period")); } if (!current_cert_info.has(CERT_SHA1_DIGEST)) { - throw LLCertException(cert, "No SHA1 digest"); + LLTHROW(LLCertException(cert, "No SHA1 digest")); } if (validation_policy & VALIDATION_POLICY_TIME) @@ -903,7 +905,7 @@ void _validateCert(int validation_policy, if((validation_date < current_cert_info[CERT_VALID_FROM].asDate()) || (validation_date > current_cert_info[CERT_VALID_TO].asDate())) { - throw LLCertValidationExpirationException(cert, validation_date); + LLTHROW(LLCertValidationExpirationException(cert, validation_date)); } } if (validation_policy & VALIDATION_POLICY_SSL_KU) @@ -914,14 +916,14 @@ void _validateCert(int validation_policy, !(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT))))) { - throw LLCertKeyUsageValidationException(cert); + LLTHROW(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); + LLTHROW(LLCertKeyUsageValidationException(cert)); } } if (validation_policy & VALIDATION_POLICY_CA_KU) @@ -930,7 +932,7 @@ void _validateCert(int validation_policy, (!_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], (std::string)CERT_KU_CERT_SIGN))) { - throw LLCertKeyUsageValidationException(cert); + LLTHROW(LLCertKeyUsageValidationException(cert)); } } @@ -942,13 +944,13 @@ void _validateCert(int validation_policy, 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); + LLTHROW(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); + LLTHROW(LLCertBasicConstraintsValidationException(cert)); } } } @@ -1018,7 +1020,7 @@ void LLBasicCertificateStore::validate(int validation_policy, if(cert_chain->size() < 1) { - throw LLCertException(NULL, "No certs in chain"); + LLTHROW(LLCertException(NULL, "No certs in chain")); } iterator current_cert = cert_chain->begin(); LLSD current_cert_info; @@ -1033,11 +1035,11 @@ void LLBasicCertificateStore::validate(int validation_policy, (*current_cert)->getLLSD(current_cert_info); if(!validation_params.has(CERT_HOSTNAME)) { - throw LLCertException((*current_cert), "No hostname passed in for validation"); + LLTHROW(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)); + LLTHROW(LLInvalidCertificate((*current_cert))); } LL_DEBUGS("SECAPI") << "Validating the hostname " << validation_params[CERT_HOSTNAME].asString() << @@ -1054,7 +1056,7 @@ void LLBasicCertificateStore::validate(int validation_policy, X509* cert_x509 = (*current_cert)->getOpenSSLX509(); if(!cert_x509) { - throw LLInvalidCertificate((*current_cert)); + LLTHROW(LLInvalidCertificate((*current_cert))); } std::string sha1_hash((const char *)cert_x509->sha1_hash, SHA_DIGEST_LENGTH); X509_free( cert_x509 ); @@ -1075,7 +1077,7 @@ void LLBasicCertificateStore::validate(int validation_policy, if((validation_date < cache_entry->second.first) || (validation_date > cache_entry->second.second)) { - throw LLCertValidationExpirationException((*current_cert), validation_date); + LLTHROW(LLCertValidationExpirationException((*current_cert), validation_date)); } } // successfully found in cache @@ -1107,7 +1109,7 @@ void LLBasicCertificateStore::validate(int validation_policy, if(!_verify_signature((*current_cert), previous_cert)) { - throw LLCertValidationInvalidSignatureException(previous_cert); + LLTHROW(LLCertValidationInvalidSignatureException(previous_cert)); } } _validateCert(local_validation_policy, @@ -1156,7 +1158,7 @@ void LLBasicCertificateStore::validate(int validation_policy, if(!_verify_signature((*found_store_cert), (*current_cert))) { - throw LLCertValidationInvalidSignatureException(*current_cert); + LLTHROW(LLCertValidationInvalidSignatureException(*current_cert)); } // successfully validated. mTrustedCertCache[sha1_hash] = std::pair<LLDate, LLDate>(from_time, to_time); @@ -1173,7 +1175,7 @@ void LLBasicCertificateStore::validate(int validation_policy, if (validation_policy & VALIDATION_POLICY_TRUSTED) { // we reached the end without finding a trusted cert. - throw LLCertValidationTrustException((*cert_chain)[cert_chain->size()-1]); + LLTHROW(LLCertValidationTrustException((*cert_chain)[cert_chain->size()-1])); } mTrustedCertCache[sha1_hash] = std::pair<LLDate, LLDate>(from_time, to_time); @@ -1261,7 +1263,7 @@ void LLSecAPIBasicHandler::_readProtectedData() protected_data_stream.read((char *)salt, STORE_SALT_SIZE); if (protected_data_stream.gcount() < STORE_SALT_SIZE) { - throw LLProtectedDataException("Config file too short."); + LLTHROW(LLProtectedDataException("Config file too short.")); } cipher.decrypt(salt, STORE_SALT_SIZE); @@ -1301,7 +1303,7 @@ void LLSecAPIBasicHandler::_readProtectedData() if (parser->parse(parse_stream, mProtectedDataMap, LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) { - throw LLProtectedDataException("Config file cannot be decrypted."); + LLTHROW(LLProtectedDataException("Config file cannot be decrypted.")); } } } @@ -1364,7 +1366,7 @@ void LLSecAPIBasicHandler::_writeProtectedData() } catch (...) { - LL_WARNS() << "LLProtectedDataException(Error writing Protected Data Store)" << LL_ENDL; + LOG_UNHANDLED_EXCEPTION("LLProtectedDataException(Error writing Protected Data Store)"); // 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. @@ -1372,39 +1374,39 @@ void LLSecAPIBasicHandler::_writeProtectedData() // EXP-1825 crash in LLSecAPIBasicHandler::_writeProtectedData() // Decided throwing an exception here was overkill until we figure out why this happens - //throw LLProtectedDataException("Error writing Protected Data Store"); + //LLTHROW(LLProtectedDataException("Error writing Protected Data Store")); } - try - { - // move the temporary file to the specified file location. - if((( (LLFile::isfile(mProtectedDataFilename) != 0) - && (LLFile::remove(mProtectedDataFilename) != 0))) - || (LLFile::rename(tmp_filename, mProtectedDataFilename))) - { - LL_WARNS() << "LLProtectedDataException(Could not overwrite protected data store)" << LL_ENDL; - LLFile::remove(tmp_filename); + try + { + // move the temporary file to the specified file location. + if((( (LLFile::isfile(mProtectedDataFilename) != 0) + && (LLFile::remove(mProtectedDataFilename) != 0))) + || (LLFile::rename(tmp_filename, mProtectedDataFilename))) + { + LL_WARNS() << "LLProtectedDataException(Could not overwrite protected data store)" << LL_ENDL; + LLFile::remove(tmp_filename); - // EXP-1825 crash in LLSecAPIBasicHandler::_writeProtectedData() - // Decided throwing an exception here was overkill until we figure out why this happens - //throw LLProtectedDataException("Could not overwrite protected data store"); - } + // EXP-1825 crash in LLSecAPIBasicHandler::_writeProtectedData() + // Decided throwing an exception here was overkill until we figure out why this happens + //LLTHROW(LLProtectedDataException("Could not overwrite protected data store")); + } } catch (...) { - LL_WARNS() << "LLProtectedDataException(Error renaming '" << tmp_filename - << "' to '" << mProtectedDataFilename << "')" << LL_ENDL; + LOG_UNHANDLED_EXCEPTION(STRINGIZE("renaming '" << tmp_filename << "' to '" + << mProtectedDataFilename << "'")); // 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. + // it may be, however). LLFile::remove(tmp_filename); //crash in LLSecAPIBasicHandler::_writeProtectedData() // Decided throwing an exception here was overkill until we figure out why this happens - //throw LLProtectedDataException("Error writing Protected Data Store"); + //LLTHROW(LLProtectedDataException("Error writing Protected Data Store")); } } - + // instantiate a certificate from a pem string LLPointer<LLCertificate> LLSecAPIBasicHandler::getCertificate(const std::string& pem_cert) { diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp index 86135ee6e8..f07f0ed86c 100644 --- a/indra/newview/llsidepanelappearance.cpp +++ b/indra/newview/llsidepanelappearance.cpp @@ -383,7 +383,7 @@ void LLSidepanelAppearance::toggleOutfitEditPanel(BOOL visible, BOOL disable_cam void LLSidepanelAppearance::toggleWearableEditPanel(BOOL visible, LLViewerWearable *wearable, BOOL disable_camera_switch) { - if (!mEditWearable || mEditWearable->getVisible() == visible) + if (!mEditWearable || ((mEditWearable->getWearable() == wearable) && mEditWearable->getVisible() == visible)) { // visibility isn't changing, hence nothing to do return; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index a2c8e7772e..4e81d78455 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -771,7 +771,11 @@ bool idle_startup() LL_DEBUGS("AppInit") << "FirstLoginThisInstall off" << LL_ENDL; } } - + display_startup(); + if (gViewerWindow->getSystemUIScaleFactorChanged()) + { + LLViewerWindow::showSystemUIScaleFactorChanged(); + } LLStartUp::setStartupState( STATE_LOGIN_WAIT ); // Wait for user input } else diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp index 2d06b8599c..d28a7cc048 100644 --- a/indra/newview/llsurfacepatch.cpp +++ b/indra/newview/llsurfacepatch.cpp @@ -230,8 +230,8 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 const F32 xyScaleInv = (1.f / xyScale)*(0.2222222222f); F32 vec[3] = { - fmod((F32)(mOriginGlobal.mdV[0] + x)*xyScaleInv, 256.f), - fmod((F32)(mOriginGlobal.mdV[1] + y)*xyScaleInv, 256.f), + (F32)fmod((F32)(mOriginGlobal.mdV[0] + x)*xyScaleInv, 256.f), + (F32)fmod((F32)(mOriginGlobal.mdV[1] + y)*xyScaleInv, 256.f), 0.f }; F32 rand_val = llclamp(noise2(vec)* 0.75f + 0.5f, 0.f, 1.f); diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 37cc908e84..36c4f0d516 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -1928,6 +1928,12 @@ bool LLTextureCache::writeToFastCache(S32 id, LLPointer<LLImageRaw> raw, S32 dis { //make a duplicate to keep the original raw image untouched. raw = raw->duplicate(); + if (raw->isBufferInvalid()) + { + LL_WARNS() << "Invalid image duplicate buffer" << LL_ENDL; + return false; + } + raw->scale(w, h) ; discardlevel += i ; diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index ad4f903dff..24bc55c998 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -1327,7 +1327,7 @@ void LLTextureCtrl::setOnTextureSelectedCallback(texture_selected_callback cb) void LLTextureCtrl::setImageAssetName(const std::string& name) { - LLPointer<LLUIImage> imagep = LLUI::getUIImage(name, LLGLTexture::BOOST_PREVIEW); + LLPointer<LLUIImage> imagep = LLUI::getUIImage(name); if(imagep) { LLViewerFetchedTexture* pTexture = dynamic_cast<LLViewerFetchedTexture*>(imagep->getImage().get()); diff --git a/indra/newview/lltool.cpp b/indra/newview/lltool.cpp index 5e703933ca..4aad650b68 100644 --- a/indra/newview/lltool.cpp +++ b/indra/newview/lltool.cpp @@ -38,7 +38,6 @@ #include "lltoolfocus.h" #include "llfocusmgr.h" #include "llagent.h" -#include "llagentcamera.h" #include "llviewerjoystick.h" extern BOOL gDebugClicks; @@ -85,14 +84,7 @@ BOOL LLTool::handleMouseDown(S32 x, S32 y, MASK mask) } // by default, didn't handle it // LL_INFOS() << "LLTool::handleMouseDown" << LL_ENDL; - if (gAgentCamera.cameraMouselook()) - { - gAgent.setControlFlags(AGENT_CONTROL_ML_LBUTTON_DOWN); - } - else - { - gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_DOWN); - } + gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_DOWN); return TRUE; } @@ -103,15 +95,8 @@ BOOL LLTool::handleMouseUp(S32 x, S32 y, MASK mask) LL_INFOS() << "LLTool left mouse up" << LL_ENDL; } // by default, didn't handle it - // LL_INFOS() << "LLTool::handleMouseUp" << LL_ENDL; - if (gAgentCamera.cameraMouselook()) - { - gAgent.setControlFlags(AGENT_CONTROL_ML_LBUTTON_UP); - } - else - { - gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_UP); - } + // LL_INFOS() << "LLTool::handleMouseUp" << LL_ENDL; + gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_UP); return TRUE; } diff --git a/indra/newview/lltoolcomp.cpp b/indra/newview/lltoolcomp.cpp index 2b4fa757f6..76a791c6e9 100644 --- a/indra/newview/lltoolcomp.cpp +++ b/indra/newview/lltoolcomp.cpp @@ -742,13 +742,12 @@ BOOL LLToolCompGun::handleHover(S32 x, S32 y, MASK mask) BOOL LLToolCompGun::handleMouseDown(S32 x, S32 y, MASK mask) { - // if the left button is blocked, don't put up the pie menu - if (gAgent.leftButtonBlocked()) - { - // in case of "grabbed" control flag will be set later - gAgent.setControlFlags(AGENT_CONTROL_ML_LBUTTON_DOWN); - return FALSE; - } + // if the left button is grabbed, don't put up the pie menu + if (gAgent.leftButtonGrabbed()) + { + gAgent.setControlFlags(AGENT_CONTROL_ML_LBUTTON_DOWN); + return FALSE; + } // On mousedown, start grabbing gGrabTransientTool = this; @@ -760,13 +759,12 @@ BOOL LLToolCompGun::handleMouseDown(S32 x, S32 y, MASK mask) BOOL LLToolCompGun::handleDoubleClick(S32 x, S32 y, MASK mask) { - // if the left button is blocked, don't put up the pie menu - if (gAgent.leftButtonBlocked()) - { - // in case of "grabbed" control flag will be set later - gAgent.setControlFlags(AGENT_CONTROL_ML_LBUTTON_DOWN); - return FALSE; - } + // if the left button is grabbed, don't put up the pie menu + if (gAgent.leftButtonGrabbed()) + { + gAgent.setControlFlags(AGENT_CONTROL_ML_LBUTTON_DOWN); + return FALSE; + } // On mousedown, start grabbing gGrabTransientTool = this; diff --git a/indra/newview/lltoolfocus.cpp b/indra/newview/lltoolfocus.cpp index c4696c3a01..caa055e5e0 100644 --- a/indra/newview/lltoolfocus.cpp +++ b/indra/newview/lltoolfocus.cpp @@ -223,6 +223,7 @@ void LLToolCamera::pickCallback(const LLPickInfo& pick_info) } if (!(pick_info.mKeyMask & MASK_ALT) && + !LLFloaterCamera::inFreeCameraMode() && gAgentCamera.cameraThirdPerson() && gViewerWindow->getLeftMouseDown() && !gSavedSettings.getBOOL("FreezeTime") && diff --git a/indra/newview/lltoolgrab.cpp b/indra/newview/lltoolgrab.cpp index c0ca4d7a9a..92e8af985b 100644 --- a/indra/newview/lltoolgrab.cpp +++ b/indra/newview/lltoolgrab.cpp @@ -143,7 +143,7 @@ BOOL LLToolGrab::handleMouseDown(S32 x, S32 y, MASK mask) // call the base class to propogate info to sim LLTool::handleMouseDown(x, y, mask); - if (!gAgent.leftButtonBlocked()) + if (!gAgent.leftButtonGrabbed()) { // can grab transparent objects (how touch event propagates, scripters rely on this) gViewerWindow->pickAsync(x, y, mask, pickCallback, /*BOOL pick_transparent*/ TRUE); diff --git a/indra/newview/lltoolmgr.cpp b/indra/newview/lltoolmgr.cpp index b0e3b5bf89..f6eb290bc3 100644 --- a/indra/newview/lltoolmgr.cpp +++ b/indra/newview/lltoolmgr.cpp @@ -267,7 +267,7 @@ bool LLToolMgr::canEdit() bool LLToolMgr::buildEnabledOrActive() { - return inEdit() || canEdit(); + return LLFloaterReg::instanceVisible("build") || canEdit(); } void LLToolMgr::toggleBuildMode(const LLSD& sdname) diff --git a/indra/newview/llviewchildren.cpp b/indra/newview/llviewchildren.cpp index 5c5bbdc8f5..32b2f7e9f5 100644 --- a/indra/newview/llviewchildren.cpp +++ b/indra/newview/llviewchildren.cpp @@ -79,8 +79,9 @@ void LLViewChildren::setBadge(const std::string& id, Badge badge, bool visible) default: case BADGE_OK: child->setValue(std::string("badge_ok.j2c")); break; case BADGE_NOTE: child->setValue(std::string("badge_note.j2c")); break; - case BADGE_WARN: child->setValue(std::string("badge_warn.j2c")); break; - case BADGE_ERROR: child->setValue(std::string("badge_error.j2c")); break; + case BADGE_WARN: + case BADGE_ERROR: + child->setValue(std::string("badge_warn.j2c")); break; } } } diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 16f40fb747..5e74e9f019 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -207,6 +207,12 @@ static bool handleVolumeLODChanged(const LLSD& newvalue) return true; } +static bool handleRiggedLODChanged(const LLSD& newvalue) +{ + LLVOVolume::sRiggedFactorMultiplier = (F32)newvalue.asReal(); + return true; +} + static bool handleAvatarLODChanged(const LLSD& newvalue) { LLVOAvatar::sLODFactor = (F32) newvalue.asReal(); @@ -619,6 +625,7 @@ void settings_setup_listeners() gSavedSettings.getControl("WindLightUseAtmosShaders")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); gSavedSettings.getControl("RenderGammaFull")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); gSavedSettings.getControl("RenderVolumeLODFactor")->getSignal()->connect(boost::bind(&handleVolumeLODChanged, _2)); + gSavedSettings.getControl("RenderRiggedFactorMultiplier")->getSignal()->connect(boost::bind(&handleRiggedLODChanged, _2)); gSavedSettings.getControl("RenderAvatarLODFactor")->getSignal()->connect(boost::bind(&handleAvatarLODChanged, _2)); gSavedSettings.getControl("RenderAvatarPhysicsLODFactor")->getSignal()->connect(boost::bind(&handleAvatarPhysicsLODChanged, _2)); gSavedSettings.getControl("RenderTerrainLODFactor")->getSignal()->connect(boost::bind(&handleTerrainLODChanged, _2)); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index d0813544f8..0bbe9fa2c2 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1007,6 +1007,22 @@ void activate_gesture_cb(const LLUUID& inv_item) LLGestureMgr::instance().activateGesture(inv_item); } +void set_default_permissions(LLViewerInventoryItem* item, std::string perm_type) +{ + llassert(item); + LLPermissions perm = item->getPermissions(); + if (perm.getMaskEveryone() != LLFloaterPerms::getEveryonePerms(perm_type) + || perm.getMaskGroup() != LLFloaterPerms::getGroupPerms(perm_type)) + { + perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms(perm_type)); + perm.setMaskGroup(LLFloaterPerms::getGroupPerms(perm_type)); + + item->setPermissions(perm); + + item->updateServer(FALSE); + } +} + void create_script_cb(const LLUUID& inv_item) { if (!inv_item.isNull()) @@ -1014,13 +1030,9 @@ void create_script_cb(const LLUUID& inv_item) LLViewerInventoryItem* item = gInventory.getItem(inv_item); if (item) { - LLPermissions perm = item->getPermissions(); - perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms("Scripts")); - perm.setMaskGroup(LLFloaterPerms::getGroupPerms("Scripts")); - - item->setPermissions(perm); + set_default_permissions(item, "Scripts"); - item->updateServer(FALSE); + // item was just created, update even if permissions did not changed gInventory.updateItem(item); gInventory.notifyObservers(); } @@ -1036,13 +1048,8 @@ void create_gesture_cb(const LLUUID& inv_item) LLViewerInventoryItem* item = gInventory.getItem(inv_item); if (item) { - LLPermissions perm = item->getPermissions(); - perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms("Gestures")); - perm.setMaskGroup(LLFloaterPerms::getGroupPerms("Gestures")); + set_default_permissions(item, "Gestures"); - item->setPermissions(perm); - - item->updateServer(FALSE); gInventory.updateItem(item); gInventory.notifyObservers(); @@ -1061,13 +1068,8 @@ void create_notecard_cb(const LLUUID& inv_item) LLViewerInventoryItem* item = gInventory.getItem(inv_item); if (item) { - LLPermissions perm = item->getPermissions(); - perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms("Notecards")); - perm.setMaskGroup(LLFloaterPerms::getGroupPerms("Notecards")); - - item->setPermissions(perm); + set_default_permissions(item, "Notecards"); - item->updateServer(FALSE); gInventory.updateItem(item); gInventory.notifyObservers(); } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index f5b06fbd19..39059fc01e 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -397,13 +397,15 @@ void set_merchant_SLM_menu() gToolBarView->enableCommand(command->id(), true); } -void check_merchant_status() +void check_merchant_status(bool force) { if (!gSavedSettings.getBOOL("InventoryOutboxDisplayBoth")) { - // Reset the SLM status: we actually want to check again, that's the point of calling check_merchant_status() - LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED); - + if (force) + { + // Reset the SLM status: we actually want to check again, that's the point of calling check_merchant_status() + LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED); + } // Hide SLM related menu item gMenuHolder->getChild<LLView>("MarketplaceListings")->setVisible(FALSE); @@ -7906,7 +7908,7 @@ void handle_web_browser_test(const LLSD& param) void handle_web_content_test(const LLSD& param) { std::string url = param.asString(); - LLWeb::loadURLInternal(url); + LLWeb::loadURLInternal(url, LLStringUtil::null, LLStringUtil::null, true); } void handle_show_url(const LLSD& param) diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index b7bdf00157..a553bb79a2 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -83,7 +83,7 @@ BOOL enable_god_full(void* user_data); BOOL enable_god_liaison(void* user_data); BOOL enable_god_basic(void* user_data); void set_underclothes_menu_options(); -void check_merchant_status(); +void check_merchant_status(bool force = false); void exchange_callingcard(const LLUUID& dest_id); @@ -108,6 +108,7 @@ void handle_look_at_selection(const LLSD& param); void handle_zoom_to_object(LLUUID object_id); void handle_object_return(); void handle_object_delete(); +void handle_object_edit(); void handle_buy_land(); diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 54b12cae12..b48b45502b 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -508,8 +508,8 @@ class LLFileEnableCloseAllWindows : public view_listener_t { bool handleEvent(const LLSD& userdata) { - LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::getInstance(); - LLFloaterOutfitSnapshot* floater_outfit_snapshot = LLFloaterOutfitSnapshot::getInstance(); + LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::findInstance(); + LLFloaterOutfitSnapshot* floater_outfit_snapshot = LLFloaterOutfitSnapshot::findInstance(); bool is_floaters_snapshot_opened = (floater_snapshot && floater_snapshot->isInVisibleChain()) || (floater_outfit_snapshot && floater_outfit_snapshot->isInVisibleChain()); bool open_children = gFloaterView->allChildrenClosed() && !is_floaters_snapshot_opened; @@ -523,10 +523,10 @@ class LLFileCloseAllWindows : public view_listener_t { bool app_quitting = false; gFloaterView->closeAllChildren(app_quitting); - LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::getInstance(); + LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::findInstance(); if (floater_snapshot) floater_snapshot->closeFloater(app_quitting); - LLFloaterOutfitSnapshot* floater_outfit_snapshot = LLFloaterOutfitSnapshot::getInstance(); + LLFloaterOutfitSnapshot* floater_outfit_snapshot = LLFloaterOutfitSnapshot::findInstance(); if (floater_outfit_snapshot) floater_outfit_snapshot->closeFloater(app_quitting); if (gMenuHolder) gMenuHolder->hideMenus(); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 92df3866f7..f472db080f 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -1639,7 +1639,7 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& LLDiscardAgentOffer* discard_agent_offer = new LLDiscardAgentOffer(mFolderID, mObjectID); discard_agent_offer->startFetch(); - if (catp || (itemp && itemp->isFinished())) + if ((catp && gInventory.isCategoryComplete(mObjectID)) || (itemp && itemp->isFinished())) { discard_agent_offer->done(); } @@ -4719,7 +4719,9 @@ void process_sound_trigger(LLMessageSystem *msg, void **) { if (!gAudiop) { +#if !LL_LINUX LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL; +#endif return; } @@ -4781,7 +4783,9 @@ void process_preload_sound(LLMessageSystem *msg, void **user_data) { if (!gAudiop) { +#if !LL_LINUX LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL; +#endif return; } @@ -6601,6 +6605,11 @@ void process_script_question(LLMessageSystem *msg, void **user_data) if (("ScriptTakeMoney" == script_perm.question) && has_not_only_debit) continue; + if (script_perm.question == "JoinAnExperience") + { // Some experience only permissions do not have an explicit permission bit. Add them here. + script_question += " " + LLTrans::getString("ForceSitAvatar") + "\n"; + } + script_question += " " + LLTrans::getString(script_perm.question) + "\n"; } } diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index 6666aecca2..2525886222 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -49,6 +49,8 @@ const std::string GRID_UPDATE_SERVICE_URL = "update_query_url_base"; const std::string GRID_HELPER_URI_VALUE = "helper_uri"; /// the splash page url const std::string GRID_LOGIN_PAGE_VALUE = "login_page"; +/// url for the web profile site +const std::string GRID_WEB_PROFILE_VALUE = "web_profile_url"; /// internal data on system grids const std::string GRID_IS_SYSTEM_GRID_VALUE = "system_grid"; /// whether this is single or double names @@ -70,6 +72,8 @@ const std::string SL_UPDATE_QUERY_URL = "https://update.secondlife.com/update"; const std::string MAIN_GRID_SLURL_BASE = "http://maps.secondlife.com/secondlife/"; const std::string SYSTEM_GRID_APP_SLURL_BASE = "secondlife:///app"; +const std::string MAIN_GRID_WEB_PROFILE_URL = "https://my.secondlife.com/"; + const char* SYSTEM_GRID_SLURL_BASE = "secondlife://%s/secondlife/"; const char* DEFAULT_SLURL_BASE = "https://%s/region/"; const char* DEFAULT_APP_SLURL_BASE = "x-grid-location-info://%s/app"; @@ -125,6 +129,7 @@ void LLGridManager::initialize(const std::string& grid_file) "https://secondlife.com/helpers/", DEFAULT_LOGIN_PAGE, SL_UPDATE_QUERY_URL, + MAIN_GRID_WEB_PROFILE_URL, "Agni"); addSystemGrid(LLTrans::getString("AditiGridLabel"), "util.aditi.lindenlab.com", @@ -132,6 +137,7 @@ void LLGridManager::initialize(const std::string& grid_file) "http://aditi-secondlife.webdev.lindenlab.com/helpers/", DEFAULT_LOGIN_PAGE, SL_UPDATE_QUERY_URL, + "https://my.aditi.lindenlab.com/", "Aditi"); LLSD other_grids; @@ -288,6 +294,10 @@ bool LLGridManager::addGrid(LLSD& grid_data) { grid_data[GRID_HELPER_URI_VALUE] = std::string("https://") + grid + "/helpers/"; } + if (!grid_data.has(GRID_WEB_PROFILE_VALUE)) + { + grid_data[GRID_WEB_PROFILE_VALUE] = std::string("https://") + grid + "/"; + } if (!grid_data.has(GRID_LOGIN_IDENTIFIER_TYPES)) { @@ -302,7 +312,8 @@ bool LLGridManager::addGrid(LLSD& grid_data) <<" id: "<<grid_data[GRID_ID_VALUE].asString()<<"\n" <<" label: "<<grid_data[GRID_LABEL_VALUE].asString()<<"\n" <<" login page: "<<grid_data[GRID_LOGIN_PAGE_VALUE].asString()<<"\n" - <<" helper page: "<<grid_data[GRID_HELPER_URI_VALUE].asString()<<"\n"; + <<" helper page: "<<grid_data[GRID_HELPER_URI_VALUE].asString()<<"\n" + <<" web profile: "<<grid_data[GRID_WEB_PROFILE_VALUE].asString()<<"\n"; /* still in LL_DEBUGS */ for (LLSD::array_const_iterator login_uris = grid_data[GRID_LOGIN_URI_VALUE].beginArray(); login_uris != grid_data[GRID_LOGIN_URI_VALUE].endArray(); @@ -339,6 +350,7 @@ void LLGridManager::addSystemGrid(const std::string& label, const std::string& helper, const std::string& login_page, const std::string& update_url_base, + const std::string& web_profile_url, const std::string& login_id) { LLSD grid = LLSD::emptyMap(); @@ -349,6 +361,7 @@ void LLGridManager::addSystemGrid(const std::string& label, grid[GRID_LOGIN_URI_VALUE].append(login_uri); grid[GRID_LOGIN_PAGE_VALUE] = login_page; grid[GRID_UPDATE_SERVICE_URL] = update_url_base; + grid[GRID_WEB_PROFILE_VALUE] = web_profile_url; grid[GRID_IS_SYSTEM_GRID_VALUE] = true; grid[GRID_LOGIN_IDENTIFIER_TYPES] = LLSD::emptyArray(); grid[GRID_LOGIN_IDENTIFIER_TYPES].append(CRED_IDENTIFIER_TYPE_AGENT); @@ -535,6 +548,21 @@ std::string LLGridManager::getLoginPage() return login_page; } +std::string LLGridManager::getWebProfileURL(const std::string& grid) +{ + std::string web_profile_url; + std::string grid_name = getGrid(grid); + if (!grid_name.empty()) + { + web_profile_url = mGridList[grid_name][GRID_WEB_PROFILE_VALUE].asString(); + } + else + { + LL_WARNS("GridManager")<<"invalid grid '"<<grid<<"'"<<LL_ENDL; + } + return web_profile_url; +} + void LLGridManager::getLoginIdentifierTypes(LLSD& idTypes) { idTypes = mGridList[mGrid][GRID_LOGIN_IDENTIFIER_TYPES]; diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h index 8526c0ba7f..228303d8e2 100644 --- a/indra/newview/llviewernetwork.h +++ b/indra/newview/llviewernetwork.h @@ -166,6 +166,13 @@ class LLGridManager : public LLSingleton<LLGridManager> /// Return the application URL prefix for the selected grid std::string getAppSLURLBase() { return getAppSLURLBase(mGrid); } + /// Return the url of the resident profile web site for the given grid + std::string getWebProfileURL(const std::string& grid); + + /// Return the url of the resident profile web site for the selected grid + std::string getWebProfileURL() { return getWebProfileURL(mGrid); } + + //@} /* ================================================================ @@ -216,6 +223,7 @@ class LLGridManager : public LLSingleton<LLGridManager> const std::string& helper, const std::string& login_page, const std::string& update_url_base, + const std::string& web_profile_url, const std::string& login_id = ""); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 5edc3c9745..5f4eeaa37b 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -133,6 +133,7 @@ std::map<std::string, U32> LLViewerObject::sObjectDataMap; // JC 3/18/2003 const F32 PHYSICS_TIMESTEP = 1.f / 45.f; +const F64 INV_REQUEST_EXPIRE_TIME_SEC = 60.f; static LLTrace::BlockTimerStatHandle FTM_CREATE_OBJECT("Create Object"); @@ -245,7 +246,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mInventory(NULL), mInventorySerialNum(0), mRegionp( regionp ), - mInventoryPending(FALSE), + mInvRequestExpireTime(0.f), mInventoryDirty(FALSE), mDead(FALSE), mOrphaned(FALSE), @@ -1434,10 +1435,14 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, setChanged(MOVED | SILHOUETTE); } - else if (mText.notNull()) + else { - mText->markDead(); - mText = NULL; + if (mText.notNull()) + { + mText->markDead(); + mText = NULL; + } + mHudText.clear(); } std::string media_url; @@ -1812,10 +1817,14 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, setChanged(TEXTURE); } - else if(mText.notNull()) + else { - mText->markDead(); - mText = NULL; + if (mText.notNull()) + { + mText->markDead(); + mText = NULL; + } + mHudText.clear(); } std::string media_url; @@ -2832,6 +2841,15 @@ void LLViewerObject::removeInventoryListener(LLVOInventoryListener* listener) } } +BOOL LLViewerObject::isInventoryPending() +{ + if (mInvRequestExpireTime == 0.f || mInvRequestExpireTime < LLFrameTimer::getTotalSeconds()) + { + return FALSE; + } + return TRUE; +} + void LLViewerObject::clearInventoryListeners() { for_each(mInventoryCallbacks.begin(), mInventoryCallbacks.end(), DeletePointer()); @@ -2870,7 +2888,7 @@ void LLViewerObject::requestInventory() void LLViewerObject::fetchInventoryFromServer() { - if (!mInventoryPending) + if (mInvRequestExpireTime == 0.f || mInvRequestExpireTime < LLFrameTimer::getTotalSeconds()) { delete mInventory; LLMessageSystem* msg = gMessageSystem; @@ -2883,7 +2901,7 @@ void LLViewerObject::fetchInventoryFromServer() msg->sendReliable(mRegionp->getHost()); // this will get reset by dirtyInventory or doInventoryCallback - mInventoryPending = TRUE; + mInvRequestExpireTime = LLFrameTimer::getTotalSeconds() + INV_REQUEST_EXPIRE_TIME_SEC; } } @@ -3099,7 +3117,7 @@ void LLViewerObject::doInventoryCallback() mInventoryCallbacks.erase(curiter); } } - mInventoryPending = FALSE; + mInvRequestExpireTime = 0.f; } void LLViewerObject::removeInventory(const LLUUID& item_id) @@ -4990,8 +5008,26 @@ void LLViewerObject::initHudText() void LLViewerObject::restoreHudText() { - if(mText) + if (mHudText.empty()) + { + if (mText) + { + mText->markDead(); + mText = NULL; + } + } + else { + if (!mText) + { + initHudText(); + } + else + { + // Restore default values + mText->setZCompare(TRUE); + mText->setDoFade(TRUE); + } mText->setColor(mHudTextColor); mText->setString(mHudText); } @@ -6271,7 +6307,7 @@ const LLUUID &LLViewerObject::extractAttachmentItemID() return getAttachmentItemID(); } -const std::string& LLViewerObject::getAttachmentItemName() +const std::string& LLViewerObject::getAttachmentItemName() const { static std::string empty; LLInventoryItem *item = gInventory.getItem(getAttachmentItemID()); diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index cb8acfdcf8..804b6c147b 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -172,7 +172,7 @@ public: void setOnActiveList(BOOL on_active) { mOnActiveList = on_active; } virtual BOOL isAttachment() const { return FALSE; } - const std::string& getAttachmentItemName(); + const std::string& getAttachmentItemName() const; virtual LLVOAvatar* getAvatar() const; //get the avatar this object is attached to, or NULL if object is not an attachment virtual BOOL isHUDAttachment() const { return FALSE; } @@ -437,7 +437,7 @@ public: // viewer object has the inventory stored locally. void registerInventoryListener(LLVOInventoryListener* listener, void* user_data); void removeInventoryListener(LLVOInventoryListener* listener); - BOOL isInventoryPending() { return mInventoryPending; } + BOOL isInventoryPending(); void clearInventoryListeners(); bool hasInventoryListeners(); void requestInventory(); @@ -757,7 +757,7 @@ protected: S16 mInventorySerialNum; LLViewerRegion *mRegionp; // Region that this object belongs to. - BOOL mInventoryPending; + F64 mInvRequestExpireTime; BOOL mInventoryDirty; BOOL mDead; BOOL mOrphaned; // This is an orphaned child diff --git a/indra/newview/llvieweroctree.cpp b/indra/newview/llvieweroctree.cpp index 023f1b92ba..86591ff4df 100644 --- a/indra/newview/llvieweroctree.cpp +++ b/indra/newview/llvieweroctree.cpp @@ -465,7 +465,7 @@ LLViewerOctreeGroup::LLViewerOctreeGroup(OctreeNode* node) { LLVector4a tmp; tmp.splat(0.f); - mExtents[0] = mExtents[1] = mObjectBounds[0] = mObjectBounds[0] = mObjectBounds[1] = + mExtents[0] = mExtents[1] = mObjectBounds[0] = mObjectBounds[1] = mObjectExtents[0] = mObjectExtents[1] = tmp; mBounds[0] = node->getCenter(); diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index ed719ae418..178aa1e646 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1200,7 +1200,7 @@ void LLViewerFetchedTexture::loadFromFastCache() { S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENTIONS; S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENTIONS; - if (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height) + if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)) { // scale oversized icon, no need to give more work to gl mRawImage->scale(expected_width, expected_height); @@ -1981,7 +1981,7 @@ bool LLViewerFetchedTexture::updateFetch() { S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENTIONS; S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENTIONS; - if (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height) + if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)) { // scale oversized icon, no need to give more work to gl mRawImage->scale(expected_width, expected_height); diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp index 0f73515b5d..88eb13e7cd 100644 --- a/indra/newview/llviewerwearable.cpp +++ b/indra/newview/llviewerwearable.cpp @@ -451,7 +451,7 @@ void LLViewerWearable::copyDataFrom(const LLViewerWearable* src) // Probably reduntant, but ensure that the newly created wearable is not dirty by setting current value of params in new wearable // to be the same as the saved values (which were loaded from src at param->cloneParam(this)) - revertValues(); + revertValuesWithoutUpdate(); } void LLViewerWearable::setItemID(const LLUUID& item_id) @@ -471,6 +471,11 @@ void LLViewerWearable::revertValues() } } +void LLViewerWearable::revertValuesWithoutUpdate() +{ + LLWearable::revertValues(); +} + void LLViewerWearable::saveValues() { LLWearable::saveValues(); diff --git a/indra/newview/llviewerwearable.h b/indra/newview/llviewerwearable.h index 62cd5e21ad..cc99f6af2f 100644 --- a/indra/newview/llviewerwearable.h +++ b/indra/newview/llviewerwearable.h @@ -85,6 +85,8 @@ public: /*virtual*/ void revertValues(); /*virtual*/ void saveValues(); + void revertValuesWithoutUpdate(); + // Something happened that requires the wearable's label to be updated (e.g. worn/unworn). /*virtual*/void setUpdated() const; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index cd9ab3e672..4f4b33fa8c 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -251,6 +251,11 @@ BOOL gDisplayBadge = FALSE; static const U8 NO_FACE = 255; BOOL gQuietSnapshot = FALSE; +// Minimum value for UIScaleFactor, also defined in preferences, ui_scale_slider +static const F32 MIN_UI_SCALE = 0.75f; +// 2.0 in preferences, but win10 supports larger scaling and value is used more as +// sanity check, so leaving space for larger values from DPI updates. +static const F32 MAX_UI_SCALE = 7.0f; static const F32 MIN_DISPLAY_SCALE = 0.75f; std::string LLViewerWindow::sSnapshotBaseName; @@ -1593,6 +1598,20 @@ BOOL LLViewerWindow::handleDeviceChange(LLWindow *window) return FALSE; } +void LLViewerWindow::handleDPIChanged(LLWindow *window, F32 ui_scale_factor, S32 window_width, S32 window_height) +{ + if (ui_scale_factor >= MIN_UI_SCALE && ui_scale_factor <= MAX_UI_SCALE) + { + gSavedSettings.setF32("UIScaleFactor", ui_scale_factor); + LLViewerWindow::reshape(window_width, window_height); + mResDirty = true; + } + else + { + LL_WARNS() << "DPI change caused UI scale to go out of bounds: " << ui_scale_factor << LL_ENDL; + } +} + void LLViewerWindow::handlePingWatchdog(LLWindow *window, const char * msg) { LLAppViewer::instance()->pingMainloopTimeout(msg); @@ -1655,7 +1674,8 @@ LLViewerWindow::LLViewerWindow(const Params& p) mResDirty(false), mStatesDirty(false), mCurrResolutionIndex(0), - mProgressView(NULL) + mProgressView(NULL), + mSystemUIScaleFactorChanged(false) { // gKeyboard is still NULL, so it doesn't do LLWindowListener any good to // pass its value right now. Instead, pass it a nullary function that @@ -1743,9 +1763,24 @@ LLViewerWindow::LLViewerWindow(const Params& p) gSavedSettings.setS32("FullScreenHeight",scr.mY); } + + F32 system_scale_factor = mWindow->getSystemUISize(); + if (system_scale_factor < MIN_UI_SCALE || system_scale_factor > MAX_UI_SCALE) + { + // reset to default; + system_scale_factor = 1.f; + } + if (p.first_run || gSavedSettings.getF32("LastSystemUIScaleFactor") != system_scale_factor) + { + mSystemUIScaleFactorChanged = !p.first_run; + gSavedSettings.setF32("LastSystemUIScaleFactor", system_scale_factor); + gSavedSettings.setF32("UIScaleFactor", system_scale_factor); + } + + // Get the real window rect the window was created with (since there are various OS-dependent reasons why // the size of a window or fullscreen context may have been adjusted slightly...) - F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor"); + F32 ui_scale_factor = llclamp(gSavedSettings.getF32("UIScaleFactor"), MIN_UI_SCALE, MAX_UI_SCALE); mDisplayScale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f)); mDisplayScale *= ui_scale_factor; @@ -1838,6 +1873,28 @@ LLViewerWindow::LLViewerWindow(const Params& p) mWorldViewRectScaled = calcScaledRect(mWorldViewRectRaw, mDisplayScale); } +//static +void LLViewerWindow::showSystemUIScaleFactorChanged() +{ + LLNotificationsUtil::add("SystemUIScaleFactorChanged", LLSD(), LLSD(), onSystemUIScaleFactorChanged); +} + +//static +bool LLViewerWindow::onSystemUIScaleFactorChanged(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if(option == 0) + { + LLFloaterReg::toggleInstanceOrBringToFront("preferences"); + LLFloater* pref_floater = LLFloaterReg::getInstance("preferences"); + LLTabContainer* tab_container = pref_floater->getChild<LLTabContainer>("pref core"); + tab_container->selectTabByName("advanced1"); + + } + return false; +} + + void LLViewerWindow::initGLDefaults() { gGL.setSceneBlendType(LLRender::BT_ALPHA); @@ -5211,7 +5268,7 @@ F32 LLViewerWindow::getWorldViewAspectRatio() const void LLViewerWindow::calcDisplayScale() { - F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor"); + F32 ui_scale_factor = llclamp(gSavedSettings.getF32("UIScaleFactor"), MIN_UI_SCALE, MAX_UI_SCALE); LLVector2 display_scale; display_scale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f)); display_scale *= ui_scale_factor; @@ -5224,7 +5281,7 @@ void LLViewerWindow::calcDisplayScale() if (display_scale != mDisplayScale) { - LL_INFOS() << "Setting display scale to " << display_scale << LL_ENDL; + LL_INFOS() << "Setting display scale to " << display_scale << " for ui scale: " << ui_scale_factor << LL_ENDL; mDisplayScale = display_scale; // Init default fonts diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index cdf5b686a7..72b7370621 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -156,7 +156,8 @@ public: min_width, min_height; Optional<bool> fullscreen, - ignore_pixel_depth; + ignore_pixel_depth, + first_run; Params(); }; @@ -210,6 +211,7 @@ public: /*virtual*/ void handleDataCopy(LLWindow *window, S32 data_type, void *data); /*virtual*/ BOOL handleTimerEvent(LLWindow *window); /*virtual*/ BOOL handleDeviceChange(LLWindow *window); + /*virtual*/ void handleDPIChanged(LLWindow *window, F32 ui_scale_factor, S32 window_width, S32 window_height); /*virtual*/ void handlePingWatchdog(LLWindow *window, const char * msg); /*virtual*/ void handlePauseWatchdog(LLWindow *window); @@ -415,6 +417,9 @@ public: void calcDisplayScale(); static LLRect calcScaledRect(const LLRect & rect, const LLVector2& display_scale); + bool getSystemUIScaleFactorChanged() { return mSystemUIScaleFactorChanged; } + static void showSystemUIScaleFactorChanged(); + private: bool shouldShowToolTipFor(LLMouseHandler *mh); @@ -428,6 +433,7 @@ private: S32 getChatConsoleBottomPad(); // Vertical padding for child console rect, varied by bottom clutter LLRect getChatConsoleRect(); // Get optimal cosole rect. + static bool onSystemUIScaleFactorChanged(const LLSD& notification, const LLSD& response); private: LLWindow* mWindow; // graphical window object bool mActive; @@ -506,6 +512,7 @@ private: LLPointer<LLViewerObject> mDragHoveredObject; static LLTrace::SampleStatHandle<> sMouseVelocityStat; + bool mSystemUIScaleFactorChanged; // system UI scale factor changed from last run }; // diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index cdc7e20c2c..fe8f54b900 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -186,6 +186,7 @@ const F32 NAMETAG_VERTICAL_SCREEN_OFFSET = 25.f; const F32 NAMETAG_VERT_OFFSET_WEIGHT = 0.17f; const U32 LLVOAvatar::VISUAL_COMPLEXITY_UNKNOWN = 0; +const F64 HUD_OVERSIZED_TEXTURE_DATA_SIZE = 1024 * 1024; enum ERenderName { @@ -7391,16 +7392,13 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) // No backsies zone - if we get here, the message should be valid and usable, will be processed. LL_INFOS("Avatar") << "Processing appearance message version " << thisAppearanceVersion << LL_ENDL; - if (isSelf()) - { - // Note: - // locally the COF is maintained via LLInventoryModel::accountForUpdate - // which is called from various places. This should match the simhost's - // idea of what the COF version is. AIS however maintains its own version - // of the COF that should be considered canonical. - mLastUpdateReceivedCOFVersion = thisAppearanceVersion; - } - + // Note: + // locally the COF is maintained via LLInventoryModel::accountForUpdate + // which is called from various places. This should match the simhost's + // idea of what the COF version is. AIS however maintains its own version + // of the COF that should be considered canonical. + mLastUpdateReceivedCOFVersion = thisAppearanceVersion; + if (applyParsedTEMessage(contents.mTEContents) > 0 && isChanged(TEXTURE)) { updateVisualComplexity(); @@ -8356,6 +8354,7 @@ void LLVOAvatar::calculateUpdateRenderComplexity() { U32 cost = VISUAL_COMPLEXITY_UNKNOWN; LLVOVolume::texture_cost_t textures; + hud_complexity_list_t hud_complexity_list; for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++) { @@ -8432,6 +8431,62 @@ void LLVOAvatar::calculateUpdateRenderComplexity() } } } + if (isSelf() + && attached_object + && attached_object->isHUDAttachment() + && attached_object->mDrawable) + { + textures.clear(); + + const LLVOVolume* volume = attached_object->mDrawable->getVOVolume(); + if (volume) + { + LLHUDComplexity hud_object_complexity; + hud_object_complexity.objectName = attached_object->getAttachmentItemName(); + hud_object_complexity.objectId = attached_object->getAttachmentItemID(); + std::string joint_name; + gAgentAvatarp->getAttachedPointName(attached_object->getAttachmentItemID(), joint_name); + hud_object_complexity.jointName = joint_name; + // get cost and individual textures + hud_object_complexity.objectsCost += volume->getRenderCost(textures); + hud_object_complexity.objectsCount++; + + LLViewerObject::const_child_list_t& child_list = attached_object->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); ++iter) + { + LLViewerObject* childp = *iter; + const LLVOVolume* chld_volume = dynamic_cast<LLVOVolume*>(childp); + if (chld_volume) + { + // get cost and individual textures + hud_object_complexity.objectsCost += chld_volume->getRenderCost(textures); + hud_object_complexity.objectsCount++; + } + } + + hud_object_complexity.texturesCount += textures.size(); + + for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin(); + volume_texture != textures.end(); + ++volume_texture) + { + // add the cost of each individual texture (ignores duplicates) + hud_object_complexity.texturesCost += volume_texture->second; + LLViewerFetchedTexture *tex = LLViewerTextureManager::getFetchedTexture(volume_texture->first); + if (tex) + { + // Note: Texture memory might be incorect since texture might be still loading. + hud_object_complexity.texturesMemoryTotal += tex->getTextureMemory(); + if (tex->getOriginalHeight() * tex->getOriginalWidth() >= HUD_OVERSIZED_TEXTURE_DATA_SIZE) + { + hud_object_complexity.largeTexturesCount++; + } + } + } + hud_complexity_list.push_back(hud_object_complexity); + } + } } } @@ -8493,11 +8548,15 @@ void LLVOAvatar::calculateUpdateRenderComplexity() static LLCachedControl<U32> show_my_complexity_changes(gSavedSettings, "ShowMyComplexityChanges", 20); - if (isSelf() && show_my_complexity_changes) - { - LLAvatarRenderNotifier::getInstance()->updateNotificationAgent(mVisualComplexity); - } - } + if (isSelf() && show_my_complexity_changes) + { + // Avatar complexity + LLAvatarRenderNotifier::getInstance()->updateNotificationAgent(mVisualComplexity); + + // HUD complexity + LLHUDRenderNotifier::getInstance()->updateNotificationHUD(hud_complexity_list); + } + } } void LLVOAvatar::setVisualMuteSettings(VisualMuteSettings set) diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index e69a8d1d1d..4aef6480cb 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -86,6 +86,7 @@ BOOL gAnimateTextures = TRUE; //extern BOOL gHideSelectedObjects; F32 LLVOVolume::sLODFactor = 1.f; +F32 LLVOVolume::sRiggedFactorMultiplier = 6.f; F32 LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop F32 LLVOVolume::sDistanceFactor = 1.0f; S32 LLVOVolume::sNumLODChanges = 0; @@ -1213,18 +1214,18 @@ void LLVOVolume::sculpt() } } -S32 LLVOVolume::computeLODDetail(F32 distance, F32 radius) +S32 LLVOVolume::computeLODDetail(F32 distance, F32 radius, F32 lod_factor) { S32 cur_detail; if (LLPipeline::sDynamicLOD) { // We've got LOD in the profile, and in the twist. Use radius. - F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance; + F32 tan_angle = (lod_factor*radius) / distance; cur_detail = LLVolumeLODGroup::getDetailFromTan(ll_round(tan_angle, 0.01f)); } else { - cur_detail = llclamp((S32) (sqrtf(radius)*LLVOVolume::sLODFactor*4.f), 0, 3); + cur_detail = llclamp((S32)(sqrtf(radius)*lod_factor*4.f), 0, 3); } return cur_detail; } @@ -1240,6 +1241,7 @@ BOOL LLVOVolume::calcLOD() F32 radius; F32 distance; + F32 lod_factor = LLVOVolume::sLODFactor; if (mDrawable->isState(LLDrawable::RIGGED)) { @@ -1251,22 +1253,38 @@ BOOL LLVOVolume::calcLOD() return FALSE; } + // Note: when changing, take note that a lot of rigged meshes have only one LOD. + lod_factor *= LLVOVolume::sRiggedFactorMultiplier; distance = avatar->mDrawable->mDistanceWRTCamera; - radius = avatar->getBinRadius(); + F32 avatar_radius = avatar->getBinRadius(); + F32 object_radius; + if (mDrawable.notNull() && !mDrawable->isDead()) + { + const LLVector4a* ext = mDrawable->getSpatialExtents(); + LLVector4a diff; + diff.setSub(ext[1], ext[0]); + object_radius = diff.getLength3().getF32(); + } + else + { + object_radius = getVolume() ? getVolume()->mLODScaleBias.scaledVec(getScale()).length() : getScale().length(); + } + radius = object_radius * LLVOVolume::sRiggedFactorMultiplier; + radius = llmin(radius, avatar_radius); } else { distance = mDrawable->mDistanceWRTCamera; radius = getVolume() ? getVolume()->mLODScaleBias.scaledVec(getScale()).length() : getScale().length(); } - + //hold onto unmodified distance for debugging //F32 debug_distance = distance; - + distance *= sDistanceFactor; - F32 rampDist = LLVOVolume::sLODFactor * 2; - + F32 rampDist = lod_factor * 2; + if (distance < rampDist) { // Boost LOD when you're REALLY close @@ -1279,7 +1297,8 @@ BOOL LLVOVolume::calcLOD() distance *= F_PI/3.f; cur_detail = computeLODDetail(ll_round(distance, 0.01f), - ll_round(radius, 0.01f)); + ll_round(radius, 0.01f), + lod_factor); if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LOD_INFO) && @@ -1753,7 +1772,10 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) dirtySpatialGroup(drawable->isState(LLDrawable::IN_REBUILD_Q1)); compiled = TRUE; lodOrSculptChanged(drawable, compiled); - genBBoxes(FALSE); + if (!mLODChanged) + { + genBBoxes(FALSE); + } } // it has its own drawable (it's moved) or it has changed UVs or it has changed xforms from global<->local else @@ -4429,7 +4451,7 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, LL_RECORD_BLOCK_TIME(FTM_REGISTER_FACE); if (type == LLRenderPass::PASS_ALPHA && facep->getTextureEntry()->getMaterialParams().notNull() && !facep->getVertexBuffer()->hasDataType(LLVertexBuffer::TYPE_TANGENT)) { - LL_WARNS("RenderMaterials") << "Oh no! No binormals for this alpha blended face!" << LL_ENDL; + LL_WARNS_ONCE("RenderMaterials") << "Oh no! No binormals for this alpha blended face!" << LL_ENDL; } if (facep->getViewerObject()->isSelected() && LLSelectMgr::getInstance()->mHideSelectedObjects) diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index a331908320..ca9917069b 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -327,7 +327,7 @@ public: void clearRiggedVolume(); protected: - S32 computeLODDetail(F32 distance, F32 radius); + S32 computeLODDetail(F32 distance, F32 radius, F32 lod_factor); BOOL calcLOD(); LLFace* addFace(S32 face_index); void updateTEData(); @@ -379,6 +379,7 @@ private: public: static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop static F32 sLODFactor; // LOD scale factor + static F32 sRiggedFactorMultiplier; // Worn rigged LOD scale factor multiplier static F32 sDistanceFactor; // LOD distance factor static LLPointer<LLObjectMediaDataClient> sObjectMediaClient; diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp index b37e41fb85..8026dc3ea8 100644 --- a/indra/newview/llweb.cpp +++ b/indra/newview/llweb.cpp @@ -104,10 +104,10 @@ void LLWeb::loadURL(const std::string& url, const std::string& target, const std // static // Explicitly open a Web URL using the Web content floater -void LLWeb::loadURLInternal(const std::string &url, const std::string& target, const std::string& uuid) +void LLWeb::loadURLInternal(const std::string &url, const std::string& target, const std::string& uuid, bool dev_mode) { LLFloaterWebContent::Params p; - p.url(url).target(target).id(uuid); + p.url(url).target(target).id(uuid).dev_mode(dev_mode); LLFloaterReg::showInstance("web_content", p); } diff --git a/indra/newview/llweb.h b/indra/newview/llweb.h index 7c90badbfe..7149ce9baf 100644 --- a/indra/newview/llweb.h +++ b/indra/newview/llweb.h @@ -57,7 +57,7 @@ public: static void loadURL(const std::string& url, const std::string& target = LLStringUtil::null, const std::string& uuid = LLStringUtil::null); // load content using built-in browser - static void loadURLInternal(const std::string &url, const std::string& target = LLStringUtil::null, const std::string& uuid = LLStringUtil::null); + static void loadURLInternal(const std::string &url, const std::string& target = LLStringUtil::null, const std::string& uuid = LLStringUtil::null, bool dev_mode = false); /// Returns escaped url (eg, " " to "%20") - used by all loadURL methods static std::string escapeURL(const std::string& url); diff --git a/indra/newview/llwlparamset.cpp b/indra/newview/llwlparamset.cpp index 066cb9a0ac..482a2a61e2 100644 --- a/indra/newview/llwlparamset.cpp +++ b/indra/newview/llwlparamset.cpp @@ -288,14 +288,6 @@ void LLWLParamSet::mix(LLWLParamSet& src, LLWLParamSet& dest, F32 weight) { // set up the iterators - // keep cloud positions and coverage the same - /// TODO masking will do this later - F32 cloudPos1X = (F32) mParamValues["cloud_pos_density1"][0].asReal(); - F32 cloudPos1Y = (F32) mParamValues["cloud_pos_density1"][1].asReal(); - F32 cloudPos2X = (F32) mParamValues["cloud_pos_density2"][0].asReal(); - F32 cloudPos2Y = (F32) mParamValues["cloud_pos_density2"][1].asReal(); - F32 cloudCover = (F32) mParamValues["cloud_shadow"][0].asReal(); - LLSD srcVal; LLSD destVal; @@ -379,15 +371,6 @@ void LLWLParamSet::mix(LLWLParamSet& src, LLWLParamSet& dest, F32 weight) setSunAngle((1 - weight) * srcSunAngle + weight * destSunAngle); setEastAngle((1 - weight) * srcEastAngle + weight * destEastAngle); - - // now setup the sun properly - - // reset those cloud positions - mParamValues["cloud_pos_density1"][0] = cloudPos1X; - mParamValues["cloud_pos_density1"][1] = cloudPos1Y; - mParamValues["cloud_pos_density2"][0] = cloudPos2X; - mParamValues["cloud_pos_density2"][1] = cloudPos2Y; - mParamValues["cloud_shadow"][0] = cloudCover; } void LLWLParamSet::updateCloudScrolling(void) diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 11d3706821..cee47a591e 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -957,10 +957,10 @@ void LLWorld::updateWaterObjects() center_y = min_y + (wy >> 1); S32 add_boundary[4] = { - 512 - (max_x - region_x), - 512 - (max_y - region_y), - 512 - (region_x - min_x), - 512 - (region_y - min_y) }; + (S32)(512 - (max_x - region_x)), + (S32)(512 - (max_y - region_y)), + (S32)(512 - (region_x - min_x)), + (S32)(512 - (region_y - min_y)) }; S32 dir; for (dir = 0; dir < 8; dir++) diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp index 97a9eb7f5f..cc3645131d 100644 --- a/indra/newview/llxmlrpclistener.cpp +++ b/indra/newview/llxmlrpclistener.cpp @@ -322,7 +322,7 @@ public: mBoundListener = LLEventPumps::instance(). obtain("mainloop"). - listen(LLEventPump::inventName(), boost::bind(&Poller::poll, this, _1)); + listen(LLEventPump::ANONYMOUS, boost::bind(&Poller::poll, this, _1)); LL_INFOS("LLXMLRPCListener") << mMethod << " request sent to " << mUri << LL_ENDL; } diff --git a/indra/newview/skins/default/textures/icons/Video_URL_Off.png b/indra/newview/skins/default/textures/icons/Video_URL_Off.png Binary files differnew file mode 100644 index 0000000000..40e5df7d81 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Video_URL_Off.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index a9a4913b21..f2da22256c 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -676,7 +676,10 @@ with the same filename but different name <texture name="Unread_Chiclet" file_name="bottomtray/Unread_Chiclet.png" preload="false" /> <texture name="UpArrow_Off" file_name="icons/UpArrow_Off.png" preload="false" /> - <texture name="Vertical Drag Handle" file_name="widgets/vertical_drag_handle.png" scale.left="2" scale.right="7" scale.bottom="8" scale.top="120" scale_type="scale_outer"/> + + <texture name="Video_URL_Off" file_name="icons/Video_URL_Off.png" preload="true" /> + + <texture name="Vertical Drag Handle" file_name="widgets/vertical_drag_handle.png" scale.left="2" scale.right="7" scale.bottom="8" scale.top="120" scale_type="scale_outer"/> <texture name="Volume_Background" file_name="windows/Volume_Background.png" preload="false" scale.left="6" scale.top="33" scale.right="63" scale.bottom="10" /> @@ -774,6 +777,9 @@ with the same filename but different name <texture name="default_land_picture.j2c" /> <texture name="default_profile_picture.j2c" /> <texture name="locked_image.j2c" /> + <texture name="badge_note.j2c" /> + <texture name="badge_warn.j2c" /> + <texture name="badge_ok.j2c" /> <texture name="materials_ui_x_24.png" /> <texture name="Progress_1" file_name="icons/Progress_1.png" preload="true" /> diff --git a/indra/newview/skins/default/xui/de/panel_status_bar.xml b/indra/newview/skins/default/xui/de/panel_status_bar.xml index d3d85de3c3..b0cb1e0592 100644 --- a/indra/newview/skins/default/xui/de/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/de/panel_status_bar.xml @@ -5,7 +5,7 @@ <panel.string name="time">[hour12, datetime, slt]:[min, datetime, slt] [ampm, datetime, slt] [timezone,datetime, slt]</panel.string> <panel.string name="timeTooltip">[weekday, datetime, slt], [day, datetime, slt] [month, datetime, slt] [year, datetime, slt]</panel.string> <panel.string name="buycurrencylabel">[AMT] L$</panel.string> - <panel left="-415" name="balance_bg" width="205"> + <panel left="-436" name="balance_bg" width="205"> <text name="balance" tool_tip="Klicken, um L$-Guthaben zu aktualisieren" value="L$ ??"/> <button label="L$ kaufen" name="buyL" tool_tip="Hier klicken, um mehr L$ zu kaufen"/> <button label="Einkaufen" name="goShop" tool_tip="Second Life-Marktplatz öffnen" width="85"/> diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml index 2bd3aa8bcc..c6b91a8b2f 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml @@ -117,7 +117,7 @@ <slider control_name="IndirectMaxComplexity" - tool_tip="Controls at what point a visually complex avatar is drawn as a jelly doll" + tool_tip="Controls at what point a visually complex avatar is drawn as a JellyDoll" follows="left|top" height="16" initial_value="101" diff --git a/indra/newview/skins/default/xui/en/floater_report_abuse.xml b/indra/newview/skins/default/xui/en/floater_report_abuse.xml index af62c7a9bc..225266af86 100644 --- a/indra/newview/skins/default/xui/en/floater_report_abuse.xml +++ b/indra/newview/skins/default/xui/en/floater_report_abuse.xml @@ -21,14 +21,6 @@ name="screenshot" top="15" width="220" /> - <check_box - height="15" - label="Use this screenshot" - layout="topleft" - left="8" - name="screen_check" - top_pad="-12" - width="116" /> <text type="string" length="1" @@ -39,7 +31,7 @@ layout="topleft" left="10" name="reporter_title" - top_pad="0" + top_pad="-2" width="100"> Reporter: </text> diff --git a/indra/newview/skins/default/xui/en/floater_web_content.xml b/indra/newview/skins/default/xui/en/floater_web_content.xml index a80440e844..4473ce0cda 100644 --- a/indra/newview/skins/default/xui/en/floater_web_content.xml +++ b/indra/newview/skins/default/xui/en/floater_web_content.xml @@ -154,6 +154,136 @@ </button> </layout_panel> <layout_panel + height="22" + layout="topleft" + left_delta="0" + name="debug_controls" + top_delta="0" + auto_resize="false" + width="585"> + <button + image_overlay="Home_Off" + image_disabled="PushButton_Disabled" + image_disabled_selected="PushButton_Disabled" + image_selected="PushButton_Selected" + image_unselected="PushButton_Off" + chrome="true" + tool_tip="Web tests home page" + enabled="true" + follows="left|top" + height="22" + layout="topleft" + left="1" + name="web_test_home_page" + top="0" + width="22"> + <button.commit_callback + function="WebContent.TestURL" + parameter="https://callum-linden.s3.amazonaws.com/ceftests.html"/> + </button> + + <button + image_overlay="Video_URL_Off" + image_disabled="PushButton_Disabled" + image_disabled_selected="PushButton_Disabled" + image_selected="PushButton_Selected" + image_unselected="PushButton_Off" + chrome="true" + tool_tip="MPEG4 Video Test" + enabled="true" + follows="left|top" + height="22" + layout="topleft" + left="27" + name="VLC Plugin Test" + top="0" + width="22"> + <button.commit_callback + function="WebContent.TestURL" + parameter="https://callum-linden.s3.amazonaws.com/sample_media/ss.mp4"/> + </button> + <button + image_overlay="Video_URL_Off" + image_disabled="PushButton_Disabled" + image_disabled_selected="PushButton_Disabled" + image_selected="PushButton_Selected" + image_unselected="PushButton_Off" + chrome="true" + tool_tip="MKV Video Test" + enabled="true" + follows="left|top" + height="22" + layout="topleft" + left="51" + name="VLC Plugin Test" + top="0" + width="22"> + <button.commit_callback + function="WebContent.TestURL" + parameter="https://callum-linden.s3.amazonaws.com/sample_media/jellyfish.mkv"/> + </button> + <button + image_overlay="Video_URL_Off" + image_disabled="PushButton_Disabled" + image_disabled_selected="PushButton_Disabled" + image_selected="PushButton_Selected" + image_unselected="PushButton_Off" + chrome="true" + tool_tip="WebM Video Test" + enabled="true" + follows="left|top" + height="22" + layout="topleft" + left="75" + name="VLC Plugin Test" + top="0" + width="22"> + <button.commit_callback + function="WebContent.TestURL" + parameter="https://callum-linden.s3.amazonaws.com/sample_media/jumprope.webm"/> + </button> + <button + image_overlay="Video_URL_Off" + image_disabled="PushButton_Disabled" + image_disabled_selected="PushButton_Disabled" + image_selected="PushButton_Selected" + image_unselected="PushButton_Off" + chrome="true" + tool_tip="MP3 audio Test" + enabled="true" + follows="left|top" + height="22" + layout="topleft" + left="99" + name="VLC Plugin Test" + top="0" + width="22"> + <button.commit_callback + function="WebContent.TestURL" + parameter="https://callum-linden.s3.amazonaws.com/alegria.mp3"/> + </button> + <button + image_overlay="Video_URL_Off" + image_disabled="PushButton_Disabled" + image_disabled_selected="PushButton_Disabled" + image_selected="PushButton_Selected" + image_unselected="PushButton_Off" + chrome="true" + tool_tip="FLV Test" + enabled="true" + follows="left|top" + height="22" + layout="topleft" + left="123" + name="VLC Plugin Test" + top="0" + width="22"> + <button.commit_callback + function="WebContent.TestURL" + parameter="https://callum-linden.s3.amazonaws.com/sample_media/vandal.flv"/> + </button> + </layout_panel> + <layout_panel height="40" layout="topleft" left_delta="0" diff --git a/indra/newview/skins/default/xui/en/fonts.xml b/indra/newview/skins/default/xui/en/fonts.xml index 5d05ecf127..550af03683 100644 --- a/indra/newview/skins/default/xui/en/fonts.xml +++ b/indra/newview/skins/default/xui/en/fonts.xml @@ -10,6 +10,7 @@ <file>ArialUni.ttf</file> </os> <os name="Mac"> + <file>ヒラギノ角ゴシック W3.ttc</file> <file>ヒラギノ角ゴ Pro W3.otf</file> <file>ヒラギノ角ゴ ProN W3.otf</file> <file>ヒラギノ明朝 ProN W3.ttc</file> diff --git a/indra/newview/skins/default/xui/en/inspect_group.xml b/indra/newview/skins/default/xui/en/inspect_group.xml index 324ff3eabd..a69585074c 100644 --- a/indra/newview/skins/default/xui/en/inspect_group.xml +++ b/indra/newview/skins/default/xui/en/inspect_group.xml @@ -66,7 +66,7 @@ Fear the moose! Fear it! And the mongoose too! width="220"> L$123 to join </text> - <icon + <group_icon follows="all" height="38" right="-10" diff --git a/indra/newview/skins/default/xui/en/menu_login.xml b/indra/newview/skins/default/xui/en/menu_login.xml index dcf2da52f1..a39ee5fddd 100644 --- a/indra/newview/skins/default/xui/en/menu_login.xml +++ b/indra/newview/skins/default/xui/en/menu_login.xml @@ -225,11 +225,11 @@ parameter="message_critical" /> </menu_item_call> <menu_item_call - label="Web Content Floater Debug Test" - name="Web Content Floater Debug Test"> + label="Media Browser" + name="Media Browser"> <menu_item_call.on_click function="Advanced.WebContentTest" - parameter="https://callum-linden.s3.amazonaws.com/ceftests.html"/> + parameter="http://google.com"/> </menu_item_call> <menu create_jump_keys="true" diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index b189d1038f..8a649a57d1 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3141,30 +3141,13 @@ label="UI" name="UI" tear_off="true"> - <!-- <menu_item_check - label="New Bottom Bar" - name="New Bottom Bar"> - <menu_item_check.on_check - function="CheckControl" - parameter="BottomPanelNew" /> - <menu_item_check.on_click - function="ToggleControl" - parameter="BottomPanelNew" /> - </menu_item_check>--> - <menu_item_call - label="Media Browser Test" - name="Web Browser Test"> - <menu_item_call.on_click - function="Advanced.WebBrowserTest" - parameter="http://secondlife.com/app/search/slurls.html"/> - </menu_item_call> <menu_item_call - label="Web Content Browser" - name="Web Content Browser" - shortcut="control|shift|Z"> + label="Media Browser" + name="Media Browser" + shortcut="control|alt|shift|Z"> <menu_item_call.on_click function="Advanced.WebContentTest" - parameter="https://callum-linden.s3.amazonaws.com/ceftests.html"/> + parameter="http://google.com"/> </menu_item_call> <menu_item_call label="FB Connect Test" diff --git a/indra/newview/skins/default/xui/en/menu_wearing_tab.xml b/indra/newview/skins/default/xui/en/menu_wearing_tab.xml index 44b2727671..75c1de24aa 100644 --- a/indra/newview/skins/default/xui/en/menu_wearing_tab.xml +++ b/indra/newview/skins/default/xui/en/menu_wearing_tab.xml @@ -28,6 +28,13 @@ function="Wearing.Edit" /> </menu_item_call> <menu_item_call + label="Edit" + layout="topleft" + name="edit_item"> + <on_click + function="Wearing.EditItem" /> + </menu_item_call> + <menu_item_call label="Show Original" layout="topleft" name="show_original"> diff --git a/indra/newview/skins/default/xui/en/mime_types.xml b/indra/newview/skins/default/xui/en/mime_types.xml index 7cb4a6e53b..c27fac6731 100644 --- a/indra/newview/skins/default/xui/en/mime_types.xml +++ b/indra/newview/skins/default/xui/en/mime_types.xml @@ -130,10 +130,21 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_cef </impl> </scheme> - <mimetype name="blank"> + <scheme name="libvlc"> + <label name="libvlc_label"> + LibVLC supported media + </label> + <widgettype> + movie + </widgettype> + <impl> + media_plugin_libvlc + </impl> + </scheme> + <mimetype name="blank"> <label name="blank_label"> - None - </label> @@ -163,7 +174,7 @@ audio </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/*"> @@ -174,7 +185,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="image/*"> @@ -196,7 +207,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="application/javascript"> @@ -218,7 +229,7 @@ audio </widgettype> <impl> - media_plugin_quicktime + media_plugin_cef </impl> </mimetype> <mimetype name="application/pdf"> @@ -295,7 +306,7 @@ audio </widgettype> <impl> - media_plugin_quicktime + media_plugin_cef </impl> </mimetype> <mimetype name="audio/mpeg"> @@ -306,7 +317,7 @@ audio </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="audio/x-aiff"> @@ -317,7 +328,7 @@ audio </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="audio/x-wav"> @@ -328,7 +339,7 @@ audio </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype menu="1" name="image/bmp"> @@ -438,7 +449,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/mp4"> @@ -449,10 +460,21 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> - <mimetype menu="1" name="video/quicktime"> + <mimetype name="application/octet-stream"> + <label name="video/octet-stream"> + Movie + </label> + <widgettype> + movie + </widgettype> + <impl> + media_plugin_libvlc + </impl> + </mimetype> + <mimetype menu="1" name="video/quicktime"> <label name="video/quicktime_label"> Movie (QuickTime) </label> @@ -460,7 +482,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/x-ms-asf"> @@ -471,7 +493,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/x-ms-wmv"> @@ -482,7 +504,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_cef </impl> </mimetype> <mimetype menu="1" name="video/x-msvideo"> @@ -493,7 +515,7 @@ movie </widgettype> <impl> - media_plugin_quicktime + media_plugin_cef </impl> </mimetype> </mimetypes> diff --git a/indra/newview/skins/default/xui/en/mime_types_linux.xml b/indra/newview/skins/default/xui/en/mime_types_linux.xml index 84aeaf3b54..7188b1e699 100644 --- a/indra/newview/skins/default/xui/en/mime_types_linux.xml +++ b/indra/newview/skins/default/xui/en/mime_types_linux.xml @@ -130,7 +130,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </scheme> <mimetype name="blank"> @@ -163,7 +163,7 @@ audio </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/*"> @@ -174,7 +174,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="image/*"> @@ -196,7 +196,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="application/javascript"> @@ -218,7 +218,7 @@ audio </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="application/pdf"> @@ -295,7 +295,7 @@ audio </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="audio/mpeg"> @@ -306,7 +306,7 @@ audio </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="audio/x-aiff"> @@ -317,7 +317,7 @@ audio </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="audio/x-wav"> @@ -328,7 +328,7 @@ audio </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype menu="1" name="image/bmp"> @@ -438,7 +438,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/mp4"> @@ -449,7 +449,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype menu="1" name="video/quicktime"> @@ -460,7 +460,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/x-ms-asf"> @@ -471,7 +471,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype name="video/x-ms-wmv"> @@ -482,7 +482,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> <mimetype menu="1" name="video/x-msvideo"> @@ -493,7 +493,7 @@ movie </widgettype> <impl> - media_plugin_gstreamer + media_plugin_libvlc </impl> </mimetype> </mimetypes> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index b2425649a4..79ee35fe3e 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -832,6 +832,13 @@ If you no longer wish to have these abilities granted to this role, disable them </notification> <notification + icon="notify.tga" + name="GroupBanUserOnBanlist" + type="notify"> +Some residents have not been sent an invite due to being banned from the group. + </notification> + + <notification icon="alertmodal.tga" name="AttachmentDrop" type="alertmodal"> @@ -1476,6 +1483,7 @@ Save Changes? icon="alertmodal.tga" name="DeleteNotecard" type="alertmodal"> + <unique/> Delete Notecard? <tag>confirm</tag> <usetemplate @@ -1631,52 +1639,14 @@ Visit [_URL] for more information? <notification icon="alertmodal.tga" - name="IntelOldDriver" + name="OldGPUDriver" type="alertmodal"> There is likely a newer driver for your graphics chip. Updating graphics drivers can substantially improve performance. - Visit [_URL] to check for driver updates? - <tag>confirm</tag> - <url option="0" name="url"> - http://www.intel.com/p/en_US/support/detect/graphics - </url> - <usetemplate - ignoretext="My graphics driver is out of date" - name="okcancelignore" - notext="No" - yestext="Yes"/> - <tag>fail</tag> - </notification> - - <notification - icon="alertmodal.tga" - name="AMDOldDriver" - type="alertmodal"> - There is likely a newer driver for your graphics chip. Updating graphics drivers can substantially improve performance. - - Visit [_URL] to check for driver updates? - <tag>confirm</tag> - <url option="0" name="url"> - http://support.amd.com/us/Pages/AMDSupportHub.aspx - </url> - <usetemplate - ignoretext="My graphics driver is out of date" - name="okcancelignore" - notext="No" - yestext="Yes"/> - <tag>fail</tag> - </notification> - - <notification - icon="alertmodal.tga" - name="NVIDIAOldDriver" - type="alertmodal"> - There is likely a newer driver for your graphics chip. Updating graphics drivers can substantially improve performance. - - Visit [_URL] to check for driver updates? + Visit [URL] to check for driver updates? <tag>confirm</tag> <url option="0" name="url"> - http://www.nvidia.com/Download/index.aspx?lang=en-us + [URL] </url> <usetemplate ignoretext="My graphics driver is out of date" @@ -1686,7 +1656,6 @@ Visit [_URL] for more information? <tag>fail</tag> </notification> - <notification icon="alertmodal.tga" name="UnknownGPU" @@ -3349,6 +3318,23 @@ Your [https://community.secondlife.com/t5/English-Knowledge-Base/Avatar-Renderin <context>AgentComplexityNotice</context> </unique> Your [https://community.secondlife.com/t5/English-Knowledge-Base/Avatar-Rendering-Complexity/ta-p/2967838 avatar complexity] is [AGENT_COMPLEXITY]. + <usetemplate + ignoretext="Warn me about my avatar complexity changes" + name="notifyignore"/> + </notification> + + <notification + icon = "notifytip.tga" + name = "HUDComplexityWarning" + type = "notifytip" + log_to_chat = "false"> + <unique combine = "cancel_old"> + <context>HUDComplexityWarning</context> + </unique> + [HUD_REASON], it is likely to negatively affect your performance. + <usetemplate + ignoretext="Warn me when my HUD complexity is too high" + name="notifyignore"/> </notification> <notification @@ -3665,6 +3651,13 @@ Can't add estate owner to estate 'Banned Resident' list. <notification icon="alertmodal.tga" + name="ProblemAddingEstateManagerBanned" + type="alertmodal"> +Unable to add banned resident to estate manager list. + </notification> + + <notification + icon="alertmodal.tga" name="CanNotChangeAppearanceUntilLoaded" type="alertmodal"> Can't change appearance until clothing and shape are loaded. @@ -4059,6 +4052,18 @@ Do you want to open your Web browser to view this content? <notification icon="alertmodal.tga" + name="SystemUIScaleFactorChanged" + type="alertmodal"> +System UI size factor has changed since last run. Do you want to open UI size adjustment settings page? + <tag>confirm</tag> + <usetemplate + name="okcancelbuttons" + notext="Cancel" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" name="WebLaunchJoinNow" type="alertmodal"> Go to your [http://secondlife.com/account/ Dashboard] to manage your account? @@ -4178,6 +4183,14 @@ Leave Group? </notification> <notification + icon="notify.tga" + name="GroupDepart" + type="notify"> +You have left the group '[group_name]'. + <tag>group</tag> + </notification> + + <notification icon="alertmodal.tga" name="OwnerCannotLeaveGroup" type="alertmodal"> @@ -4189,6 +4202,17 @@ Leave Group? </notification> <notification + icon="alertmodal.tga" + name="GroupDepartError" + type="alertmodal"> + Unable to leave group. + <tag>group</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification icon="alert.tga" name="ConfirmKick" type="alert"> @@ -5310,6 +5334,17 @@ There are too many prims selected. Please select [MAX_PRIM_COUNT] or fewer prim </notification> <notification + icon="alertmodal.tga" + name="TooManyScriptsSelected" + type="alertmodal"> +Too many scripts in the objects selected. Please select fewer objects and try again + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification icon="alertmodal.tga" name="ProblemImportingEstateCovenant" type="alertmodal"> @@ -5728,6 +5763,17 @@ Warning: The 'Pay object' click action has been set, but it will only <notification icon="alertmodal.tga" + name="PaymentBlockedButtonMismatch" + type="alertmodal"> + Payment stopped: the price paid does not match any of the pay buttons set for this object. + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" name="OpenObjectCannotCopy" type="alertmodal"> There are no items in this object that you are allowed to copy. @@ -5830,7 +5876,20 @@ You cannot undo this action. notext="Cancel" yestext="Unlink"/> </notification> - + + <notification + icon="alertmodal.tga" + name="HelpReportAbuseConfirm" + type="alertmodal"> + <unique/> +Thank you for taking the time to inform us of this issue. +We will review your report for possible violations and take +the appropriate action. + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + <notification icon="alertmodal.tga" name="HelpReportAbuseSelectCategory" @@ -6901,6 +6960,15 @@ This area has building disabled. You can't build or rez objects here. </notification> <notification + icon="notify.tga" + name="AutopilotCanceled" + persist="true" + type="notify"> + <unique/> +Autopilot canceled + </notification> + + <notification icon="notify.tga" name="PathfindingDirty" persist="true" @@ -7339,7 +7407,8 @@ Your object named <nolink>[OBJECTFROMNAME]</nolink> has given you th name="TeleportOffered_MaturityExceeded" log_to_im="true" log_to_chat="false" - type="offer"> + type="offer" + sound="UISndNewIncomingIMSession"> [NAME_SLURL] has offered to teleport you to their location: “[MESSAGE]” @@ -7364,7 +7433,8 @@ This region contains [REGION_CONTENT_MATURITY] content, but your current prefere name="TeleportOffered_MaturityBlocked" log_to_im="true" log_to_chat="false" - type="notifytip"> + type="notifytip" + sound="UISndNewIncomingIMSession"> [NAME_SLURL] has offered to teleport you to their location: “[MESSAGE]” @@ -8804,23 +8874,6 @@ Click and drag anywhere on the world to rotate your view </notification> <notification - name="PopupAttempt" - icon="Popup_Caution" - type="browser"> - A pop-up was prevented from opening. - <form name="form"> - <ignore name="ignore" - control="MediaEnablePopups" - invert_control="true" - text="Enable all pop-ups"/> - <button default="true" - index="0" - name="open" - text="Open pop-up window"/> - </form> - </notification> - - <notification icon="alertmodal.tga" name="SOCKS_NOT_PERMITTED" type="alertmodal"> @@ -9802,6 +9855,14 @@ Can't move object '[OBJECT_NAME]' to <notification icon="alertmodal.tga" + name="NoParcelPermsNoObject" + type="notify"> + <tag>fail</tag> +Copy failed because you lack access to that parcel. + </notification> + + <notification + icon="alertmodal.tga" name="CantMoveObjectRegionVersion" type="notify"> <tag>fail</tag> @@ -9837,6 +9898,17 @@ You don't have permission to modify that object <notification icon="alertmodal.tga" + name="TooMuchObjectInventorySelected" + type="alertmodal"> + <tag>fail</tag> + Too many objects with large inventory are selected. Please select fewer objects and try again. + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" name="CantEnablePhysObjContributesToNav" type="notify"> <tag>fail</tag> @@ -9934,6 +10006,22 @@ Cannot save to object contents: This would modify the attachment permissions. <notification icon="alertmodal.tga" + name="AttachmentHasTooMuchInventory" + type="notify"> + <tag>fail</tag> +Your attachments contain too much inventory to add more. + </notification> + + <notification + icon="alertmodal.tga" + name="IllegalAttachment" + type="notify"> + <tag>fail</tag> +The attachment has requested a nonexistent point on the avatar. It has been attached to the chest instead. + </notification> + + <notification + icon="alertmodal.tga" name="TooManyScripts" type="notify"> <tag>fail</tag> @@ -10972,6 +11060,14 @@ Money transfers to objects are currently disabled in this region. <notification icon="alertmodal.tga" + name="DroppedMoneyTransferRequest" + type="notify"> + <tag>fail</tag> +Unable to make payment due to system load. + </notification> + + <notification + icon="alertmodal.tga" name="CantPayNoAgent" type="notify"> <tag>fail</tag> diff --git a/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml b/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml index d85b778db2..42a7974316 100644 --- a/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml +++ b/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml @@ -9,6 +9,26 @@ name="Wearing" top="0" width="312"> +<panel.string + name="no_attachments"> + No attachments worn. + </panel.string> + <accordion + fit_parent="true" + follows="all" + height="400" + layout="topleft" + left="0" + single_expansion="true" + top="0" + name="wearables_accordion" + background_visible="true" + bg_alpha_color="DkGray2" + width="309"> + <accordion_tab + layout="topleft" + name="tab_wearables" + title="Wearables"> <wearable_items_list follows="all" height="400" @@ -20,6 +40,27 @@ top="0" width="309" worn_indication_enabled="false" /> + </accordion_tab> + <accordion_tab + layout="topleft" + name="tab_temp_attachments" + title="Temporary attachments"> + <scroll_list + draw_heading="false" + left="3" + width="309" + height="400" + follows="all" + name="temp_attachments_list"> + <scroll_list.columns + name="icon" + width="15" /> + <scroll_list.columns + name="text" + width="210" /> + </scroll_list> + </accordion_tab> + </accordion> <panel background_visible="true" follows="bottom|left|right" 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 a9b8e197bc..e002d9dee1 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml @@ -233,7 +233,7 @@ <slider control_name="IndirectMaxComplexity" - tool_tip="Controls at what point a visually complex avatar is drawn as a jelly doll" + tool_tip="Controls at what point a visually complex avatar is drawn as a JellyDoll" follows="left|top" height="16" initial_value="101" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml index 0b605cf6f7..c20f9b2c51 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml @@ -273,6 +273,18 @@ name="update_willing_to_test" width="400" top_pad="5"/> + <check_box + top_delta="4" + enabled="true" + follows="left|top" + height="14" + control_name="UpdaterShowReleaseNotes" + label="Show Release Notes after update" + left_delta="0" + mouse_opaque="true" + name="update_show_release_notes" + width="400" + top_pad="5"/> <text type="string" length="1" diff --git a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml index eb67d07601..068e4420bc 100644 --- a/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml +++ b/indra/newview/skins/default/xui/en/panel_prim_media_controls.xml @@ -374,9 +374,11 @@ layout="topleft" tool_tip="Movie play progress" width="200"> - <slider_bar.commit_callback - function="MediaCtrl.JumpProgress" /> - </slider_bar> + <slider_bar.mouse_down_callback + function="MediaCtrl.MouseDown" /> + <slider_bar.mouse_up_callback + function="MediaCtrl.MouseUp" /> + </slider_bar> </layout_panel> <layout_panel name="skip_back" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 8988c3e028..9b3fb06bdf 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -51,13 +51,14 @@ OpenGL Version: [OPENGL_VERSION] J2C Decoder Version: [J2C_VERSION] Audio Driver Version: [AUDIO_DRIVER_VERSION] LLCEFLib/CEF Version: [LLCEFLIB_VERSION] +LibVLC Version: [LIBVLC_VERSION] Voice Server Version: [VOICE_VERSION] </string> <string name="AboutTraffic">Packets Lost: [PACKETS_LOST,number,0]/[PACKETS_IN,number,0] ([PACKETS_PCT,number,1]%)</string> <string name="AboutTime">[month, datetime, slt] [day, datetime, slt] [year, datetime, slt] [hour, datetime, slt]:[min, datetime, slt]:[second,datetime,slt]</string> <string name="ErrorFetchingServerReleaseNotesURL">Error fetching server release notes URL.</string> <string name="BuildConfiguration">Build Configuration</string> - + <!-- progress --> <string name="ProgressRestoring">Restoring...</string> <string name="ProgressChangingResolution">Changing resolution...</string> @@ -499,6 +500,7 @@ Please try logging in again in a minute.</string> <string name="TeleportYourAgent">Teleport you</string> <string name="ManageEstateSilently">Manage your estates silently</string> <string name="ChangeYourDefaultAnimations">Change your default animations</string> + <string name="ForceSitAvatar">Force your avatar to sit</string> <string name="NotConnected">Not Connected</string> <string name="AgentNameSubst">(You)</string> <!-- Substitution for agent name --> @@ -2499,6 +2501,15 @@ This feature is currently in Beta. Please add your name to this [http://goo.gl/f <string name="av_render_most_of">You may not be rendered by most of those around you.</string> <string name="av_render_anyone">You may not be rendered by anyone around you.</string> + <!-- HUD complexity rendering messages, see llavatarrendernotifier. --> + <string name="hud_description_total">Your HUD</string> + <string name="hud_name_with_joint">[OBJ_NAME] (worn on [JNT_NAME])</string> + <string name="hud_render_memory_warning">[HUD_DETAILS] uses a lot of texture memory</string> + <string name="hud_render_cost_warning">[HUD_DETAILS] contains a lot of expensive objects and textures</string> + <string name="hud_render_heavy_textures_warning">[HUD_DETAILS] contains a lot of large textures</string> + <string name="hud_render_cramped_warning">[HUD_DETAILS] contains too many objects</string> + <string name="hud_render_textures_warning">[HUD_DETAILS] contains too many textures</string> + <!-- AgeYearsA = singular, AgeYearsB = plural, AgeYearsC = plural for non-English languages like Russian @@ -2566,7 +2577,8 @@ This feature is currently in Beta. Please add your name to this [http://goo.gl/f <string name="SaveComplete">Save complete.</string> <string name="UploadFailed">File upload failed: </string> <string name="ObjectOutOfRange">Script (object out of range)</string> - + <string name="ScriptWasDeleted">Script (deleted from inventory)</string> + <!-- god tools --> <string name="GodToolsObjectOwnedBy">Object [OBJECT] owned by [OWNER]</string> @@ -2834,6 +2846,12 @@ Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh <string name="Multiple Media">Multiple Media</string> <string name="Play Media">Play/Pause Media</string> + <!-- Drivers support/update pages --> + <string name="IntelDriverPage">http://www.intel.com/p/en_US/support/detect/graphics</string> + <string name="NvidiaDriverPage">http://www.nvidia.com/Download/index.aspx?lang=en-us</string> + <string name="AMDDriverPage">http://support.amd.com/us/Pages/AMDSupportHub.aspx</string> + + <!-- OSMessageBox messages --> <string name="MBCmdLineError"> An error was found parsing the command line. diff --git a/indra/newview/skins/default/xui/es/panel_status_bar.xml b/indra/newview/skins/default/xui/es/panel_status_bar.xml index 8ea56c5262..8aaa236475 100644 --- a/indra/newview/skins/default/xui/es/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/es/panel_status_bar.xml @@ -5,7 +5,7 @@ <panel.string name="time">[hour12, datetime, slt]:[min, datetime, slt] [ampm, datetime, slt] [timezone,datetime, slt]</panel.string> <panel.string name="timeTooltip">[weekday, datetime, slt], [day, datetime, slt] [month, datetime, slt] [year, datetime, slt]</panel.string> <panel.string name="buycurrencylabel">[AMT] L$</panel.string> - <panel left="-410" name="balance_bg" width="200"> + <panel left="-431" name="balance_bg" width="200"> <text name="balance" tool_tip="Haz clic para actualizar tu saldo en L$" value="L$??"/> <button label="Comprar L$" name="buyL" tool_tip="Pulsa para comprar más L$"/> <button label="Comprar" name="goShop" tool_tip="Abrir el mercado de Second Life" width="80"/> diff --git a/indra/newview/skins/default/xui/fr/panel_status_bar.xml b/indra/newview/skins/default/xui/fr/panel_status_bar.xml index e2f05a525e..fef0379c2c 100644 --- a/indra/newview/skins/default/xui/fr/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/fr/panel_status_bar.xml @@ -5,7 +5,7 @@ <panel.string name="time">[hour12, datetime, slt]:[min, datetime, slt] [ampm, datetime, slt] [timezone,datetime, slt]</panel.string> <panel.string name="timeTooltip">[weekday, datetime, slt] [sday, datetime, slt] [month, datetime, slt] [year, datetime, slt]</panel.string> <panel.string name="buycurrencylabel">[AMT] L$</panel.string> - <panel left="-405" name="balance_bg" width="195"> + <panel left="-426" name="balance_bg" width="195"> <text name="balance" tool_tip="Cliquer sur ce bouton pour actualiser votre solde en L$." value="L$ ??"/> <button label="Acheter L$" name="buyL" tool_tip="Cliquer pour acheter plus de L$."/> <button label="Achats" name="goShop" tool_tip="Ouvrir la Place du marché Second Life." width="75"/> diff --git a/indra/newview/skins/default/xui/it/panel_status_bar.xml b/indra/newview/skins/default/xui/it/panel_status_bar.xml index 83d2ae5bab..295ca8d9f2 100644 --- a/indra/newview/skins/default/xui/it/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/it/panel_status_bar.xml @@ -5,7 +5,7 @@ <panel.string name="time">[hour12, datetime, slt]:[min, datetime, slt] [ampm, datetime, slt] [timezone,datetime, slt]</panel.string> <panel.string name="timeTooltip">[weekday, datetime, slt], [day, datetime, slt] [month, datetime, slt] [year, datetime, slt]</panel.string> <panel.string name="buycurrencylabel">L$ [AMT]</panel.string> - <panel left="-405" name="balance_bg" width="195"> + <panel left="-426" name="balance_bg" width="195"> <text name="balance" tool_tip="Clicca per aggiornare il tuo saldo in L$" value="L$ ??"/> <button label="Acquista L$" name="buyL" tool_tip="Clicca per acquistare più L$"/> <button label="Acquisti" name="goShop" tool_tip="Apri Mercato Second Life" width="75"/> diff --git a/indra/newview/skins/default/xui/ja/panel_status_bar.xml b/indra/newview/skins/default/xui/ja/panel_status_bar.xml index 2e1446d450..3f3845e491 100644 --- a/indra/newview/skins/default/xui/ja/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/ja/panel_status_bar.xml @@ -5,7 +5,7 @@ <panel.string name="time">[hour12, datetime, slt]:[min, datetime, slt] [ampm, datetime, slt] [timezone,datetime, slt]</panel.string> <panel.string name="timeTooltip">[year, datetime, slt] [month, datetime, slt] [day, datetime, slt] ([weekday, datetime, slt])</panel.string> <panel.string name="buycurrencylabel">L$ [AMT]</panel.string> - <panel left="-370" name="balance_bg" width="160"> + <panel left="-391" name="balance_bg" width="160"> <text name="balance" tool_tip="クリックして L$ 残高を更新" value="L$??"/> <button label="L$ の購入" name="buyL" tool_tip="クリックして L$ を購入します"/> <button label="店" name="goShop" tool_tip="Second Life マーケットプレイスを開く" width="40"/> diff --git a/indra/newview/skins/default/xui/pt/panel_status_bar.xml b/indra/newview/skins/default/xui/pt/panel_status_bar.xml index cfe52ff404..c35863734f 100644 --- a/indra/newview/skins/default/xui/pt/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/pt/panel_status_bar.xml @@ -5,7 +5,7 @@ <panel.string name="time">[hour12, datetime, slt]:[min, datetime, slt] [ampm, datetime, slt] [timezone,datetime, slt]</panel.string> <panel.string name="timeTooltip">[weekday, datetime, slt], [day, datetime, slt] [month, datetime, slt] [year, datetime, slt]</panel.string> <panel.string name="buycurrencylabel">L$ [AMT]</panel.string> - <panel left="-410" name="balance_bg" width="200"> + <panel left="-431" name="balance_bg" width="200"> <text name="balance" tool_tip="Atualizar saldo de L$" value="L$??"/> <button label="Comprar L$" name="buyL" tool_tip="Comprar mais L$"/> <button label="Comprar" name="goShop" tool_tip="Abrir Mercado do Second Life" width="80"/> diff --git a/indra/newview/skins/default/xui/ru/panel_status_bar.xml b/indra/newview/skins/default/xui/ru/panel_status_bar.xml index 630925fa60..49c48ae654 100644 --- a/indra/newview/skins/default/xui/ru/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/ru/panel_status_bar.xml @@ -5,7 +5,7 @@ <panel.string name="time">[hour, datetime, slt]:[min, datetime, slt] [timezone,datetime, slt]</panel.string> <panel.string name="timeTooltip">[weekday, datetime, slt], [day, datetime, slt] [month, datetime, slt] [year, datetime, slt]</panel.string> <panel.string name="buycurrencylabel">L$ [AMT]</panel.string> - <panel left="-450" name="balance_bg" width="240"> + <panel left="-471" name="balance_bg" width="240"> <text name="balance" tool_tip="Щелкните для обновления вашего баланса L$" value="L$??"/> <button label="Купить L$" name="buyL" tool_tip="Щелкните для покупки L$"/> <button label="Торговый центр" name="goShop" tool_tip="Открыть торговый центр Second Life" width="121"/> diff --git a/indra/newview/skins/default/xui/tr/panel_status_bar.xml b/indra/newview/skins/default/xui/tr/panel_status_bar.xml index 9e2669ec2b..7c7bfc7e14 100644 --- a/indra/newview/skins/default/xui/tr/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/tr/panel_status_bar.xml @@ -5,7 +5,7 @@ <panel.string name="time">[hour12, datetime, slt]:[min, datetime, slt] [ampm, datetime, slt] [timezone,datetime, slt]</panel.string> <panel.string name="timeTooltip">[weekday, datetime, slt], [day, datetime, slt] [month, datetime, slt] [year, datetime, slt]</panel.string> <panel.string name="buycurrencylabel">L$ [AMT]</panel.string> - <panel left="-425" name="balance_bg" width="215"> + <panel left="-446" name="balance_bg" width="215"> <text name="balance" tool_tip="L$ bakiyenizi yenilemek için buraya tıklayın" value="L$??"/> <button label="L$ Satın Al" name="buyL" tool_tip="Daha fazla L$ satın almak için tıklayın"/> <button label="Alışveriş yap" name="goShop" tool_tip="Second Life Pazaryeri Aç" width="95"/> diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 1b0334498e..b603157ca7 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -136,6 +136,7 @@ void LLGridManager::addSystemGrid(const std::string& label, const std::string& helper, const std::string& login_page, const std::string& update_url_base, + const std::string& web_profile_url, const std::string& login_id) { } diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index f3d89bb866..66d730d1ac 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -444,6 +444,11 @@ class Windows_i686_Manifest(ViewerManifest): self.path("media_plugin_cef.dll") self.end_prefix() + # Media plugins - LibVLC + if self.prefix(src='../media_plugins/libvlc/%s' % self.args['configuration'], dst="llplugin"): + self.path("media_plugin_libvlc.dll") + self.end_prefix() + # winmm.dll shim if self.prefix(src='../media_plugins/winmmshim/%s' % self.args['configuration'], dst=""): self.path("winmm.dll") @@ -550,6 +555,12 @@ class Windows_i686_Manifest(ViewerManifest): self.path("zh-TW.pak") self.end_prefix() + if self.prefix(src=os.path.join(os.pardir, 'packages', 'bin', 'release'), dst="llplugin"): + self.path("libvlc.dll") + self.path("libvlccore.dll") + self.path("plugins/") + self.end_prefix() + # pull in the crash logger and updater from other projects # tag:"crash-logger" here as a cue to the exporter self.path(src='../win_crash_logger/%s/windows-crash-logger.exe' % self.args['configuration'], @@ -1089,8 +1100,18 @@ class LinuxManifest(ViewerManifest): # plugins if self.prefix(src="", dst="bin/llplugin"): self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") + self.path("../media_plugins/libvlc/libmedia_plugin_libvlc.so", "libmedia_plugin_libvlc.so") self.end_prefix("bin/llplugin") + if self.prefix(src=os.path.join(os.pardir, 'packages', 'lib', 'vlc', 'plugins'), dst="bin/llplugin/vlc/plugins"): + self.path( "plugins.dat" ) + self.path( "*/*.so" ) + self.end_prefix() + + if self.prefix(src=os.path.join(os.pardir, 'packages', 'lib' ), dst="lib"): + self.path( "libvlc*.so*" ) + self.end_prefix() + # llcommon if not self.path("../llcommon/libllcommon.so", "lib/libllcommon.so"): print "Skipping llcommon.so (assuming llcommon was linked statically)" @@ -1144,7 +1165,7 @@ class LinuxManifest(ViewerManifest): def strip_binaries(self): if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer(): print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build" - self.run_command(r"find %(d)r/bin %(d)r/lib -type f \! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure + self.run_command(r"find %(d)r/bin %(d)r/lib -type f \! -name update_install \! -name *.dat | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure class Linux_i686_Manifest(LinuxManifest): def construct(self): diff --git a/indra/test/llapp_tut.cpp b/indra/test/llapp_tut.cpp index aa5c0672e6..98e714a497 100644 --- a/indra/test/llapp_tut.cpp +++ b/indra/test/llapp_tut.cpp @@ -41,7 +41,7 @@ namespace tut public: virtual bool init() { return true; } virtual bool cleanup() { return true; } - virtual bool mainLoop() { return true; } + virtual bool frame() { return true; } }; LLTestApp* mApp; application() diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index 53d4acc9e0..c767d52c7b 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -42,6 +42,8 @@ #include "llevents.h" #include "lleventfilter.h" #include "lleventcoro.h" +#include "llexception.h" +#include "stringize.h" //********************* // LLLogin @@ -128,30 +130,23 @@ void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params) void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) { - try - { - LLSD printable_params = login_params; - //if(printable_params.has("params") - // && printable_params["params"].has("passwd")) - //{ - // printable_params["params"]["passwd"] = "*******"; - //} - LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::instance().getName() + LLSD printable_params = login_params; + if (printable_params.has("params") + && printable_params["params"].has("passwd")) + { + printable_params["params"]["passwd"] = "*******"; + } + try + { + LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::instance().getName() << " with uri '" << uri << "', parameters " << printable_params << LL_ENDL; - // Arriving in SRVRequest state - LLEventStream replyPump("SRVreply", true); - // Should be an array of one or more uri strings. - LLEventPump& xmlrpcPump(LLEventPumps::instance().obtain("LLXMLRPCTransaction")); // EXT-4193: use a DIFFERENT reply pump than for the SRV request. We used // to share them -- but the EXT-3934 fix made it possible for an abandoned // SRV response to arrive just as we were expecting the XMLRPC response. LLEventStream loginReplyPump("loginreply", true); - // Loop through the rewrittenURIs, counting attempts along the way. - // Because of possible redirect responses, we may make more than one - // attempt per rewrittenURIs entry. LLSD::Integer attempts = 0; LLSD request(login_params); @@ -167,11 +162,11 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) LLSD progress_data; progress_data["attempt"] = attempts; progress_data["request"] = request; - if(progress_data["request"].has("params") - && progress_data["request"]["params"].has("passwd")) - { - progress_data["request"]["params"]["passwd"] = "*******"; - } + if (progress_data["request"].has("params") + && progress_data["request"]["params"].has("passwd")) + { + progress_data["request"]["params"]["passwd"] = "*******"; + } sendProgressEvent("offline", "authenticating", progress_data); // We expect zero or more "Downloading" status events, followed by @@ -189,8 +184,8 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) // Still Downloading -- send progress update. sendProgressEvent("offline", "downloading"); } - - LL_DEBUGS("LLLogin") << "Auth Response: " << mAuthResponse << LL_ENDL; + + LL_DEBUGS("LLLogin") << "Auth Response: " << mAuthResponse << LL_ENDL; status = mAuthResponse["status"].asString(); // Okay, we've received our final status event for this @@ -202,7 +197,7 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) break; } - sendProgressEvent("offline", "indeterminate", mAuthResponse["responses"]); + sendProgressEvent("offline", "indeterminate", mAuthResponse["responses"]); // Here the login service at the current URI is redirecting us // to some other URI ("indeterminate" -- why not "redirect"?). @@ -212,8 +207,7 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) request["method"] = mAuthResponse["responses"]["next_method"].asString(); } // loop back to try the redirected URI - // Here we're done with redirects for the current rewrittenURIs - // entry. + // Here we're done with redirects. if (status == "Complete") { // StatusComplete does not imply auth success. Check the @@ -230,14 +224,14 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) return; // Done! } -// /* Sometimes we end with "Started" here. Slightly slow server? -// * Seems to be ok to just skip it. Otherwise we'd error out and crash in the if below. -// */ -// if( status == "Started") -// { -// LL_DEBUGS("LLLogin") << mAuthResponse << LL_ENDL; -// continue; -// } +// /* Sometimes we end with "Started" here. Slightly slow server? +// * Seems to be ok to just skip it. Otherwise we'd error out and crash in the if below. +// */ +// if( status == "Started") +// { +// LL_DEBUGS("LLLogin") << mAuthResponse << LL_ENDL; +// continue; +// } // If we don't recognize status at all, trouble if (! (status == "CURLError" @@ -250,27 +244,25 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params) } // Here status IS one of the errors tested above. - - // Here we got through all the rewrittenURIs without succeeding. Tell - // caller this didn't work out so well. Of course, the only failure data - // we can reasonably show are from the last of the rewrittenURIs. - - // *NOTE: The response from LLXMLRPCListener's Poller::poll method returns an - // llsd with no "responses" node. To make the output from an incomplete login symmetrical - // to success, add a data/message and data/reason fields. - LLSD error_response; - error_response["reason"] = mAuthResponse["status"]; - error_response["errorcode"] = mAuthResponse["errorcode"]; - error_response["message"] = mAuthResponse["error"]; - if(mAuthResponse.has("certificate")) - { - error_response["certificate"] = mAuthResponse["certificate"]; - } - sendProgressEvent("offline", "fail.login", error_response); - } - catch (...) { - LL_ERRS() << "login exception caught" << LL_ENDL; - } + // Tell caller this didn't work out so well. + + // *NOTE: The response from LLXMLRPCListener's Poller::poll method returns an + // llsd with no "responses" node. To make the output from an incomplete login symmetrical + // to success, add a data/message and data/reason fields. + LLSD error_response; + error_response["reason"] = mAuthResponse["status"]; + error_response["errorcode"] = mAuthResponse["errorcode"]; + error_response["message"] = mAuthResponse["error"]; + if(mAuthResponse.has("certificate")) + { + error_response["certificate"] = mAuthResponse["certificate"]; + } + sendProgressEvent("offline", "fail.login", error_response); + } + catch (...) { + CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName() + << "('" << uri << "', " << printable_params << ")")); + } } void LLLogin::Impl::disconnect() diff --git a/indra/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp index 382689afa0..04e0395c50 100644 --- a/indra/viewer_components/updater/llupdatedownloader.cpp +++ b/indra/viewer_components/updater/llupdatedownloader.cpp @@ -27,7 +27,7 @@ #include "llupdatedownloader.h" #include "httpcommon.h" -#include <stdexcept> +#include "llexception.h" #include <boost/format.hpp> #include <boost/lexical_cast.hpp> #include <curl/curl.h> @@ -85,11 +85,11 @@ private: namespace { class DownloadError: - public std::runtime_error + public LLException { public: DownloadError(const char * message): - std::runtime_error(message) + LLException(message) { ; // No op. } @@ -467,7 +467,7 @@ void LLUpdateDownloader::Implementation::initializeCurlGet(std::string const & u if(!mCurl) { - throw DownloadError("failed to initialize curl"); + LLTHROW(DownloadError("failed to initialize curl")); } throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_NOSIGNAL, true)); throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_FOLLOWLOCATION, true)); @@ -508,7 +508,7 @@ void LLUpdateDownloader::Implementation::resumeDownloading(size_t startByte) mHeaderList = curl_slist_append(mHeaderList, rangeHeaderFormat.str().c_str()); if(mHeaderList == 0) { - throw DownloadError("cannot add Range header"); + LLTHROW(DownloadError("cannot add Range header")); } throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_HTTPHEADER, mHeaderList)); @@ -524,7 +524,7 @@ void LLUpdateDownloader::Implementation::startDownloading(LLURI const & uri, std mDownloadData["hash"] = hash; mDownloadData["current_version"] = ll_get_version(); LLSD path = uri.pathArray(); - if(path.size() == 0) throw DownloadError("no file path"); + if(path.size() == 0) LLTHROW(DownloadError("no file path")); std::string fileName = path[path.size() - 1].asString(); std::string filePath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, fileName); mDownloadData["path"] = filePath; @@ -547,9 +547,9 @@ void LLUpdateDownloader::Implementation::throwOnCurlError(CURLcode code) if(code != CURLE_OK) { const char * errorString = curl_easy_strerror(code); if(errorString != 0) { - throw DownloadError(curl_easy_strerror(code)); + LLTHROW(DownloadError(curl_easy_strerror(code))); } else { - throw DownloadError("unknown curl error"); + LLTHROW(DownloadError("unknown curl error")); } } else { ; // No op. diff --git a/indra/viewer_components/updater/llupdateinstaller.cpp b/indra/viewer_components/updater/llupdateinstaller.cpp index a0e2c0b362..1c7629da23 100644 --- a/indra/viewer_components/updater/llupdateinstaller.cpp +++ b/indra/viewer_components/updater/llupdateinstaller.cpp @@ -30,23 +30,25 @@ #include "llupdateinstaller.h" #include "lldir.h" #include "llsd.h" +#include "llexception.h" #if defined(LL_WINDOWS) #pragma warning(disable: 4702) // disable 'unreachable code' so we can use lexical_cast (really!). #endif #include <boost/lexical_cast.hpp> - namespace { - class RelocateError {}; - - + struct RelocateError: public LLException + { + RelocateError(): LLException("llupdateinstaller: RelocateError") {} + }; + std::string copy_to_temp(std::string const & path) { std::string scriptFile = gDirUtilp->getBaseFileName(path); std::string newPath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, scriptFile); apr_status_t status = apr_file_copy(path.c_str(), newPath.c_str(), APR_FILE_SOURCE_PERMS, gAPRPoolp); - if(status != APR_SUCCESS) throw RelocateError(); + if(status != APR_SUCCESS) LLTHROW(RelocateError()); return newPath; } diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp index 788955a1b2..1665e41e70 100644 --- a/indra/viewer_components/updater/llupdaterservice.cpp +++ b/indra/viewer_components/updater/llupdaterservice.cpp @@ -32,6 +32,7 @@ #include "lltimer.h" #include "llupdatechecker.h" #include "llupdateinstaller.h" +#include "llexception.h" #include <boost/scoped_ptr.hpp> #include <boost/weak_ptr.hpp> @@ -190,8 +191,8 @@ void LLUpdaterServiceImpl::initialize(const std::string& channel, { if(mIsChecking || mIsDownloading) { - throw LLUpdaterService::UsageError("LLUpdaterService::initialize call " - "while updater is running."); + LLTHROW(LLUpdaterService::UsageError("LLUpdaterService::initialize call " + "while updater is running.")); } mChannel = channel; @@ -222,8 +223,8 @@ void LLUpdaterServiceImpl::startChecking(bool install_if_ready) { if(mChannel.empty() || mVersion.empty()) { - throw LLUpdaterService::UsageError("Set params before call to " - "LLUpdaterService::startCheck()."); + LLTHROW(LLUpdaterService::UsageError("Set params before call to " + "LLUpdaterService::startCheck().")); } mIsChecking = true; diff --git a/indra/viewer_components/updater/llupdaterservice.h b/indra/viewer_components/updater/llupdaterservice.h index 95bbe1695c..78e8c6b290 100644 --- a/indra/viewer_components/updater/llupdaterservice.h +++ b/indra/viewer_components/updater/llupdaterservice.h @@ -29,16 +29,17 @@ #include <boost/shared_ptr.hpp> #include <boost/function.hpp> #include "llhasheduniqueid.h" +#include "llexception.h" class LLUpdaterServiceImpl; class LLUpdaterService { public: - class UsageError: public std::runtime_error + class UsageError: public LLException { public: - UsageError(const std::string& msg) : std::runtime_error(msg) {} + UsageError(const std::string& msg) : LLException(msg) {} }; // Name of the event pump through which update events will be delivered. diff --git a/indra/win_crash_logger/llcrashloggerwindows.cpp b/indra/win_crash_logger/llcrashloggerwindows.cpp index 23c29e6b18..167acf6ac7 100644 --- a/indra/win_crash_logger/llcrashloggerwindows.cpp +++ b/indra/win_crash_logger/llcrashloggerwindows.cpp @@ -454,7 +454,7 @@ void LLCrashLoggerWindows::gatherPlatformSpecificFiles() //mDebugLog["DisplayDeviceInfo"] = gDXHardware.getDisplayInfo(); //Not initialized. } -bool LLCrashLoggerWindows::mainLoop() +bool LLCrashLoggerWindows::frame() { LL_INFOS() << "CrashSubmitBehavior is " << mCrashBehavior << LL_ENDL; @@ -503,14 +503,14 @@ bool LLCrashLoggerWindows::mainLoop() TranslateMessage(&msg); DispatchMessage(&msg); } - return msg.wParam; + return true; // msg.wParam; } else { LL_WARNS() << "Unknown crash behavior " << mCrashBehavior << LL_ENDL; - return 1; + return true; // 1; } - return 0; + return true; // 0; } void LLCrashLoggerWindows::updateApplication(const std::string& message) diff --git a/indra/win_crash_logger/llcrashloggerwindows.h b/indra/win_crash_logger/llcrashloggerwindows.h index 1812e2737e..f89b8708dc 100644 --- a/indra/win_crash_logger/llcrashloggerwindows.h +++ b/indra/win_crash_logger/llcrashloggerwindows.h @@ -46,7 +46,7 @@ public: static LLCrashLoggerWindows* sInstance; virtual bool init(); - virtual bool mainLoop(); + virtual bool frame(); virtual void updateApplication(const std::string& message = LLStringUtil::null); virtual bool cleanup(); virtual void gatherPlatformSpecificFiles(); diff --git a/indra/win_crash_logger/win_crash_logger.cpp b/indra/win_crash_logger/win_crash_logger.cpp index 366edd894b..7466dbb766 100644 --- a/indra/win_crash_logger/win_crash_logger.cpp +++ b/indra/win_crash_logger/win_crash_logger.cpp @@ -52,7 +52,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, } app.processingLoop(); - app.mainLoop(); + app.frame(); app.cleanup(); LL_INFOS() << "Crash reporter finished normally." << LL_ENDL; return 0; |