summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgtags21
-rw-r--r--doc/contributions.txt17
-rw-r--r--indra/cmake/GoogleMock.cmake5
-rw-r--r--indra/cmake/LLAddBuildTest.cmake5
-rw-r--r--indra/llcharacter/llanimationstates.cpp278
-rw-r--r--indra/llcommon/CMakeLists.txt1
-rw-r--r--indra/llcommon/llavatarconstants.h8
-rw-r--r--indra/llcommon/lleventdispatcher.cpp566
-rw-r--r--indra/llcommon/lleventdispatcher.h393
-rw-r--r--indra/llcommon/llevents.cpp13
-rw-r--r--indra/llcommon/llevents.h14
-rw-r--r--indra/llcommon/lllslconstants.h2
-rw-r--r--indra/llcommon/llmemory.cpp178
-rw-r--r--indra/llcommon/llmemory.h47
-rw-r--r--indra/llcommon/llmemtype.cpp1
-rw-r--r--indra/llcommon/llmetricperformancetester.h2
-rw-r--r--indra/llcommon/llsdutil.cpp27
-rw-r--r--indra/llcommon/llsdutil.h285
-rw-r--r--indra/llcommon/llversionviewer.h2
-rw-r--r--indra/llcommon/tests/lleventdispatcher_test.cpp1324
-rw-r--r--indra/llimage/llimageworker.cpp6
-rw-r--r--indra/llimage/llimageworker.h2
-rw-r--r--indra/llmath/llcamera.cpp1
-rw-r--r--indra/llmath/llcamera.h9
-rw-r--r--indra/llplugin/llpluginclassmedia.cpp5
-rw-r--r--indra/llplugin/llpluginclassmedia.h1
-rw-r--r--indra/llplugin/llplugininstance.cpp20
-rw-r--r--indra/llplugin/llplugininstance.h2
-rw-r--r--indra/llplugin/llpluginprocesschild.cpp3
-rw-r--r--indra/llplugin/llpluginprocesschild.h1
-rw-r--r--indra/llplugin/llpluginprocessparent.cpp4
-rw-r--r--indra/llplugin/llpluginprocessparent.h2
-rw-r--r--indra/llui/llfloaterreglistener.cpp17
-rw-r--r--indra/llui/llfloaterreglistener.h1
-rw-r--r--indra/llui/tests/llurlentry_stub.cpp4
-rw-r--r--indra/llwindow/CMakeLists.txt2
-rw-r--r--indra/llwindow/llwindow.cpp10
-rw-r--r--indra/llwindow/llwindow.h2
-rw-r--r--indra/llwindow/llwindowlistener.cpp174
-rw-r--r--indra/llwindow/llwindowlistener.h53
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/app_settings/settings.xml33
-rw-r--r--indra/newview/llagentlistener.cpp36
-rw-r--r--indra/newview/llagentlistener.h2
-rw-r--r--indra/newview/llappviewer.cpp16
-rw-r--r--indra/newview/llchathistory.cpp19
-rw-r--r--indra/newview/llcolorswatch.cpp4
-rw-r--r--indra/newview/llfilepicker.cpp4
-rw-r--r--indra/newview/llfirstuse.cpp58
-rw-r--r--indra/newview/llfirstuse.h9
-rw-r--r--indra/newview/llfloatermap.cpp3
-rwxr-xr-xindra/newview/llfloaterpreference.cpp16
-rw-r--r--indra/newview/llfloaterpreference.h4
-rw-r--r--indra/newview/llfloaterregiondebugconsole.cpp36
-rw-r--r--indra/newview/llfloaterregiondebugconsole.h36
-rw-r--r--indra/newview/llfloatersnapshot.cpp37
-rw-r--r--indra/newview/llimview.cpp9
-rw-r--r--indra/newview/llinspectavatar.cpp86
-rw-r--r--indra/newview/llinventorybridge.cpp5
-rw-r--r--indra/newview/lllogininstance.cpp16
-rw-r--r--indra/newview/llmaniprotate.cpp36
-rw-r--r--indra/newview/llmemoryview.cpp53
-rw-r--r--indra/newview/llmemoryview.h1
-rw-r--r--indra/newview/llnamelistctrl.cpp24
-rw-r--r--indra/newview/llnetmap.cpp149
-rw-r--r--indra/newview/llnetmap.h18
-rw-r--r--indra/newview/llpanelavatar.cpp68
-rw-r--r--indra/newview/llpanelavatar.h595
-rw-r--r--indra/newview/llpanelmaininventory.cpp93
-rw-r--r--indra/newview/llpanelpeople.cpp13
-rw-r--r--indra/newview/llpanelpeople.h1
-rw-r--r--indra/newview/llpreviewgesture.cpp2
-rw-r--r--indra/newview/llsidetray.cpp22
-rw-r--r--indra/newview/llsidetray.h10
-rw-r--r--indra/newview/llsidetraylistener.cpp162
-rw-r--r--indra/newview/llsidetraylistener.h36
-rw-r--r--indra/newview/llsimplestat.h36
-rw-r--r--indra/newview/lltexturefetch.cpp6022
-rw-r--r--indra/newview/lltoastnotifypanel.cpp10
-rw-r--r--indra/newview/lltranslate.cpp48
-rw-r--r--indra/newview/lltranslate.h48
-rw-r--r--indra/newview/llviewerassetstats.cpp36
-rw-r--r--indra/newview/llviewerassetstats.h36
-rw-r--r--indra/newview/llviewerdisplay.cpp32
-rw-r--r--indra/newview/llviewermedia.cpp26
-rw-r--r--indra/newview/llviewermessage.cpp8
-rw-r--r--indra/newview/llviewerobject.cpp15
-rw-r--r--indra/newview/llviewerobjectlist.cpp21
-rw-r--r--indra/newview/llviewerobjectlist.h1
-rw-r--r--indra/newview/llviewerwindow.cpp88
-rw-r--r--indra/newview/llviewerwindowlistener.cpp5
-rw-r--r--indra/newview/llvoavatar.cpp16
-rw-r--r--indra/newview/llvocache.cpp2
-rw-r--r--indra/newview/llvosky.cpp35
-rw-r--r--indra/newview/llvosky.h44
-rw-r--r--indra/newview/llvovolume.cpp2
-rw-r--r--indra/newview/llworld.cpp2
-rw-r--r--indra/newview/pipeline.cpp27
-rw-r--r--indra/newview/skins/default/textures/textures.xml1
-rw-r--r--indra/newview/skins/default/xui/da/floater_web_content.xml14
-rw-r--r--indra/newview/skins/default/xui/da/menu_login.xml3
-rw-r--r--indra/newview/skins/default/xui/da/menu_mini_map.xml1
-rw-r--r--indra/newview/skins/default/xui/da/menu_viewer.xml7
-rw-r--r--indra/newview/skins/default/xui/da/notifications.xml82
-rw-r--r--indra/newview/skins/default/xui/da/panel_avatar_list_item.xml2
-rw-r--r--indra/newview/skins/default/xui/da/panel_edit_alpha.xml16
-rw-r--r--indra/newview/skins/default/xui/da/panel_login.xml5
-rw-r--r--indra/newview/skins/default/xui/da/panel_my_profile.xml29
-rw-r--r--indra/newview/skins/default/xui/da/panel_notify_textbox.xml5
-rw-r--r--indra/newview/skins/default/xui/da/panel_people.xml18
-rw-r--r--indra/newview/skins/default/xui/da/panel_preferences_colors.xml6
-rw-r--r--indra/newview/skins/default/xui/da/panel_preferences_privacy.xml2
-rw-r--r--indra/newview/skins/default/xui/da/panel_preferences_setup.xml8
-rw-r--r--indra/newview/skins/default/xui/da/panel_preferences_sound.xml2
-rw-r--r--indra/newview/skins/default/xui/da/panel_status_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/da/strings.xml20
-rw-r--r--indra/newview/skins/default/xui/de/panel_preferences_sound.xml2
-rw-r--r--indra/newview/skins/default/xui/de/strings.xml20
-rw-r--r--indra/newview/skins/default/xui/en/floater_about_land.xml4
-rw-r--r--indra/newview/skins/default/xui/en/floater_map.xml6
-rw-r--r--indra/newview/skins/default/xui/en/floater_region_debug_console.xml3
-rw-r--r--indra/newview/skins/default/xui/en/floater_tools.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_web_content.xml16
-rw-r--r--indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml20
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml11
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml22
-rw-r--r--indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml4
-rw-r--r--indra/newview/skins/default/xui/en/panel_people.xml26
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_sound.xml12
-rw-r--r--indra/newview/skins/default/xui/en/panel_profile.xml8
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml140
-rw-r--r--indra/newview/skins/default/xui/en/widgets/talk_button.xml10
-rw-r--r--indra/newview/skins/default/xui/es/panel_preferences_sound.xml2
-rw-r--r--indra/newview/skins/default/xui/es/strings.xml20
-rw-r--r--indra/newview/skins/default/xui/fr/floater_preview_gesture.xml2
-rw-r--r--indra/newview/skins/default/xui/fr/menu_hide_navbar.xml2
-rw-r--r--indra/newview/skins/default/xui/fr/panel_preferences_sound.xml4
-rw-r--r--indra/newview/skins/default/xui/fr/strings.xml20
-rw-r--r--indra/newview/skins/default/xui/it/panel_preferences_sound.xml2
-rw-r--r--indra/newview/skins/default/xui/it/strings.xml20
-rw-r--r--indra/newview/skins/default/xui/ja/panel_preferences_sound.xml2
-rw-r--r--indra/newview/skins/default/xui/ja/strings.xml20
-rw-r--r--indra/newview/skins/default/xui/nl/strings.xml20
-rw-r--r--indra/newview/skins/default/xui/pl/panel_preferences_sound.xml2
-rw-r--r--indra/newview/skins/default/xui/pl/strings.xml20
-rw-r--r--indra/newview/skins/default/xui/pt/panel_preferences_sound.xml2
-rw-r--r--indra/newview/skins/default/xui/pt/strings.xml20
-rw-r--r--indra/newview/tests/llsimplestat_test.cpp36
-rw-r--r--indra/newview/tests/llviewerassetstats_test.cpp36
-rw-r--r--indra/test_apps/llplugintest/llmediaplugintest.cpp4
-rw-r--r--indra/viewer_components/updater/llupdaterservice.cpp6
151 files changed, 8182 insertions, 4369 deletions
diff --git a/.hgtags b/.hgtags
index 1e2e10eb97..2b143b0013 100644
--- a/.hgtags
+++ b/.hgtags
@@ -51,4 +51,23 @@ a82e5b1e22c7f90e3c7977d146b80588f004ed0d 2.5.0-start
345b17e7cf630db77e840b4fe3451bd476d750a3 2.5.0-beta1
345b17e7cf630db77e840b4fe3451bd476d750a3 76f586a8e22b
0000000000000000000000000000000000000000 76f586a8e22b
-54d772d8687c69b1d773f6ce14bbc7bdc9d6c05f DRTVWR-33--2.5.0beta2
+54d772d8687c69b1d773f6ce14bbc7bdc9d6c05f 2.5.0-beta2
+b542f8134a2bb5dd054ff4e509a44b2ee463b1bf nat-eventapi2-base
+7076e22f9f43f479a4ea75eac447a36364bead5a DRTVWR-5_2.2.0-beta1
+9822eb3e25f7fe0c28ffd8aba45c507caa383cbc DRTVWR-3_2.2.0-beta2
+b0cd7e150009809a0b5b0a9d5785cd4bb230413a DRTVWR-7_2.2.0-beta3
+1415e6538d54fd5d568ee88343424d57c6803c2c DRTVWR-8_2.2.0-release
+a3c12342b1af0951b8aa3b828aacef17fcea8178 DRTVWR-14_2.3.0-beta1
+db0fe9bb65187f365e58a717dd23d0f4754a9c1d DRTVWR-17_2.3.0-beta2
+6ad3d6fa35a4e320e9ce442fce2bf9c7fc852556 DRTVWR-20_2.3.0-beta3
+6ad3d6fa35a4e320e9ce442fce2bf9c7fc852556 DRTVWR-13_2.3.0-release
+3bc1f50a72e117f4d4ad8d555f0c785ea8cc201e DRTVWR-26_2.4.0-beta1
+25bd6007e3d2fc15db9326ed4b18a24a5969a46a DRTVWR-27_2.4.0-beta2
+1ed382c6a08ba3850b6ce9061bc551ddece0ea07 DRTVWR-25_2.4.0-release
+345b17e7cf630db77e840b4fe3451bd476d750a3 DRTVWR-32_2.5.0-beta1
+54d772d8687c69b1d773f6ce14bbc7bdc9d6c05f DRTVWR-33_2.5.0-beta2
+b723921b5c711bd24dbe77dc76ef488b544dac78 2.5.0-beta3
+b723921b5c711bd24dbe77dc76ef488b544dac78 DRTVWR-34_2.5.0-beta3
+b723921b5c711bd24dbe77dc76ef488b544dac78 2.5.0-release
+b723921b5c711bd24dbe77dc76ef488b544dac78 DRTVWR-31_2.5.0-release
+92e58e51776a4f8c29069b1a62ff21454d2085f0 2.6.0-start
diff --git a/doc/contributions.txt b/doc/contributions.txt
index 4e91bbdd36..5d59627a92 100644
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -66,6 +66,7 @@ Aleric Inglewood
SNOW-626
SNOW-756
SNOW-764
+ SNOW-800
VWR-10001
VWR-10579
VWR-10759
@@ -85,17 +86,21 @@ Aleric Inglewood
VWR-24320
VWR-24321
VWR-24354
+ VWR-24366
VWR-24519
SNOW-84
SNOW-477
SNOW-744
SNOW-766
STORM-163
+ STORM-955
+ STORM-960
Ales Beaumont
VWR-9352
SNOW-240
Alexandrea Fride
STORM-255
+ STORM-960
Alissa Sabre
VWR-81
VWR-83
@@ -373,20 +378,24 @@ JB Kraft
Joghert LeSabre
VWR-64
Jonathan Yap
+ STORM-435
STORM-523
STORM-596
STORM-615
STORM-616
+ STORM-643
STORM-679
STORM-723
STORM-726
STORM-737
- STORM-869
STORM-785
STORM-812
+ STORM-829
+ STORM-844
+ STORM-869
VWR-17801
VWR-24347
- STORM-844
+ STORM-975
Kage Pixel
VWR-11
Ken March
@@ -614,6 +623,7 @@ Robin Cornelius
SNOW-599
SNOW-747
STORM-422
+ STORM-960
VWR-2488
VWR-9557
VWR-10579
@@ -662,6 +672,8 @@ Sergen Davies
CT-321
Shawn Kaufmat
SNOW-240
+Siana Gearz
+ STORM-960
SignpostMarv Martin
VWR-153
VWR-154
@@ -769,6 +781,7 @@ Twisted Laws
STORM-466
STORM-467
STORM-844
+ STORM-643
Vadim Bigbear
VWR-2681
Vector Hastings
diff --git a/indra/cmake/GoogleMock.cmake b/indra/cmake/GoogleMock.cmake
index ca5a8034ba..06d6d847a0 100644
--- a/indra/cmake/GoogleMock.cmake
+++ b/indra/cmake/GoogleMock.cmake
@@ -8,9 +8,10 @@ set(GOOGLEMOCK_INCLUDE_DIRS
${LIBS_PREBUILT_DIR}/include)
if (LINUX)
+ # VWR-24366: gmock is underlinked, it needs gtest.
set(GOOGLEMOCK_LIBRARIES
- gmock
- gtest)
+ gmock -Wl,--no-as-needed
+ gtest -Wl,--as-needed)
elseif(WINDOWS)
set(GOOGLEMOCK_LIBRARIES
gmock)
diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake
index 05f0492234..cd0eada2d0 100644
--- a/indra/cmake/LLAddBuildTest.cmake
+++ b/indra/cmake/LLAddBuildTest.cmake
@@ -57,11 +57,6 @@ INCLUDE(GoogleMock)
${CMAKE_SOURCE_DIR}/test/test.h
)
- # Use the default flags
- if (LINUX)
- SET(CMAKE_EXE_LINKER_FLAGS "")
- endif (LINUX)
-
# start the source test executable definitions
SET(${project}_TEST_OUTPUT "")
FOREACH (source ${sources})
diff --git a/indra/llcharacter/llanimationstates.cpp b/indra/llcharacter/llanimationstates.cpp
index a30113a478..155226cf17 100644
--- a/indra/llcharacter/llanimationstates.cpp
+++ b/indra/llcharacter/llanimationstates.cpp
@@ -33,145 +33,145 @@
#include "llanimationstates.h"
#include "llstring.h"
-const LLUUID ANIM_AGENT_AFRAID = LLUUID("6b61c8e8-4747-0d75-12d7-e49ff207a4ca");
-const LLUUID ANIM_AGENT_AIM_BAZOOKA_R = LLUUID("b5b4a67d-0aee-30d2-72cd-77b333e932ef");
-const LLUUID ANIM_AGENT_AIM_BOW_L = LLUUID("46bb4359-de38-4ed8-6a22-f1f52fe8f506");
-const LLUUID ANIM_AGENT_AIM_HANDGUN_R = LLUUID("3147d815-6338-b932-f011-16b56d9ac18b");
-const LLUUID ANIM_AGENT_AIM_RIFLE_R = LLUUID("ea633413-8006-180a-c3ba-96dd1d756720");
-const LLUUID ANIM_AGENT_ANGRY = LLUUID("5747a48e-073e-c331-f6f3-7c2149613d3e");
-const LLUUID ANIM_AGENT_AWAY = LLUUID("fd037134-85d4-f241-72c6-4f42164fedee");
-const LLUUID ANIM_AGENT_BACKFLIP = LLUUID("c4ca6188-9127-4f31-0158-23c4e2f93304");
-const LLUUID ANIM_AGENT_BELLY_LAUGH = LLUUID("18b3a4b5-b463-bd48-e4b6-71eaac76c515");
-const LLUUID ANIM_AGENT_BLOW_KISS = LLUUID("db84829b-462c-ee83-1e27-9bbee66bd624");
-const LLUUID ANIM_AGENT_BORED = LLUUID("b906c4ba-703b-1940-32a3-0c7f7d791510");
-const LLUUID ANIM_AGENT_BOW = LLUUID("82e99230-c906-1403-4d9c-3889dd98daba");
-const LLUUID ANIM_AGENT_BRUSH = LLUUID("349a3801-54f9-bf2c-3bd0-1ac89772af01");
-const LLUUID ANIM_AGENT_BUSY = LLUUID("efcf670c-2d18-8128-973a-034ebc806b67");
-const LLUUID ANIM_AGENT_CLAP = LLUUID("9b0c1c4e-8ac7-7969-1494-28c874c4f668");
-const LLUUID ANIM_AGENT_COURTBOW = LLUUID("9ba1c942-08be-e43a-fb29-16ad440efc50");
-const LLUUID ANIM_AGENT_CROUCH = LLUUID("201f3fdf-cb1f-dbec-201f-7333e328ae7c");
-const LLUUID ANIM_AGENT_CROUCHWALK = LLUUID("47f5f6fb-22e5-ae44-f871-73aaaf4a6022");
-const LLUUID ANIM_AGENT_CRY = LLUUID("92624d3e-1068-f1aa-a5ec-8244585193ed");
-const LLUUID ANIM_AGENT_CUSTOMIZE = LLUUID("038fcec9-5ebd-8a8e-0e2e-6e71a0a1ac53");
-const LLUUID ANIM_AGENT_CUSTOMIZE_DONE = LLUUID("6883a61a-b27b-5914-a61e-dda118a9ee2c");
-const LLUUID ANIM_AGENT_DANCE1 = LLUUID("b68a3d7c-de9e-fc87-eec8-543d787e5b0d");
-const LLUUID ANIM_AGENT_DANCE2 = LLUUID("928cae18-e31d-76fd-9cc9-2f55160ff818");
-const LLUUID ANIM_AGENT_DANCE3 = LLUUID("30047778-10ea-1af7-6881-4db7a3a5a114");
-const LLUUID ANIM_AGENT_DANCE4 = LLUUID("951469f4-c7b2-c818-9dee-ad7eea8c30b7");
-const LLUUID ANIM_AGENT_DANCE5 = LLUUID("4bd69a1d-1114-a0b4-625f-84e0a5237155");
-const LLUUID ANIM_AGENT_DANCE6 = LLUUID("cd28b69b-9c95-bb78-3f94-8d605ff1bb12");
-const LLUUID ANIM_AGENT_DANCE7 = LLUUID("a54d8ee2-28bb-80a9-7f0c-7afbbe24a5d6");
-const LLUUID ANIM_AGENT_DANCE8 = LLUUID("b0dc417c-1f11-af36-2e80-7e7489fa7cdc");
-const LLUUID ANIM_AGENT_DEAD = LLUUID("57abaae6-1d17-7b1b-5f98-6d11a6411276");
-const LLUUID ANIM_AGENT_DRINK = LLUUID("0f86e355-dd31-a61c-fdb0-3a96b9aad05f");
-const LLUUID ANIM_AGENT_EMBARRASSED = LLUUID("514af488-9051-044a-b3fc-d4dbf76377c6");
-const LLUUID ANIM_AGENT_EXPRESS_AFRAID = LLUUID("aa2df84d-cf8f-7218-527b-424a52de766e");
-const LLUUID ANIM_AGENT_EXPRESS_ANGER = LLUUID("1a03b575-9634-b62a-5767-3a679e81f4de");
-const LLUUID ANIM_AGENT_EXPRESS_BORED = LLUUID("214aa6c1-ba6a-4578-f27c-ce7688f61d0d");
-const LLUUID ANIM_AGENT_EXPRESS_CRY = LLUUID("d535471b-85bf-3b4d-a542-93bea4f59d33");
-const LLUUID ANIM_AGENT_EXPRESS_DISDAIN = LLUUID("d4416ff1-09d3-300f-4183-1b68a19b9fc1");
-const LLUUID ANIM_AGENT_EXPRESS_EMBARRASSED = LLUUID("0b8c8211-d78c-33e8-fa28-c51a9594e424");
-const LLUUID ANIM_AGENT_EXPRESS_FROWN = LLUUID("fee3df48-fa3d-1015-1e26-a205810e3001");
-const LLUUID ANIM_AGENT_EXPRESS_KISS = LLUUID("1e8d90cc-a84e-e135-884c-7c82c8b03a14");
-const LLUUID ANIM_AGENT_EXPRESS_LAUGH = LLUUID("62570842-0950-96f8-341c-809e65110823");
-const LLUUID ANIM_AGENT_EXPRESS_OPEN_MOUTH = LLUUID("d63bc1f9-fc81-9625-a0c6-007176d82eb7");
-const LLUUID ANIM_AGENT_EXPRESS_REPULSED = LLUUID("f76cda94-41d4-a229-2872-e0296e58afe1");
-const LLUUID ANIM_AGENT_EXPRESS_SAD = LLUUID("eb6ebfb2-a4b3-a19c-d388-4dd5c03823f7");
-const LLUUID ANIM_AGENT_EXPRESS_SHRUG = LLUUID("a351b1bc-cc94-aac2-7bea-a7e6ebad15ef");
-const LLUUID ANIM_AGENT_EXPRESS_SMILE = LLUUID("b7c7c833-e3d3-c4e3-9fc0-131237446312");
-const LLUUID ANIM_AGENT_EXPRESS_SURPRISE = LLUUID("728646d9-cc79-08b2-32d6-937f0a835c24");
-const LLUUID ANIM_AGENT_EXPRESS_TONGUE_OUT = LLUUID("835965c6-7f2f-bda2-5deb-2478737f91bf");
-const LLUUID ANIM_AGENT_EXPRESS_TOOTHSMILE = LLUUID("b92ec1a5-e7ce-a76b-2b05-bcdb9311417e");
-const LLUUID ANIM_AGENT_EXPRESS_WINK = LLUUID("da020525-4d94-59d6-23d7-81fdebf33148");
-const LLUUID ANIM_AGENT_EXPRESS_WORRY = LLUUID("9c05e5c7-6f07-6ca4-ed5a-b230390c3950");
-const LLUUID ANIM_AGENT_FALLDOWN = LLUUID("666307d9-a860-572d-6fd4-c3ab8865c094");
-const LLUUID ANIM_AGENT_FEMALE_RUN_NEW = LLUUID("85995026-eade-5d78-d364-94a64512cb66");
-const LLUUID ANIM_AGENT_FEMALE_WALK = LLUUID("f5fc7433-043d-e819-8298-f519a119b688");
-const LLUUID ANIM_AGENT_FEMALE_WALK_NEW = LLUUID("d60c41d2-7c24-7074-d3fa-6101cea22a51");
-const LLUUID ANIM_AGENT_FINGER_WAG = LLUUID("c1bc7f36-3ba0-d844-f93c-93be945d644f");
-const LLUUID ANIM_AGENT_FIST_PUMP = LLUUID("7db00ccd-f380-f3ee-439d-61968ec69c8a");
-const LLUUID ANIM_AGENT_FLY = LLUUID("aec4610c-757f-bc4e-c092-c6e9caf18daf");
-const LLUUID ANIM_AGENT_FLYSLOW = LLUUID("2b5a38b2-5e00-3a97-a495-4c826bc443e6");
-const LLUUID ANIM_AGENT_HELLO = LLUUID("9b29cd61-c45b-5689-ded2-91756b8d76a9");
-const LLUUID ANIM_AGENT_HOLD_BAZOOKA_R = LLUUID("ef62d355-c815-4816-2474-b1acc21094a6");
-const LLUUID ANIM_AGENT_HOLD_BOW_L = LLUUID("8b102617-bcba-037b-86c1-b76219f90c88");
-const LLUUID ANIM_AGENT_HOLD_HANDGUN_R = LLUUID("efdc1727-8b8a-c800-4077-975fc27ee2f2");
-const LLUUID ANIM_AGENT_HOLD_RIFLE_R = LLUUID("3d94bad0-c55b-7dcc-8763-033c59405d33");
-const LLUUID ANIM_AGENT_HOLD_THROW_R = LLUUID("7570c7b5-1f22-56dd-56ef-a9168241bbb6");
-const LLUUID ANIM_AGENT_HOVER = LLUUID("4ae8016b-31b9-03bb-c401-b1ea941db41d");
-const LLUUID ANIM_AGENT_HOVER_DOWN = LLUUID("20f063ea-8306-2562-0b07-5c853b37b31e");
-const LLUUID ANIM_AGENT_HOVER_UP = LLUUID("62c5de58-cb33-5743-3d07-9e4cd4352864");
-const LLUUID ANIM_AGENT_IMPATIENT = LLUUID("5ea3991f-c293-392e-6860-91dfa01278a3");
-const LLUUID ANIM_AGENT_JUMP = LLUUID("2305bd75-1ca9-b03b-1faa-b176b8a8c49e");
-const LLUUID ANIM_AGENT_JUMP_FOR_JOY = LLUUID("709ea28e-1573-c023-8bf8-520c8bc637fa");
-const LLUUID ANIM_AGENT_KISS_MY_BUTT = LLUUID("19999406-3a3a-d58c-a2ac-d72e555dcf51");
-const LLUUID ANIM_AGENT_LAND = LLUUID("7a17b059-12b2-41b1-570a-186368b6aa6f");
-const LLUUID ANIM_AGENT_LAUGH_SHORT = LLUUID("ca5b3f14-3194-7a2b-c894-aa699b718d1f");
-const LLUUID ANIM_AGENT_MEDIUM_LAND = LLUUID("f4f00d6e-b9fe-9292-f4cb-0ae06ea58d57");
-const LLUUID ANIM_AGENT_MOTORCYCLE_SIT = LLUUID("08464f78-3a8e-2944-cba5-0c94aff3af29");
-const LLUUID ANIM_AGENT_MUSCLE_BEACH = LLUUID("315c3a41-a5f3-0ba4-27da-f893f769e69b");
-const LLUUID ANIM_AGENT_NO = LLUUID("5a977ed9-7f72-44e9-4c4c-6e913df8ae74");
-const LLUUID ANIM_AGENT_NO_UNHAPPY = LLUUID("d83fa0e5-97ed-7eb2-e798-7bd006215cb4");
-const LLUUID ANIM_AGENT_NYAH_NYAH = LLUUID("f061723d-0a18-754f-66ee-29a44795a32f");
-const LLUUID ANIM_AGENT_ONETWO_PUNCH = LLUUID("eefc79be-daae-a239-8c04-890f5d23654a");
-const LLUUID ANIM_AGENT_PEACE = LLUUID("b312b10e-65ab-a0a4-8b3c-1326ea8e3ed9");
-const LLUUID ANIM_AGENT_POINT_ME = LLUUID("17c024cc-eef2-f6a0-3527-9869876d7752");
-const LLUUID ANIM_AGENT_POINT_YOU = LLUUID("ec952cca-61ef-aa3b-2789-4d1344f016de");
-const LLUUID ANIM_AGENT_PRE_JUMP = LLUUID("7a4e87fe-de39-6fcb-6223-024b00893244");
-const LLUUID ANIM_AGENT_PUNCH_LEFT = LLUUID("f3300ad9-3462-1d07-2044-0fef80062da0");
-const LLUUID ANIM_AGENT_PUNCH_RIGHT = LLUUID("c8e42d32-7310-6906-c903-cab5d4a34656");
-const LLUUID ANIM_AGENT_REPULSED = LLUUID("36f81a92-f076-5893-dc4b-7c3795e487cf");
-const LLUUID ANIM_AGENT_ROUNDHOUSE_KICK = LLUUID("49aea43b-5ac3-8a44-b595-96100af0beda");
-const LLUUID ANIM_AGENT_RPS_COUNTDOWN = LLUUID("35db4f7e-28c2-6679-cea9-3ee108f7fc7f");
-const LLUUID ANIM_AGENT_RPS_PAPER = LLUUID("0836b67f-7f7b-f37b-c00a-460dc1521f5a");
-const LLUUID ANIM_AGENT_RPS_ROCK = LLUUID("42dd95d5-0bc6-6392-f650-777304946c0f");
-const LLUUID ANIM_AGENT_RPS_SCISSORS = LLUUID("16803a9f-5140-e042-4d7b-d28ba247c325");
-const LLUUID ANIM_AGENT_RUN = LLUUID("05ddbff8-aaa9-92a1-2b74-8fe77a29b445");
-const LLUUID ANIM_AGENT_RUN_NEW = LLUUID("1ab1b236-cd08-21e6-0cbc-0d923fc6eca2");
-const LLUUID ANIM_AGENT_SAD = LLUUID("0eb702e2-cc5a-9a88-56a5-661a55c0676a");
-const LLUUID ANIM_AGENT_SALUTE = LLUUID("cd7668a6-7011-d7e2-ead8-fc69eff1a104");
-const LLUUID ANIM_AGENT_SHOOT_BOW_L = LLUUID("e04d450d-fdb5-0432-fd68-818aaf5935f8");
-const LLUUID ANIM_AGENT_SHOUT = LLUUID("6bd01860-4ebd-127a-bb3d-d1427e8e0c42");
-const LLUUID ANIM_AGENT_SHRUG = LLUUID("70ea714f-3a97-d742-1b01-590a8fcd1db5");
-const LLUUID ANIM_AGENT_SIT = LLUUID("1a5fe8ac-a804-8a5d-7cbd-56bd83184568");
-const LLUUID ANIM_AGENT_SIT_FEMALE = LLUUID("b1709c8d-ecd3-54a1-4f28-d55ac0840782");
-const LLUUID ANIM_AGENT_SIT_GENERIC = LLUUID("245f3c54-f1c0-bf2e-811f-46d8eeb386e7");
-const LLUUID ANIM_AGENT_SIT_GROUND = LLUUID("1c7600d6-661f-b87b-efe2-d7421eb93c86");
-const LLUUID ANIM_AGENT_SIT_GROUND_CONSTRAINED = LLUUID("1a2bd58e-87ff-0df8-0b4c-53e047b0bb6e");
-const LLUUID ANIM_AGENT_SIT_TO_STAND = LLUUID("a8dee56f-2eae-9e7a-05a2-6fb92b97e21e");
-const LLUUID ANIM_AGENT_SLEEP = LLUUID("f2bed5f9-9d44-39af-b0cd-257b2a17fe40");
-const LLUUID ANIM_AGENT_SMOKE_IDLE = LLUUID("d2f2ee58-8ad1-06c9-d8d3-3827ba31567a");
-const LLUUID ANIM_AGENT_SMOKE_INHALE = LLUUID("6802d553-49da-0778-9f85-1599a2266526");
-const LLUUID ANIM_AGENT_SMOKE_THROW_DOWN = LLUUID("0a9fb970-8b44-9114-d3a9-bf69cfe804d6");
-const LLUUID ANIM_AGENT_SNAPSHOT = LLUUID("eae8905b-271a-99e2-4c0e-31106afd100c");
-const LLUUID ANIM_AGENT_STAND = LLUUID("2408fe9e-df1d-1d7d-f4ff-1384fa7b350f");
-const LLUUID ANIM_AGENT_STANDUP = LLUUID("3da1d753-028a-5446-24f3-9c9b856d9422");
-const LLUUID ANIM_AGENT_STAND_1 = LLUUID("15468e00-3400-bb66-cecc-646d7c14458e");
-const LLUUID ANIM_AGENT_STAND_2 = LLUUID("370f3a20-6ca6-9971-848c-9a01bc42ae3c");
-const LLUUID ANIM_AGENT_STAND_3 = LLUUID("42b46214-4b44-79ae-deb8-0df61424ff4b");
-const LLUUID ANIM_AGENT_STAND_4 = LLUUID("f22fed8b-a5ed-2c93-64d5-bdd8b93c889f");
-const LLUUID ANIM_AGENT_STRETCH = LLUUID("80700431-74ec-a008-14f8-77575e73693f");
-const LLUUID ANIM_AGENT_STRIDE = LLUUID("1cb562b0-ba21-2202-efb3-30f82cdf9595");
-const LLUUID ANIM_AGENT_SURF = LLUUID("41426836-7437-7e89-025d-0aa4d10f1d69");
-const LLUUID ANIM_AGENT_SURPRISE = LLUUID("313b9881-4302-73c0-c7d0-0e7a36b6c224");
-const LLUUID ANIM_AGENT_SWORD_STRIKE = LLUUID("85428680-6bf9-3e64-b489-6f81087c24bd");
-const LLUUID ANIM_AGENT_TALK = LLUUID("5c682a95-6da4-a463-0bf6-0f5b7be129d1");
-const LLUUID ANIM_AGENT_TANTRUM = LLUUID("11000694-3f41-adc2-606b-eee1d66f3724");
-const LLUUID ANIM_AGENT_THROW_R = LLUUID("aa134404-7dac-7aca-2cba-435f9db875ca");
-const LLUUID ANIM_AGENT_TRYON_SHIRT = LLUUID("83ff59fe-2346-f236-9009-4e3608af64c1");
-const LLUUID ANIM_AGENT_TURNLEFT = LLUUID("56e0ba0d-4a9f-7f27-6117-32f2ebbf6135");
-const LLUUID ANIM_AGENT_TURNRIGHT = LLUUID("2d6daa51-3192-6794-8e2e-a15f8338ec30");
-const LLUUID ANIM_AGENT_TYPE = LLUUID("c541c47f-e0c0-058b-ad1a-d6ae3a4584d9");
-const LLUUID ANIM_AGENT_WALK = LLUUID("6ed24bd8-91aa-4b12-ccc7-c97c857ab4e0");
-const LLUUID ANIM_AGENT_WALK_NEW = LLUUID("33339176-7ddc-9397-94a4-bf3403cbc8f5");
-const LLUUID ANIM_AGENT_WHISPER = LLUUID("7693f268-06c7-ea71-fa21-2b30d6533f8f");
-const LLUUID ANIM_AGENT_WHISTLE = LLUUID("b1ed7982-c68e-a982-7561-52a88a5298c0");
-const LLUUID ANIM_AGENT_WINK = LLUUID("869ecdad-a44b-671e-3266-56aef2e3ac2e");
-const LLUUID ANIM_AGENT_WINK_HOLLYWOOD = LLUUID("c0c4030f-c02b-49de-24ba-2331f43fe41c");
-const LLUUID ANIM_AGENT_WORRY = LLUUID("9f496bd2-589a-709f-16cc-69bf7df1d36c");
-const LLUUID ANIM_AGENT_YES = LLUUID("15dd911d-be82-2856-26db-27659b142875");
-const LLUUID ANIM_AGENT_YES_HAPPY = LLUUID("b8c8b2a3-9008-1771-3bfc-90924955ab2d");
-const LLUUID ANIM_AGENT_YOGA_FLOAT = LLUUID("42ecd00b-9947-a97c-400a-bbc9174c7aeb");
+LLUUID const ANIM_AGENT_AFRAID ("6b61c8e8-4747-0d75-12d7-e49ff207a4ca");
+LLUUID const ANIM_AGENT_AIM_BAZOOKA_R ("b5b4a67d-0aee-30d2-72cd-77b333e932ef");
+LLUUID const ANIM_AGENT_AIM_BOW_L ("46bb4359-de38-4ed8-6a22-f1f52fe8f506");
+LLUUID const ANIM_AGENT_AIM_HANDGUN_R ("3147d815-6338-b932-f011-16b56d9ac18b");
+LLUUID const ANIM_AGENT_AIM_RIFLE_R ("ea633413-8006-180a-c3ba-96dd1d756720");
+LLUUID const ANIM_AGENT_ANGRY ("5747a48e-073e-c331-f6f3-7c2149613d3e");
+LLUUID const ANIM_AGENT_AWAY ("fd037134-85d4-f241-72c6-4f42164fedee");
+LLUUID const ANIM_AGENT_BACKFLIP ("c4ca6188-9127-4f31-0158-23c4e2f93304");
+LLUUID const ANIM_AGENT_BELLY_LAUGH ("18b3a4b5-b463-bd48-e4b6-71eaac76c515");
+LLUUID const ANIM_AGENT_BLOW_KISS ("db84829b-462c-ee83-1e27-9bbee66bd624");
+LLUUID const ANIM_AGENT_BORED ("b906c4ba-703b-1940-32a3-0c7f7d791510");
+LLUUID const ANIM_AGENT_BOW ("82e99230-c906-1403-4d9c-3889dd98daba");
+LLUUID const ANIM_AGENT_BRUSH ("349a3801-54f9-bf2c-3bd0-1ac89772af01");
+LLUUID const ANIM_AGENT_BUSY ("efcf670c-2d18-8128-973a-034ebc806b67");
+LLUUID const ANIM_AGENT_CLAP ("9b0c1c4e-8ac7-7969-1494-28c874c4f668");
+LLUUID const ANIM_AGENT_COURTBOW ("9ba1c942-08be-e43a-fb29-16ad440efc50");
+LLUUID const ANIM_AGENT_CROUCH ("201f3fdf-cb1f-dbec-201f-7333e328ae7c");
+LLUUID const ANIM_AGENT_CROUCHWALK ("47f5f6fb-22e5-ae44-f871-73aaaf4a6022");
+LLUUID const ANIM_AGENT_CRY ("92624d3e-1068-f1aa-a5ec-8244585193ed");
+LLUUID const ANIM_AGENT_CUSTOMIZE ("038fcec9-5ebd-8a8e-0e2e-6e71a0a1ac53");
+LLUUID const ANIM_AGENT_CUSTOMIZE_DONE ("6883a61a-b27b-5914-a61e-dda118a9ee2c");
+LLUUID const ANIM_AGENT_DANCE1 ("b68a3d7c-de9e-fc87-eec8-543d787e5b0d");
+LLUUID const ANIM_AGENT_DANCE2 ("928cae18-e31d-76fd-9cc9-2f55160ff818");
+LLUUID const ANIM_AGENT_DANCE3 ("30047778-10ea-1af7-6881-4db7a3a5a114");
+LLUUID const ANIM_AGENT_DANCE4 ("951469f4-c7b2-c818-9dee-ad7eea8c30b7");
+LLUUID const ANIM_AGENT_DANCE5 ("4bd69a1d-1114-a0b4-625f-84e0a5237155");
+LLUUID const ANIM_AGENT_DANCE6 ("cd28b69b-9c95-bb78-3f94-8d605ff1bb12");
+LLUUID const ANIM_AGENT_DANCE7 ("a54d8ee2-28bb-80a9-7f0c-7afbbe24a5d6");
+LLUUID const ANIM_AGENT_DANCE8 ("b0dc417c-1f11-af36-2e80-7e7489fa7cdc");
+LLUUID const ANIM_AGENT_DEAD ("57abaae6-1d17-7b1b-5f98-6d11a6411276");
+LLUUID const ANIM_AGENT_DRINK ("0f86e355-dd31-a61c-fdb0-3a96b9aad05f");
+LLUUID const ANIM_AGENT_EMBARRASSED ("514af488-9051-044a-b3fc-d4dbf76377c6");
+LLUUID const ANIM_AGENT_EXPRESS_AFRAID ("aa2df84d-cf8f-7218-527b-424a52de766e");
+LLUUID const ANIM_AGENT_EXPRESS_ANGER ("1a03b575-9634-b62a-5767-3a679e81f4de");
+LLUUID const ANIM_AGENT_EXPRESS_BORED ("214aa6c1-ba6a-4578-f27c-ce7688f61d0d");
+LLUUID const ANIM_AGENT_EXPRESS_CRY ("d535471b-85bf-3b4d-a542-93bea4f59d33");
+LLUUID const ANIM_AGENT_EXPRESS_DISDAIN ("d4416ff1-09d3-300f-4183-1b68a19b9fc1");
+LLUUID const ANIM_AGENT_EXPRESS_EMBARRASSED ("0b8c8211-d78c-33e8-fa28-c51a9594e424");
+LLUUID const ANIM_AGENT_EXPRESS_FROWN ("fee3df48-fa3d-1015-1e26-a205810e3001");
+LLUUID const ANIM_AGENT_EXPRESS_KISS ("1e8d90cc-a84e-e135-884c-7c82c8b03a14");
+LLUUID const ANIM_AGENT_EXPRESS_LAUGH ("62570842-0950-96f8-341c-809e65110823");
+LLUUID const ANIM_AGENT_EXPRESS_OPEN_MOUTH ("d63bc1f9-fc81-9625-a0c6-007176d82eb7");
+LLUUID const ANIM_AGENT_EXPRESS_REPULSED ("f76cda94-41d4-a229-2872-e0296e58afe1");
+LLUUID const ANIM_AGENT_EXPRESS_SAD ("eb6ebfb2-a4b3-a19c-d388-4dd5c03823f7");
+LLUUID const ANIM_AGENT_EXPRESS_SHRUG ("a351b1bc-cc94-aac2-7bea-a7e6ebad15ef");
+LLUUID const ANIM_AGENT_EXPRESS_SMILE ("b7c7c833-e3d3-c4e3-9fc0-131237446312");
+LLUUID const ANIM_AGENT_EXPRESS_SURPRISE ("728646d9-cc79-08b2-32d6-937f0a835c24");
+LLUUID const ANIM_AGENT_EXPRESS_TONGUE_OUT ("835965c6-7f2f-bda2-5deb-2478737f91bf");
+LLUUID const ANIM_AGENT_EXPRESS_TOOTHSMILE ("b92ec1a5-e7ce-a76b-2b05-bcdb9311417e");
+LLUUID const ANIM_AGENT_EXPRESS_WINK ("da020525-4d94-59d6-23d7-81fdebf33148");
+LLUUID const ANIM_AGENT_EXPRESS_WORRY ("9c05e5c7-6f07-6ca4-ed5a-b230390c3950");
+LLUUID const ANIM_AGENT_FALLDOWN ("666307d9-a860-572d-6fd4-c3ab8865c094");
+LLUUID const ANIM_AGENT_FEMALE_RUN_NEW ("85995026-eade-5d78-d364-94a64512cb66");
+LLUUID const ANIM_AGENT_FEMALE_WALK ("f5fc7433-043d-e819-8298-f519a119b688");
+LLUUID const ANIM_AGENT_FEMALE_WALK_NEW ("d60c41d2-7c24-7074-d3fa-6101cea22a51");
+LLUUID const ANIM_AGENT_FINGER_WAG ("c1bc7f36-3ba0-d844-f93c-93be945d644f");
+LLUUID const ANIM_AGENT_FIST_PUMP ("7db00ccd-f380-f3ee-439d-61968ec69c8a");
+LLUUID const ANIM_AGENT_FLY ("aec4610c-757f-bc4e-c092-c6e9caf18daf");
+LLUUID const ANIM_AGENT_FLYSLOW ("2b5a38b2-5e00-3a97-a495-4c826bc443e6");
+LLUUID const ANIM_AGENT_HELLO ("9b29cd61-c45b-5689-ded2-91756b8d76a9");
+LLUUID const ANIM_AGENT_HOLD_BAZOOKA_R ("ef62d355-c815-4816-2474-b1acc21094a6");
+LLUUID const ANIM_AGENT_HOLD_BOW_L ("8b102617-bcba-037b-86c1-b76219f90c88");
+LLUUID const ANIM_AGENT_HOLD_HANDGUN_R ("efdc1727-8b8a-c800-4077-975fc27ee2f2");
+LLUUID const ANIM_AGENT_HOLD_RIFLE_R ("3d94bad0-c55b-7dcc-8763-033c59405d33");
+LLUUID const ANIM_AGENT_HOLD_THROW_R ("7570c7b5-1f22-56dd-56ef-a9168241bbb6");
+LLUUID const ANIM_AGENT_HOVER ("4ae8016b-31b9-03bb-c401-b1ea941db41d");
+LLUUID const ANIM_AGENT_HOVER_DOWN ("20f063ea-8306-2562-0b07-5c853b37b31e");
+LLUUID const ANIM_AGENT_HOVER_UP ("62c5de58-cb33-5743-3d07-9e4cd4352864");
+LLUUID const ANIM_AGENT_IMPATIENT ("5ea3991f-c293-392e-6860-91dfa01278a3");
+LLUUID const ANIM_AGENT_JUMP ("2305bd75-1ca9-b03b-1faa-b176b8a8c49e");
+LLUUID const ANIM_AGENT_JUMP_FOR_JOY ("709ea28e-1573-c023-8bf8-520c8bc637fa");
+LLUUID const ANIM_AGENT_KISS_MY_BUTT ("19999406-3a3a-d58c-a2ac-d72e555dcf51");
+LLUUID const ANIM_AGENT_LAND ("7a17b059-12b2-41b1-570a-186368b6aa6f");
+LLUUID const ANIM_AGENT_LAUGH_SHORT ("ca5b3f14-3194-7a2b-c894-aa699b718d1f");
+LLUUID const ANIM_AGENT_MEDIUM_LAND ("f4f00d6e-b9fe-9292-f4cb-0ae06ea58d57");
+LLUUID const ANIM_AGENT_MOTORCYCLE_SIT ("08464f78-3a8e-2944-cba5-0c94aff3af29");
+LLUUID const ANIM_AGENT_MUSCLE_BEACH ("315c3a41-a5f3-0ba4-27da-f893f769e69b");
+LLUUID const ANIM_AGENT_NO ("5a977ed9-7f72-44e9-4c4c-6e913df8ae74");
+LLUUID const ANIM_AGENT_NO_UNHAPPY ("d83fa0e5-97ed-7eb2-e798-7bd006215cb4");
+LLUUID const ANIM_AGENT_NYAH_NYAH ("f061723d-0a18-754f-66ee-29a44795a32f");
+LLUUID const ANIM_AGENT_ONETWO_PUNCH ("eefc79be-daae-a239-8c04-890f5d23654a");
+LLUUID const ANIM_AGENT_PEACE ("b312b10e-65ab-a0a4-8b3c-1326ea8e3ed9");
+LLUUID const ANIM_AGENT_POINT_ME ("17c024cc-eef2-f6a0-3527-9869876d7752");
+LLUUID const ANIM_AGENT_POINT_YOU ("ec952cca-61ef-aa3b-2789-4d1344f016de");
+LLUUID const ANIM_AGENT_PRE_JUMP ("7a4e87fe-de39-6fcb-6223-024b00893244");
+LLUUID const ANIM_AGENT_PUNCH_LEFT ("f3300ad9-3462-1d07-2044-0fef80062da0");
+LLUUID const ANIM_AGENT_PUNCH_RIGHT ("c8e42d32-7310-6906-c903-cab5d4a34656");
+LLUUID const ANIM_AGENT_REPULSED ("36f81a92-f076-5893-dc4b-7c3795e487cf");
+LLUUID const ANIM_AGENT_ROUNDHOUSE_KICK ("49aea43b-5ac3-8a44-b595-96100af0beda");
+LLUUID const ANIM_AGENT_RPS_COUNTDOWN ("35db4f7e-28c2-6679-cea9-3ee108f7fc7f");
+LLUUID const ANIM_AGENT_RPS_PAPER ("0836b67f-7f7b-f37b-c00a-460dc1521f5a");
+LLUUID const ANIM_AGENT_RPS_ROCK ("42dd95d5-0bc6-6392-f650-777304946c0f");
+LLUUID const ANIM_AGENT_RPS_SCISSORS ("16803a9f-5140-e042-4d7b-d28ba247c325");
+LLUUID const ANIM_AGENT_RUN ("05ddbff8-aaa9-92a1-2b74-8fe77a29b445");
+LLUUID const ANIM_AGENT_RUN_NEW ("1ab1b236-cd08-21e6-0cbc-0d923fc6eca2");
+LLUUID const ANIM_AGENT_SAD ("0eb702e2-cc5a-9a88-56a5-661a55c0676a");
+LLUUID const ANIM_AGENT_SALUTE ("cd7668a6-7011-d7e2-ead8-fc69eff1a104");
+LLUUID const ANIM_AGENT_SHOOT_BOW_L ("e04d450d-fdb5-0432-fd68-818aaf5935f8");
+LLUUID const ANIM_AGENT_SHOUT ("6bd01860-4ebd-127a-bb3d-d1427e8e0c42");
+LLUUID const ANIM_AGENT_SHRUG ("70ea714f-3a97-d742-1b01-590a8fcd1db5");
+LLUUID const ANIM_AGENT_SIT ("1a5fe8ac-a804-8a5d-7cbd-56bd83184568");
+LLUUID const ANIM_AGENT_SIT_FEMALE ("b1709c8d-ecd3-54a1-4f28-d55ac0840782");
+LLUUID const ANIM_AGENT_SIT_GENERIC ("245f3c54-f1c0-bf2e-811f-46d8eeb386e7");
+LLUUID const ANIM_AGENT_SIT_GROUND ("1c7600d6-661f-b87b-efe2-d7421eb93c86");
+LLUUID const ANIM_AGENT_SIT_GROUND_CONSTRAINED("1a2bd58e-87ff-0df8-0b4c-53e047b0bb6e");
+LLUUID const ANIM_AGENT_SIT_TO_STAND ("a8dee56f-2eae-9e7a-05a2-6fb92b97e21e");
+LLUUID const ANIM_AGENT_SLEEP ("f2bed5f9-9d44-39af-b0cd-257b2a17fe40");
+LLUUID const ANIM_AGENT_SMOKE_IDLE ("d2f2ee58-8ad1-06c9-d8d3-3827ba31567a");
+LLUUID const ANIM_AGENT_SMOKE_INHALE ("6802d553-49da-0778-9f85-1599a2266526");
+LLUUID const ANIM_AGENT_SMOKE_THROW_DOWN ("0a9fb970-8b44-9114-d3a9-bf69cfe804d6");
+LLUUID const ANIM_AGENT_SNAPSHOT ("eae8905b-271a-99e2-4c0e-31106afd100c");
+LLUUID const ANIM_AGENT_STAND ("2408fe9e-df1d-1d7d-f4ff-1384fa7b350f");
+LLUUID const ANIM_AGENT_STANDUP ("3da1d753-028a-5446-24f3-9c9b856d9422");
+LLUUID const ANIM_AGENT_STAND_1 ("15468e00-3400-bb66-cecc-646d7c14458e");
+LLUUID const ANIM_AGENT_STAND_2 ("370f3a20-6ca6-9971-848c-9a01bc42ae3c");
+LLUUID const ANIM_AGENT_STAND_3 ("42b46214-4b44-79ae-deb8-0df61424ff4b");
+LLUUID const ANIM_AGENT_STAND_4 ("f22fed8b-a5ed-2c93-64d5-bdd8b93c889f");
+LLUUID const ANIM_AGENT_STRETCH ("80700431-74ec-a008-14f8-77575e73693f");
+LLUUID const ANIM_AGENT_STRIDE ("1cb562b0-ba21-2202-efb3-30f82cdf9595");
+LLUUID const ANIM_AGENT_SURF ("41426836-7437-7e89-025d-0aa4d10f1d69");
+LLUUID const ANIM_AGENT_SURPRISE ("313b9881-4302-73c0-c7d0-0e7a36b6c224");
+LLUUID const ANIM_AGENT_SWORD_STRIKE ("85428680-6bf9-3e64-b489-6f81087c24bd");
+LLUUID const ANIM_AGENT_TALK ("5c682a95-6da4-a463-0bf6-0f5b7be129d1");
+LLUUID const ANIM_AGENT_TANTRUM ("11000694-3f41-adc2-606b-eee1d66f3724");
+LLUUID const ANIM_AGENT_THROW_R ("aa134404-7dac-7aca-2cba-435f9db875ca");
+LLUUID const ANIM_AGENT_TRYON_SHIRT ("83ff59fe-2346-f236-9009-4e3608af64c1");
+LLUUID const ANIM_AGENT_TURNLEFT ("56e0ba0d-4a9f-7f27-6117-32f2ebbf6135");
+LLUUID const ANIM_AGENT_TURNRIGHT ("2d6daa51-3192-6794-8e2e-a15f8338ec30");
+LLUUID const ANIM_AGENT_TYPE ("c541c47f-e0c0-058b-ad1a-d6ae3a4584d9");
+LLUUID const ANIM_AGENT_WALK ("6ed24bd8-91aa-4b12-ccc7-c97c857ab4e0");
+LLUUID const ANIM_AGENT_WALK_NEW ("33339176-7ddc-9397-94a4-bf3403cbc8f5");
+LLUUID const ANIM_AGENT_WHISPER ("7693f268-06c7-ea71-fa21-2b30d6533f8f");
+LLUUID const ANIM_AGENT_WHISTLE ("b1ed7982-c68e-a982-7561-52a88a5298c0");
+LLUUID const ANIM_AGENT_WINK ("869ecdad-a44b-671e-3266-56aef2e3ac2e");
+LLUUID const ANIM_AGENT_WINK_HOLLYWOOD ("c0c4030f-c02b-49de-24ba-2331f43fe41c");
+LLUUID const ANIM_AGENT_WORRY ("9f496bd2-589a-709f-16cc-69bf7df1d36c");
+LLUUID const ANIM_AGENT_YES ("15dd911d-be82-2856-26db-27659b142875");
+LLUUID const ANIM_AGENT_YES_HAPPY ("b8c8b2a3-9008-1771-3bfc-90924955ab2d");
+LLUUID const ANIM_AGENT_YOGA_FLOAT ("42ecd00b-9947-a97c-400a-bbc9174c7aeb");
LLUUID AGENT_WALK_ANIMS[] = {ANIM_AGENT_WALK, ANIM_AGENT_RUN, ANIM_AGENT_CROUCHWALK, ANIM_AGENT_TURNLEFT, ANIM_AGENT_TURNRIGHT};
S32 NUM_AGENT_WALK_ANIMS = LL_ARRAY_SIZE(AGENT_WALK_ANIMS);
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 9342a22d46..dc9f93df3b 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -313,6 +313,7 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(reflection "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
# *TODO - reenable these once tcmalloc libs no longer break the build.
#ADD_BUILD_TEST(llallocator llcommon)
diff --git a/indra/llcommon/llavatarconstants.h b/indra/llcommon/llavatarconstants.h
index 596b0643ef..f47f447b45 100644
--- a/indra/llcommon/llavatarconstants.h
+++ b/indra/llcommon/llavatarconstants.h
@@ -46,10 +46,10 @@ const U32 AVATAR_TRANSACTED = 0x1 << 3; // whether avatar has actively used p
const U32 AVATAR_ONLINE = 0x1 << 4; // the online status of this avatar, if known.
const U32 AVATAR_AGEVERIFIED = 0x1 << 5; // whether avatar has been age-verified
-static const std::string VISIBILITY_DEFAULT("default");
-static const std::string VISIBILITY_HIDDEN("hidden");
-static const std::string VISIBILITY_VISIBLE("visible");
-static const std::string VISIBILITY_INVISIBLE("invisible");
+char const* const VISIBILITY_DEFAULT = "default";
+char const* const VISIBILITY_HIDDEN = "hidden";
+char const* const VISIBILITY_VISIBLE = "visible";
+char const* const VISIBILITY_INVISIBLE = "invisible";
#endif
diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp
index d6e820d793..5b6d4efbe9 100644
--- a/indra/llcommon/lleventdispatcher.cpp
+++ b/indra/llcommon/lleventdispatcher.cpp
@@ -41,6 +41,354 @@
#include "llevents.h"
#include "llerror.h"
#include "llsdutil.h"
+#include "stringize.h"
+#include <memory> // std::auto_ptr
+
+/*****************************************************************************
+* LLSDArgsSource
+*****************************************************************************/
+/**
+ * Store an LLSD array, producing its elements one at a time. Die with LL_ERRS
+ * if the consumer requests more elements than the array contains.
+ */
+class LL_COMMON_API LLSDArgsSource
+{
+public:
+ LLSDArgsSource(const std::string function, const LLSD& args);
+ ~LLSDArgsSource();
+
+ LLSD next();
+
+ void done() const;
+
+private:
+ std::string _function;
+ LLSD _args;
+ LLSD::Integer _index;
+};
+
+LLSDArgsSource::LLSDArgsSource(const std::string function, const LLSD& args):
+ _function(function),
+ _args(args),
+ _index(0)
+{
+ if (! (_args.isUndefined() || _args.isArray()))
+ {
+ LL_ERRS("LLSDArgsSource") << _function << " needs an args array instead of "
+ << _args << LL_ENDL;
+ }
+}
+
+LLSDArgsSource::~LLSDArgsSource()
+{
+ done();
+}
+
+LLSD LLSDArgsSource::next()
+{
+ if (_index >= _args.size())
+ {
+ LL_ERRS("LLSDArgsSource") << _function << " requires more arguments than the "
+ << _args.size() << " provided: " << _args << LL_ENDL;
+ }
+ return _args[_index++];
+}
+
+void LLSDArgsSource::done() const
+{
+ if (_index < _args.size())
+ {
+ LL_WARNS("LLSDArgsSource") << _function << " only consumed " << _index
+ << " of the " << _args.size() << " arguments provided: "
+ << _args << LL_ENDL;
+ }
+}
+
+/*****************************************************************************
+* LLSDArgsMapper
+*****************************************************************************/
+/**
+ * From a formal parameters description and a map of arguments, construct an
+ * arguments array.
+ *
+ * That is, given:
+ * - an LLSD array of length n containing parameter-name strings,
+ * corresponding to the arguments of a function of interest
+ * - an LLSD collection specifying default parameter values, either:
+ * - an LLSD array of length m <= n, matching the rightmost m params, or
+ * - an LLSD map explicitly stating default name=value pairs
+ * - an LLSD map of parameter names and actual values for a particular
+ * function call
+ * construct an LLSD array of actual argument values for this function call.
+ *
+ * The parameter-names array and the defaults collection describe the function
+ * being called. The map might vary with every call, providing argument values
+ * for the described parameters.
+ *
+ * The array of parameter names must match the number of parameters expected
+ * by the function of interest.
+ *
+ * If you pass a map of default parameter values, it provides default values
+ * as you might expect. It is an error to specify a default value for a name
+ * not listed in the parameters array.
+ *
+ * If you pass an array of default parameter values, it is mapped to the
+ * rightmost m of the n parameter names. It is an error if the default-values
+ * array is longer than the parameter-names array. Consider the following
+ * parameter names: ["a", "b", "c", "d"].
+ *
+ * - An empty array of default values (or an isUndefined() value) asserts that
+ * every one of the above parameter names is required.
+ * - An array of four default values [1, 2, 3, 4] asserts that every one of
+ * the above parameters is optional. If the current parameter map is empty,
+ * they will be passed to the function as [1, 2, 3, 4].
+ * - An array of two default values [11, 12] asserts that parameters "a" and
+ * "b" are required, while "c" and "d" are optional, having default values
+ * "c"=11 and "d"=12.
+ *
+ * The arguments array is constructed as follows:
+ *
+ * - Arguments-map keys not found in the parameter-names array are ignored.
+ * - Entries from the map provide values for an improper subset of the
+ * parameters named in the parameter-names array. This results in a
+ * tentative values array with "holes." (size of map) + (number of holes) =
+ * (size of names array)
+ * - Holes are filled with the default values.
+ * - Any remaining holes constitute an error.
+ */
+class LL_COMMON_API LLSDArgsMapper
+{
+public:
+ /// Accept description of function: function name, param names, param
+ /// default values
+ LLSDArgsMapper(const std::string& function, const LLSD& names, const LLSD& defaults);
+
+ /// Given arguments map, return LLSD::Array of parameter values, or LL_ERRS.
+ LLSD map(const LLSD& argsmap) const;
+
+private:
+ static std::string formatlist(const LLSD&);
+
+ // The function-name string is purely descriptive. We want error messages
+ // to be able to indicate which function's LLSDArgsMapper has the problem.
+ std::string _function;
+ // Store the names array pretty much as given.
+ LLSD _names;
+ // Though we're handed an array of name strings, it's more useful to us to
+ // store it as a map from name string to position index. Of course that's
+ // easy to generate from the incoming names array, but why do it more than
+ // once?
+ typedef std::map<LLSD::String, LLSD::Integer> IndexMap;
+ IndexMap _indexes;
+ // Generated array of default values, aligned with the array of param names.
+ LLSD _defaults;
+ // Indicate whether we have a default value for each param.
+ typedef std::vector<char> FilledVector;
+ FilledVector _has_dft;
+};
+
+LLSDArgsMapper::LLSDArgsMapper(const std::string& function,
+ const LLSD& names, const LLSD& defaults):
+ _function(function),
+ _names(names),
+ _has_dft(names.size())
+{
+ if (! (_names.isUndefined() || _names.isArray()))
+ {
+ LL_ERRS("LLSDArgsMapper") << function << " names must be an array, not " << names << LL_ENDL;
+ }
+ LLSD::Integer nparams(_names.size());
+ // From _names generate _indexes.
+ for (LLSD::Integer ni = 0, nend = _names.size(); ni < nend; ++ni)
+ {
+ _indexes[_names[ni]] = ni;
+ }
+
+ // Presize _defaults() array so we don't have to resize it more than once.
+ // All entries are initialized to LLSD(); but since _has_dft is still all
+ // 0, they're all "holes" for now.
+ if (nparams)
+ {
+ _defaults[nparams - 1] = LLSD();
+ }
+
+ if (defaults.isUndefined() || defaults.isArray())
+ {
+ LLSD::Integer ndefaults = defaults.size();
+ // defaults is a (possibly empty) array. Right-align it with names.
+ if (ndefaults > nparams)
+ {
+ LL_ERRS("LLSDArgsMapper") << function << " names array " << names
+ << " shorter than defaults array " << defaults << LL_ENDL;
+ }
+
+ // Offset by which we slide defaults array right to right-align with
+ // _names array
+ LLSD::Integer offset = nparams - ndefaults;
+ // Fill rightmost _defaults entries from defaults, and mark them as
+ // filled
+ for (LLSD::Integer i = 0, iend = ndefaults; i < iend; ++i)
+ {
+ _defaults[i + offset] = defaults[i];
+ _has_dft[i + offset] = 1;
+ }
+ }
+ else if (defaults.isMap())
+ {
+ // defaults is a map. Use it to populate the _defaults array.
+ LLSD bogus;
+ for (LLSD::map_const_iterator mi(defaults.beginMap()), mend(defaults.endMap());
+ mi != mend; ++mi)
+ {
+ IndexMap::const_iterator ixit(_indexes.find(mi->first));
+ if (ixit == _indexes.end())
+ {
+ bogus.append(mi->first);
+ continue;
+ }
+
+ LLSD::Integer pos = ixit->second;
+ // Store default value at that position in the _defaults array.
+ _defaults[pos] = mi->second;
+ // Don't forget to record the fact that we've filled this
+ // position.
+ _has_dft[pos] = 1;
+ }
+ if (bogus.size())
+ {
+ LL_ERRS("LLSDArgsMapper") << function << " defaults specified for nonexistent params "
+ << formatlist(bogus) << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_ERRS("LLSDArgsMapper") << function << " defaults must be a map or an array, not "
+ << defaults << LL_ENDL;
+ }
+}
+
+LLSD LLSDArgsMapper::map(const LLSD& argsmap) const
+{
+ if (! (argsmap.isUndefined() || argsmap.isMap() || argsmap.isArray()))
+ {
+ LL_ERRS("LLSDArgsMapper") << _function << " map() needs a map or array, not "
+ << argsmap << LL_ENDL;
+ }
+ // Initialize the args array. Indexing a non-const LLSD array grows it
+ // to appropriate size, but we don't want to resize this one on each
+ // new operation. Just make it as big as we need before we start
+ // stuffing values into it.
+ LLSD args(LLSD::emptyArray());
+ if (_defaults.size() == 0)
+ {
+ // If this function requires no arguments, fast exit. (Don't try to
+ // assign to args[-1].)
+ return args;
+ }
+ args[_defaults.size() - 1] = LLSD();
+
+ // Get a vector of chars to indicate holes. It's tempting to just scan
+ // for LLSD::isUndefined() values after filling the args array from
+ // the map, but it's plausible for caller to explicitly pass
+ // isUndefined() as the value of some parameter name. That's legal
+ // since isUndefined() has well-defined conversions (default value)
+ // for LLSD data types. So use a whole separate array for detecting
+ // holes. (Avoid std::vector<bool> which is known to be odd -- can we
+ // iterate?)
+ FilledVector filled(args.size());
+
+ if (argsmap.isArray())
+ {
+ // Fill args from array. If there are too many args in passed array,
+ // ignore the rest.
+ LLSD::Integer size(argsmap.size());
+ if (size > args.size())
+ {
+ // We don't just use std::min() because we want to sneak in this
+ // warning if caller passes too many args.
+ LL_WARNS("LLSDArgsMapper") << _function << " needs " << args.size()
+ << " params, ignoring last " << (size - args.size())
+ << " of passed " << size << ": " << argsmap << LL_ENDL;
+ size = args.size();
+ }
+ for (LLSD::Integer i(0); i < size; ++i)
+ {
+ // Copy the actual argument from argsmap
+ args[i] = argsmap[i];
+ // Note that it's been filled
+ filled[i] = 1;
+ }
+ }
+ else
+ {
+ // argsmap is in fact a map. Walk the map.
+ for (LLSD::map_const_iterator mi(argsmap.beginMap()), mend(argsmap.endMap());
+ mi != mend; ++mi)
+ {
+ // mi->first is a parameter-name string, with mi->second its
+ // value. Look up the name's position index in _indexes.
+ IndexMap::const_iterator ixit(_indexes.find(mi->first));
+ if (ixit == _indexes.end())
+ {
+ // Allow for a map containing more params than were passed in
+ // our names array. Caller typically receives a map containing
+ // the function name, cruft such as reqid, etc. Ignore keys
+ // not defined in _indexes.
+ LL_DEBUGS("LLSDArgsMapper") << _function << " ignoring "
+ << mi->first << "=" << mi->second << LL_ENDL;
+ continue;
+ }
+ LLSD::Integer pos = ixit->second;
+ // Store the value at that position in the args array.
+ args[pos] = mi->second;
+ // Don't forget to record the fact that we've filled this
+ // position.
+ filled[pos] = 1;
+ }
+ }
+
+ // Fill any remaining holes from _defaults.
+ LLSD unfilled(LLSD::emptyArray());
+ for (LLSD::Integer i = 0, iend = args.size(); i < iend; ++i)
+ {
+ if (! filled[i])
+ {
+ // If there's no default value for this parameter, that's an
+ // error.
+ if (! _has_dft[i])
+ {
+ unfilled.append(_names[i]);
+ }
+ else
+ {
+ args[i] = _defaults[i];
+ }
+ }
+ }
+ // If any required args -- args without defaults -- were left unfilled
+ // by argsmap, that's a problem.
+ if (unfilled.size())
+ {
+ LL_ERRS("LLSDArgsMapper") << _function << " missing required arguments "
+ << formatlist(unfilled) << " from " << argsmap << LL_ENDL;
+ }
+
+ // done
+ return args;
+}
+
+std::string LLSDArgsMapper::formatlist(const LLSD& list)
+{
+ std::ostringstream out;
+ const char* delim = "";
+ for (LLSD::array_const_iterator li(list.beginArray()), lend(list.endArray());
+ li != lend; ++li)
+ {
+ out << delim << li->asString();
+ delim = ", ";
+ }
+ return out.str();
+}
LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key):
mDesc(desc),
@@ -52,12 +400,181 @@ LLEventDispatcher::~LLEventDispatcher()
{
}
+/**
+ * DispatchEntry subclass used for callables accepting(const LLSD&)
+ */
+struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchEntry
+{
+ LLSDDispatchEntry(const std::string& desc, const Callable& func, const LLSD& required):
+ DispatchEntry(desc),
+ mFunc(func),
+ mRequired(required)
+ {}
+
+ Callable mFunc;
+ LLSD mRequired;
+
+ virtual void call(const std::string& desc, const LLSD& event) const
+ {
+ // Validate the syntax of the event itself.
+ std::string mismatch(llsd_matches(mRequired, event));
+ if (! mismatch.empty())
+ {
+ LL_ERRS("LLEventDispatcher") << desc << ": bad request: " << mismatch << LL_ENDL;
+ }
+ // Event syntax looks good, go for it!
+ mFunc(event);
+ }
+
+ virtual LLSD addMetadata(LLSD meta) const
+ {
+ meta["required"] = mRequired;
+ return meta;
+ }
+};
+
+/**
+ * DispatchEntry subclass for passing LLSD to functions accepting
+ * arbitrary argument types (convertible via LLSDParam)
+ */
+struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::DispatchEntry
+{
+ ParamsDispatchEntry(const std::string& desc, const invoker_function& func):
+ DispatchEntry(desc),
+ mInvoker(func)
+ {}
+
+ invoker_function mInvoker;
+
+ virtual void call(const std::string& desc, const LLSD& event) const
+ {
+ LLSDArgsSource src(desc, event);
+ mInvoker(boost::bind(&LLSDArgsSource::next, boost::ref(src)));
+ }
+};
+
+/**
+ * DispatchEntry subclass for dispatching LLSD::Array to functions accepting
+ * arbitrary argument types (convertible via LLSDParam)
+ */
+struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
+{
+ ArrayParamsDispatchEntry(const std::string& desc, const invoker_function& func,
+ LLSD::Integer arity):
+ ParamsDispatchEntry(desc, func),
+ mArity(arity)
+ {}
+
+ LLSD::Integer mArity;
+
+ virtual LLSD addMetadata(LLSD meta) const
+ {
+ LLSD array(LLSD::emptyArray());
+ // Resize to number of arguments required
+ if (mArity)
+ array[mArity - 1] = LLSD();
+ llassert_always(array.size() == mArity);
+ meta["required"] = array;
+ return meta;
+ }
+};
+
+/**
+ * DispatchEntry subclass for dispatching LLSD::Map to functions accepting
+ * arbitrary argument types (convertible via LLSDParam)
+ */
+struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
+{
+ MapParamsDispatchEntry(const std::string& name, const std::string& desc,
+ const invoker_function& func,
+ const LLSD& params, const LLSD& defaults):
+ ParamsDispatchEntry(desc, func),
+ mMapper(name, params, defaults),
+ mRequired(LLSD::emptyMap())
+ {
+ // Build the set of all param keys, then delete the ones that are
+ // optional. What's left are the ones that are required.
+ for (LLSD::array_const_iterator pi(params.beginArray()), pend(params.endArray());
+ pi != pend; ++pi)
+ {
+ mRequired[pi->asString()] = LLSD();
+ }
+
+ if (defaults.isArray() || defaults.isUndefined())
+ {
+ // Right-align the params and defaults arrays.
+ LLSD::Integer offset = params.size() - defaults.size();
+ // Now the name of every defaults[i] is at params[i + offset].
+ for (LLSD::Integer i(0), iend(defaults.size()); i < iend; ++i)
+ {
+ // Erase this optional param from mRequired.
+ mRequired.erase(params[i + offset].asString());
+ // Instead, make an entry in mOptional with the default
+ // param's name and value.
+ mOptional[params[i + offset].asString()] = defaults[i];
+ }
+ }
+ else if (defaults.isMap())
+ {
+ // if defaults is already a map, then it's already in the form we
+ // intend to deliver in metadata
+ mOptional = defaults;
+ // Just delete from mRequired every key appearing in mOptional.
+ for (LLSD::map_const_iterator mi(mOptional.beginMap()), mend(mOptional.endMap());
+ mi != mend; ++mi)
+ {
+ mRequired.erase(mi->first);
+ }
+ }
+ }
+
+ LLSDArgsMapper mMapper;
+ LLSD mRequired;
+ LLSD mOptional;
+
+ virtual void call(const std::string& desc, const LLSD& event) const
+ {
+ // Just convert from LLSD::Map to LLSD::Array using mMapper, then pass
+ // to base-class call() method.
+ ParamsDispatchEntry::call(desc, mMapper.map(event));
+ }
+
+ virtual LLSD addMetadata(LLSD meta) const
+ {
+ meta["required"] = mRequired;
+ meta["optional"] = mOptional;
+ return meta;
+ }
+};
+
+void LLEventDispatcher::addArrayParamsDispatchEntry(const std::string& name,
+ const std::string& desc,
+ const invoker_function& invoker,
+ LLSD::Integer arity)
+{
+ mDispatch.insert(
+ DispatchMap::value_type(name, DispatchMap::mapped_type(
+ new ArrayParamsDispatchEntry(desc, invoker, arity))));
+}
+
+void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,
+ const std::string& desc,
+ const invoker_function& invoker,
+ const LLSD& params,
+ const LLSD& defaults)
+{
+ mDispatch.insert(
+ DispatchMap::value_type(name, DispatchMap::mapped_type(
+ new MapParamsDispatchEntry(name, desc, invoker, params, defaults))));
+}
+
/// Register a callable by name
void LLEventDispatcher::add(const std::string& name, const std::string& desc,
const Callable& callable, const LLSD& required)
{
- mDispatch.insert(DispatchMap::value_type(name,
- DispatchMap::mapped_type(callable, desc, required)));
+ mDispatch.insert(
+ DispatchMap::value_type(name, DispatchMap::mapped_type(
+ new LLSDDispatchEntry(desc, callable, required))));
}
void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const
@@ -83,7 +600,7 @@ bool LLEventDispatcher::remove(const std::string& name)
/// such callable exists, die with LL_ERRS.
void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
{
- if (! attemptCall(name, event))
+ if (! try_call(name, event))
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name
<< "' not found" << LL_ENDL;
@@ -98,44 +615,29 @@ void LLEventDispatcher::operator()(const LLSD& event) const
// This could/should be implemented in terms of the two-arg overload.
// However -- we can produce a more informative error message.
std::string name(event[mKey]);
- if (! attemptCall(name, event))
+ if (! try_call(name, event))
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey
<< " value '" << name << "'" << LL_ENDL;
}
}
-bool LLEventDispatcher::attemptCall(const std::string& name, const LLSD& event) const
+bool LLEventDispatcher::try_call(const LLSD& event) const
{
- DispatchMap::const_iterator found = mDispatch.find(name);
- if (found == mDispatch.end())
- {
- // The reason we only return false, leaving it up to our caller to die
- // with LL_ERRS, is that different callers have different amounts of
- // available information.
- return false;
- }
- // Found the name, so it's plausible to even attempt the call. But first,
- // validate the syntax of the event itself.
- std::string mismatch(llsd_matches(found->second.mRequired, event));
- if (! mismatch.empty())
- {
- LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ") calling '" << name
- << "': bad request: " << mismatch << LL_ENDL;
- }
- // Event syntax looks good, go for it!
- (found->second.mFunc)(event);
- return true; // tell caller we were able to call
+ return try_call(event[mKey], event);
}
-LLEventDispatcher::Callable LLEventDispatcher::get(const std::string& name) const
+bool LLEventDispatcher::try_call(const std::string& name, const LLSD& event) const
{
DispatchMap::const_iterator found = mDispatch.find(name);
if (found == mDispatch.end())
{
- return Callable();
+ return false;
}
- return found->second.mFunc;
+ // Found the name, so it's plausible to even attempt the call.
+ found->second->call(STRINGIZE("LLEventDispatcher(" << mDesc << ") calling '" << name << "'"),
+ event);
+ return true; // tell caller we were able to call
}
LLSD LLEventDispatcher::getMetadata(const std::string& name) const
@@ -147,9 +649,8 @@ LLSD LLEventDispatcher::getMetadata(const std::string& name) const
}
LLSD meta;
meta["name"] = name;
- meta["desc"] = found->second.mDesc;
- meta["required"] = found->second.mRequired;
- return meta;
+ meta["desc"] = found->second->mDesc;
+ return found->second->addMetadata(meta);
}
LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):
@@ -164,3 +665,8 @@ bool LLDispatchListener::process(const LLSD& event)
(*this)(event);
return false;
}
+
+LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc):
+ mDesc(desc)
+{}
+
diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h
index dfffd59eb6..7acc61de4e 100644
--- a/indra/llcommon/lleventdispatcher.h
+++ b/indra/llcommon/lleventdispatcher.h
@@ -27,18 +27,56 @@
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
+ *
+ * The invoker machinery that constructs a boost::fusion argument list for use
+ * with boost::fusion::invoke() is derived from
+ * http://www.boost.org/doc/libs/1_45_0/libs/function_types/example/interpreter.hpp
+ * whose license information is copied below:
+ *
+ * "(C) Copyright Tobias Schwinger
+ *
+ * Use modification and distribution are subject to the boost Software License,
+ * Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)."
*/
#if ! defined(LL_LLEVENTDISPATCHER_H)
#define LL_LLEVENTDISPATCHER_H
+// nil is too generic a term to be allowed to be a global macro. In
+// particular, boost::fusion defines a 'class nil' (properly encapsulated in a
+// namespace) that a global 'nil' macro breaks badly.
+#if defined(nil)
+// Capture the value of the macro 'nil', hoping int is an appropriate type.
+static const int nil_(nil);
+// Now forget the macro.
+#undef nil
+// Finally, reintroduce 'nil' as a properly-scoped alias for the previously-
+// defined const 'nil_'. Make it static since otherwise it produces duplicate-
+// symbol link errors later.
+static const int& nil(nil_);
+#endif
+
#include <string>
-#include <map>
+#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/iterator/transform_iterator.hpp>
+#include <boost/utility/enable_if.hpp>
+#include <boost/function_types/is_nonmember_callable_builtin.hpp>
+#include <boost/function_types/parameter_types.hpp>
+#include <boost/function_types/function_arity.hpp>
+#include <boost/type_traits/remove_cv.hpp>
+#include <boost/type_traits/remove_reference.hpp>
+#include <boost/fusion/include/push_back.hpp>
+#include <boost/fusion/include/cons.hpp>
+#include <boost/fusion/include/invoke.hpp>
+#include <boost/mpl/begin.hpp>
+#include <boost/mpl/end.hpp>
+#include <boost/mpl/next.hpp>
+#include <boost/mpl/deref.hpp>
#include <typeinfo>
#include "llevents.h"
+#include "llsdutil.h"
class LLSD;
@@ -54,12 +92,18 @@ public:
LLEventDispatcher(const std::string& desc, const std::string& key);
virtual ~LLEventDispatcher();
- /// Accept any C++ callable, typically a boost::bind() expression
+ /// @name Register functions accepting(const LLSD&)
+ //@{
+
+ /// Accept any C++ callable with the right signature, typically a
+ /// boost::bind() expression
typedef boost::function<void(const LLSD&)> Callable;
/**
- * Register a @a callable by @a name. The optional @a required parameter
- * is used to validate the structure of each incoming event (see
+ * Register a @a callable by @a name. The passed @a callable accepts a
+ * single LLSD value and uses it in any way desired, e.g. extract
+ * parameters and call some other function. The optional @a required
+ * parameter is used to validate the structure of each incoming event (see
* llsd_matches()).
*/
void add(const std::string& name,
@@ -68,9 +112,23 @@ public:
const LLSD& required=LLSD());
/**
+ * The case of a free function (or static method) accepting(const LLSD&)
+ * could also be intercepted by the arbitrary-args overload below. Ensure
+ * that it's directed to the Callable overload above instead.
+ */
+ void add(const std::string& name,
+ const std::string& desc,
+ void (*f)(const LLSD&),
+ const LLSD& required=LLSD())
+ {
+ add(name, desc, Callable(f), required);
+ }
+
+ /**
* Special case: a subclass of this class can pass an unbound member
- * function pointer without explicitly specifying the
- * <tt>boost::bind()</tt> expression.
+ * function pointer (of an LLEventDispatcher subclass) without explicitly
+ * specifying the <tt>boost::bind()</tt> expression. The passed @a method
+ * accepts a single LLSD value, presumably containing other parameters.
*/
template <class CLASS>
void add(const std::string& name,
@@ -81,7 +139,8 @@ public:
addMethod<CLASS>(name, desc, method, required);
}
- /// Overload for both const and non-const methods
+ /// Overload for both const and non-const methods. The passed @a method
+ /// accepts a single LLSD value, presumably containing other parameters.
template <class CLASS>
void add(const std::string& name,
const std::string& desc,
@@ -91,15 +150,106 @@ public:
addMethod<CLASS>(name, desc, method, required);
}
- /// Convenience: for LLEventDispatcher, not every callable needs a
- /// documentation string.
- template <typename CALLABLE>
- void add(const std::string& name,
- CALLABLE callable,
- const LLSD& required=LLSD())
- {
- add(name, "", callable, required);
- }
+ //@}
+
+ /// @name Register functions with arbitrary param lists
+ //@{
+
+ /**
+ * Register a free function with arbitrary parameters. (This also works
+ * for static class methods.)
+ *
+ * @note This supports functions with up to about 6 parameters -- after
+ * that you start getting dismaying compile errors in which
+ * boost::fusion::joint_view is mentioned a surprising number of times.
+ *
+ * When calling this name, pass an LLSD::Array. Each entry in turn will be
+ * converted to the corresponding parameter type using LLSDParam.
+ */
+ template<typename Function>
+ typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
+ >::type add(const std::string& name,
+ const std::string& desc,
+ Function f);
+
+ /**
+ * Register a nonstatic class method with arbitrary parameters.
+ *
+ * @note This supports functions with up to about 6 parameters -- after
+ * that you start getting dismaying compile errors in which
+ * boost::fusion::joint_view is mentioned a surprising number of times.
+ *
+ * To cover cases such as a method on an LLSingleton we don't yet want to
+ * instantiate, instead of directly storing an instance pointer, accept a
+ * nullary callable returning a pointer/reference to the desired class
+ * instance. If you already have an instance in hand,
+ * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
+ * produce suitable callables.
+ *
+ * When calling this name, pass an LLSD::Array. Each entry in turn will be
+ * converted to the corresponding parameter type using LLSDParam.
+ */
+ template<typename Method, typename InstanceGetter>
+ typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
+ >::type add(const std::string& name,
+ const std::string& desc,
+ Method f,
+ const InstanceGetter& getter);
+
+ /**
+ * Register a free function with arbitrary parameters. (This also works
+ * for static class methods.)
+ *
+ * @note This supports functions with up to about 6 parameters -- after
+ * that you start getting dismaying compile errors in which
+ * boost::fusion::joint_view is mentioned a surprising number of times.
+ *
+ * Pass an LLSD::Array of parameter names, and optionally another
+ * LLSD::Array of default parameter values, a la LLSDArgsMapper.
+ *
+ * When calling this name, pass an LLSD::Map. We will internally generate
+ * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
+ * to the corresponding parameter type using LLSDParam.
+ */
+ template<typename Function>
+ typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
+ >::type add(const std::string& name,
+ const std::string& desc,
+ Function f,
+ const LLSD& params,
+ const LLSD& defaults=LLSD());
+
+ /**
+ * Register a nonstatic class method with arbitrary parameters.
+ *
+ * @note This supports functions with up to about 6 parameters -- after
+ * that you start getting dismaying compile errors in which
+ * boost::fusion::joint_view is mentioned a surprising number of times.
+ *
+ * To cover cases such as a method on an LLSingleton we don't yet want to
+ * instantiate, instead of directly storing an instance pointer, accept a
+ * nullary callable returning a pointer/reference to the desired class
+ * instance. If you already have an instance in hand,
+ * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
+ * produce suitable callables.
+ *
+ * Pass an LLSD::Array of parameter names, and optionally another
+ * LLSD::Array of default parameter values, a la LLSDArgsMapper.
+ *
+ * When calling this name, pass an LLSD::Map. We will internally generate
+ * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
+ * to the corresponding parameter type using LLSDParam.
+ */
+ template<typename Method, typename InstanceGetter>
+ typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
+ >::type add(const std::string& name,
+ const std::string& desc,
+ Method f,
+ const InstanceGetter& getter,
+ const LLSD& params,
+ const LLSD& defaults=LLSD());
+
+ //@}
/// Unregister a callable
bool remove(const std::string& name);
@@ -109,12 +259,25 @@ public:
/// the @a required prototype specified at add() time, die with LL_ERRS.
void operator()(const std::string& name, const LLSD& event) const;
+ /// Call a registered callable with an explicitly-specified name and
+ /// return <tt>true</tt>. If no such callable exists, return
+ /// <tt>false</tt>. If the @a event fails to match the @a required
+ /// prototype specified at add() time, die with LL_ERRS.
+ bool try_call(const std::string& name, const LLSD& event) const;
+
/// Extract the @a key value from the incoming @a event, and call the
/// callable whose name is specified by that map @a key. If no such
/// callable exists, die with LL_ERRS. If the @a event fails to match the
/// @a required prototype specified at add() time, die with LL_ERRS.
void operator()(const LLSD& event) const;
+ /// Extract the @a key value from the incoming @a event, call the callable
+ /// whose name is specified by that map @a key and return <tt>true</tt>.
+ /// If no such callable exists, return <tt>false</tt>. If the @a event
+ /// fails to match the @a required prototype specified at add() time, die
+ /// with LL_ERRS.
+ bool try_call(const LLSD& event) const;
+
/// @name Iterate over defined names
//@{
typedef std::pair<std::string, std::string> NameDesc;
@@ -122,16 +285,22 @@ public:
private:
struct DispatchEntry
{
- DispatchEntry(const Callable& func, const std::string& desc, const LLSD& required):
- mFunc(func),
- mDesc(desc),
- mRequired(required)
- {}
- Callable mFunc;
+ DispatchEntry(const std::string& desc);
+ virtual ~DispatchEntry() {} // suppress MSVC warning, sigh
+
std::string mDesc;
- LLSD mRequired;
+
+ virtual void call(const std::string& desc, const LLSD& event) const = 0;
+ virtual LLSD addMetadata(LLSD) const = 0;
};
- typedef std::map<std::string, DispatchEntry> DispatchMap;
+ // Tried using boost::ptr_map<std::string, DispatchEntry>, but ptr_map<>
+ // wants its value type to be "clonable," even just to dereference an
+ // iterator. I don't want to clone entries -- if I have to copy an entry
+ // around, I want it to continue pointing to the same DispatchEntry
+ // subclass object. However, I definitely want DispatchMap to destroy
+ // DispatchEntry if no references are outstanding at the time an entry is
+ // removed. This looks like a job for boost::shared_ptr.
+ typedef std::map<std::string, boost::shared_ptr<DispatchEntry> > DispatchMap;
public:
/// We want the flexibility to redefine what data we store per name,
@@ -149,10 +318,6 @@ public:
}
//@}
- /// Fetch the Callable for the specified name. If no such name was
- /// registered, return an empty() Callable.
- Callable get(const std::string& name) const;
-
/// Get information about a specific Callable
LLSD getMetadata(const std::string& name) const;
@@ -175,18 +340,184 @@ private:
}
}
void addFail(const std::string& name, const std::string& classname) const;
- /// try to dispatch, return @c true if success
- bool attemptCall(const std::string& name, const LLSD& event) const;
std::string mDesc, mKey;
DispatchMap mDispatch;
static NameDesc makeNameDesc(const DispatchMap::value_type& item)
{
- return NameDesc(item.first, item.second.mDesc);
+ return NameDesc(item.first, item.second->mDesc);
+ }
+
+ struct LLSDDispatchEntry;
+ struct ParamsDispatchEntry;
+ struct ArrayParamsDispatchEntry;
+ struct MapParamsDispatchEntry;
+
+ // Step 2 of parameter analysis. Instantiating invoker<some_function_type>
+ // implicitly sets its From and To parameters to the (compile time) begin
+ // and end iterators over that function's parameter types.
+ template< typename Function
+ , class From = typename boost::mpl::begin< boost::function_types::parameter_types<Function> >::type
+ , class To = typename boost::mpl::end< boost::function_types::parameter_types<Function> >::type
+ >
+ struct invoker;
+
+ // deliver LLSD arguments one at a time
+ typedef boost::function<LLSD()> args_source;
+ // obtain args from an args_source to build param list and call target
+ // function
+ typedef boost::function<void(const args_source&)> invoker_function;
+
+ template <typename Function>
+ invoker_function make_invoker(Function f);
+ template <typename Method, typename InstanceGetter>
+ invoker_function make_invoker(Method f, const InstanceGetter& getter);
+ void addArrayParamsDispatchEntry(const std::string& name,
+ const std::string& desc,
+ const invoker_function& invoker,
+ LLSD::Integer arity);
+ void addMapParamsDispatchEntry(const std::string& name,
+ const std::string& desc,
+ const invoker_function& invoker,
+ const LLSD& params,
+ const LLSD& defaults);
+};
+
+/*****************************************************************************
+* LLEventDispatcher template implementation details
+*****************************************************************************/
+// Step 3 of parameter analysis, the recursive case.
+template<typename Function, class From, class To>
+struct LLEventDispatcher::invoker
+{
+ template<typename T>
+ struct remove_cv_ref
+ : boost::remove_cv< typename boost::remove_reference<T>::type >
+ { };
+
+ // apply() accepts an arbitrary boost::fusion sequence as args. It
+ // examines the next parameter type in the parameter-types sequence
+ // bounded by From and To, obtains the next LLSD object from the passed
+ // args_source and constructs an LLSDParam of appropriate type to try
+ // to convert the value. It then recurs with the next parameter-types
+ // iterator, passing the args sequence thus far.
+ template<typename Args>
+ static inline
+ void apply(Function func, const args_source& argsrc, Args const & args)
+ {
+ typedef typename boost::mpl::deref<From>::type arg_type;
+ typedef typename boost::mpl::next<From>::type next_iter_type;
+ typedef typename remove_cv_ref<arg_type>::type plain_arg_type;
+
+ invoker<Function, next_iter_type, To>::apply
+ ( func, argsrc, boost::fusion::push_back(args, LLSDParam<plain_arg_type>(argsrc())));
+ }
+
+ // Special treatment for instance (first) parameter of a non-static member
+ // function. Accept the instance-getter callable, calling that to produce
+ // the first args value. Since we know we're at the top of the recursion
+ // chain, we need not also require a partial args sequence from our caller.
+ template <typename InstanceGetter>
+ static inline
+ void method_apply(Function func, const args_source& argsrc, const InstanceGetter& getter)
+ {
+ typedef typename boost::mpl::next<From>::type next_iter_type;
+
+ // Instead of grabbing the first item from argsrc and making an
+ // LLSDParam of it, call getter() and pass that as the instance param.
+ invoker<Function, next_iter_type, To>::apply
+ ( func, argsrc, boost::fusion::push_back(boost::fusion::nil(), boost::ref(getter())));
+ }
+};
+
+// Step 4 of parameter analysis, the leaf case. When the general
+// invoker<Function, From, To> logic has advanced From until it matches To,
+// the compiler will pick this template specialization.
+template<typename Function, class To>
+struct LLEventDispatcher::invoker<Function,To,To>
+{
+ // the argument list is complete, now call the function
+ template<typename Args>
+ static inline
+ void apply(Function func, const args_source&, Args const & args)
+ {
+ boost::fusion::invoke(func, args);
}
};
+template<typename Function>
+typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
+LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f)
+{
+ // Construct an invoker_function, a callable accepting const args_source&.
+ // Add to DispatchMap an ArrayParamsDispatchEntry that will handle the
+ // caller's LLSD::Array.
+ addArrayParamsDispatchEntry(name, desc, make_invoker(f),
+ boost::function_types::function_arity<Function>::value);
+}
+
+template<typename Method, typename InstanceGetter>
+typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
+LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
+ const InstanceGetter& getter)
+{
+ // Subtract 1 from the compile-time arity because the getter takes care of
+ // the first parameter. We only need (arity - 1) additional arguments.
+ addArrayParamsDispatchEntry(name, desc, make_invoker(f, getter),
+ boost::function_types::function_arity<Method>::value - 1);
+}
+
+template<typename Function>
+typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
+LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f,
+ const LLSD& params, const LLSD& defaults)
+{
+ // See comments for previous is_nonmember_callable_builtin add().
+ addMapParamsDispatchEntry(name, desc, make_invoker(f), params, defaults);
+}
+
+template<typename Method, typename InstanceGetter>
+typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
+LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
+ const InstanceGetter& getter,
+ const LLSD& params, const LLSD& defaults)
+{
+ addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults);
+}
+
+template <typename Function>
+LLEventDispatcher::invoker_function
+LLEventDispatcher::make_invoker(Function f)
+{
+ // Step 1 of parameter analysis, the top of the recursion. Passing a
+ // suitable f (see add()'s enable_if condition) to this method causes it
+ // to infer the function type; specifying that function type to invoker<>
+ // causes it to fill in the begin/end MPL iterators over the function's
+ // list of parameter types.
+ // While normally invoker::apply() could infer its template type from the
+ // boost::fusion::nil parameter value, here we must be explicit since
+ // we're boost::bind()ing it rather than calling it directly.
+ return boost::bind(&invoker<Function>::template apply<boost::fusion::nil>,
+ f,
+ _1,
+ boost::fusion::nil());
+}
+
+template <typename Method, typename InstanceGetter>
+LLEventDispatcher::invoker_function
+LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter)
+{
+ // Use invoker::method_apply() to treat the instance (first) arg specially.
+ return boost::bind(&invoker<Method>::template method_apply<InstanceGetter>,
+ f,
+ _1,
+ getter);
+}
+
+/*****************************************************************************
+* LLDispatchListener
+*****************************************************************************/
/**
* Bundle an LLEventPump and a listener with an LLEventDispatcher. A class
* that contains (or derives from) LLDispatchListener need only specify the
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 97e2bdeb57..ff03506e84 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -588,3 +588,16 @@ void LLReqID::stamp(LLSD& response) const
}
response["reqid"] = mReqid;
}
+
+bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey)
+{
+ // Copy 'reply' to modify it.
+ LLSD newreply(reply);
+ // Get the ["reqid"] element from request
+ LLReqID reqID(request);
+ // and copy it to 'newreply'.
+ reqID.stamp(newreply);
+ // Send reply on LLEventPump named in request[replyKey]. Don't forget to
+ // send the modified 'newreply' instead of the original 'reply'.
+ return LLEventPumps::instance().obtain(request[replyKey]).post(newreply);
+}
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index 2491cf1371..65b0fef354 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -692,6 +692,20 @@ private:
};
/**
+ * Conventionally send a reply to a request event.
+ *
+ * @a reply is the LLSD reply event to send
+ * @a request is the corresponding LLSD request event
+ * @a replyKey is the key in the @a request event, conventionally ["reply"],
+ * whose value is the name of the LLEventPump on which to send the reply.
+ *
+ * Before sending the reply event, sendReply() copies the ["reqid"] item from
+ * the request to the reply.
+ */
+LL_COMMON_API bool sendReply(const LLSD& reply, const LLSD& request,
+ const std::string& replyKey="reply");
+
+/**
* Base class for LLListenerWrapper. See visit_and_connect() and llwrap(). We
* provide virtual @c accept_xxx() methods, customization points allowing a
* subclass access to certain data visible at LLEventPump::listen() time.
diff --git a/indra/llcommon/lllslconstants.h b/indra/llcommon/lllslconstants.h
index 539c807020..9f32598e61 100644
--- a/indra/llcommon/lllslconstants.h
+++ b/indra/llcommon/lllslconstants.h
@@ -181,7 +181,7 @@ const S32 OBJECT_GROUP = 7;
const S32 OBJECT_CREATOR = 8;
// llTextBox() magic token string - yes this is a hack. sue me.
-const std::string TEXTBOX_MAGIC_TOKEN = "!!llTextBox!!";
+char const* const TEXTBOX_MAGIC_TOKEN = "!!llTextBox!!";
// changed() event flags
const U32 CHANGED_NONE = 0x0;
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
index a502d1a7eb..51fcd5b717 100644
--- a/indra/llcommon/llmemory.cpp
+++ b/indra/llcommon/llmemory.cpp
@@ -26,6 +26,12 @@
#include "linden_common.h"
+#include "llmemory.h"
+
+#if MEM_TRACK_MEM
+#include "llthread.h"
+#endif
+
#if defined(LL_WINDOWS)
# include <windows.h>
# include <psapi.h>
@@ -37,8 +43,6 @@
# include <unistd.h>
#endif
-#include "llmemory.h"
-
//----------------------------------------------------------------------------
//static
@@ -105,6 +109,20 @@ U64 LLMemory::getCurrentRSS()
return counters.WorkingSetSize;
}
+//static
+U32 LLMemory::getWorkingSetSize()
+{
+ PROCESS_MEMORY_COUNTERS pmc ;
+ U32 ret = 0 ;
+
+ if (GetProcessMemoryInfo( GetCurrentProcess(), &pmc, sizeof(pmc)) )
+ {
+ ret = pmc.WorkingSetSize ;
+ }
+
+ return ret ;
+}
+
#elif defined(LL_DARWIN)
/*
@@ -151,6 +169,11 @@ U64 LLMemory::getCurrentRSS()
return residentSize;
}
+U32 LLMemory::getWorkingSetSize()
+{
+ return 0 ;
+}
+
#elif defined(LL_LINUX)
U64 LLMemory::getCurrentRSS()
@@ -185,6 +208,11 @@ bail:
return rss;
}
+U32 LLMemory::getWorkingSetSize()
+{
+ return 0 ;
+}
+
#elif LL_SOLARIS
#include <sys/types.h>
#include <sys/stat.h>
@@ -213,6 +241,12 @@ U64 LLMemory::getCurrentRSS()
return((U64)proc_psinfo.pr_rssize * 1024);
}
+
+U32 LLMemory::getWorkingSetSize()
+{
+ return 0 ;
+}
+
#else
U64 LLMemory::getCurrentRSS()
@@ -220,4 +254,144 @@ U64 LLMemory::getCurrentRSS()
return 0;
}
+U32 LLMemory::getWorkingSetSize()
+{
+ return 0 ;
+}
+
#endif
+
+//--------------------------------------------------------------------------------------------------
+#if MEM_TRACK_MEM
+#include "llframetimer.h"
+
+//static
+LLMemTracker* LLMemTracker::sInstance = NULL ;
+
+LLMemTracker::LLMemTracker()
+{
+ mLastAllocatedMem = LLMemory::getWorkingSetSize() ;
+ mCapacity = 128 ;
+ mCurIndex = 0 ;
+ mCounter = 0 ;
+ mDrawnIndex = 0 ;
+ mPaused = FALSE ;
+
+ mMutexp = new LLMutex(NULL) ;
+ mStringBuffer = new char*[128] ;
+ mStringBuffer[0] = new char[mCapacity * 128] ;
+ for(S32 i = 1 ; i < mCapacity ; i++)
+ {
+ mStringBuffer[i] = mStringBuffer[i-1] + 128 ;
+ }
+}
+
+LLMemTracker::~LLMemTracker()
+{
+ delete[] mStringBuffer[0] ;
+ delete[] mStringBuffer;
+ delete mMutexp ;
+}
+
+//static
+LLMemTracker* LLMemTracker::getInstance()
+{
+ if(!sInstance)
+ {
+ sInstance = new LLMemTracker() ;
+ }
+ return sInstance ;
+}
+
+//static
+void LLMemTracker::release()
+{
+ if(sInstance)
+ {
+ delete sInstance ;
+ sInstance = NULL ;
+ }
+}
+
+//static
+void LLMemTracker::track(const char* function, const int line)
+{
+ static const S32 MIN_ALLOCATION = 0 ; //1KB
+
+ if(mPaused)
+ {
+ return ;
+ }
+
+ U32 allocated_mem = LLMemory::getWorkingSetSize() ;
+
+ LLMutexLock lock(mMutexp) ;
+
+ S32 delta_mem = allocated_mem - mLastAllocatedMem ;
+ mLastAllocatedMem = allocated_mem ;
+
+ if(delta_mem <= 0)
+ {
+ return ; //occupied memory does not grow
+ }
+
+ if(delta_mem < MIN_ALLOCATION)
+ {
+ return ;
+ }
+
+ char* buffer = mStringBuffer[mCurIndex++] ;
+ F32 time = (F32)LLFrameTimer::getElapsedSeconds() ;
+ S32 hours = (S32)(time / (60*60));
+ S32 mins = (S32)((time - hours*(60*60)) / 60);
+ S32 secs = (S32)((time - hours*(60*60) - mins*60));
+ strcpy(buffer, function) ;
+ sprintf(buffer + strlen(function), " line: %d DeltaMem: %d (bytes) Time: %d:%02d:%02d", line, delta_mem, hours,mins,secs) ;
+
+ if(mCounter < mCapacity)
+ {
+ mCounter++ ;
+ }
+ if(mCurIndex >= mCapacity)
+ {
+ mCurIndex = 0 ;
+ }
+}
+
+
+//static
+void LLMemTracker::preDraw(BOOL pause)
+{
+ mMutexp->lock() ;
+
+ mPaused = pause ;
+ mDrawnIndex = mCurIndex - 1;
+ mNumOfDrawn = 0 ;
+}
+
+//static
+void LLMemTracker::postDraw()
+{
+ mMutexp->unlock() ;
+}
+
+//static
+const char* LLMemTracker::getNextLine()
+{
+ if(mNumOfDrawn >= mCounter)
+ {
+ return NULL ;
+ }
+ mNumOfDrawn++;
+
+ if(mDrawnIndex < 0)
+ {
+ mDrawnIndex = mCapacity - 1 ;
+ }
+
+ return mStringBuffer[mDrawnIndex--] ;
+}
+
+#endif //MEM_TRACK_MEM
+//--------------------------------------------------------------------------------------------------
+
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index 9bf4248bb7..11406f59b0 100644
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -26,7 +26,7 @@
#ifndef LLMEMORY_H
#define LLMEMORY_H
-
+#include "llmemtype.h"
extern S32 gTotalDAlloc;
extern S32 gTotalDAUse;
@@ -44,10 +44,55 @@ public:
// Return the resident set size of the current process, in bytes.
// Return value is zero if not known.
static U64 getCurrentRSS();
+ static U32 getWorkingSetSize();
private:
static char* reserveMem;
};
+//----------------------------------------------------------------------------
+#if MEM_TRACK_MEM
+class LLMutex ;
+class LL_COMMON_API LLMemTracker
+{
+private:
+ LLMemTracker() ;
+ ~LLMemTracker() ;
+
+public:
+ static void release() ;
+ static LLMemTracker* getInstance() ;
+
+ void track(const char* function, const int line) ;
+ void preDraw(BOOL pause) ;
+ void postDraw() ;
+ const char* getNextLine() ;
+
+private:
+ static LLMemTracker* sInstance ;
+
+ char** mStringBuffer ;
+ S32 mCapacity ;
+ U32 mLastAllocatedMem ;
+ S32 mCurIndex ;
+ S32 mCounter;
+ S32 mDrawnIndex;
+ S32 mNumOfDrawn;
+ BOOL mPaused;
+ LLMutex* mMutexp ;
+};
+
+#define MEM_TRACK_RELEASE LLMemTracker::release() ;
+#define MEM_TRACK LLMemTracker::getInstance()->track(__FUNCTION__, __LINE__) ;
+
+#else // MEM_TRACK_MEM
+
+#define MEM_TRACK_RELEASE
+#define MEM_TRACK
+
+#endif // MEM_TRACK_MEM
+
+//----------------------------------------------------------------------------
+
// LLRefCount moved to llrefcount.h
// LLPointer moved to llpointer.h
diff --git a/indra/llcommon/llmemtype.cpp b/indra/llcommon/llmemtype.cpp
index fe83f87d4b..6290a7158f 100644
--- a/indra/llcommon/llmemtype.cpp
+++ b/indra/llcommon/llmemtype.cpp
@@ -229,3 +229,4 @@ char const * LLMemType::getNameFromID(S32 id)
return DeclareMemType::mNameList[id];
}
+//--------------------------------------------------------------------------------------------------
diff --git a/indra/llcommon/llmetricperformancetester.h b/indra/llcommon/llmetricperformancetester.h
index 925010ac96..1372f48dcf 100644
--- a/indra/llcommon/llmetricperformancetester.h
+++ b/indra/llcommon/llmetricperformancetester.h
@@ -27,7 +27,7 @@
#ifndef LL_METRICPERFORMANCETESTER_H
#define LL_METRICPERFORMANCETESTER_H
-const std::string DEFAULT_METRIC_NAME("metric");
+char const* const DEFAULT_METRIC_NAME = "metric";
/**
* @class LLMetricPerformanceTesterBasic
diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp
index f8f9ece058..803417d368 100644
--- a/indra/llcommon/llsdutil.cpp
+++ b/indra/llcommon/llsdutil.cpp
@@ -41,6 +41,7 @@
#include "llsdserialize.h"
#include "stringize.h"
+#include "is_approx_equal_fraction.h"
#include <map>
#include <set>
@@ -571,7 +572,7 @@ std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::str
return match_types(prototype.type(), TypeVector(), data.type(), pfx);
}
-bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
+bool llsd_equals(const LLSD& lhs, const LLSD& rhs, unsigned bits)
{
// We're comparing strict equality of LLSD representation rather than
// performing any conversions. So if the types aren't equal, the LLSD
@@ -588,6 +589,20 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
// Both are TypeUndefined. There's nothing more to know.
return true;
+ case LLSD::TypeReal:
+ // This is where the 'bits' argument comes in handy. If passed
+ // explicitly, it means to use is_approx_equal_fraction() to compare.
+ if (bits >= 0)
+ {
+ return is_approx_equal_fraction(lhs.asReal(), rhs.asReal(), bits);
+ }
+ // Otherwise we compare bit representations, and the usual caveats
+ // about comparing floating-point numbers apply. Omitting 'bits' when
+ // comparing Real values is only useful when we expect identical bit
+ // representation for a given Real value, e.g. for integer-valued
+ // Reals.
+ return (lhs.asReal() == rhs.asReal());
+
#define COMPARE_SCALAR(type) \
case LLSD::Type##type: \
/* LLSD::URI has operator!=() but not operator==() */ \
@@ -596,10 +611,6 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
COMPARE_SCALAR(Boolean);
COMPARE_SCALAR(Integer);
- // The usual caveats about comparing floating-point numbers apply. This is
- // only useful when we expect identical bit representation for a given
- // Real value, e.g. for integer-valued Reals.
- COMPARE_SCALAR(Real);
COMPARE_SCALAR(String);
COMPARE_SCALAR(UUID);
COMPARE_SCALAR(Date);
@@ -617,7 +628,7 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
for ( ; lai != laend && rai != raend; ++lai, ++rai)
{
// If any one array element is unequal, the arrays are unequal.
- if (! llsd_equals(*lai, *rai))
+ if (! llsd_equals(*lai, *rai, bits))
return false;
}
// Here we've reached the end of one or the other array. They're equal
@@ -644,7 +655,7 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
if (rhskeys.erase(lmi->first) != 1)
return false;
// Both maps have the current key. Compare values.
- if (! llsd_equals(lmi->second, rhs[lmi->first]))
+ if (! llsd_equals(lmi->second, rhs[lmi->first], bits))
return false;
}
// We've now established that all the lhs keys have equal values in
@@ -657,7 +668,7 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
// We expect that every possible type() value is specifically handled
// above. Failing to extend this switch to support a new LLSD type is
// an error that must be brought to the coder's attention.
- LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << "): "
+ LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << ", " << bits << "): "
"unknown type " << lhs.type() << LL_ENDL;
return false; // pacify the compiler
}
diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h
index bb8c0690b1..65c7297cbf 100644
--- a/indra/llcommon/llsdutil.h
+++ b/indra/llcommon/llsdutil.h
@@ -123,8 +123,10 @@ LL_COMMON_API BOOL compare_llsd_with_template(
*/
LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx="");
-/// Deep equality
-LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs);
+/// Deep equality. If you want to compare LLSD::Real values for approximate
+/// equality rather than bitwise equality, pass @a bits as for
+/// is_approx_equal_fraction().
+LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, unsigned bits=-1);
// Simple function to copy data out of input & output iterators if
// there is no need for casting.
@@ -138,4 +140,283 @@ template<typename Input> LLSD llsd_copy_array(Input iter, Input end)
return dest;
}
+/*****************************************************************************
+* LLSDArray
+*****************************************************************************/
+/**
+ * Construct an LLSD::Array inline, with implicit conversion to LLSD. Usage:
+ *
+ * @code
+ * void somefunc(const LLSD&);
+ * ...
+ * somefunc(LLSDArray("text")(17)(3.14));
+ * @endcode
+ *
+ * For completeness, LLSDArray() with no args constructs an empty array, so
+ * <tt>LLSDArray()("text")(17)(3.14)</tt> produces an array equivalent to the
+ * above. But for most purposes, LLSD() is already equivalent to an empty
+ * array, and if you explicitly want an empty isArray(), there's
+ * LLSD::emptyArray(). However, supporting a no-args LLSDArray() constructor
+ * follows the principle of least astonishment.
+ */
+class LLSDArray
+{
+public:
+ LLSDArray():
+ _data(LLSD::emptyArray())
+ {}
+
+ /**
+ * Need an explicit copy constructor. Consider the following:
+ *
+ * @code
+ * LLSD array_of_arrays(LLSDArray(LLSDArray(17)(34))
+ * (LLSDArray("x")("y")));
+ * @endcode
+ *
+ * The coder intends to construct [[17, 34], ["x", "y"]].
+ *
+ * With the compiler's implicit copy constructor, s/he gets instead
+ * [17, 34, ["x", "y"]].
+ *
+ * The expression LLSDArray(17)(34) constructs an LLSDArray with those two
+ * values. The reader assumes it should be converted to LLSD, as we always
+ * want with LLSDArray, before passing it to the @em outer LLSDArray
+ * constructor! This copy constructor makes that happen.
+ */
+ LLSDArray(const LLSDArray& inner):
+ _data(LLSD::emptyArray())
+ {
+ _data.append(inner);
+ }
+
+ LLSDArray(const LLSD& value):
+ _data(LLSD::emptyArray())
+ {
+ _data.append(value);
+ }
+
+ LLSDArray& operator()(const LLSD& value)
+ {
+ _data.append(value);
+ return *this;
+ }
+
+ operator LLSD() const { return _data; }
+ LLSD get() const { return _data; }
+
+private:
+ LLSD _data;
+};
+
+/*****************************************************************************
+* LLSDMap
+*****************************************************************************/
+/**
+ * Construct an LLSD::Map inline, with implicit conversion to LLSD. Usage:
+ *
+ * @code
+ * void somefunc(const LLSD&);
+ * ...
+ * somefunc(LLSDMap("alpha", "abc")("number", 17)("pi", 3.14));
+ * @endcode
+ *
+ * For completeness, LLSDMap() with no args constructs an empty map, so
+ * <tt>LLSDMap()("alpha", "abc")("number", 17)("pi", 3.14)</tt> produces a map
+ * equivalent to the above. But for most purposes, LLSD() is already
+ * equivalent to an empty map, and if you explicitly want an empty isMap(),
+ * there's LLSD::emptyMap(). However, supporting a no-args LLSDMap()
+ * constructor follows the principle of least astonishment.
+ */
+class LLSDMap
+{
+public:
+ LLSDMap():
+ _data(LLSD::emptyMap())
+ {}
+ LLSDMap(const LLSD::String& key, const LLSD& value):
+ _data(LLSD::emptyMap())
+ {
+ _data[key] = value;
+ }
+
+ LLSDMap& operator()(const LLSD::String& key, const LLSD& value)
+ {
+ _data[key] = value;
+ return *this;
+ }
+
+ operator LLSD() const { return _data; }
+ LLSD get() const { return _data; }
+
+private:
+ LLSD _data;
+};
+
+/*****************************************************************************
+* LLSDParam
+*****************************************************************************/
+/**
+ * LLSDParam is a customization point for passing LLSD values to function
+ * parameters of more or less arbitrary type. LLSD provides a small set of
+ * native conversions; but if a generic algorithm explicitly constructs an
+ * LLSDParam object in the function's argument list, a consumer can provide
+ * LLSDParam specializations to support more different parameter types than
+ * LLSD's native conversions.
+ *
+ * Usage:
+ *
+ * @code
+ * void somefunc(const paramtype&);
+ * ...
+ * somefunc(..., LLSDParam<paramtype>(someLLSD), ...);
+ * @endcode
+ */
+template <typename T>
+class LLSDParam
+{
+public:
+ /**
+ * Default implementation converts to T on construction, saves converted
+ * value for later retrieval
+ */
+ LLSDParam(const LLSD& value):
+ _value(value)
+ {}
+
+ operator T() const { return _value; }
+
+private:
+ T _value;
+};
+
+/**
+ * Turns out that several target types could accept an LLSD param using any of
+ * a few different conversions, e.g. LLUUID's constructor can accept LLUUID or
+ * std::string. Therefore, the compiler can't decide which LLSD conversion
+ * operator to choose, even though to us it seems obvious. But that's okay, we
+ * can specialize LLSDParam for such target types, explicitly specifying the
+ * desired conversion -- that's part of what LLSDParam is all about. Turns out
+ * we have to do that enough to make it worthwhile generalizing. Use a macro
+ * because I need to specify one of the asReal, etc., explicit conversion
+ * methods as well as a type. If I'm overlooking a clever way to implement
+ * that using a template instead, feel free to reimplement.
+ */
+#define LLSDParam_for(T, AS) \
+template <> \
+class LLSDParam<T> \
+{ \
+public: \
+ LLSDParam(const LLSD& value): \
+ _value(value.AS()) \
+ {} \
+ \
+ operator T() const { return _value; } \
+ \
+private: \
+ T _value; \
+}
+
+LLSDParam_for(float, asReal);
+LLSDParam_for(LLUUID, asUUID);
+LLSDParam_for(LLDate, asDate);
+LLSDParam_for(LLURI, asURI);
+LLSDParam_for(LLSD::Binary, asBinary);
+
+/**
+ * LLSDParam<const char*> is an example of the kind of conversion you can
+ * support with LLSDParam beyond native LLSD conversions. Normally you can't
+ * pass an LLSD object to a function accepting const char* -- but you can
+ * safely pass an LLSDParam<const char*>(yourLLSD).
+ */
+template <>
+class LLSDParam<const char*>
+{
+private:
+ // The difference here is that we store a std::string rather than a const
+ // char*. It's important that the LLSDParam object own the std::string.
+ std::string _value;
+ // We don't bother storing the incoming LLSD object, but we do have to
+ // distinguish whether _value is an empty string because the LLSD object
+ // contains an empty string or because it's isUndefined().
+ bool _undefined;
+
+public:
+ LLSDParam(const LLSD& value):
+ _value(value),
+ _undefined(value.isUndefined())
+ {}
+
+ // The const char* we retrieve is for storage owned by our _value member.
+ // That's how we guarantee that the const char* is valid for the lifetime
+ // of this LLSDParam object. Constructing your LLSDParam in the argument
+ // list should ensure that the LLSDParam object will persist for the
+ // duration of the function call.
+ operator const char*() const
+ {
+ if (_undefined)
+ {
+ // By default, an isUndefined() LLSD object's asString() method
+ // will produce an empty string. But for a function accepting
+ // const char*, it's often important to be able to pass NULL, and
+ // isUndefined() seems like the best way. If you want to pass an
+ // empty string, you can still pass LLSD(""). Without this special
+ // case, though, no LLSD value could pass NULL.
+ return NULL;
+ }
+ return _value.c_str();
+ }
+};
+
+namespace llsd
+{
+
+/*****************************************************************************
+* BOOST_FOREACH() helpers for LLSD
+*****************************************************************************/
+/// Usage: BOOST_FOREACH(LLSD item, inArray(someLLSDarray)) { ... }
+class inArray
+{
+public:
+ inArray(const LLSD& array):
+ _array(array)
+ {}
+
+ typedef LLSD::array_const_iterator const_iterator;
+ typedef LLSD::array_iterator iterator;
+
+ iterator begin() { return _array.beginArray(); }
+ iterator end() { return _array.endArray(); }
+ const_iterator begin() const { return _array.beginArray(); }
+ const_iterator end() const { return _array.endArray(); }
+
+private:
+ LLSD _array;
+};
+
+/// MapEntry is what you get from dereferencing an LLSD::map_[const_]iterator.
+typedef std::map<LLSD::String, LLSD>::value_type MapEntry;
+
+/// Usage: BOOST_FOREACH([const] MapEntry& e, inMap(someLLSDmap)) { ... }
+class inMap
+{
+public:
+ inMap(const LLSD& map):
+ _map(map)
+ {}
+
+ typedef LLSD::map_const_iterator const_iterator;
+ typedef LLSD::map_iterator iterator;
+
+ iterator begin() { return _map.beginMap(); }
+ iterator end() { return _map.endMap(); }
+ const_iterator begin() const { return _map.beginMap(); }
+ const_iterator end() const { return _map.endMap(); }
+
+private:
+ LLSD _map;
+};
+
+} // namespace llsd
+
#endif // LL_LLSDUTIL_H
diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
index 7d5afe92dc..7703132d90 100644
--- a/indra/llcommon/llversionviewer.h
+++ b/indra/llcommon/llversionviewer.h
@@ -28,7 +28,7 @@
#define LL_LLVERSIONVIEWER_H
const S32 LL_VERSION_MAJOR = 2;
-const S32 LL_VERSION_MINOR = 6;
+const S32 LL_VERSION_MINOR = 7;
const S32 LL_VERSION_PATCH = 0;
const S32 LL_VERSION_BUILD = 0;
diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp
new file mode 100644
index 0000000000..263c9b171f
--- /dev/null
+++ b/indra/llcommon/tests/lleventdispatcher_test.cpp
@@ -0,0 +1,1324 @@
+/**
+ * @file lleventdispatcher_test.cpp
+ * @author Nat Goodspeed
+ * @date 2011-01-20
+ * @brief Test for lleventdispatcher.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "lleventdispatcher.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "../test/lltut.h"
+#include "llsd.h"
+#include "llsdutil.h"
+#include "stringize.h"
+#include "tests/wrapllerrs.h"
+
+#include <map>
+#include <string>
+#include <stdexcept>
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/range.hpp>
+#include <boost/foreach.hpp>
+#define foreach BOOST_FOREACH
+
+#include <boost/lambda/lambda.hpp>
+
+#include <iostream>
+#include <iomanip>
+
+using boost::lambda::constant;
+using boost::lambda::constant_ref;
+using boost::lambda::var;
+
+using namespace llsd;
+
+/*****************************************************************************
+* Output control
+*****************************************************************************/
+#ifdef DEBUG_ON
+using std::cout;
+#else
+static std::ostringstream cout;
+#endif
+
+/*****************************************************************************
+* Example data, functions, classes
+*****************************************************************************/
+// We don't need a whole lot of different arbitrary-params methods, just (no |
+// (const LLSD&) | arbitrary) args (function | static method | non-static
+// method), where 'arbitrary' is (every LLSD datatype + (const char*)).
+// But we need to register each one under different names for the different
+// registration styles. Don't forget LLEventDispatcher subclass methods(const
+// LLSD&).
+
+// However, the number of target parameter conversions we want to try exceeds
+// boost::fusion::invoke()'s supported parameter-list size. Break out two
+// different lists.
+#define NPARAMSa bool b, int i, float f, double d, const char* cp
+#define NPARAMSb const std::string& s, const LLUUID& uuid, const LLDate& date, \
+ const LLURI& uri, const std::vector<U8>& bin
+#define NARGSa b, i, f, d, cp
+#define NARGSb s, uuid, date, uri, bin
+
+// For some registration methods we need methods on a subclass of
+// LLEventDispatcher. To simplify things, we'll use this Dispatcher subclass
+// for all our testing, including testing its own methods.
+class Dispatcher: public LLEventDispatcher
+{
+public:
+ Dispatcher(const std::string& name, const std::string& key):
+ LLEventDispatcher(name, key)
+ {}
+
+ // sensing member, mutable because we want to know when we've reached our
+ // const method too
+ mutable LLSD llsd;
+
+ void method1(const LLSD& obj) { llsd = obj; }
+ void cmethod1(const LLSD& obj) const { llsd = obj; }
+};
+
+// sensing vars, captured in a struct to make it convenient to clear them
+struct Vars
+{
+ LLSD llsd;
+ bool b;
+ int i;
+ float f;
+ double d;
+ // Capture param passed as char*. But merely storing a char* received from
+ // our caller, possibly the .c_str() from a concatenation expression,
+ // would be Bad: the pointer will be invalidated long before we can query
+ // it. We could allocate a new chunk of memory, copy the string data and
+ // point to that instead -- but hey, guess what, we already have a class
+ // that does that!
+ std::string cp;
+ std::string s;
+ LLUUID uuid;
+ LLDate date;
+ LLURI uri;
+ std::vector<U8> bin;
+
+ Vars():
+ // Only need to initialize the POD types, the rest should take care of
+ // default-constructing themselves.
+ b(false),
+ i(0),
+ f(0),
+ d(0)
+ {}
+
+ // Detect any non-default values for convenient testing
+ LLSD inspect() const
+ {
+ LLSD result;
+
+ if (llsd.isDefined())
+ result["llsd"] = llsd;
+ if (b)
+ result["b"] = b;
+ if (i)
+ result["i"] = i;
+ if (f)
+ result["f"] = f;
+ if (d)
+ result["d"] = d;
+ if (! cp.empty())
+ result["cp"] = cp;
+ if (! s.empty())
+ result["s"] = s;
+ if (uuid != LLUUID())
+ result["uuid"] = uuid;
+ if (date != LLDate())
+ result["date"] = date;
+ if (uri != LLURI())
+ result["uri"] = uri;
+ if (! bin.empty())
+ result["bin"] = bin;
+
+ return result;
+ }
+
+ /*------------- no-args (non-const, const, static) methods -------------*/
+ void method0()
+ {
+ cout << "method0()\n";
+ i = 17;
+ }
+
+ void cmethod0() const
+ {
+ cout << 'c';
+ const_cast<Vars*>(this)->method0();
+ }
+
+ static void smethod0();
+
+ /*------------ Callable (non-const, const, static) methods -------------*/
+ void method1(const LLSD& obj)
+ {
+ cout << "method1(" << obj << ")\n";
+ llsd = obj;
+ }
+
+ void cmethod1(const LLSD& obj) const
+ {
+ cout << 'c';
+ const_cast<Vars*>(this)->method1(obj);
+ }
+
+ static void smethod1(const LLSD& obj);
+
+ /*-------- Arbitrary-params (non-const, const, static) methods ---------*/
+ void methodna(NPARAMSa)
+ {
+ // Because our const char* param cp might be NULL, and because we
+ // intend to capture the value in a std::string, have to distinguish
+ // between the NULL value and any non-NULL value. Use a convention
+ // easy for a human reader: enclose any non-NULL value in single
+ // quotes, reserving the unquoted string "NULL" to represent a NULL ptr.
+ std::string vcp;
+ if (cp == NULL)
+ vcp = "NULL";
+ else
+ vcp = std::string("'") + cp + "'";
+
+ cout << "methodna(" << b
+ << ", " << i
+ << ", " << f
+ << ", " << d
+ << ", " << vcp
+ << ")\n";
+
+ this->b = b;
+ this->i = i;
+ this->f = f;
+ this->d = d;
+ this->cp = vcp;
+ }
+
+ void methodnb(NPARAMSb)
+ {
+ std::ostringstream vbin;
+ foreach(U8 byte, bin)
+ {
+ vbin << std::hex << std::setfill('0') << std::setw(2) << unsigned(byte);
+ }
+
+ cout << "methodnb(" << "'" << s << "'"
+ << ", " << uuid
+ << ", " << date
+ << ", '" << uri << "'"
+ << ", " << vbin.str()
+ << ")\n";
+
+ this->s = s;
+ this->uuid = uuid;
+ this->date = date;
+ this->uri = uri;
+ this->bin = bin;
+ }
+
+ void cmethodna(NPARAMSa) const
+ {
+ cout << 'c';
+ const_cast<Vars*>(this)->methodna(NARGSa);
+ }
+
+ void cmethodnb(NPARAMSb) const
+ {
+ cout << 'c';
+ const_cast<Vars*>(this)->methodnb(NARGSb);
+ }
+
+ static void smethodna(NPARAMSa);
+ static void smethodnb(NPARAMSb);
+};
+/*------- Global Vars instance for free functions and static methods -------*/
+static Vars g;
+
+/*------------ Static Vars method implementations reference 'g' ------------*/
+void Vars::smethod0()
+{
+ cout << "smethod0() -> ";
+ g.method0();
+}
+
+void Vars::smethod1(const LLSD& obj)
+{
+ cout << "smethod1(" << obj << ") -> ";
+ g.method1(obj);
+}
+
+void Vars::smethodna(NPARAMSa)
+{
+ cout << "smethodna(...) -> ";
+ g.methodna(NARGSa);
+}
+
+void Vars::smethodnb(NPARAMSb)
+{
+ cout << "smethodnb(...) -> ";
+ g.methodnb(NARGSb);
+}
+
+/*--------------------------- Reset global Vars ----------------------------*/
+void clear()
+{
+ g = Vars();
+}
+
+/*------------------- Free functions also reference 'g' --------------------*/
+void free0()
+{
+ cout << "free0() -> ";
+ g.method0();
+}
+
+void free1(const LLSD& obj)
+{
+ cout << "free1(" << obj << ") -> ";
+ g.method1(obj);
+}
+
+void freena(NPARAMSa)
+{
+ cout << "freena(...) -> ";
+ g.methodna(NARGSa);
+}
+
+void freenb(NPARAMSb)
+{
+ cout << "freenb(...) -> ";
+ g.methodnb(NARGSb);
+}
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct lleventdispatcher_data
+ {
+ WrapLL_ERRS redirect;
+ Dispatcher work;
+ Vars v;
+ std::string name, desc;
+ // Capture our own copy of all registered functions' descriptions
+ typedef std::map<std::string, std::string> DescMap;
+ DescMap descs;
+ // Capture the Vars instance on which we expect each function to operate
+ typedef std::map<std::string, Vars*> VarsMap;
+ VarsMap funcvars;
+ // Required structure for Callables with requirements
+ LLSD required;
+ // Parameter names for freena(), freenb()
+ LLSD params;
+ // Full, partial defaults arrays for params for freena(), freenb()
+ LLSD dft_array_full, dft_array_partial;
+ // Start index of partial defaults arrays
+ const LLSD::Integer partial_offset;
+ // Full, partial defaults maps for params for freena(), freenb()
+ LLSD dft_map_full, dft_map_partial;
+ // Most of the above are indexed by "a" or "b". Useful to have an
+ // array containing those strings for iterating.
+ std::vector<LLSD::String> ab;
+
+ lleventdispatcher_data():
+ work("test dispatcher", "op"),
+ // map {d=double, array=[3 elements]}
+ required(LLSDMap("d", LLSD::Real(0))("array", LLSDArray(LLSD())(LLSD())(LLSD()))),
+ // first several params are required, last couple optional
+ partial_offset(3)
+ {
+ // This object is reconstructed for every test<n> method. But
+ // clear global variables every time too.
+ ::clear();
+
+ const char* abs[] = { "a", "b" };
+ ab.assign(boost::begin(abs), boost::end(abs));
+
+ // Registration cases:
+ // - (Callable | subclass const method | subclass non-const method |
+ // non-subclass method) (with | without) required
+ // - (Free function | static method | non-static method), (no | arbitrary) params,
+ // array style
+ // - (Free function | static method | non-static method), (no | arbitrary) params,
+ // map style, (empty | partial | full) (array | map) defaults
+ // - Map-style errors:
+ // - (scalar | map) param names
+ // - defaults scalar
+ // - defaults array longer than params array
+ // - defaults map with plural unknown param names
+
+ // I hate to have to write things twice, because of having to keep
+ // them consistent. If we had variadic functions, addf() would be
+ // a variadic method, capturing the name and desc and passing them
+ // plus "everything else" to work.add(). If I could return a pair
+ // and use that pair as the first two args to work.add(), I'd do
+ // that. But the best I can do with present C++ is to set two
+ // instance variables as a side effect of addf(), and pass those
+ // variables to each work.add() call. :-P
+
+ /*------------------------- Callables --------------------------*/
+
+ // Arbitrary Callable with/out required params
+ addf("free1", "free1", &g);
+ work.add(name, desc, free1);
+ addf("free1_req", "free1", &g);
+ work.add(name, desc, free1, required);
+ // Subclass non-const method with/out required params
+ addf("Dmethod1", "method1", NULL);
+ work.add(name, desc, &Dispatcher::method1);
+ addf("Dmethod1_req", "method1", NULL);
+ work.add(name, desc, &Dispatcher::method1, required);
+ // Subclass const method with/out required params
+ addf("Dcmethod1", "cmethod1", NULL);
+ work.add(name, desc, &Dispatcher::cmethod1);
+ addf("Dcmethod1_req", "cmethod1", NULL);
+ work.add(name, desc, &Dispatcher::cmethod1, required);
+ // Non-subclass method with/out required params
+ addf("method1", "method1", &v);
+ work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1));
+ addf("method1_req", "method1", &v);
+ work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1), required);
+
+ /*--------------- Arbitrary params, array style ----------------*/
+
+ // (Free function | static method) with (no | arbitrary) params, array style
+ addf("free0_array", "free0", &g);
+ work.add(name, desc, free0);
+ addf("freena_array", "freena", &g);
+ work.add(name, desc, freena);
+ addf("freenb_array", "freenb", &g);
+ work.add(name, desc, freenb);
+ addf("smethod0_array", "smethod0", &g);
+ work.add(name, desc, &Vars::smethod0);
+ addf("smethodna_array", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna);
+ addf("smethodnb_array", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb);
+ // Non-static method with (no | arbitrary) params, array style
+ addf("method0_array", "method0", &v);
+ work.add(name, desc, &Vars::method0, boost::lambda::var(v));
+ addf("methodna_array", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, boost::lambda::var(v));
+ addf("methodnb_array", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, boost::lambda::var(v));
+
+ /*---------------- Arbitrary params, map style -----------------*/
+
+ // We lay out each params list as an array, also each array of
+ // default values we'll register. We'll zip these into
+ // (param=value) maps. Why not define them as maps and just
+ // extract the keys and values to arrays? Because that wouldn't
+ // give us the right params-list order.
+
+ // freena(), methodna(), cmethodna(), smethodna() all take same param list.
+ // Same for freenb() et al.
+ params = LLSDMap("a", LLSDArray("b")("i")("f")("d")("cp"))
+ ("b", LLSDArray("s")("uuid")("date")("uri")("bin"));
+ cout << "params:\n" << params << "\nparams[\"a\"]:\n" << params["a"] << "\nparams[\"b\"]:\n" << params["b"] << std::endl;
+ // default LLSD::Binary value
+ std::vector<U8> binary;
+ for (size_t ix = 0, h = 0xaa; ix < 6; ++ix, h += 0x11)
+ {
+ binary.push_back(h);
+ }
+ // Full defaults arrays. We actually don't care what the LLUUID or
+ // LLDate values are, as long as they're different from the
+ // LLUUID() and LLDate() default values so inspect() will report
+ // them.
+ dft_array_full = LLSDMap("a", LLSDArray(true)(17)(3.14)(123456.78)("classic"))
+ ("b", LLSDArray("string")
+ (LLUUID::generateNewID())
+ (LLDate::now())
+ (LLURI("http://www.ietf.org/rfc/rfc3986.txt"))
+ (binary));
+ cout << "dft_array_full:\n" << dft_array_full << std::endl;
+ // Partial defaults arrays.
+ foreach(LLSD::String a, ab)
+ {
+ LLSD::Integer partition(std::min(partial_offset, dft_array_full[a].size()));
+ dft_array_partial[a] =
+ llsd_copy_array(dft_array_full[a].beginArray() + partition,
+ dft_array_full[a].endArray());
+ }
+ cout << "dft_array_partial:\n" << dft_array_partial << std::endl;
+
+ foreach(LLSD::String a, ab)
+ {
+ // Generate full defaults maps by zipping (params, dft_array_full).
+ dft_map_full[a] = zipmap(params[a], dft_array_full[a]);
+
+ // Generate partial defaults map by zipping alternate entries from
+ // (params, dft_array_full). Part of the point of using map-style
+ // defaults is to allow any subset of the target function's
+ // parameters to be optional, not just the rightmost.
+ for (LLSD::Integer ix = 0, ixend = params[a].size(); ix < ixend; ix += 2)
+ {
+ dft_map_partial[a][params[a][ix].asString()] = dft_array_full[a][ix];
+ }
+ }
+ cout << "dft_map_full:\n" << dft_map_full << "\ndft_map_partial:\n" << dft_map_partial << '\n';
+
+ // (Free function | static method) with (no | arbitrary) params,
+ // map style, no (empty array) defaults
+ addf("free0_map", "free0", &g);
+ work.add(name, desc, free0, LLSD::emptyArray());
+ addf("smethod0_map", "smethod0", &g);
+ work.add(name, desc, &Vars::smethod0, LLSD::emptyArray());
+ addf("freena_map_allreq", "freena", &g);
+ work.add(name, desc, freena, params["a"]);
+ addf("freenb_map_allreq", "freenb", &g);
+ work.add(name, desc, freenb, params["b"]);
+ addf("smethodna_map_allreq", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"]);
+ addf("smethodnb_map_allreq", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"]);
+ // Non-static method with (no | arbitrary) params, map style, no
+ // (empty array) defaults
+ addf("method0_map", "method0", &v);
+ work.add(name, desc, &Vars::method0, var(v), LLSD::emptyArray());
+ addf("methodna_map_allreq", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"]);
+ addf("methodnb_map_allreq", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"]);
+
+ // Except for the "more (array | map) defaults than params" error
+ // cases, tested separately below, the (partial | full)(array |
+ // map) defaults cases don't apply to no-params functions/methods.
+ // So eliminate free0, smethod0, method0 from the cases below.
+
+ // (Free function | static method) with arbitrary params, map
+ // style, partial (array | map) defaults
+ addf("freena_map_leftreq", "freena", &g);
+ work.add(name, desc, freena, params["a"], dft_array_partial["a"]);
+ addf("freenb_map_leftreq", "freenb", &g);
+ work.add(name, desc, freenb, params["b"], dft_array_partial["b"]);
+ addf("smethodna_map_leftreq", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"], dft_array_partial["a"]);
+ addf("smethodnb_map_leftreq", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"], dft_array_partial["b"]);
+ addf("freena_map_skipreq", "freena", &g);
+ work.add(name, desc, freena, params["a"], dft_map_partial["a"]);
+ addf("freenb_map_skipreq", "freenb", &g);
+ work.add(name, desc, freenb, params["b"], dft_map_partial["b"]);
+ addf("smethodna_map_skipreq", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"], dft_map_partial["a"]);
+ addf("smethodnb_map_skipreq", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"], dft_map_partial["b"]);
+ // Non-static method with arbitrary params, map style, partial
+ // (array | map) defaults
+ addf("methodna_map_leftreq", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_array_partial["a"]);
+ addf("methodnb_map_leftreq", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_array_partial["b"]);
+ addf("methodna_map_skipreq", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_map_partial["a"]);
+ addf("methodnb_map_skipreq", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_map_partial["b"]);
+
+ // (Free function | static method) with arbitrary params, map
+ // style, full (array | map) defaults
+ addf("freena_map_adft", "freena", &g);
+ work.add(name, desc, freena, params["a"], dft_array_full["a"]);
+ addf("freenb_map_adft", "freenb", &g);
+ work.add(name, desc, freenb, params["b"], dft_array_full["b"]);
+ addf("smethodna_map_adft", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"], dft_array_full["a"]);
+ addf("smethodnb_map_adft", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"], dft_array_full["b"]);
+ addf("freena_map_mdft", "freena", &g);
+ work.add(name, desc, freena, params["a"], dft_map_full["a"]);
+ addf("freenb_map_mdft", "freenb", &g);
+ work.add(name, desc, freenb, params["b"], dft_map_full["b"]);
+ addf("smethodna_map_mdft", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"], dft_map_full["a"]);
+ addf("smethodnb_map_mdft", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"], dft_map_full["b"]);
+ // Non-static method with arbitrary params, map style, full
+ // (array | map) defaults
+ addf("methodna_map_adft", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_array_full["a"]);
+ addf("methodnb_map_adft", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_array_full["b"]);
+ addf("methodna_map_mdft", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_map_full["a"]);
+ addf("methodnb_map_mdft", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_map_full["b"]);
+
+ // All the above are expected to succeed, and are setup for the
+ // tests to follow. Registration error cases are exercised as
+ // tests rather than as test setup.
+ }
+
+ void addf(const std::string& n, const std::string& d, Vars* v)
+ {
+ // This method is to capture in our own DescMap the name and
+ // description of every registered function, for metadata query
+ // testing.
+ descs[n] = d;
+ // Also capture the Vars instance on which each function should operate.
+ funcvars[n] = v;
+ // See constructor for rationale for setting these instance vars.
+ this->name = n;
+ this->desc = d;
+ }
+
+ void verify_descs()
+ {
+ // Copy descs to a temp map of same type.
+ DescMap forgotten(descs.begin(), descs.end());
+ // LLEventDispatcher intentionally provides only const_iterator:
+ // since dereferencing that iterator generates values on the fly,
+ // it's meaningless to have a modifiable iterator. But since our
+ // 'work' object isn't const, by default BOOST_FOREACH() wants to
+ // use non-const iterators. Persuade it to use the const_iterator.
+ foreach(LLEventDispatcher::NameDesc nd, const_cast<const Dispatcher&>(work))
+ {
+ DescMap::iterator found = forgotten.find(nd.first);
+ ensure(STRINGIZE("LLEventDispatcher records function '" << nd.first
+ << "' we didn't enter"),
+ found != forgotten.end());
+ ensure_equals(STRINGIZE("LLEventDispatcher desc '" << nd.second <<
+ "' doesn't match what we entered: '" << found->second << "'"),
+ nd.second, found->second);
+ // found in our map the name from LLEventDispatcher, good, erase
+ // our map entry
+ forgotten.erase(found);
+ }
+ if (! forgotten.empty())
+ {
+ std::ostringstream out;
+ out << "LLEventDispatcher failed to report";
+ const char* delim = ": ";
+ foreach(const DescMap::value_type& fme, forgotten)
+ {
+ out << delim << fme.first;
+ delim = ", ";
+ }
+ ensure(out.str(), false);
+ }
+ }
+
+ Vars* varsfor(const std::string& name)
+ {
+ VarsMap::const_iterator found = funcvars.find(name);
+ ensure(STRINGIZE("No Vars* for " << name), found != funcvars.end());
+ ensure(STRINGIZE("NULL Vars* for " << name), found->second);
+ return found->second;
+ }
+
+ void ensure_has(const std::string& outer, const std::string& inner)
+ {
+ ensure(STRINGIZE("'" << outer << "' does not contain '" << inner << "'").c_str(),
+ outer.find(inner) != std::string::npos);
+ }
+
+ void call_exc(const std::string& func, const LLSD& args, const std::string& exc_frag)
+ {
+ std::string threw;
+ try
+ {
+ work(func, args);
+ }
+ catch (const std::runtime_error& e)
+ {
+ cout << "*** " << e.what() << '\n';
+ threw = e.what();
+ }
+ ensure_has(threw, exc_frag);
+ }
+
+ LLSD getMetadata(const std::string& name)
+ {
+ LLSD meta(work.getMetadata(name));
+ ensure(STRINGIZE("No metadata for " << name), meta.isDefined());
+ return meta;
+ }
+
+ // From two related LLSD arrays, e.g. a param-names array and a values
+ // array, zip them together into an LLSD map.
+ LLSD zipmap(const LLSD& keys, const LLSD& values)
+ {
+ LLSD map;
+ for (LLSD::Integer i = 0, iend = keys.size(); i < iend; ++i)
+ {
+ // Have to select asString() since you can index an LLSD
+ // object with either String or Integer.
+ map[keys[i].asString()] = values[i];
+ }
+ return map;
+ }
+
+ // If I call this ensure_equals(), it blocks visibility of all other
+ // ensure_equals() overloads. Normally I could say 'using
+ // baseclass::ensure_equals;' and fix that, but I don't know what the
+ // base class is!
+ void ensure_llsd(const std::string& msg, const LLSD& actual, const LLSD& expected, U32 bits)
+ {
+ std::ostringstream out;
+ if (! msg.empty())
+ {
+ out << msg << ": ";
+ }
+ out << "expected " << expected << ", actual " << actual;
+ ensure(out.str(), llsd_equals(actual, expected, bits));
+ }
+
+ void ensure_llsd(const LLSD& actual, const LLSD& expected, U32 bits)
+ {
+ ensure_llsd("", actual, expected, bits);
+ }
+ };
+ typedef test_group<lleventdispatcher_data> lleventdispatcher_group;
+ typedef lleventdispatcher_group::object object;
+ lleventdispatcher_group lleventdispatchergrp("lleventdispatcher");
+
+ // Call cases:
+ // - (try_call | call) (explicit name | event key) (real | bogus) name
+ // - Callable with args that (do | do not) match required
+ // - (Free function | non-static method), no args, (array | map) style
+ // - (Free function | non-static method), arbitrary args,
+ // (array style with (scalar | map) | map style with scalar)
+ // - (Free function | non-static method), arbitrary args, array style with
+ // array (too short | too long | just right)
+ // [trap LL_WARNS for too-long case?]
+ // - (Free function | non-static method), arbitrary args, map style with
+ // (array | map) (all | too many | holes (with | without) defaults)
+ // - const char* param gets ("" | NULL)
+
+ // Query cases:
+ // - Iterate over all (with | without) remove()
+ // - getDispatchKey()
+ // - Callable style (with | without) required
+ // - (Free function | non-static method), array style, (no | arbitrary) params
+ // - (Free function | non-static method), map style, (no | arbitrary) params,
+ // (empty | full | partial (array | map)) defaults
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("map-style registration with non-array params");
+ // Pass "param names" as scalar or as map
+ LLSD attempts(LLSDArray(17)(LLSDMap("pi", 3.14)("two", 2)));
+ foreach(LLSD ae, inArray(attempts))
+ {
+ std::string threw;
+ try
+ {
+ work.add("freena_err", "freena", freena, ae);
+ }
+ catch (const std::exception& e)
+ {
+ threw = e.what();
+ }
+ ensure_has(threw, "must be an array");
+ }
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("map-style registration with badly-formed defaults");
+ std::string threw;
+ try
+ {
+ work.add("freena_err", "freena", freena, LLSDArray("a")("b"), 17);
+ }
+ catch (const std::exception& e)
+ {
+ threw = e.what();
+ }
+ ensure_has(threw, "must be a map or an array");
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("map-style registration with too many array defaults");
+ std::string threw;
+ try
+ {
+ work.add("freena_err", "freena", freena,
+ LLSDArray("a")("b"),
+ LLSDArray(17)(0.9)("gack"));
+ }
+ catch (const std::exception& e)
+ {
+ threw = e.what();
+ }
+ ensure_has(threw, "shorter than");
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ set_test_name("map-style registration with too many map defaults");
+ std::string threw;
+ try
+ {
+ work.add("freena_err", "freena", freena,
+ LLSDArray("a")("b"),
+ LLSDMap("b", 17)("foo", 3.14)("bar", "sinister"));
+ }
+ catch (const std::exception& e)
+ {
+ threw = e.what();
+ }
+ ensure_has(threw, "nonexistent params");
+ ensure_has(threw, "foo");
+ ensure_has(threw, "bar");
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("query all");
+ verify_descs();
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("query all with remove()");
+ ensure("remove('bogus') returned true", ! work.remove("bogus"));
+ ensure("remove('real') returned false", work.remove("free1"));
+ // Of course, remove that from 'descs' too...
+ descs.erase("free1");
+ verify_descs();
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ set_test_name("getDispatchKey()");
+ ensure_equals(work.getDispatchKey(), "op");
+ }
+
+ template<> template<>
+ void object::test<8>()
+ {
+ set_test_name("query Callables with/out required params");
+ LLSD names(LLSDArray("free1")("Dmethod1")("Dcmethod1")("method1"));
+ foreach(LLSD nm, inArray(names))
+ {
+ LLSD metadata(getMetadata(nm));
+ ensure_equals("name mismatch", metadata["name"], nm);
+ ensure_equals(metadata["desc"].asString(), descs[nm]);
+ ensure("should not have required structure", metadata["required"].isUndefined());
+ ensure("should not have optional", metadata["optional"].isUndefined());
+
+ std::string name_req(nm.asString() + "_req");
+ metadata = getMetadata(name_req);
+ ensure_equals(metadata["name"].asString(), name_req);
+ ensure_equals(metadata["desc"].asString(), descs[name_req]);
+ ensure_equals("required mismatch", required, metadata["required"]);
+ ensure("should not have optional", metadata["optional"].isUndefined());
+ }
+ }
+
+ template<> template<>
+ void object::test<9>()
+ {
+ set_test_name("query array-style functions/methods");
+ // Associate each registered name with expected arity.
+ LLSD expected(LLSDArray
+ (LLSDArray
+ (0)(LLSDArray("free0_array")("smethod0_array")("method0_array")))
+ (LLSDArray
+ (5)(LLSDArray("freena_array")("smethodna_array")("methodna_array")))
+ (LLSDArray
+ (5)(LLSDArray("freenb_array")("smethodnb_array")("methodnb_array"))));
+ foreach(LLSD ae, inArray(expected))
+ {
+ LLSD::Integer arity(ae[0].asInteger());
+ LLSD names(ae[1]);
+ LLSD req(LLSD::emptyArray());
+ if (arity)
+ req[arity - 1] = LLSD();
+ foreach(LLSD nm, inArray(names))
+ {
+ LLSD metadata(getMetadata(nm));
+ ensure_equals("name mismatch", metadata["name"], nm);
+ ensure_equals(metadata["desc"].asString(), descs[nm]);
+ ensure_equals(STRINGIZE("mismatched required for " << nm.asString()),
+ metadata["required"], req);
+ ensure("should not have optional", metadata["optional"].isUndefined());
+ }
+ }
+ }
+
+ template<> template<>
+ void object::test<10>()
+ {
+ set_test_name("query map-style no-params functions/methods");
+ // - (Free function | non-static method), map style, no params (ergo
+ // no defaults)
+ LLSD names(LLSDArray("free0_map")("smethod0_map")("method0_map"));
+ foreach(LLSD nm, inArray(names))
+ {
+ LLSD metadata(getMetadata(nm));
+ ensure_equals("name mismatch", metadata["name"], nm);
+ ensure_equals(metadata["desc"].asString(), descs[nm]);
+ ensure("should not have required",
+ (metadata["required"].isUndefined() || metadata["required"].size() == 0));
+ ensure("should not have optional", metadata["optional"].isUndefined());
+ }
+ }
+
+ template<> template<>
+ void object::test<11>()
+ {
+ set_test_name("query map-style arbitrary-params functions/methods: "
+ "full array defaults vs. full map defaults");
+ // With functions registered with no defaults ("_allreq" suffixes),
+ // there is of course no difference between array defaults and map
+ // defaults. (We don't even bother registering with LLSD::emptyArray()
+ // vs. LLSD::emptyMap().) With functions registered with all defaults,
+ // there should (!) be no difference beween array defaults and map
+ // defaults. Verify, so we can ignore the distinction for all other
+ // tests.
+ LLSD equivalences(LLSDArray
+ (LLSDArray("freena_map_adft")("freena_map_mdft"))
+ (LLSDArray("freenb_map_adft")("freenb_map_mdft"))
+ (LLSDArray("smethodna_map_adft")("smethodna_map_mdft"))
+ (LLSDArray("smethodnb_map_adft")("smethodnb_map_mdft"))
+ (LLSDArray("methodna_map_adft")("methodna_map_mdft"))
+ (LLSDArray("methodnb_map_adft")("methodnb_map_mdft")));
+ foreach(LLSD eq, inArray(equivalences))
+ {
+ LLSD adft(eq[0]);
+ LLSD mdft(eq[1]);
+ // We can't just compare the results of the two getMetadata()
+ // calls, because they contain ["name"], which are different. So
+ // capture them, verify that each ["name"] is as expected, then
+ // remove for comparing the rest.
+ LLSD ameta(getMetadata(adft));
+ LLSD mmeta(getMetadata(mdft));
+ ensure_equals("adft name", adft, ameta["name"]);
+ ensure_equals("mdft name", mdft, mmeta["name"]);
+ ameta.erase("name");
+ mmeta.erase("name");
+ ensure_equals(STRINGIZE("metadata for " << adft.asString()
+ << " vs. " << mdft.asString()),
+ ameta, mmeta);
+ }
+ }
+
+ template<> template<>
+ void object::test<12>()
+ {
+ set_test_name("query map-style arbitrary-params functions/methods");
+ // - (Free function | non-static method), map style, arbitrary params,
+ // (empty | full | partial (array | map)) defaults
+
+ // Generate maps containing all parameter names for cases in which all
+ // params are required. Also maps containing left requirements for
+ // partial defaults arrays. Also defaults maps from defaults arrays.
+ LLSD allreq, leftreq, rightdft;
+ foreach(LLSD::String a, ab)
+ {
+ // The map in which all params are required uses params[a] as
+ // keys, with all isUndefined() as values. We can accomplish that
+ // by passing zipmap() an empty values array.
+ allreq[a] = zipmap(params[a], LLSD::emptyArray());
+ // Same for leftreq, save that we use the subset of the params not
+ // supplied by dft_array_partial[a].
+ LLSD::Integer partition(params[a].size() - dft_array_partial[a].size());
+ leftreq[a] = zipmap(llsd_copy_array(params[a].beginArray(),
+ params[a].beginArray() + partition),
+ LLSD::emptyArray());
+ // Generate map pairing dft_array_partial[a] values with their
+ // param names.
+ rightdft[a] = zipmap(llsd_copy_array(params[a].beginArray() + partition,
+ params[a].endArray()),
+ dft_array_partial[a]);
+ }
+ cout << "allreq:\n" << allreq << "\nleftreq:\n" << leftreq << "\nrightdft:\n" << rightdft << std::endl;
+
+ // Generate maps containing parameter names not provided by the
+ // dft_map_partial maps.
+ LLSD skipreq(allreq);
+ foreach(LLSD::String a, ab)
+ {
+ foreach(const MapEntry& me, inMap(dft_map_partial[a]))
+ {
+ skipreq[a].erase(me.first);
+ }
+ }
+ cout << "skipreq:\n" << skipreq << std::endl;
+
+ LLSD groups(LLSDArray // array of groups
+
+ (LLSDArray // group
+ (LLSDArray("freena_map_allreq")("smethodna_map_allreq")("methodna_map_allreq"))
+ (LLSDArray(allreq["a"])(LLSD()))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freenb_map_allreq")("smethodnb_map_allreq")("methodnb_map_allreq"))
+ (LLSDArray(allreq["b"])(LLSD()))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freena_map_leftreq")("smethodna_map_leftreq")("methodna_map_leftreq"))
+ (LLSDArray(leftreq["a"])(rightdft["a"]))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freenb_map_leftreq")("smethodnb_map_leftreq")("methodnb_map_leftreq"))
+ (LLSDArray(leftreq["b"])(rightdft["b"]))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freena_map_skipreq")("smethodna_map_skipreq")("methodna_map_skipreq"))
+ (LLSDArray(skipreq["a"])(dft_map_partial["a"]))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freenb_map_skipreq")("smethodnb_map_skipreq")("methodnb_map_skipreq"))
+ (LLSDArray(skipreq["b"])(dft_map_partial["b"]))) // required, optional
+
+ // We only need mention the full-map-defaults ("_mdft" suffix)
+ // registrations, having established their equivalence with the
+ // full-array-defaults ("_adft" suffix) registrations in another test.
+ (LLSDArray // group
+ (LLSDArray("freena_map_mdft")("smethodna_map_mdft")("methodna_map_mdft"))
+ (LLSDArray(LLSD::emptyMap())(dft_map_full["a"]))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freenb_map_mdft")("smethodnb_map_mdft")("methodnb_map_mdft"))
+ (LLSDArray(LLSD::emptyMap())(dft_map_full["b"])))); // required, optional
+
+ foreach(LLSD grp, inArray(groups))
+ {
+ // Internal structure of each group in 'groups':
+ LLSD names(grp[0]);
+ LLSD required(grp[1][0]);
+ LLSD optional(grp[1][1]);
+ cout << "For " << names << ",\n" << "required:\n" << required << "\noptional:\n" << optional << std::endl;
+
+ // Loop through 'names'
+ foreach(LLSD nm, inArray(names))
+ {
+ LLSD metadata(getMetadata(nm));
+ ensure_equals("name mismatch", metadata["name"], nm);
+ ensure_equals(nm.asString(), metadata["desc"].asString(), descs[nm]);
+ ensure_equals(STRINGIZE(nm << " required mismatch"),
+ metadata["required"], required);
+ ensure_equals(STRINGIZE(nm << " optional mismatch"),
+ metadata["optional"], optional);
+ }
+ }
+ }
+
+ template<> template<>
+ void object::test<13>()
+ {
+ set_test_name("try_call()");
+ ensure("try_call(bogus name, LLSD()) returned true", ! work.try_call("freek", LLSD()));
+ ensure("try_call(bogus name) returned true", ! work.try_call(LLSDMap("op", "freek")));
+ ensure("try_call(real name, LLSD()) returned false", work.try_call("free0_array", LLSD()));
+ ensure("try_call(real name) returned false", work.try_call(LLSDMap("op", "free0_map")));
+ }
+
+ template<> template<>
+ void object::test<14>()
+ {
+ set_test_name("call with bad name");
+ call_exc("freek", LLSD(), "not found");
+ // We don't have a comparable helper function for the one-arg
+ // operator() method, and it's not worth building one just for this
+ // case. Write it out.
+ std::string threw;
+ try
+ {
+ work(LLSDMap("op", "freek"));
+ }
+ catch (const std::runtime_error& e)
+ {
+ cout << "*** " << e.what() << "\n";
+ threw = e.what();
+ }
+ ensure_has(threw, "bad");
+ ensure_has(threw, "op");
+ ensure_has(threw, "freek");
+ }
+
+ template<> template<>
+ void object::test<15>()
+ {
+ set_test_name("call with event key");
+ // We don't need a separate test for operator()(string, LLSD) with
+ // valid name, because all the rest of the tests exercise that case.
+ // The one we don't exercise elsewhere is operator()(LLSD) with valid
+ // name, so here it is.
+ work(LLSDMap("op", "free0_map"));
+ ensure_equals(g.i, 17);
+ }
+
+ // Cannot be defined inside function body... remind me again why we use C++... :-P
+ struct CallablesTriple
+ {
+ std::string name, name_req;
+ LLSD& llsd;
+ };
+
+ template<> template<>
+ void object::test<16>()
+ {
+ set_test_name("call Callables");
+ CallablesTriple tests[] =
+ {
+ { "free1", "free1_req", g.llsd },
+ { "Dmethod1", "Dmethod1_req", work.llsd },
+ { "Dcmethod1", "Dcmethod1_req", work.llsd },
+ { "method1", "method1_req", v.llsd }
+ };
+ // Arbitrary LLSD value that we should be able to pass to Callables
+ // without 'required', but should not be able to pass to Callables
+ // with 'required'.
+ LLSD answer(42);
+ // LLSD value matching 'required' according to llsd_matches() rules.
+ LLSD matching(LLSDMap("d", 3.14)("array", LLSDArray("answer")(true)(answer)));
+ // Okay, walk through 'tests'.
+ foreach(const CallablesTriple& tr, tests)
+ {
+ // Should be able to pass 'answer' to Callables registered
+ // without 'required'.
+ work(tr.name, answer);
+ ensure_equals("answer mismatch", tr.llsd, answer);
+ // Should NOT be able to pass 'answer' to Callables registered
+ // with 'required'.
+ call_exc(tr.name_req, answer, "bad request");
+ // But SHOULD be able to pass 'matching' to Callables registered
+ // with 'required'.
+ work(tr.name_req, matching);
+ ensure_equals("matching mismatch", tr.llsd, matching);
+ }
+ }
+
+ template<> template<>
+ void object::test<17>()
+ {
+ set_test_name("passing wrong args to (map | array)-style registrations");
+
+ // Pass scalar/map to array-style functions, scalar/array to map-style
+ // functions. As that validation happens well before we engage the
+ // argument magic, it seems pointless to repeat this with every
+ // variation: (free function | non-static method), (no | arbitrary)
+ // args. We should only need to engage it for one map-style
+ // registration and one array-style registration.
+ std::string array_exc("needs an args array");
+ call_exc("free0_array", 17, array_exc);
+ call_exc("free0_array", LLSDMap("pi", 3.14), array_exc);
+
+ std::string map_exc("needs a map");
+ call_exc("free0_map", 17, map_exc);
+ // Passing an array to a map-style function works now! No longer an
+ // error case!
+// call_exc("free0_map", LLSDArray("a")("b"), map_exc);
+ }
+
+ template<> template<>
+ void object::test<18>()
+ {
+ set_test_name("call no-args functions");
+ LLSD names(LLSDArray
+ ("free0_array")("free0_map")
+ ("smethod0_array")("smethod0_map")
+ ("method0_array")("method0_map"));
+ foreach(LLSD name, inArray(names))
+ {
+ // Look up the Vars instance for this function.
+ Vars* vars(varsfor(name));
+ // Both the global and stack Vars instances are automatically
+ // cleared at the start of each test<n> method. But since we're
+ // calling these things several different times in the same
+ // test<n> method, manually reset the Vars between each.
+ *vars = Vars();
+ ensure_equals(vars->i, 0);
+ // call function with empty array (or LLSD(), should be equivalent)
+ work(name, LLSD());
+ ensure_equals(vars->i, 17);
+ }
+ }
+
+ // Break out this data because we use it in a couple different tests.
+ LLSD array_funcs(LLSDArray
+ (LLSDMap("a", "freena_array") ("b", "freenb_array"))
+ (LLSDMap("a", "smethodna_array")("b", "smethodnb_array"))
+ (LLSDMap("a", "methodna_array") ("b", "methodnb_array")));
+
+ template<> template<>
+ void object::test<19>()
+ {
+ set_test_name("call array-style functions with too-short arrays");
+ // Could have two different too-short arrays, one for *na and one for
+ // *nb, but since they both take 5 params...
+ LLSD tooshort(LLSDArray("this")("array")("too")("short"));
+ foreach(const LLSD& funcsab, inArray(array_funcs))
+ {
+ foreach(const llsd::MapEntry& e, inMap(funcsab))
+ {
+ call_exc(e.second, tooshort, "requires more arguments");
+ }
+ }
+ }
+
+ template<> template<>
+ void object::test<20>()
+ {
+ set_test_name("call array-style functions with (just right | too long) arrays");
+ std::vector<U8> binary;
+ for (size_t h(0x01), i(0); i < 5; h+= 0x22, ++i)
+ {
+ binary.push_back(h);
+ }
+ LLSD args(LLSDMap("a", LLSDArray(true)(17)(3.14)(123.456)("char*"))
+ ("b", LLSDArray("string")
+ (LLUUID("01234567-89ab-cdef-0123-456789abcdef"))
+ (LLDate("2011-02-03T15:07:00Z"))
+ (LLURI("http://secondlife.com"))
+ (binary)));
+ LLSD argsplus(args);
+ argsplus["a"].append("bogus");
+ argsplus["b"].append("bogus");
+ LLSD expect;
+ foreach(LLSD::String a, ab)
+ {
+ expect[a] = zipmap(params[a], args[a]);
+ }
+ // Adjust expect["a"]["cp"] for special Vars::cp treatment.
+ expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'";
+ cout << "expect: " << expect << '\n';
+
+ // Use substantially the same logic for args and argsplus
+ LLSD argsarrays(LLSDArray(args)(argsplus));
+ // So i==0 selects 'args', i==1 selects argsplus
+ for (LLSD::Integer i(0), iend(argsarrays.size()); i < iend; ++i)
+ {
+ foreach(const LLSD& funcsab, inArray(array_funcs))
+ {
+ foreach(LLSD::String a, ab)
+ {
+ // Reset the Vars instance before each call
+ Vars* vars(varsfor(funcsab[a]));
+ *vars = Vars();
+ work(funcsab[a], argsarrays[i][a]);
+ ensure_llsd(STRINGIZE(funcsab[a].asString() <<
+ ": expect[\"" << a << "\"] mismatch"),
+ vars->inspect(), expect[a], 7); // 7 bits ~= 2 decimal digits
+
+ // TODO: in the i==1 or argsplus case, intercept LL_WARNS
+ // output? Even without that, using argsplus verifies that
+ // passing too many args isn't fatal; it works -- but
+ // would be nice to notice the warning too.
+ }
+ }
+ }
+ }
+
+ template<> template<>
+ void object::test<21>()
+ {
+ set_test_name("verify that passing LLSD() to const char* sends NULL");
+
+ ensure_equals("Vars::cp init", v.cp, "");
+ work("methodna_map_mdft", LLSDMap("cp", LLSD()));
+ ensure_equals("passing LLSD()", v.cp, "NULL");
+ work("methodna_map_mdft", LLSDMap("cp", ""));
+ ensure_equals("passing \"\"", v.cp, "''");
+ work("methodna_map_mdft", LLSDMap("cp", "non-NULL"));
+ ensure_equals("passing \"non-NULL\"", v.cp, "'non-NULL'");
+ }
+
+ template<> template<>
+ void object::test<22>()
+ {
+ set_test_name("call map-style functions with (full | oversized) (arrays | maps)");
+ const char binary[] = "\x99\x88\x77\x66\x55";
+ LLSD array_full(LLSDMap
+ ("a", LLSDArray(false)(255)(98.6)(1024.5)("pointer"))
+ ("b", LLSDArray("object")(LLUUID::generateNewID())(LLDate::now())(LLURI("http://wiki.lindenlab.com/wiki"))(LLSD::Binary(boost::begin(binary), boost::end(binary)))));
+ LLSD array_overfull(array_full);
+ foreach(LLSD::String a, ab)
+ {
+ array_overfull[a].append("bogus");
+ }
+ cout << "array_full: " << array_full << "\narray_overfull: " << array_overfull << std::endl;
+ // We rather hope that LLDate::now() will generate a timestamp
+ // distinct from the one it generated in the constructor, moments ago.
+ ensure_not_equals("Timestamps too close",
+ array_full["b"][2].asDate(), dft_array_full["b"][2].asDate());
+ // We /insist/ that LLUUID::generateNewID() do so.
+ ensure_not_equals("UUID collision",
+ array_full["b"][1].asUUID(), dft_array_full["b"][1].asUUID());
+ LLSD map_full, map_overfull;
+ foreach(LLSD::String a, ab)
+ {
+ map_full[a] = zipmap(params[a], array_full[a]);
+ map_overfull[a] = map_full[a];
+ map_overfull[a]["extra"] = "ignore";
+ }
+ cout << "map_full: " << map_full << "\nmap_overfull: " << map_overfull << std::endl;
+ LLSD expect(map_full);
+ // Twiddle the const char* param.
+ expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'";
+ // Another adjustment. For each data type, we're trying to distinguish
+ // three values: the Vars member's initial value (member wasn't
+ // stored; control never reached the set function), the registered
+ // default param value from dft_array_full, and the array_full value
+ // in this test. But bool can only distinguish two values. In this
+ // case, we want to differentiate the local array_full value from the
+ // dft_array_full value, so we use 'false'. However, that means
+ // Vars::inspect() doesn't differentiate it from the initial value,
+ // so won't bother returning it. Predict that behavior to match the
+ // LLSD values.
+ expect["a"].erase("b");
+ cout << "expect: " << expect << std::endl;
+ // For this test, calling functions registered with different sets of
+ // parameter defaults should make NO DIFFERENCE WHATSOEVER. Every call
+ // should pass all params.
+ LLSD names(LLSDMap
+ ("a", LLSDArray
+ ("freena_map_allreq") ("smethodna_map_allreq") ("methodna_map_allreq")
+ ("freena_map_leftreq")("smethodna_map_leftreq")("methodna_map_leftreq")
+ ("freena_map_skipreq")("smethodna_map_skipreq")("methodna_map_skipreq")
+ ("freena_map_adft") ("smethodna_map_adft") ("methodna_map_adft")
+ ("freena_map_mdft") ("smethodna_map_mdft") ("methodna_map_mdft"))
+ ("b", LLSDArray
+ ("freenb_map_allreq") ("smethodnb_map_allreq") ("methodnb_map_allreq")
+ ("freenb_map_leftreq")("smethodnb_map_leftreq")("methodnb_map_leftreq")
+ ("freenb_map_skipreq")("smethodnb_map_skipreq")("methodnb_map_skipreq")
+ ("freenb_map_adft") ("smethodnb_map_adft") ("methodnb_map_adft")
+ ("freenb_map_mdft") ("smethodnb_map_mdft") ("methodnb_map_mdft")));
+ // Treat (full | overfull) (array | map) the same.
+ LLSD argssets(LLSDArray(array_full)(array_overfull)(map_full)(map_overfull));
+ foreach(const LLSD& args, inArray(argssets))
+ {
+ foreach(LLSD::String a, ab)
+ {
+ foreach(LLSD::String name, inArray(names[a]))
+ {
+ // Reset the Vars instance
+ Vars* vars(varsfor(name));
+ *vars = Vars();
+ work(name, args[a]);
+ ensure_llsd(STRINGIZE(name << ": expect[\"" << a << "\"] mismatch"),
+ vars->inspect(), expect[a], 7); // 7 bits, 2 decimal digits
+ // intercept LL_WARNS for the two overfull cases?
+ }
+ }
+ }
+ }
+} // namespace tut
diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp
index d1c74b6fa1..28dc3bd313 100644
--- a/indra/llimage/llimageworker.cpp
+++ b/indra/llimage/llimageworker.cpp
@@ -38,6 +38,12 @@ LLImageDecodeThread::LLImageDecodeThread(bool threaded)
mCreationMutex = new LLMutex(getAPRPool());
}
+//virtual
+LLImageDecodeThread::~LLImageDecodeThread()
+{
+ delete mCreationMutex ;
+}
+
// MAIN THREAD
// virtual
S32 LLImageDecodeThread::update(U32 max_time_ms)
diff --git a/indra/llimage/llimageworker.h b/indra/llimage/llimageworker.h
index c3c92ec832..c684222fa5 100644
--- a/indra/llimage/llimageworker.h
+++ b/indra/llimage/llimageworker.h
@@ -73,6 +73,8 @@ public:
public:
LLImageDecodeThread(bool threaded = true);
+ virtual ~LLImageDecodeThread();
+
handle_t decodeImage(LLImageFormatted* image,
U32 priority, S32 discard, BOOL needs_aux,
Responder* responder);
diff --git a/indra/llmath/llcamera.cpp b/indra/llmath/llcamera.cpp
index bad4d00fd6..687c1a7d45 100644
--- a/indra/llmath/llcamera.cpp
+++ b/indra/llmath/llcamera.cpp
@@ -703,6 +703,7 @@ void LLCamera::calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom)
mLocalPlanes[PLANE_BOTTOM].setVec( a, c, b);
//calculate center and radius squared of frustum in world absolute coordinates
+ static LLVector3 const X_AXIS(1.f, 0.f, 0.f);
mFrustCenter = X_AXIS*mFarPlane*0.5f;
mFrustCenter = transformToAbsolute(mFrustCenter);
mFrustRadiusSquared = mFarPlane*0.5f;
diff --git a/indra/llmath/llcamera.h b/indra/llmath/llcamera.h
index 922d6f9fac..531144db39 100644
--- a/indra/llmath/llcamera.h
+++ b/indra/llmath/llcamera.h
@@ -50,15 +50,6 @@ const F32 MIN_FAR_PLANE = 0.2f;
static const F32 MIN_FIELD_OF_VIEW = 5.0f * DEG_TO_RAD;
static const F32 MAX_FIELD_OF_VIEW = 175.f * DEG_TO_RAD;
-static const LLVector3 X_AXIS(1.f,0.f,0.f);
-static const LLVector3 Y_AXIS(0.f,1.f,0.f);
-static const LLVector3 Z_AXIS(0.f,0.f,1.f);
-
-static const LLVector3 NEG_X_AXIS(-1.f,0.f,0.f);
-static const LLVector3 NEG_Y_AXIS(0.f,-1.f,0.f);
-static const LLVector3 NEG_Z_AXIS(0.f,0.f,-1.f);
-
-
// An LLCamera is an LLCoorFrame with a view frustum.
// This means that it has several methods for moving it around
// that are inherited from the LLCoordFrame() class :
diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp
index 595c470a19..26a20cede8 100644
--- a/indra/llplugin/llpluginclassmedia.cpp
+++ b/indra/llplugin/llpluginclassmedia.cpp
@@ -64,9 +64,10 @@ LLPluginClassMedia::~LLPluginClassMedia()
reset();
}
-bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug)
+bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::string &plugin_dir, const std::string &plugin_filename, bool debug)
{
LL_DEBUGS("Plugin") << "launcher: " << launcher_filename << LL_ENDL;
+ LL_DEBUGS("Plugin") << "dir: " << plugin_dir << LL_ENDL;
LL_DEBUGS("Plugin") << "plugin: " << plugin_filename << LL_ENDL;
mPlugin = new LLPluginProcessParent(this);
@@ -77,7 +78,7 @@ bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::s
message.setValue("target", mTarget);
sendMessage(message);
- mPlugin->init(launcher_filename, plugin_filename, debug);
+ mPlugin->init(launcher_filename, plugin_dir, plugin_filename, debug);
return true;
}
diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h
index c826e13c40..618e928a08 100644
--- a/indra/llplugin/llpluginclassmedia.h
+++ b/indra/llplugin/llpluginclassmedia.h
@@ -45,6 +45,7 @@ public:
// local initialization, called by the media manager when creating a source
virtual bool init(const std::string &launcher_filename,
+ const std::string &plugin_dir,
const std::string &plugin_filename,
bool debug);
diff --git a/indra/llplugin/llplugininstance.cpp b/indra/llplugin/llplugininstance.cpp
index c326961db4..7cde82a20e 100644
--- a/indra/llplugin/llplugininstance.cpp
+++ b/indra/llplugin/llplugininstance.cpp
@@ -32,6 +32,10 @@
#include "llapr.h"
+#if LL_WINDOWS
+#include "direct.h" // needed for _chdir()
+#endif
+
/** Virtual destructor. */
LLPluginInstanceMessageListener::~LLPluginInstanceMessageListener()
{
@@ -73,10 +77,24 @@ LLPluginInstance::~LLPluginInstance()
* @param[in] plugin_file Name of plugin dll/dylib/so. TODO:DOC is this correct? see .h
* @return 0 if successful, APR error code or error code from the plugin's init function on failure.
*/
-int LLPluginInstance::load(std::string &plugin_file)
+int LLPluginInstance::load(const std::string& plugin_dir, std::string &plugin_file)
{
pluginInitFunction init_function = NULL;
+ if ( plugin_dir.length() )
+ {
+#if LL_WINDOWS
+ // VWR-21275:
+ // *SOME* Windows systems fail to load the Qt plugins if the current working
+ // directory is not the same as the directory with the Qt DLLs in.
+ // This should not cause any run time issues since we are changing the cwd for the
+ // plugin shell process and not the viewer.
+ // Changing back to the previous directory is not necessary since the plugin shell
+ // quits once the plugin exits.
+ _chdir( plugin_dir.c_str() );
+#endif
+ };
+
int result = apr_dso_load(&mDSOHandle,
plugin_file.c_str(),
gAPRPoolp);
diff --git a/indra/llplugin/llplugininstance.h b/indra/llplugin/llplugininstance.h
index 50531ca77f..e6926c3e37 100644
--- a/indra/llplugin/llplugininstance.h
+++ b/indra/llplugin/llplugininstance.h
@@ -56,7 +56,7 @@ public:
// Load a plugin dll/dylib/so
// Returns 0 if successful, APR error code or error code returned from the plugin's init function on failure.
- int load(std::string &plugin_file);
+ int load(const std::string& plugin_dir, std::string &plugin_file);
// Sends a message to the plugin.
void sendMessage(const std::string &message);
diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp
index 45a86476ac..0beb46d0e5 100644
--- a/indra/llplugin/llpluginprocesschild.cpp
+++ b/indra/llplugin/llpluginprocesschild.cpp
@@ -139,7 +139,7 @@ void LLPluginProcessChild::idle(void)
if(!mPluginFile.empty())
{
mInstance = new LLPluginInstance(this);
- if(mInstance->load(mPluginFile) == 0)
+ if(mInstance->load(mPluginDir, mPluginFile) == 0)
{
mHeartbeat.start();
mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS);
@@ -348,6 +348,7 @@ void LLPluginProcessChild::receiveMessageRaw(const std::string &message)
if(message_name == "load_plugin")
{
mPluginFile = parsed.getValue("file");
+ mPluginDir = parsed.getValue("dir");
}
else if(message_name == "shm_add")
{
diff --git a/indra/llplugin/llpluginprocesschild.h b/indra/llplugin/llpluginprocesschild.h
index 22ff403ad6..a9d6794e40 100644
--- a/indra/llplugin/llpluginprocesschild.h
+++ b/indra/llplugin/llpluginprocesschild.h
@@ -92,6 +92,7 @@ private:
LLSocket::ptr_t mSocket;
std::string mPluginFile;
+ std::string mPluginDir;
LLPluginInstance *mInstance;
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp
index c002de0462..db4b8b1316 100644
--- a/indra/llplugin/llpluginprocessparent.cpp
+++ b/indra/llplugin/llpluginprocessparent.cpp
@@ -157,10 +157,11 @@ void LLPluginProcessParent::errorState(void)
setState(STATE_ERROR);
}
-void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug)
+void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_dir, const std::string &plugin_filename, bool debug)
{
mProcess.setExecutable(launcher_filename);
mPluginFile = plugin_filename;
+ mPluginDir = plugin_dir;
mCPUUsage = 0.0f;
mDebug = debug;
setState(STATE_INITIALIZED);
@@ -445,6 +446,7 @@ void LLPluginProcessParent::idle(void)
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin");
message.setValue("file", mPluginFile);
+ message.setValue("dir", mPluginDir);
sendMessage(message);
}
diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h
index 32394809ef..c66723f175 100644
--- a/indra/llplugin/llpluginprocessparent.h
+++ b/indra/llplugin/llpluginprocessparent.h
@@ -57,6 +57,7 @@ public:
~LLPluginProcessParent();
void init(const std::string &launcher_filename,
+ const std::string &plugin_dir,
const std::string &plugin_filename,
bool debug);
@@ -151,6 +152,7 @@ private:
LLProcessLauncher mProcess;
std::string mPluginFile;
+ std::string mPluginDir;
LLPluginProcessParentOwner *mOwner;
diff --git a/indra/llui/llfloaterreglistener.cpp b/indra/llui/llfloaterreglistener.cpp
index 821d4543ae..7525b8cab3 100644
--- a/indra/llui/llfloaterreglistener.cpp
+++ b/indra/llui/llfloaterreglistener.cpp
@@ -60,6 +60,11 @@ LLFloaterRegListener::LLFloaterRegListener():
"Ask to toggle the state of the floater specified in [\"name\"]",
&LLFloaterRegListener::toggleInstance,
requiredName);
+ add("instanceVisible",
+ "Return on [\"reply\"] an event whose [\"visible\"] indicates the visibility "
+ "of the floater specified in [\"name\"]",
+ &LLFloaterRegListener::instanceVisible,
+ requiredName);
LLSD requiredNameButton;
requiredNameButton["name"] = LLSD();
requiredNameButton["button"] = LLSD();
@@ -71,9 +76,7 @@ LLFloaterRegListener::LLFloaterRegListener():
void LLFloaterRegListener::getBuildMap(const LLSD& event) const
{
- // Honor the "reqid" convention by echoing event["reqid"] in our reply packet.
- LLReqID reqID(event);
- LLSD reply(reqID.makeResponse());
+ LLSD reply;
// Build an LLSD map that mirrors sBuildMap. Since we have no good way to
// represent a C++ callable in LLSD, the only part of BuildData we can
// store is the filename. For each LLSD map entry, it would be more
@@ -86,7 +89,7 @@ void LLFloaterRegListener::getBuildMap(const LLSD& event) const
reply[mi->first] = mi->second.mFile;
}
// Send the reply to the LLEventPump named in event["reply"].
- LLEventPumps::instance().obtain(event["reply"]).post(reply);
+ sendReply(reply, event);
}
void LLFloaterRegListener::showInstance(const LLSD& event) const
@@ -104,6 +107,12 @@ void LLFloaterRegListener::toggleInstance(const LLSD& event) const
LLFloaterReg::toggleInstance(event["name"], event["key"]);
}
+void LLFloaterRegListener::instanceVisible(const LLSD& event) const
+{
+ sendReply(LLSDMap("visible", LLFloaterReg::instanceVisible(event["name"], event["key"])),
+ event);
+}
+
void LLFloaterRegListener::clickButton(const LLSD& event) const
{
// If the caller requests a reply, build the reply.
diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h
index 586656667c..24311a2dfa 100644
--- a/indra/llui/llfloaterreglistener.h
+++ b/indra/llui/llfloaterreglistener.h
@@ -47,6 +47,7 @@ private:
void showInstance(const LLSD& event) const;
void hideInstance(const LLSD& event) const;
void toggleInstance(const LLSD& event) const;
+ void instanceVisible(const LLSD& event) const;
void clickButton(const LLSD& event) const;
};
diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp
index 58b27671e8..c11ad11de9 100644
--- a/indra/llui/tests/llurlentry_stub.cpp
+++ b/indra/llui/tests/llurlentry_stub.cpp
@@ -193,8 +193,8 @@ LLFontGL* LLFontGL::getFontDefault()
return NULL;
}
-char* _PREHASH_AgentData = (char *) "AgentData";
-char* _PREHASH_AgentID = (char *) "AgentID";
+char* _PREHASH_AgentData = const_cast<char*>("AgentData");
+char* _PREHASH_AgentID = const_cast<char*>("AgentID");
LLHost LLHost::invalid(INVALID_PORT,INVALID_HOST_IP_ADDRESS);
diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt
index 4d2677fd91..00aaba2052 100644
--- a/indra/llwindow/CMakeLists.txt
+++ b/indra/llwindow/CMakeLists.txt
@@ -36,6 +36,7 @@ set(llwindow_SOURCE_FILES
llkeyboard.cpp
llwindowheadless.cpp
llwindowcallbacks.cpp
+ llwindowlistener.cpp
)
set(llwindow_HEADER_FILES
@@ -44,6 +45,7 @@ set(llwindow_HEADER_FILES
llkeyboard.h
llwindowheadless.h
llwindowcallbacks.h
+ llwindowlistener.h
)
set(viewer_SOURCE_FILES
diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp
index 072f694c24..2d00c37719 100644
--- a/indra/llwindow/llwindow.cpp
+++ b/indra/llwindow/llwindow.cpp
@@ -41,6 +41,7 @@
#include "llkeyboard.h"
#include "linked_lists.h"
#include "llwindowcallbacks.h"
+#include "llwindowlistener.h"
//
@@ -115,10 +116,15 @@ LLWindow::LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags)
mHideCursorPermanent(FALSE),
mFlags(flags),
mHighSurrogate(0)
-{ }
+{
+ mListener = new LLWindowListener(callbacks, gKeyboard);
+}
LLWindow::~LLWindow()
-{ }
+{
+ delete mListener;
+ mListener = NULL;
+}
//virtual
BOOL LLWindow::isValid()
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
index e8a86a1880..6bdc01ae88 100644
--- a/indra/llwindow/llwindow.h
+++ b/indra/llwindow/llwindow.h
@@ -36,6 +36,7 @@
class LLSplashScreen;
class LLPreeditor;
class LLWindowCallbacks;
+class LLWindowListener;
// Refer to llwindow_test in test/common/llwindow for usage example
@@ -188,6 +189,7 @@ protected:
BOOL mHideCursorPermanent;
U32 mFlags;
U16 mHighSurrogate;
+ LLWindowListener* mListener;
// Handle a UTF-16 encoding unit received from keyboard.
// Converting the series of UTF-16 encoding units to UTF-32 data,
diff --git a/indra/llwindow/llwindowlistener.cpp b/indra/llwindow/llwindowlistener.cpp
new file mode 100644
index 0000000000..22cc12acee
--- /dev/null
+++ b/indra/llwindow/llwindowlistener.cpp
@@ -0,0 +1,174 @@
+/**
+ * @file llwindowlistener.cpp
+ * @brief EventAPI interface for injecting input into LLWindow
+ *
+ * $LicenseInfo:firstyear=2001&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$
+ */
+
+#include "linden_common.h"
+
+#include "llwindowlistener.h"
+
+#include "llcoord.h"
+#include "llkeyboard.h"
+#include "llwindowcallbacks.h"
+
+LLWindowListener::LLWindowListener(LLWindowCallbacks *window, LLKeyboard * keyboard)
+ : LLEventAPI("LLWindow", "Inject input events into the LLWindow instance"),
+ mWindow(window),
+ mKeyboard(keyboard)
+{
+ add("keyDown",
+ "Given [\"keycode\"] or [\"char\"], will inject the given keypress event.",
+ &LLWindowListener::keyDown);
+ add("keyUp",
+ "Given [\"keycode\"] or [\"char\"], will inject the given key release event.",
+ &LLWindowListener::keyUp);
+ add("mouseDown",
+ "Given [\"button\"], [\"x\"] and [\"y\"], will inject the given mouse click event.",
+ &LLWindowListener::mouseDown);
+ add("mouseUp",
+ "Given [\"button\"], [\"x\"] and [\"y\"], will inject the given mouse release event.",
+ &LLWindowListener::mouseUp);
+ add("mouseMove",
+ "Given [\"x\"] and [\"y\"], will inject the given mouse movement event.",
+ &LLWindowListener::mouseMove);
+ add("mouseScroll",
+ "Given a number of [\"clicks\"], will inject the given mouse scroll event.",
+ &LLWindowListener::mouseScroll);
+}
+
+void LLWindowListener::keyDown(LLSD const & evt)
+{
+ if(NULL == mKeyboard)
+ {
+ // *HACK to handle the fact that LLWindow subclasses have to initialize
+ // things in an inconvenient order
+ mKeyboard = gKeyboard;
+ }
+
+ KEY keycode = 0;
+ if(evt.has("keycode"))
+ {
+ keycode = KEY(evt["keycode"].asInteger());
+ }
+ else
+ {
+ keycode = KEY(evt["char"].asString()[0]);
+ }
+
+ // *TODO - figure out how to handle the mask
+ mKeyboard->handleTranslatedKeyDown(keycode, 0);
+}
+
+void LLWindowListener::keyUp(LLSD const & evt)
+{
+ if(NULL == mKeyboard)
+ {
+ // *HACK to handle the fact that LLWindow subclasses have to initialize
+ // things in an inconvenient order
+ mKeyboard = gKeyboard;
+ }
+
+ KEY keycode = 0;
+ if(evt.has("keycode"))
+ {
+ keycode = KEY(evt["keycode"].asInteger());
+ }
+ else
+ {
+ keycode = KEY(evt["char"].asString()[0]);
+ }
+
+ // *TODO - figure out how to handle the mask
+ mKeyboard->handleTranslatedKeyDown(keycode, 0);
+}
+
+void LLWindowListener::mouseDown(LLSD const & evt)
+{
+ LLCoordGL pos(evt["x"].asInteger(), evt["y"].asInteger());
+
+ std::string const & button = evt["button"].asString();
+
+ if(button == "LEFT")
+ {
+ // *TODO - figure out how to handle the mask
+ mWindow->handleMouseDown(NULL, pos, 0);
+ }
+ else if (button == "RIGHT")
+ {
+ // *TODO - figure out how to handle the mask
+ mWindow->handleRightMouseDown(NULL, pos, 0);
+ }
+ else if (button == "MIDDLE")
+ {
+ // *TODO - figure out how to handle the mask
+ mWindow->handleMiddleMouseDown(NULL, pos, 0);
+ }
+ else
+ {
+ llwarns << "ignoring unknown mous button \"" << button << '\"' << llendl;
+ }
+}
+
+void LLWindowListener::mouseUp(LLSD const & evt)
+{
+ LLCoordGL pos(evt["x"].asInteger(), evt["y"].asInteger());
+
+ std::string const & button = evt["button"].asString();
+
+ if(button == "LEFT")
+ {
+ // *TODO - figure out how to handle the mask
+ mWindow->handleMouseUp(NULL, pos, 0);
+ }
+ else if (button == "RIGHT")
+ {
+ // *TODO - figure out how to handle the mask
+ mWindow->handleRightMouseUp(NULL, pos, 0);
+ }
+ else if (button == "MIDDLE")
+ {
+ // *TODO - figure out how to handle the mask
+ mWindow->handleMiddleMouseUp(NULL, pos, 0);
+ }
+ else
+ {
+ llwarns << "ignoring unknown mous button \"" << button << '\"' << llendl;
+ }
+}
+
+void LLWindowListener::mouseMove(LLSD const & evt)
+{
+ LLCoordGL pos(evt["x"].asInteger(), evt["y"].asInteger());
+
+ // *TODO - figure out how to handle the mask
+ mWindow->handleMouseMove(NULL, pos, 0);
+}
+
+void LLWindowListener::mouseScroll(LLSD const & evt)
+{
+ S32 clicks = evt["clicks"].asInteger();
+
+ mWindow->handleScrollWheel(NULL, clicks);
+}
+
diff --git a/indra/llwindow/llwindowlistener.h b/indra/llwindow/llwindowlistener.h
new file mode 100644
index 0000000000..5b234c5d1d
--- /dev/null
+++ b/indra/llwindow/llwindowlistener.h
@@ -0,0 +1,53 @@
+/**
+ * @file llwindowlistener.h
+ * @brief EventAPI interface for injecting input into LLWindow
+ *
+ * $LicenseInfo:firstyear=2001&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$
+ */
+
+#ifndef LL_LLWINDOWLISTENER_H
+#define LL_LLWINDOWLISTENER_H
+
+#include "lleventapi.h"
+
+class LLKeyboard;
+class LLWindowCallbacks;
+
+class LLWindowListener : public LLEventAPI
+{
+public:
+ LLWindowListener(LLWindowCallbacks * window, LLKeyboard * keyboard);
+
+ void keyDown(LLSD const & evt);
+ void keyUp(LLSD const & evt);
+ void mouseDown(LLSD const & evt);
+ void mouseUp(LLSD const & evt);
+ void mouseMove(LLSD const & evt);
+ void mouseScroll(LLSD const & evt);
+
+private:
+ LLWindowCallbacks * mWindow;
+ LLKeyboard * mKeyboard;
+};
+
+
+#endif // LL_LLWINDOWLISTENER_H
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index af6beacdfa..11bebf04ef 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -412,6 +412,7 @@ set(viewer_SOURCE_FILES
llsidepaneliteminfo.cpp
llsidepaneltaskinfo.cpp
llsidetray.cpp
+ llsidetraylistener.cpp
llsidetraypanelcontainer.cpp
llsky.cpp
llslurl.cpp
@@ -947,6 +948,7 @@ set(viewer_HEADER_FILES
llsidepaneliteminfo.h
llsidepaneltaskinfo.h
llsidetray.h
+ llsidetraylistener.h
llsidetraypanelcontainer.h
llsky.h
llslurl.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 603fddbccd..f2b90b3d4a 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -697,28 +697,6 @@
<key>Value</key>
<integer>0</integer>
</map>
- <key>BrowserUseDefaultCAFile</key>
- <map>
- <key>Comment</key>
- <string>Tell the built-in web browser to use the CA.pem file shipped with the client.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>Boolean</string>
- <key>Value</key>
- <integer>1</integer>
- </map>
- <key>BrowserCAFilePath</key>
- <map>
- <key>Comment</key>
- <string>Tell the built-in web browser the path to an alternative CA.pem file (only used if BrowserUseDefaultCAFile is false).</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>String</string>
- <key>Value</key>
- <string></string>
- </map>
<key>BlockAvatarAppearanceMessages</key>
<map>
<key>Comment</key>
@@ -1852,6 +1830,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>DebugShowMemory</key>
+ <map>
+ <key>Comment</key>
+ <string>Show Total Allocated Memory</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>DebugShowRenderInfo</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp
index d520debc31..c453fe91f4 100644
--- a/indra/newview/llagentlistener.cpp
+++ b/indra/newview/llagentlistener.cpp
@@ -37,6 +37,8 @@
#include "llviewerobject.h"
#include "llviewerobjectlist.h"
#include "llviewerregion.h"
+#include "llsdutil.h"
+#include "llsdutil_math.h"
LLAgentListener::LLAgentListener(LLAgent &agent)
: LLEventAPI("LLAgent",
@@ -53,6 +55,15 @@ LLAgentListener::LLAgentListener(LLAgent &agent)
add("requestStand",
"Ask to stand up",
&LLAgentListener::requestStand);
+ add("resetAxes",
+ "Set the agent to a fixed orientation (optionally specify [\"lookat\"] = array of [x, y, z])",
+ &LLAgentListener::resetAxes);
+ add("getAxes",
+ "Send information about the agent's orientation on [\"reply\"]:\n"
+ "[\"euler\"]: map of {roll, pitch, yaw}\n"
+ "[\"quat\"]: array of [x, y, z, w] quaternion values",
+ &LLAgentListener::getAxes,
+ LLSDMap("reply", LLSD()));
}
void LLAgentListener::requestTeleport(LLSD const & event_data) const
@@ -104,3 +115,28 @@ void LLAgentListener::requestStand(LLSD const & event_data) const
mAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
}
+void LLAgentListener::resetAxes(const LLSD& event) const
+{
+ if (event.has("lookat"))
+ {
+ mAgent.resetAxes(ll_vector3_from_sd(event["lookat"]));
+ }
+ else
+ {
+ // no "lookat", default call
+ mAgent.resetAxes();
+ }
+}
+
+void LLAgentListener::getAxes(const LLSD& event) const
+{
+ LLQuaternion quat(mAgent.getQuat());
+ F32 roll, pitch, yaw;
+ quat.getEulerAngles(&roll, &pitch, &yaw);
+ // The official query API for LLQuaternion's [x, y, z, w] values is its
+ // public member mQ...
+ sendReply(LLSDMap
+ ("quat", llsd_copy_array(boost::begin(quat.mQ), boost::end(quat.mQ)))
+ ("euler", LLSDMap("roll", roll)("pitch", pitch)("yaw", yaw)),
+ event);
+}
diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h
index 9b585152f4..0aa58d0b16 100644
--- a/indra/newview/llagentlistener.h
+++ b/indra/newview/llagentlistener.h
@@ -44,6 +44,8 @@ private:
void requestTeleport(LLSD const & event_data) const;
void requestSit(LLSD const & event_data) const;
void requestStand(LLSD const & event_data) const;
+ void resetAxes(const LLSD& event) const;
+ void getAxes(const LLSD& event) const;
private:
LLAgent & mAgent;
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 0595d6f3c7..f7ec83bea9 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1296,7 +1296,7 @@ bool LLAppViewer::mainLoop()
resumeMainloopTimeout();
pingMainloopTimeout("Main:End");
- }
+ }
}
catch(std::bad_alloc)
{
@@ -1779,6 +1779,8 @@ bool LLAppViewer::cleanup()
ll_close_fail_log();
+ MEM_TRACK_RELEASE
+
llinfos << "Goodbye!" << llendflush;
// return 0;
@@ -2471,16 +2473,24 @@ namespace {
if(data["required"].asBoolean())
{
- apply_callback = &apply_update_ok_callback;
if(LLStartUp::getStartupState() <= STATE_LOGIN_WAIT)
{
// The user never saw the progress bar.
+ apply_callback = &apply_update_ok_callback;
notification_name = "RequiredUpdateDownloadedVerboseDialog";
}
- else
+ else if(LLStartUp::getStartupState() < STATE_WORLD_INIT)
{
+ // The user is logging in but blocked.
+ apply_callback = &apply_update_ok_callback;
notification_name = "RequiredUpdateDownloadedDialog";
}
+ else
+ {
+ // The user is already logged in; treat like an optional update.
+ apply_callback = &apply_update_callback;
+ notification_name = "DownloadBackgroundTip";
+ }
}
else
{
diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index c98bcbda45..5ff22f89ab 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -899,31 +899,14 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
}
}
- LLTextEditor* text_editor = notify_box->getChild<LLTextEditor>("text_editor_box", TRUE);
- S32 text_heigth = 0;
- if(text_editor != NULL)
- {
- text_heigth = text_editor->getTextBoundingRect().getHeight();
- }
-
//Prepare the rect for the view
LLRect target_rect = mEditor->getDocumentView()->getRect();
// squeeze down the widget by subtracting padding off left and right
target_rect.mLeft += mLeftWidgetPad + mEditor->getHPad();
target_rect.mRight -= mRightWidgetPad;
- notify_box->reshape(target_rect.getWidth(),
- notify_box->getRect().getHeight());
+ notify_box->reshape(target_rect.getWidth(), notify_box->getRect().getHeight());
notify_box->setOrigin(target_rect.mLeft, notify_box->getRect().mBottom);
- if (text_editor != NULL)
- {
- S32 text_heigth_delta =
- text_editor->getTextBoundingRect().getHeight()
- - text_heigth;
- notify_box->reshape(target_rect.getWidth(),
- notify_box->getRect().getHeight() + text_heigth_delta);
- }
-
LLInlineViewSegment::Params params;
params.view = notify_box;
params.left_pad = mLeftWidgetPad;
diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp
index 6f02192d0a..d77ebc5367 100644
--- a/indra/newview/llcolorswatch.cpp
+++ b/indra/newview/llcolorswatch.cpp
@@ -185,6 +185,10 @@ BOOL LLColorSwatchCtrl::handleMouseUp(S32 x, S32 y, MASK mask)
llassert(getEnabled());
llassert(getVisible());
+ // Focus the widget now in order to return the focus
+ // after the color picker is closed.
+ setFocus(TRUE);
+
showPicker(FALSE);
}
}
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index f0840774bd..51e76bcf9b 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -403,9 +403,9 @@ BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename)
{
wcsncpy( mFilesW,L"untitled.jpeg", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
}
- mOFN.lpstrDefExt = L"jpeg";
+ mOFN.lpstrDefExt = L"jpg";
mOFN.lpstrFilter =
- L"JPEG Images (*.jpeg)\0*.jpeg\0" \
+ L"JPEG Images (*.jpg *.jpeg)\0*.jpg;*.jpeg\0" \
L"\0";
break;
case FFSAVE_AVI:
diff --git a/indra/newview/llfirstuse.cpp b/indra/newview/llfirstuse.cpp
index 4c17199895..4d252dc662 100644
--- a/indra/newview/llfirstuse.cpp
+++ b/indra/newview/llfirstuse.cpp
@@ -41,35 +41,36 @@
// static
-std::set<std::string> LLFirstUse::sConfigVariables;
+//std::set<std::string> LLFirstUse::sConfigVariables;
+std::set<std::string> LLFirstUse::sConfigVariablesEnabled;
// static
-void LLFirstUse::addConfigVariable(const std::string& var)
-{
- sConfigVariables.insert(var);
-}
+//void LLFirstUse::addConfigVariable(const std::string& var)
+//{
+// sConfigVariables.insert(var);
+//}
// static
-void LLFirstUse::disableFirstUse()
-{
- // Set all first-use warnings to disabled
- for (std::set<std::string>::iterator iter = sConfigVariables.begin();
- iter != sConfigVariables.end(); ++iter)
- {
- gWarningSettings.setBOOL(*iter, FALSE);
- }
-}
+//void LLFirstUse::disableFirstUse()
+//{
+// // Set all first-use warnings to disabled
+// for (std::set<std::string>::iterator iter = sConfigVariables.begin();
+// iter != sConfigVariables.end(); ++iter)
+// {
+// gWarningSettings.setBOOL(*iter, FALSE);
+// }
+//}
// static
-void LLFirstUse::resetFirstUse()
-{
- // Set all first-use warnings to disabled
- for (std::set<std::string>::iterator iter = sConfigVariables.begin();
- iter != sConfigVariables.end(); ++iter)
- {
- gWarningSettings.setBOOL(*iter, TRUE);
- }
-}
+//void LLFirstUse::resetFirstUse()
+//{
+// // Set all first-use warnings to disabled
+// for (std::set<std::string>::iterator iter = sConfigVariables.begin();
+// iter != sConfigVariables.end(); ++iter)
+// {
+// gWarningSettings.setBOOL(*iter, TRUE);
+// }
+//}
// static
void LLFirstUse::otherAvatarChatFirst(bool enable)
@@ -151,13 +152,21 @@ void LLFirstUse::firstUseNotification(const std::string& control_var, bool enabl
if (enable)
{
+ if(sConfigVariablesEnabled.find(control_var) != sConfigVariablesEnabled.end())
+ {
+ return ; //already added
+ }
+
if (gSavedSettings.getBOOL("EnableUIHints"))
{
LL_DEBUGS("LLFirstUse") << "Trigger first use notification " << notification_name << LL_ENDL;
// if notification doesn't already exist and this notification hasn't been disabled...
if (gWarningSettings.getBOOL(control_var))
- { // create new notification
+ {
+ sConfigVariablesEnabled.insert(control_var) ;
+
+ // create new notification
LLNotifications::instance().add(LLNotification::Params().name(notification_name).substitutions(args).payload(payload.with("control_var", control_var)));
}
}
@@ -169,7 +178,6 @@ void LLFirstUse::firstUseNotification(const std::string& control_var, bool enabl
// redundantly clear settings var here, in case there are no notifications to cancel
gWarningSettings.setBOOL(control_var, FALSE);
}
-
}
// static
diff --git a/indra/newview/llfirstuse.h b/indra/newview/llfirstuse.h
index 81659988e6..489f58626a 100644
--- a/indra/newview/llfirstuse.h
+++ b/indra/newview/llfirstuse.h
@@ -78,11 +78,11 @@ class LLFirstUse
public:
// Add a config variable to be reset on resetFirstUse()
- static void addConfigVariable(const std::string& var);
+ //static void addConfigVariable(const std::string& var);
// Sets all controls back to show the dialogs.
- static void disableFirstUse();
- static void resetFirstUse();
+ //static void disableFirstUse();
+ //static void resetFirstUse();
static void otherAvatarChatFirst(bool enable = true);
static void sit(bool enable = true);
@@ -98,7 +98,8 @@ public:
protected:
static void firstUseNotification(const std::string& control_var, bool enable, const std::string& notification_name, LLSD args = LLSD(), LLSD payload = LLSD());
- static std::set<std::string> sConfigVariables;
+ //static std::set<std::string> sConfigVariables;
+ static std::set<std::string> sConfigVariablesEnabled;
static void init();
static bool processNotification(const LLSD& notify);
diff --git a/indra/newview/llfloatermap.cpp b/indra/newview/llfloatermap.cpp
index 1b94d8cbcd..80920c80d6 100644
--- a/indra/newview/llfloatermap.cpp
+++ b/indra/newview/llfloatermap.cpp
@@ -83,7 +83,8 @@ LLFloaterMap::~LLFloaterMap()
BOOL LLFloaterMap::postBuild()
{
mMap = getChild<LLNetMap>("Net Map");
- mMap->setToolTipMsg(getString("ToolTipMsg"));
+ mMap->setToolTipMsg(gSavedSettings.getBOOL("DoubleClickTeleport") ?
+ getString("AltToolTipMsg") : getString("ToolTipMsg"));
sendChildToBack(mMap);
mTextBoxNorth = getChild<LLTextBox> ("floater_map_north");
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 8c9dfe435a..724096b443 100755
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -1539,6 +1539,7 @@ LLPanelPreference::LLPanelPreference()
: LLPanel()
{
mCommitCallbackRegistrar.add("Pref.setControlFalse", boost::bind(&LLPanelPreference::setControlFalse,this, _2));
+ mCommitCallbackRegistrar.add("Pref.updateMediaAutoPlayCheckbox", boost::bind(&LLPanelPreference::updateMediaAutoPlayCheckbox, this, _1));
}
//virtual
@@ -1700,6 +1701,21 @@ void LLPanelPreference::setControlFalse(const LLSD& user_data)
control->set(LLSD(FALSE));
}
+void LLPanelPreference::updateMediaAutoPlayCheckbox(LLUICtrl* ctrl)
+{
+ std::string name = ctrl->getName();
+
+ // Disable "Allow Media to auto play" only when both
+ // "Streaming Music" and "Media" are unchecked. STORM-513.
+ if ((name == "enable_music") || (name == "enable_media"))
+ {
+ bool music_enabled = getChild<LLCheckBoxCtrl>("enable_music")->get();
+ bool media_enabled = getChild<LLCheckBoxCtrl>("enable_media")->get();
+
+ getChild<LLCheckBoxCtrl>("media_auto_play_btn")->setEnabled(music_enabled || media_enabled);
+ }
+}
+
static LLRegisterPanelClassWrapper<LLPanelPreferenceGraphics> t_pref_graph("panel_preference_graphics");
BOOL LLPanelPreferenceGraphics::postBuild()
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index 784033ae95..46014804ec 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -189,6 +189,10 @@ public:
void setControlFalse(const LLSD& user_data);
virtual void setHardwareDefaults(){};
+ // Disables "Allow Media to auto play" check box only when both
+ // "Streaming Music" and "Media" are unchecked. Otherwise enables it.
+ void updateMediaAutoPlayCheckbox(LLUICtrl* ctrl);
+
// This function squirrels away the current values of the controls so that
// cancel() can restore them.
virtual void saveSettings();
diff --git a/indra/newview/llfloaterregiondebugconsole.cpp b/indra/newview/llfloaterregiondebugconsole.cpp
index b3b7645dd4..ada0dcf569 100644
--- a/indra/newview/llfloaterregiondebugconsole.cpp
+++ b/indra/newview/llfloaterregiondebugconsole.cpp
@@ -3,31 +3,25 @@
* @author Brad Kittenbrink <brad@lindenlab.com>
* @brief Quick and dirty console for region debug settings
*
- * $LicenseInfo:firstyear=2010&license=viewergpl$
- *
- * Copyright (c) 2010-2010, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 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.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/newview/llfloaterregiondebugconsole.h b/indra/newview/llfloaterregiondebugconsole.h
index 4171a4da6b..3aa525724e 100644
--- a/indra/newview/llfloaterregiondebugconsole.h
+++ b/indra/newview/llfloaterregiondebugconsole.h
@@ -3,31 +3,25 @@
* @author Brad Kittenbrink <brad@lindenlab.com>
* @brief Quick and dirty console for region debug settings
*
- * $LicenseInfo:firstyear=2010&license=viewergpl$
- *
- * Copyright (c) 2010-2010, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 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.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
index 0931f77281..add591895b 100644
--- a/indra/newview/llfloatersnapshot.cpp
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -1363,6 +1363,36 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
floater->getChildView("auto_snapshot_check")->setVisible( is_advance);
floater->getChildView("image_quality_slider")->setVisible( is_advance && show_slider);
+ if (gSavedSettings.getBOOL("RenderUIInSnapshot") || gSavedSettings.getBOOL("RenderHUDInSnapshot"))
+ { //clamp snapshot resolution to window size when showing UI or HUD in snapshot
+
+ LLSpinCtrl* width_ctrl = floater->getChild<LLSpinCtrl>("snapshot_width");
+ LLSpinCtrl* height_ctrl = floater->getChild<LLSpinCtrl>("snapshot_height");
+
+ S32 width = gViewerWindow->getWindowWidthRaw();
+ S32 height = gViewerWindow->getWindowHeightRaw();
+
+ width_ctrl->setMaxValue(width);
+
+ height_ctrl->setMaxValue(height);
+
+ if (width_ctrl->getValue().asInteger() > width)
+ {
+ width_ctrl->forceSetValue(width);
+ }
+ if (height_ctrl->getValue().asInteger() > height)
+ {
+ height_ctrl->forceSetValue(height);
+ }
+ }
+ else
+ {
+ LLSpinCtrl* width = floater->getChild<LLSpinCtrl>("snapshot_width");
+ width->setMaxValue(6016);
+ LLSpinCtrl* height = floater->getChild<LLSpinCtrl>("snapshot_height");
+ height->setMaxValue(6016);
+ }
+
LLSnapshotLivePreview* previewp = getPreviewView(floater);
BOOL got_bytes = previewp && previewp->getDataSize() > 0;
BOOL got_snap = previewp && previewp->getSnapshotUpToDate();
@@ -1810,6 +1840,13 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, BOOL
previewp->getSize(width, height);
+ if (gSavedSettings.getBOOL("RenderUIInSnapshot") || gSavedSettings.getBOOL("RenderHUDInSnapshot"))
+ { //clamp snapshot resolution to window size when showing UI or HUD in snapshot
+ width = llmin(width, gViewerWindow->getWindowWidthRaw());
+ height = llmin(height, gViewerWindow->getWindowHeightRaw());
+ }
+
+
if(checkImageSize(previewp, width, height, TRUE, previewp->getMaxImageSize()))
{
resetSnapshotSizeOnUI(view, width, height) ;
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 060ad17c02..ec3fe48151 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -2719,6 +2719,15 @@ void LLIMMgr::inviteToSession(
{
LLFloaterReg::showInstance("incoming_call", payload, FALSE);
}
+
+ // Add the caller to the Recent List here (at this point
+ // "incoming_call" floater is shown and the recipient can
+ // reject the call), because even if a recipient will reject
+ // the call, the caller should be added to the recent list
+ // anyway. STORM-507.
+ if(type == IM_SESSION_P2P_INVITE)
+ LLRecentPeople::instance().add(caller_id);
+
mPendingInvitations[session_id.asString()] = LLSD();
}
}
diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp
index 2bb6dbf277..17d0b0ffbb 100644
--- a/indra/newview/llinspectavatar.cpp
+++ b/indra/newview/llinspectavatar.cpp
@@ -47,6 +47,7 @@
#include "llvoiceclient.h"
#include "llviewerobjectlist.h"
#include "lltransientfloatermgr.h"
+#include "llnotificationsutil.h"
// Linden libraries
#include "llfloater.h"
@@ -126,16 +127,20 @@ private:
void onClickReport();
void onClickFreeze();
void onClickEject();
+ void onClickKick();
+ void onClickCSR();
void onClickZoomIn();
void onClickFindOnMap();
bool onVisibleFindOnMap();
- bool onVisibleFreezeEject();
+ bool onVisibleEject();
+ bool onVisibleFreeze();
bool onVisibleZoomIn();
void onClickMuteVolume();
void onVolumeChange(const LLSD& data);
bool enableMute();
bool enableUnmute();
bool enableTeleportOffer();
+ bool godModeEnabled();
// Is used to determine if "Add friend" option should be enabled in gear menu
bool isNotFriend();
@@ -214,20 +219,21 @@ LLInspectAvatar::LLInspectAvatar(const LLSD& sd)
mCommitCallbackRegistrar.add("InspectAvatar.Pay", boost::bind(&LLInspectAvatar::onClickPay, this));
mCommitCallbackRegistrar.add("InspectAvatar.Share", boost::bind(&LLInspectAvatar::onClickShare, this));
mCommitCallbackRegistrar.add("InspectAvatar.ToggleMute", boost::bind(&LLInspectAvatar::onToggleMute, this));
- mCommitCallbackRegistrar.add("InspectAvatar.Freeze",
- boost::bind(&LLInspectAvatar::onClickFreeze, this));
- mCommitCallbackRegistrar.add("InspectAvatar.Eject",
- boost::bind(&LLInspectAvatar::onClickEject, this));
+ mCommitCallbackRegistrar.add("InspectAvatar.Freeze", boost::bind(&LLInspectAvatar::onClickFreeze, this));
+ mCommitCallbackRegistrar.add("InspectAvatar.Eject", boost::bind(&LLInspectAvatar::onClickEject, this));
+ mCommitCallbackRegistrar.add("InspectAvatar.Kick", boost::bind(&LLInspectAvatar::onClickKick, this));
+ mCommitCallbackRegistrar.add("InspectAvatar.CSR", boost::bind(&LLInspectAvatar::onClickCSR, this));
mCommitCallbackRegistrar.add("InspectAvatar.Report", boost::bind(&LLInspectAvatar::onClickReport, this));
mCommitCallbackRegistrar.add("InspectAvatar.FindOnMap", boost::bind(&LLInspectAvatar::onClickFindOnMap, this));
mCommitCallbackRegistrar.add("InspectAvatar.ZoomIn", boost::bind(&LLInspectAvatar::onClickZoomIn, this));
mCommitCallbackRegistrar.add("InspectAvatar.DisableVoice", boost::bind(&LLInspectAvatar::toggleSelectedVoice, this, false));
mCommitCallbackRegistrar.add("InspectAvatar.EnableVoice", boost::bind(&LLInspectAvatar::toggleSelectedVoice, this, true));
+
+ mEnableCallbackRegistrar.add("InspectAvatar.EnableGod", boost::bind(&LLInspectAvatar::godModeEnabled, this));
mEnableCallbackRegistrar.add("InspectAvatar.VisibleFindOnMap", boost::bind(&LLInspectAvatar::onVisibleFindOnMap, this));
- mEnableCallbackRegistrar.add("InspectAvatar.VisibleFreezeEject",
- boost::bind(&LLInspectAvatar::onVisibleFreezeEject, this));
- mEnableCallbackRegistrar.add("InspectAvatar.VisibleZoomIn",
- boost::bind(&LLInspectAvatar::onVisibleZoomIn, this));
+ mEnableCallbackRegistrar.add("InspectAvatar.VisibleEject", boost::bind(&LLInspectAvatar::onVisibleEject, this));
+ mEnableCallbackRegistrar.add("InspectAvatar.VisibleFreeze", boost::bind(&LLInspectAvatar::onVisibleFreeze, this));
+ mEnableCallbackRegistrar.add("InspectAvatar.VisibleZoomIn", boost::bind(&LLInspectAvatar::onVisibleZoomIn, this));
mEnableCallbackRegistrar.add("InspectAvatar.Gear.Enable", boost::bind(&LLInspectAvatar::isNotFriend, this));
mEnableCallbackRegistrar.add("InspectAvatar.Gear.EnableCall", boost::bind(&LLAvatarActions::canCall));
mEnableCallbackRegistrar.add("InspectAvatar.Gear.EnableTeleportOffer", boost::bind(&LLInspectAvatar::enableTeleportOffer, this));
@@ -656,11 +662,18 @@ bool LLInspectAvatar::onVisibleFindOnMap()
return gAgent.isGodlike() || is_agent_mappable(mAvatarID);
}
-bool LLInspectAvatar::onVisibleFreezeEject()
+bool LLInspectAvatar::onVisibleEject()
{
return enable_freeze_eject( LLSD(mAvatarID) );
}
+bool LLInspectAvatar::onVisibleFreeze()
+{
+ // either user is a god and can do long distance freeze
+ // or check for target proximity and permissions
+ return gAgent.isGodlike() || enable_freeze_eject(LLSD(mAvatarID));
+}
+
bool LLInspectAvatar::onVisibleZoomIn()
{
return gObjectList.findObject(mAvatarID);
@@ -725,9 +738,41 @@ void LLInspectAvatar::onClickReport()
closeFloater();
}
+bool godlike_freeze(const LLSD& notification, const LLSD& response)
+{
+ LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID();
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+
+ switch (option)
+ {
+ case 0:
+ LLAvatarActions::freeze(avatar_id);
+ break;
+ case 1:
+ LLAvatarActions::unfreeze(avatar_id);
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
void LLInspectAvatar::onClickFreeze()
{
- handle_avatar_freeze( LLSD(mAvatarID) );
+ if (gAgent.isGodlike())
+ {
+ // use godlike freeze-at-a-distance, with confirmation
+ LLNotificationsUtil::add("FreezeAvatar",
+ LLSD(),
+ LLSD().with("avatar_id", mAvatarID),
+ godlike_freeze);
+ }
+ else
+ {
+ // use default "local" version of freezing that requires avatar to be in range
+ handle_avatar_freeze( LLSD(mAvatarID) );
+ }
closeFloater();
}
@@ -737,6 +782,20 @@ void LLInspectAvatar::onClickEject()
closeFloater();
}
+void LLInspectAvatar::onClickKick()
+{
+ LLAvatarActions::kick(mAvatarID);
+ closeFloater();
+}
+
+void LLInspectAvatar::onClickCSR()
+{
+ std::string name;
+ gCacheName->getFullName(mAvatarID, name);
+ LLAvatarActions::csr(mAvatarID, name);
+ closeFloater();
+}
+
void LLInspectAvatar::onClickZoomIn()
{
handle_zoom_to_object(mAvatarID);
@@ -785,6 +844,11 @@ bool LLInspectAvatar::enableTeleportOffer()
return LLAvatarActions::canOfferTeleport(mAvatarID);
}
+bool LLInspectAvatar::godModeEnabled()
+{
+ return gAgent.isGodlike();
+}
+
//////////////////////////////////////////////////////////////////////////////
// LLInspectAvatarUtil
//////////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 5108f68592..4c2e0fa709 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -3230,7 +3230,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
}
else if(LLToolDragAndDrop::SOURCE_NOTECARD == source)
{
- accept = TRUE;
+ // Don't allow placing an original item from a notecard to Current Outfit or an outfit folder
+ // because they must contain only links to wearable items.
+ accept = !(move_is_into_current_outfit || move_is_into_outfit);
+
if(drop)
{
copy_inventory_from_notecard(LLToolDragAndDrop::getInstance()->getObjectID(),
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index 33e051bfab..abcd8588dc 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -480,10 +480,12 @@ LLLoginInstance::LLLoginInstance() :
{
mLoginModule->getEventPump().listen("lllogininstance",
boost::bind(&LLLoginInstance::handleLoginEvent, this, _1));
- mDispatcher.add("fail.login", boost::bind(&LLLoginInstance::handleLoginFailure, this, _1));
- mDispatcher.add("connect", boost::bind(&LLLoginInstance::handleLoginSuccess, this, _1));
- mDispatcher.add("disconnect", boost::bind(&LLLoginInstance::handleDisconnect, this, _1));
- mDispatcher.add("indeterminate", boost::bind(&LLLoginInstance::handleIndeterminate, this, _1));
+ // This internal use of LLEventDispatcher doesn't really need
+ // per-function descriptions.
+ mDispatcher.add("fail.login", "", boost::bind(&LLLoginInstance::handleLoginFailure, this, _1));
+ mDispatcher.add("connect", "", boost::bind(&LLLoginInstance::handleLoginSuccess, this, _1));
+ mDispatcher.add("disconnect", "", boost::bind(&LLLoginInstance::handleDisconnect, this, _1));
+ mDispatcher.add("indeterminate", "", boost::bind(&LLLoginInstance::handleIndeterminate, this, _1));
}
LLLoginInstance::~LLLoginInstance()
@@ -625,11 +627,7 @@ bool LLLoginInstance::handleLoginEvent(const LLSD& event)
// Call the method registered in constructor, if any, for more specific
// handling
- LLEventDispatcher::Callable method(mDispatcher.get(event["change"]));
- if (! method.empty())
- {
- method(event);
- }
+ mDispatcher.try_call(event);
return false;
}
diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp
index 43a16f8ed8..f1c7e952d1 100644
--- a/indra/newview/llmaniprotate.cpp
+++ b/indra/newview/llmaniprotate.cpp
@@ -901,32 +901,32 @@ void LLManipRotate::renderSnapGuides()
{
if (i == 0)
{
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Forward") : LLTrans::getString("East"), LLColor4::white);
+ renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Forward") : LLTrans::getString("Direction_East"), LLColor4::white);
}
else if (i == 16)
{
if (constraint_axis.mV[VZ] > 0.f)
{
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Left") : LLTrans::getString("North"), LLColor4::white);
+ renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Left") : LLTrans::getString("Direction_North"), LLColor4::white);
}
else
{
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Right") : LLTrans::getString("South"), LLColor4::white);
+ renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Right") : LLTrans::getString("Direction_South"), LLColor4::white);
}
}
else if (i == 32)
{
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Back") : LLTrans::getString("West"), LLColor4::white);
+ renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Back") : LLTrans::getString("Direction_West"), LLColor4::white);
}
else
{
if (constraint_axis.mV[VZ] > 0.f)
{
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Right") : LLTrans::getString("South"), LLColor4::white);
+ renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Right") : LLTrans::getString("Direction_South"), LLColor4::white);
}
else
{
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Left") : LLTrans::getString("North"), LLColor4::white);
+ renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Left") : LLTrans::getString("Direction_North"), LLColor4::white);
}
}
}
@@ -934,32 +934,32 @@ void LLManipRotate::renderSnapGuides()
{
if (i == 0)
{
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Left") : LLTrans::getString("North"), LLColor4::white);
+ renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Left") : LLTrans::getString("Direction_North"), LLColor4::white);
}
else if (i == 16)
{
if (constraint_axis.mV[VX] > 0.f)
{
- renderTickText(text_point, LLTrans::getString("Up"), LLColor4::white);
+ renderTickText(text_point, LLTrans::getString("Direction_Up"), LLColor4::white);
}
else
{
- renderTickText(text_point, LLTrans::getString("Down"), LLColor4::white);
+ renderTickText(text_point, LLTrans::getString("Direction_Down"), LLColor4::white);
}
}
else if (i == 32)
{
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Right") : LLTrans::getString("South"), LLColor4::white);
+ renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Right") : LLTrans::getString("Direction_South"), LLColor4::white);
}
else
{
if (constraint_axis.mV[VX] > 0.f)
{
- renderTickText(text_point, LLTrans::getString("Down"), LLColor4::white);
+ renderTickText(text_point, LLTrans::getString("Direction_Down"), LLColor4::white);
}
else
{
- renderTickText(text_point, LLTrans::getString("Up"), LLColor4::white);
+ renderTickText(text_point, LLTrans::getString("Direction_Up"), LLColor4::white);
}
}
}
@@ -967,32 +967,32 @@ void LLManipRotate::renderSnapGuides()
{
if (i == 0)
{
- renderTickText(text_point, LLTrans::getString("Up"), LLColor4::white);
+ renderTickText(text_point, LLTrans::getString("Direction_Up"), LLColor4::white);
}
else if (i == 16)
{
if (constraint_axis.mV[VY] > 0.f)
{
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Forward") : LLTrans::getString("East"), LLColor4::white);
+ renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Forward") : LLTrans::getString("Direction_East"), LLColor4::white);
}
else
{
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Back") : LLTrans::getString("West"), LLColor4::white);
+ renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Back") : LLTrans::getString("Direction_West"), LLColor4::white);
}
}
else if (i == 32)
{
- renderTickText(text_point, LLTrans::getString("Down"), LLColor4::white);
+ renderTickText(text_point, LLTrans::getString("Direction_Down"), LLColor4::white);
}
else
{
if (constraint_axis.mV[VY] > 0.f)
{
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Back") : LLTrans::getString("West"), LLColor4::white);
+ renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Back") : LLTrans::getString("Direction_West"), LLColor4::white);
}
else
{
- renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Forward") : LLTrans::getString("East"), LLColor4::white);
+ renderTickText(text_point, mObjectSelection->isAttachment() ? LLTrans::getString("Direction_Forward") : LLTrans::getString("Direction_East"), LLColor4::white);
}
}
}
diff --git a/indra/newview/llmemoryview.cpp b/indra/newview/llmemoryview.cpp
index 9a244e2562..7e9c3c84a7 100644
--- a/indra/newview/llmemoryview.cpp
+++ b/indra/newview/llmemoryview.cpp
@@ -37,9 +37,11 @@
#include <sstream>
#include <boost/algorithm/string/split.hpp>
+#include "llmemory.h"
LLMemoryView::LLMemoryView(const LLMemoryView::Params& p)
: LLView(p),
+ mPaused(FALSE),
//mDelay(120),
mAlloc(NULL)
{
@@ -59,6 +61,7 @@ BOOL LLMemoryView::handleMouseDown(S32 x, S32 y, MASK mask)
}
else
{
+ mPaused = !mPaused;
}
return TRUE;
}
@@ -148,13 +151,14 @@ void LLMemoryView::draw()
// cut off lines on bottom
U32 max_lines = U32((height - 2 * line_height) / line_height);
- std::vector<LLWString>::const_iterator end = mLines.end();
+ y_pos = height - MARGIN_AMT - line_height;
+ y_off = 0.f;
+
+#if !MEM_TRACK_MEM
+ std::vector<LLWString>::const_iterator end = mLines.end();
if(mLines.size() > max_lines) {
end = mLines.begin() + max_lines;
}
-
- y_pos = height - MARGIN_AMT - line_height;
- y_off = 0.f;
for (std::vector<LLWString>::const_iterator i = mLines.begin(); i != end; ++i)
{
font->render(*i, 0, MARGIN_AMT, y_pos - y_off,
@@ -169,6 +173,47 @@ void LLMemoryView::draw()
y_off += line_height;
}
+#else
+ LLMemTracker::getInstance()->preDraw(mPaused) ;
+
+ {
+ F32 x_pos = MARGIN_AMT ;
+ U32 lines = 0 ;
+ const char* str = LLMemTracker::getInstance()->getNextLine() ;
+ while(str != NULL)
+ {
+ lines++ ;
+ font->renderUTF8(str, 0, x_pos, y_pos - y_off,
+ LLColor4::white,
+ LLFontGL::LEFT,
+ LLFontGL::BASELINE,
+ LLFontGL::NORMAL,
+ LLFontGL::DROP_SHADOW,
+ S32_MAX,
+ target_width,
+ NULL, FALSE);
+
+ str = LLMemTracker::getInstance()->getNextLine() ;
+ y_off += line_height;
+
+ if(lines >= max_lines)
+ {
+ lines = 0 ;
+ x_pos += 512.f ;
+ if(x_pos + 512.f > target_width)
+ {
+ break ;
+ }
+
+ y_pos = height - MARGIN_AMT - line_height;
+ y_off = 0.f;
+ }
+ }
+ }
+
+ LLMemTracker::getInstance()->postDraw() ;
+#endif
+
#if MEM_TRACK_TYPE
S32 left, top, right, bottom;
diff --git a/indra/newview/llmemoryview.h b/indra/newview/llmemoryview.h
index 24ea058279..9bdc59ab10 100644
--- a/indra/newview/llmemoryview.h
+++ b/indra/newview/llmemoryview.h
@@ -55,6 +55,7 @@ public:
private:
std::vector<LLWString> mLines;
LLAllocator* mAlloc;
+ BOOL mPaused ;
};
diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp
index 38100aa6c5..afceb58ccf 100644
--- a/indra/newview/llnamelistctrl.cpp
+++ b/indra/newview/llnamelistctrl.cpp
@@ -143,6 +143,30 @@ void LLNameListCtrl::mouseOverHighlightNthItem( S32 target_index )
S32 cur_index = getHighlightedItemInx();
if (cur_index != target_index)
{
+ bool is_mouse_over_name_cell = false;
+
+ S32 mouse_x, mouse_y;
+ LLUI::getMousePositionLocal(this, &mouse_x, &mouse_y);
+
+ S32 column_index = getColumnIndexFromOffset(mouse_x);
+ LLScrollListItem* hit_item = hitItem(mouse_x, mouse_y);
+ if (hit_item && column_index == mNameColumnIndex)
+ {
+ // Get the name cell which is currently under the mouse pointer.
+ LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
+ if (hit_cell)
+ {
+ is_mouse_over_name_cell = getCellRect(cur_index, column_index).pointInRect(mouse_x, mouse_y);
+ }
+ }
+
+ // If the tool tip is visible and the mouse is over the currently highlighted item's name cell,
+ // we should not reset the highlighted item index i.e. set mHighlightedItem = -1
+ // and should not increase the width of the text inside the cell because it may
+ // overlap the tool tip icon.
+ if (LLToolTipMgr::getInstance()->toolTipVisible() && is_mouse_over_name_cell)
+ return;
+
if(0 <= cur_index && cur_index < (S32)getItemList().size())
{
LLScrollListItem* item = getItemList()[cur_index];
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index 1a8ec4991d..93039d935d 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -47,6 +47,7 @@
#include "llagentcamera.h"
#include "llappviewer.h" // for gDisconnected
#include "llcallingcard.h" // LLAvatarTracker
+#include "llfloaterworldmap.h"
#include "lltracker.h"
#include "llsurface.h"
#include "llviewercamera.h"
@@ -91,7 +92,8 @@ LLNetMap::LLNetMap (const Params & p)
mObjectImagep(),
mClosestAgentToCursor(),
mClosestAgentAtLastRightClick(),
- mToolTipMsg()
+ mToolTipMsg(),
+ mPopupMenu(NULL)
{
mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS);
setScale(gSavedSettings.getF32("MiniMapScale"));
@@ -102,6 +104,21 @@ LLNetMap::~LLNetMap()
gSavedSettings.setF32("MiniMapScale", mScale);
}
+BOOL LLNetMap::postBuild()
+{
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+
+ registrar.add("Minimap.Zoom", boost::bind(&LLNetMap::handleZoom, this, _2));
+ registrar.add("Minimap.Tracker", boost::bind(&LLNetMap::handleStopTracking, this, _2));
+
+ mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_mini_map.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ if (mPopupMenu && !LLTracker::isTracking(0))
+ {
+ mPopupMenu->setItemEnabled ("Stop Tracking", false);
+ }
+ return TRUE;
+}
+
void LLNetMap::setScale( F32 scale )
{
scale = llclamp(scale, MAP_SCALE_MIN, MAP_SCALE_MAX);
@@ -354,16 +371,49 @@ void LLNetMap::draw()
pos_map = globalPosToView(pos_global);
+ LLUUID uuid(NULL);
BOOL show_as_friend = FALSE;
if( i < regionp->mMapAvatarIDs.count())
{
- show_as_friend = (LLAvatarTracker::instance().getBuddyInfo(regionp->mMapAvatarIDs.get(i)) != NULL);
+ uuid = regionp->mMapAvatarIDs.get(i);
+ show_as_friend = (LLAvatarTracker::instance().getBuddyInfo(uuid) != NULL);
}
+
+ LLColor4 color = show_as_friend ? map_avatar_friend_color : map_avatar_color;
LLWorldMapView::drawAvatar(
pos_map.mV[VX], pos_map.mV[VY],
- show_as_friend ? map_avatar_friend_color : map_avatar_color,
+ color,
pos_map.mV[VZ], mDotRadius);
+ if(uuid.notNull())
+ {
+ bool selected = false;
+ uuid_vec_t::iterator sel_iter = gmSelected.begin();
+ for (; sel_iter != gmSelected.end(); sel_iter++)
+ {
+ if(*sel_iter == uuid)
+ {
+ selected = true;
+ break;
+ }
+ }
+ if(selected)
+ {
+ if( (pos_map.mV[VX] < 0) ||
+ (pos_map.mV[VY] < 0) ||
+ (pos_map.mV[VX] >= getRect().getWidth()) ||
+ (pos_map.mV[VY] >= getRect().getHeight()) )
+ {
+ S32 x = llround( pos_map.mV[VX] );
+ S32 y = llround( pos_map.mV[VY] );
+ LLWorldMapView::drawTrackingCircle( getRect(), x, y, color, 1, 10);
+ } else
+ {
+ LLWorldMapView::drawTrackingDot(pos_map.mV[VX],pos_map.mV[VY],color,0.f);
+ }
+ }
+ }
+
F32 dist_to_cursor = dist_vec(LLVector2(pos_map.mV[VX], pos_map.mV[VY]),
LLVector2(local_mouse_x,local_mouse_y));
if(dist_to_cursor < min_pick_dist && dist_to_cursor < closest_dist)
@@ -460,6 +510,13 @@ void LLNetMap::draw()
gGL.popUIMatrix();
LLUICtrl::draw();
+
+ if (LLTracker::isTracking(0))
+ {
+ mPopupMenu->setItemEnabled ("Stop Tracking", true);
+ }
+
+
}
void LLNetMap::reshape(S32 width, S32 height, BOOL called_from_parent)
@@ -600,7 +657,6 @@ BOOL LLNetMap::handleToolTip( S32 x, S32 y, MASK mask )
args["[REGION]"] = region_name;
std::string msg = mToolTipMsg;
LLStringUtil::format(msg, args);
-
LLToolTipMgr::instance().show(LLToolTip::Params()
.message(msg)
.sticky_rect(sticky_rect));
@@ -793,6 +849,9 @@ BOOL LLNetMap::handleMouseDown( S32 x, S32 y, MASK mask )
BOOL LLNetMap::handleMouseUp( S32 x, S32 y, MASK mask )
{
+ if(abs(mMouseDown.mX-x)<3 && abs(mMouseDown.mY-y)<3)
+ handleClick(x,y,mask);
+
if (hasMouseCapture())
{
if (mPanning)
@@ -821,6 +880,53 @@ BOOL LLNetMap::handleMouseUp( S32 x, S32 y, MASK mask )
return FALSE;
}
+BOOL LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mPopupMenu)
+ {
+ mPopupMenu->buildDrawLabels();
+ mPopupMenu->updateParent(LLMenuGL::sMenuContainer);
+ LLMenuGL::showPopup(this, mPopupMenu, x, y);
+ }
+ return TRUE;
+}
+
+BOOL LLNetMap::handleClick(S32 x, S32 y, MASK mask)
+{
+ // TODO: allow clicking an avatar on minimap to select avatar in the nearby avatar list
+ // if(mClosestAgentToCursor.notNull())
+ // mNearbyList->selectUser(mClosestAgentToCursor);
+ // Needs a registered observer i guess to accomplish this without using
+ // globals to tell the mNearbyList in llpeoplepanel to select the user
+ return TRUE;
+}
+
+BOOL LLNetMap::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ LLVector3d pos_global = viewPosToGlobal(x, y);
+
+ // If we're not tracking a beacon already, double-click will set one
+ if (!LLTracker::isTracking(NULL))
+ {
+ LLFloaterWorldMap* world_map = LLFloaterWorldMap::getInstance();
+ if (world_map)
+ {
+ world_map->trackLocation(pos_global);
+ }
+ }
+
+ if (gSavedSettings.getBOOL("DoubleClickTeleport"))
+ {
+ // If DoubleClickTeleport is on, double clicking the minimap will teleport there
+ gAgent.teleportViaLocationLookAt(pos_global);
+ }
+ else
+ {
+ LLFloaterReg::showInstance("world_map");
+ }
+ return TRUE;
+}
+
// static
bool LLNetMap::outsideSlop( S32 x, S32 y, S32 start_x, S32 start_y, S32 slop )
{
@@ -871,3 +977,38 @@ BOOL LLNetMap::handleHover( S32 x, S32 y, MASK mask )
return TRUE;
}
+
+void LLNetMap::handleZoom(const LLSD& userdata)
+{
+ std::string level = userdata.asString();
+
+ F32 scale = 0.0f;
+ if (level == std::string("default"))
+ {
+ LLControlVariable *pvar = gSavedSettings.getControl("MiniMapScale");
+ if(pvar)
+ {
+ pvar->resetToDefault();
+ scale = gSavedSettings.getF32("MiniMapScale");
+ }
+ }
+ else if (level == std::string("close"))
+ scale = LLNetMap::MAP_SCALE_MAX;
+ else if (level == std::string("medium"))
+ scale = LLNetMap::MAP_SCALE_MID;
+ else if (level == std::string("far"))
+ scale = LLNetMap::MAP_SCALE_MIN;
+ if (scale != 0.0f)
+ {
+ setScale(scale);
+ }
+}
+
+void LLNetMap::handleStopTracking (const LLSD& userdata)
+{
+ if (mPopupMenu)
+ {
+ mPopupMenu->setItemEnabled ("Stop Tracking", false);
+ LLTracker::stopTracking ((void*)LLTracker::isTracking(NULL));
+ }
+}
diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h
index e053b1c177..20fcee0814 100644
--- a/indra/newview/llnetmap.h
+++ b/indra/newview/llnetmap.h
@@ -39,6 +39,7 @@ class LLCoordGL;
class LLImageRaw;
class LLViewerTexture;
class LLFloaterMap;
+class LLMenuGL;
class LLNetMap : public LLUICtrl
{
@@ -72,7 +73,12 @@ public:
/*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask );
/*virtual*/ BOOL handleToolTip( S32 x, S32 y, MASK mask);
/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
-
+
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+
void setScale( F32 scale );
void setToolTipMsg(const std::string& msg) { mToolTipMsg = msg; }
void renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius );
@@ -120,6 +126,16 @@ private:
LLUUID mClosestAgentAtLastRightClick;
std::string mToolTipMsg;
+
+public:
+ void setSelected(uuid_vec_t uuids) { gmSelected=uuids; };
+
+private:
+ void handleZoom(const LLSD& userdata);
+ void handleStopTracking (const LLSD& userdata);
+
+ LLMenuGL* mPopupMenu;
+ uuid_vec_t gmSelected;
};
diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp
index 94b2340c93..73c4722b82 100644
--- a/indra/newview/llpanelavatar.cpp
+++ b/indra/newview/llpanelavatar.cpp
@@ -625,10 +625,15 @@ void LLPanelAvatarProfile::processGroupProperties(const LLAvatarGroups* avatar_g
getChild<LLUICtrl>("sl_groups")->setValue(groups);
}
-void LLPanelAvatarProfile::got_full_name_callback( const LLUUID& id, const std::string& full_name, bool is_group )
-{
- LLStringUtil::format_map_t args;
-
+static void got_full_name_callback( LLHandle<LLPanel> profile_panel_handle, const std::string& full_name )
+{
+ if (profile_panel_handle.isDead() ) return;
+
+ LLPanelAvatarProfile* profile_panel = dynamic_cast<LLPanelAvatarProfile*>(profile_panel_handle.get());
+ if ( ! profile_panel ) return;
+
+ LLStringUtil::format_map_t args;
+
std::string name;
if (LLAvatarNameCache::useDisplayNames())
{
@@ -637,21 +642,21 @@ void LLPanelAvatarProfile::got_full_name_callback( const LLUUID& id, const std::
else
{
name = full_name;
- }
-
- args["[NAME]"] = name;
-
- std::string linden_name = getString("name_text_args", args);
- getChild<LLUICtrl>("name_descr_text")->setValue(linden_name);
-}
+ }
+
+ args["[NAME]"] = name;
+
+ std::string linden_name = profile_panel->getString("name_text_args", args);
+ profile_panel->getChild<LLUICtrl>("name_descr_text")->setValue(linden_name);
+}
void LLPanelAvatarProfile::onNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
{
- LLStringUtil::format_map_t args;
- args["[DISPLAY_NAME]"] = av_name.mDisplayName;
-
- std::string display_name = getString("display_name_text_args", args);
- getChild<LLUICtrl>("display_name_descr_text")->setValue(display_name);
+ LLStringUtil::format_map_t args;
+ args["[DISPLAY_NAME]"] = av_name.mDisplayName;
+
+ std::string display_name = getString("display_name_text_args", args);
+ getChild<LLUICtrl>("display_name_descr_text")->setValue(display_name);
}
void LLPanelAvatarProfile::fillCommonData(const LLAvatarData* avatar_data)
@@ -667,22 +672,23 @@ void LLPanelAvatarProfile::fillCommonData(const LLAvatarData* avatar_data)
}
// ask (asynchronously) for the avatar name
- std::string full_name;
- if (gCacheName->getFullName(avatar_data->agent_id, full_name))
- {
- // name in cache, call callback directly
- got_full_name_callback( avatar_data->agent_id, full_name, false );
- }
- else
- {
- // not in cache, lookup name
- gCacheName->get(avatar_data->agent_id, false, boost::bind( &LLPanelAvatarProfile::got_full_name_callback, this, _1, _2, _3 ));
- }
-
- // get display name
+ LLHandle<LLPanel> profile_panel_handle = getHandle();
+ std::string full_name;
+ if (gCacheName->getFullName(avatar_data->agent_id, full_name))
+ {
+ // name in cache, call callback directly
+ got_full_name_callback( profile_panel_handle, full_name );
+ }
+ else
+ {
+ // not in cache, lookup name
+ gCacheName->get(avatar_data->agent_id, false, boost::bind( got_full_name_callback, profile_panel_handle, _2 ));
+ }
+
+ // get display name
LLAvatarNameCache::get(avatar_data->avatar_id,
- boost::bind(&LLPanelAvatarProfile::onNameCache, this, _1, _2));
-
+ boost::bind(&LLPanelAvatarProfile::onNameCache, this, _1, _2));
+
args["[AGE]"] = LLDateUtil::ageFromDate( avatar_data->born_on, LLDate::now());
std::string register_date = getString("RegisterDateFormat", args);
getChild<LLUICtrl>("register_date")->setValue(register_date );
diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h
index 070fe4579a..e95441cd58 100644
--- a/indra/newview/llpanelavatar.h
+++ b/indra/newview/llpanelavatar.h
@@ -1,298 +1,297 @@
-/**
- * @file llpanelavatar.h
- * @brief LLPanelAvatar and related class definitions
- *
- * $LicenseInfo:firstyear=2004&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$
- */
-
-#ifndef LL_LLPANELAVATAR_H
-#define LL_LLPANELAVATAR_H
-
-#include "llpanel.h"
-#include "llavatarpropertiesprocessor.h"
-#include "llcallingcard.h"
-#include "llvoiceclient.h"
-#include "llavatarnamecache.h"
-
-class LLComboBox;
-class LLLineEditor;
-
-enum EOnlineStatus
-{
- ONLINE_STATUS_NO = 0,
- ONLINE_STATUS_YES = 1
-};
-
-/**
-* Base class for any Profile View or My Profile Panel.
-*/
-class LLPanelProfileTab
- : public LLPanel
- , public LLAvatarPropertiesObserver
-{
-public:
-
- /**
- * Sets avatar ID, sets panel as observer of avatar related info replies from server.
- */
- virtual void setAvatarId(const LLUUID& id);
-
- /**
- * Returns avatar ID.
- */
- virtual const LLUUID& getAvatarId() { return mAvatarId; }
-
- /**
- * Sends update data request to server.
- */
- virtual void updateData() = 0;
-
- /**
- * Clears panel data if viewing avatar info for first time and sends update data request.
- */
- virtual void onOpen(const LLSD& key);
-
- /**
- * Profile tabs should close any opened panels here.
- *
- * Called from LLPanelProfile::onOpen() before opening new profile.
- * See LLPanelPicks::onClosePanel for example. LLPanelPicks closes picture info panel
- * before new profile is displayed, otherwise new profile will
- * be hidden behind picture info panel.
- */
- virtual void onClosePanel() {}
-
- /**
- * Resets controls visibility, state, etc.
- */
- virtual void resetControls(){};
-
- /**
- * Clears all data received from server.
- */
- virtual void resetData(){};
-
- /*virtual*/ ~LLPanelProfileTab();
-
-protected:
-
- LLPanelProfileTab();
-
- /**
- * Scrolls panel to top when viewing avatar info for first time.
- */
- void scrollToTop();
-
- virtual void onMapButtonClick();
-
- virtual void updateButtons();
-
-private:
-
- LLUUID mAvatarId;
-};
-
-/**
-* Panel for displaying Avatar's first and second life related info.
-*/
-class LLPanelAvatarProfile
- : public LLPanelProfileTab
- , public LLFriendObserver
- , public LLVoiceClientStatusObserver
-{
-public:
- LLPanelAvatarProfile();
- /*virtual*/ ~LLPanelAvatarProfile();
-
- /*virtual*/ void onOpen(const LLSD& key);
-
- /**
- * LLFriendObserver trigger
- */
- virtual void changed(U32 mask);
-
- // Implements LLVoiceClientStatusObserver::onChange() to enable the call
- // button when voice is available
- /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
-
- /*virtual*/ void setAvatarId(const LLUUID& id);
-
- /**
- * Processes data received from server.
- */
- /*virtual*/ void processProperties(void* data, EAvatarProcessorType type);
-
- /*virtual*/ BOOL postBuild();
-
- /*virtual*/ void updateData();
-
- /*virtual*/ void resetControls();
-
- /*virtual*/ void resetData();
-
-protected:
-
- /**
- * Process profile related data received from server.
- */
- virtual void processProfileProperties(const LLAvatarData* avatar_data);
-
- /**
- * Processes group related data received from server.
- */
- virtual void processGroupProperties(const LLAvatarGroups* avatar_groups);
-
- /**
- * Fills common for Avatar profile and My Profile fields.
- */
- virtual void fillCommonData(const LLAvatarData* avatar_data);
-
- /**
- * Fills partner data.
- */
- virtual void fillPartnerData(const LLAvatarData* avatar_data);
-
- /**
- * Fills account status.
- */
- virtual void fillAccountStatus(const LLAvatarData* avatar_data);
-
- /**
- * Opens "Pay Resident" dialog.
- */
- void pay();
-
- /**
- * opens inventory and IM for sharing items
- */
- void share();
-
- /**
- * Add/remove resident to/from your block list.
- */
- void toggleBlock();
-
- void kick();
- void freeze();
- void unfreeze();
- void csr();
-
- bool enableShowOnMap();
- bool enableBlock();
- bool enableUnblock();
- bool enableGod();
-
- void onSeeProfileBtnClick();
- void onAddFriendButtonClick();
- void onIMButtonClick();
- void onCallButtonClick();
- void onTeleportButtonClick();
- void onShareButtonClick();
-
-private:
- void got_full_name_callback( const LLUUID& id, const std::string& full_name, bool is_group );
- void onNameCache(const LLUUID& agent_id, const LLAvatarName& av_name);
-
- typedef std::map< std::string,LLUUID> group_map_t;
- group_map_t mGroups;
-};
-
-/**
- * Panel for displaying own first and second life related info.
- */
-class LLPanelMyProfile
- : public LLPanelAvatarProfile
-{
-public:
- LLPanelMyProfile();
-
- /*virtual*/ BOOL postBuild();
-
-protected:
-
- /*virtual*/ void onOpen(const LLSD& key);
-
- /*virtual*/ void processProfileProperties(const LLAvatarData* avatar_data);
-
- /*virtual*/ void resetControls();
-
-protected:
- void onStatusMessageChanged();
-};
-
-/**
- * Panel for displaying Avatar's notes and modifying friend's rights.
- */
-class LLPanelAvatarNotes
- : public LLPanelProfileTab
- , public LLFriendObserver
- , public LLVoiceClientStatusObserver
-{
-public:
- LLPanelAvatarNotes();
- /*virtual*/ ~LLPanelAvatarNotes();
-
- virtual void setAvatarId(const LLUUID& id);
-
- /**
- * LLFriendObserver trigger
- */
- virtual void changed(U32 mask);
-
- // Implements LLVoiceClientStatusObserver::onChange() to enable the call
- // button when voice is available
- /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
-
- /*virtual*/ void onOpen(const LLSD& key);
-
- /*virtual*/ BOOL postBuild();
-
- /*virtual*/ void processProperties(void* data, EAvatarProcessorType type);
-
- /*virtual*/ void updateData();
-
-protected:
-
- /*virtual*/ void resetControls();
-
- /*virtual*/ void resetData();
-
- /**
- * Fills rights data for friends.
- */
- void fillRightsData();
-
- void rightsConfirmationCallback(const LLSD& notification,
- const LLSD& response, S32 rights);
- void confirmModifyRights(bool grant, S32 rights);
- void onCommitRights();
- void onCommitNotes();
-
- void onAddFriendButtonClick();
- void onIMButtonClick();
- void onCallButtonClick();
- void onTeleportButtonClick();
- void onShareButtonClick();
- void enableCheckboxes(bool enable);
-};
-
-#endif // LL_LLPANELAVATAR_H
+/**
+ * @file llpanelavatar.h
+ * @brief LLPanelAvatar and related class definitions
+ *
+ * $LicenseInfo:firstyear=2004&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$
+ */
+
+#ifndef LL_LLPANELAVATAR_H
+#define LL_LLPANELAVATAR_H
+
+#include "llpanel.h"
+#include "llavatarpropertiesprocessor.h"
+#include "llcallingcard.h"
+#include "llvoiceclient.h"
+#include "llavatarnamecache.h"
+
+class LLComboBox;
+class LLLineEditor;
+
+enum EOnlineStatus
+{
+ ONLINE_STATUS_NO = 0,
+ ONLINE_STATUS_YES = 1
+};
+
+/**
+* Base class for any Profile View or My Profile Panel.
+*/
+class LLPanelProfileTab
+ : public LLPanel
+ , public LLAvatarPropertiesObserver
+{
+public:
+
+ /**
+ * Sets avatar ID, sets panel as observer of avatar related info replies from server.
+ */
+ virtual void setAvatarId(const LLUUID& id);
+
+ /**
+ * Returns avatar ID.
+ */
+ virtual const LLUUID& getAvatarId() { return mAvatarId; }
+
+ /**
+ * Sends update data request to server.
+ */
+ virtual void updateData() = 0;
+
+ /**
+ * Clears panel data if viewing avatar info for first time and sends update data request.
+ */
+ virtual void onOpen(const LLSD& key);
+
+ /**
+ * Profile tabs should close any opened panels here.
+ *
+ * Called from LLPanelProfile::onOpen() before opening new profile.
+ * See LLPanelPicks::onClosePanel for example. LLPanelPicks closes picture info panel
+ * before new profile is displayed, otherwise new profile will
+ * be hidden behind picture info panel.
+ */
+ virtual void onClosePanel() {}
+
+ /**
+ * Resets controls visibility, state, etc.
+ */
+ virtual void resetControls(){};
+
+ /**
+ * Clears all data received from server.
+ */
+ virtual void resetData(){};
+
+ /*virtual*/ ~LLPanelProfileTab();
+
+protected:
+
+ LLPanelProfileTab();
+
+ /**
+ * Scrolls panel to top when viewing avatar info for first time.
+ */
+ void scrollToTop();
+
+ virtual void onMapButtonClick();
+
+ virtual void updateButtons();
+
+private:
+
+ LLUUID mAvatarId;
+};
+
+/**
+* Panel for displaying Avatar's first and second life related info.
+*/
+class LLPanelAvatarProfile
+ : public LLPanelProfileTab
+ , public LLFriendObserver
+ , public LLVoiceClientStatusObserver
+{
+public:
+ LLPanelAvatarProfile();
+ /*virtual*/ ~LLPanelAvatarProfile();
+
+ /*virtual*/ void onOpen(const LLSD& key);
+
+ /**
+ * LLFriendObserver trigger
+ */
+ virtual void changed(U32 mask);
+
+ // Implements LLVoiceClientStatusObserver::onChange() to enable the call
+ // button when voice is available
+ /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+
+ /*virtual*/ void setAvatarId(const LLUUID& id);
+
+ /**
+ * Processes data received from server.
+ */
+ /*virtual*/ void processProperties(void* data, EAvatarProcessorType type);
+
+ /*virtual*/ BOOL postBuild();
+
+ /*virtual*/ void updateData();
+
+ /*virtual*/ void resetControls();
+
+ /*virtual*/ void resetData();
+
+protected:
+
+ /**
+ * Process profile related data received from server.
+ */
+ virtual void processProfileProperties(const LLAvatarData* avatar_data);
+
+ /**
+ * Processes group related data received from server.
+ */
+ virtual void processGroupProperties(const LLAvatarGroups* avatar_groups);
+
+ /**
+ * Fills common for Avatar profile and My Profile fields.
+ */
+ virtual void fillCommonData(const LLAvatarData* avatar_data);
+
+ /**
+ * Fills partner data.
+ */
+ virtual void fillPartnerData(const LLAvatarData* avatar_data);
+
+ /**
+ * Fills account status.
+ */
+ virtual void fillAccountStatus(const LLAvatarData* avatar_data);
+
+ /**
+ * Opens "Pay Resident" dialog.
+ */
+ void pay();
+
+ /**
+ * opens inventory and IM for sharing items
+ */
+ void share();
+
+ /**
+ * Add/remove resident to/from your block list.
+ */
+ void toggleBlock();
+
+ void kick();
+ void freeze();
+ void unfreeze();
+ void csr();
+
+ bool enableShowOnMap();
+ bool enableBlock();
+ bool enableUnblock();
+ bool enableGod();
+
+ void onSeeProfileBtnClick();
+ void onAddFriendButtonClick();
+ void onIMButtonClick();
+ void onCallButtonClick();
+ void onTeleportButtonClick();
+ void onShareButtonClick();
+
+private:
+ void onNameCache(const LLUUID& agent_id, const LLAvatarName& av_name);
+
+ typedef std::map< std::string,LLUUID> group_map_t;
+ group_map_t mGroups;
+};
+
+/**
+ * Panel for displaying own first and second life related info.
+ */
+class LLPanelMyProfile
+ : public LLPanelAvatarProfile
+{
+public:
+ LLPanelMyProfile();
+
+ /*virtual*/ BOOL postBuild();
+
+protected:
+
+ /*virtual*/ void onOpen(const LLSD& key);
+
+ /*virtual*/ void processProfileProperties(const LLAvatarData* avatar_data);
+
+ /*virtual*/ void resetControls();
+
+protected:
+ void onStatusMessageChanged();
+};
+
+/**
+ * Panel for displaying Avatar's notes and modifying friend's rights.
+ */
+class LLPanelAvatarNotes
+ : public LLPanelProfileTab
+ , public LLFriendObserver
+ , public LLVoiceClientStatusObserver
+{
+public:
+ LLPanelAvatarNotes();
+ /*virtual*/ ~LLPanelAvatarNotes();
+
+ virtual void setAvatarId(const LLUUID& id);
+
+ /**
+ * LLFriendObserver trigger
+ */
+ virtual void changed(U32 mask);
+
+ // Implements LLVoiceClientStatusObserver::onChange() to enable the call
+ // button when voice is available
+ /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+
+ /*virtual*/ void onOpen(const LLSD& key);
+
+ /*virtual*/ BOOL postBuild();
+
+ /*virtual*/ void processProperties(void* data, EAvatarProcessorType type);
+
+ /*virtual*/ void updateData();
+
+protected:
+
+ /*virtual*/ void resetControls();
+
+ /*virtual*/ void resetData();
+
+ /**
+ * Fills rights data for friends.
+ */
+ void fillRightsData();
+
+ void rightsConfirmationCallback(const LLSD& notification,
+ const LLSD& response, S32 rights);
+ void confirmModifyRights(bool grant, S32 rights);
+ void onCommitRights();
+ void onCommitNotes();
+
+ void onAddFriendButtonClick();
+ void onIMButtonClick();
+ void onCallButtonClick();
+ void onTeleportButtonClick();
+ void onShareButtonClick();
+ void enableCheckboxes(bool enable);
+};
+
+#endif // LL_LLPANELAVATAR_H
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index c83176d980..0c3f2f3e31 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -118,18 +118,6 @@ LLPanelMainInventory::LLPanelMainInventory()
mCommitCallbackRegistrar.add("Inventory.SetSortBy", boost::bind(&LLPanelMainInventory::setSortBy, this, _2));
mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars));
- // Controls
- // *TODO: Just use persistant settings for each of these
- U32 sort_order = gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER);
- BOOL sort_by_name = ! ( sort_order & LLInventoryFilter::SO_DATE );
- BOOL sort_folders_by_name = ( sort_order & LLInventoryFilter::SO_FOLDERS_BY_NAME );
- BOOL sort_system_folders_to_top = ( sort_order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP );
-
- gSavedSettings.declareBOOL("Inventory.SortByName", sort_by_name, "Declared in code", FALSE);
- gSavedSettings.declareBOOL("Inventory.SortByDate", !sort_by_name, "Declared in code", FALSE);
- gSavedSettings.declareBOOL("Inventory.FoldersAlwaysByName", sort_folders_by_name, "Declared in code", FALSE);
- gSavedSettings.declareBOOL("Inventory.SystemFoldersToTop", sort_system_folders_to_top, "Declared in code", FALSE);
-
mSavedFolderState = new LLSaveFolderState();
mSavedFolderState->setApply(FALSE);
}
@@ -325,67 +313,41 @@ void LLPanelMainInventory::resetFilters()
void LLPanelMainInventory::setSortBy(const LLSD& userdata)
{
- std::string sort_field = userdata.asString();
- if (sort_field == "name")
+ U32 sort_order_mask = getActivePanel()->getSortOrder();
+ std::string sort_type = userdata.asString();
+ if (sort_type == "name")
{
- U32 order = getActivePanel()->getSortOrder();
- order &= ~LLInventoryFilter::SO_DATE;
-
- getActivePanel()->setSortOrder( order );
-
- gSavedSettings.setU32("InventorySortOrder", order);
-
- gSavedSettings.setBOOL("Inventory.SortByName", TRUE );
- gSavedSettings.setBOOL("Inventory.SortByDate", FALSE );
+ sort_order_mask &= ~LLInventoryFilter::SO_DATE;
}
- else if (sort_field == "date")
+ else if (sort_type == "date")
{
- U32 order = getActivePanel()->getSortOrder();
- order |= LLInventoryFilter::SO_DATE;
-
- getActivePanel()->setSortOrder( order );
-
- gSavedSettings.setU32("InventorySortOrder", order);
-
- gSavedSettings.setBOOL("Inventory.SortByName", FALSE );
- gSavedSettings.setBOOL("Inventory.SortByDate", TRUE );
+ sort_order_mask |= LLInventoryFilter::SO_DATE;
}
- else if (sort_field == "foldersalwaysbyname")
+ else if (sort_type == "foldersalwaysbyname")
{
- U32 order = getActivePanel()->getSortOrder();
- if ( order & LLInventoryFilter::SO_FOLDERS_BY_NAME )
+ if ( sort_order_mask & LLInventoryFilter::SO_FOLDERS_BY_NAME )
{
- order &= ~LLInventoryFilter::SO_FOLDERS_BY_NAME;
-
- gSavedSettings.setBOOL("Inventory.FoldersAlwaysByName", FALSE );
+ sort_order_mask &= ~LLInventoryFilter::SO_FOLDERS_BY_NAME;
}
else
{
- order |= LLInventoryFilter::SO_FOLDERS_BY_NAME;
-
- gSavedSettings.setBOOL("Inventory.FoldersAlwaysByName", TRUE );
+ sort_order_mask |= LLInventoryFilter::SO_FOLDERS_BY_NAME;
}
- getActivePanel()->setSortOrder( order );
}
- else if (sort_field == "systemfolderstotop")
+ else if (sort_type == "systemfolderstotop")
{
- U32 order = getActivePanel()->getSortOrder();
- if ( order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP )
+ if ( sort_order_mask & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP )
{
- order &= ~LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
-
- gSavedSettings.setBOOL("Inventory.SystemFoldersToTop", FALSE );
+ sort_order_mask &= ~LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
}
else
{
- order |= LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
-
- gSavedSettings.setBOOL("Inventory.SystemFoldersToTop", TRUE );
+ sort_order_mask |= LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
}
- getActivePanel()->setSortOrder( order );
-
- gSavedSettings.setU32("InventorySortOrder", order);
}
+
+ getActivePanel()->setSortOrder(sort_order_mask);
+ gSavedSettings.setU32("InventorySortOrder", sort_order_mask);
}
// static
@@ -1013,6 +975,11 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata)
const LLSD arg = "date";
setSortBy(arg);
}
+ if (command_name == "sort_folders_by_name")
+ {
+ const LLSD arg = "foldersalwaysbyname";
+ setSortBy(arg);
+ }
if (command_name == "sort_system_folders_to_top")
{
const LLSD arg = "systemfolderstotop";
@@ -1193,24 +1160,26 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata)
BOOL LLPanelMainInventory::isActionChecked(const LLSD& userdata)
{
+ U32 sort_order_mask = getActivePanel()->getSortOrder();
const std::string command_name = userdata.asString();
-
if (command_name == "sort_by_name")
{
- U32 order = getActivePanel()->getSortOrder();
- return ~order & LLInventoryFilter::SO_DATE;
+ return ~sort_order_mask & LLInventoryFilter::SO_DATE;
}
if (command_name == "sort_by_recent")
{
- U32 order = getActivePanel()->getSortOrder();
- return order & LLInventoryFilter::SO_DATE;
+ return sort_order_mask & LLInventoryFilter::SO_DATE;
+ }
+
+ if (command_name == "sort_folders_by_name")
+ {
+ return sort_order_mask & LLInventoryFilter::SO_FOLDERS_BY_NAME;
}
if (command_name == "sort_system_folders_to_top")
{
- U32 order = getActivePanel()->getSortOrder();
- return order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
+ return sort_order_mask & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
}
return FALSE;
diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp
index 54198d6aa4..b07a46a222 100644
--- a/indra/newview/llpanelpeople.cpp
+++ b/indra/newview/llpanelpeople.cpp
@@ -54,6 +54,7 @@
#include "llgroupactions.h"
#include "llgrouplist.h"
#include "llinventoryobserver.h"
+#include "llnetmap.h"
#include "llpanelpeoplemenus.h"
#include "llsidetray.h"
#include "llsidetraypanelcontainer.h"
@@ -494,7 +495,8 @@ LLPanelPeople::LLPanelPeople()
mNearbyGearButton(NULL),
mFriendsGearButton(NULL),
mGroupsGearButton(NULL),
- mRecentGearButton(NULL)
+ mRecentGearButton(NULL),
+ mMiniMap(NULL)
{
mFriendListUpdater = new LLFriendListUpdater(boost::bind(&LLPanelPeople::updateFriendList, this));
mNearbyListUpdater = new LLNearbyListUpdater(boost::bind(&LLPanelPeople::updateNearbyList, this));
@@ -567,6 +569,9 @@ BOOL LLPanelPeople::postBuild()
mNearbyList->setNoItemsMsg(getString("no_one_near"));
mNearbyList->setNoFilteredItemsMsg(getString("no_one_filtered_near"));
mNearbyList->setShowIcons("NearbyListShowIcons");
+ mMiniMap = (LLNetMap*)getChildView("Net Map",true);
+ mMiniMap->setToolTipMsg(gSavedSettings.getBOOL("DoubleClickTeleport") ?
+ getString("AltMiniMapToolTipMsg") : getString("MiniMapToolTipMsg"));
mRecentList = getChild<LLPanel>(RECENT_TAB_NAME)->getChild<LLAvatarList>("avatar_list");
mRecentList->setNoItemsCommentText(getString("no_recent_people"));
@@ -1088,6 +1093,12 @@ void LLPanelPeople::onAvatarListDoubleClicked(LLUICtrl* ctrl)
void LLPanelPeople::onAvatarListCommitted(LLAvatarList* list)
{
+ if (getActiveTabName() == NEARBY_TAB_NAME)
+ {
+ uuid_vec_t selected_uuids;
+ getCurrentItemIDs(selected_uuids);
+ mMiniMap->setSelected(selected_uuids);
+ } else
// Make sure only one of the friends lists (online/all) has selection.
if (getActiveTabName() == FRIENDS_TAB_NAME)
{
diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h
index b496bb3779..46c58cd139 100644
--- a/indra/newview/llpanelpeople.h
+++ b/indra/newview/llpanelpeople.h
@@ -142,6 +142,7 @@ private:
LLAvatarList* mNearbyList;
LLAvatarList* mRecentList;
LLGroupList* mGroupList;
+ LLNetMap* mMiniMap;
LLHandle<LLView> mGroupPlusMenuHandle;
LLHandle<LLView> mNearbyViewSortMenuHandle;
diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp
index 16284d1a7e..8e5beb33ce 100644
--- a/indra/newview/llpreviewgesture.cpp
+++ b/indra/newview/llpreviewgesture.cpp
@@ -1597,7 +1597,7 @@ std::string LLPreviewGesture::getLabel(std::vector<std::string> labels)
if(v_labels[0]=="Chat")
{
- result=LLTrans::getString("Chat");
+ result=LLTrans::getString("Chat Message");
}
else if(v_labels[0]=="Sound")
{
diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp
index 19d1bdee86..85efebabfe 100644
--- a/indra/newview/llsidetray.cpp
+++ b/indra/newview/llsidetray.cpp
@@ -53,6 +53,8 @@
#include "llsidepanelappearance.h"
+#include "llsidetraylistener.h"
+
//#include "llscrollcontainer.h"
using namespace std;
@@ -71,6 +73,8 @@ static const std::string TAB_PANEL_CAPTION_TITLE_BOX = "sidetray_tab_title";
LLSideTray* LLSideTray::sInstance = 0;
+static LLSideTrayListener sSideTrayListener(LLSideTray::getInstance);
+
// static
LLSideTray* LLSideTray::getInstance()
{
@@ -141,6 +145,8 @@ public:
void toggleTabDocked();
+ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+
LLPanel *getPanel();
private:
std::string mTabTitle;
@@ -269,6 +275,15 @@ void LLSideTrayTab::toggleTabDocked()
LLFloaterReg::toggleInstance("side_bar_tab", tab_name);
}
+BOOL LLSideTrayTab::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ // Let children handle the event
+ LLUICtrl::handleScrollWheel(x, y, clicks);
+
+ // and then eat it to prevent in-world scrolling (STORM-351).
+ return TRUE;
+}
+
void LLSideTrayTab::dock(LLFloater* floater_tab)
{
LLSideTray* side_tray = getSideTray();
@@ -406,6 +421,11 @@ LLSideTrayTab* LLSideTrayTab::createInstance ()
return tab;
}
+// Now that we know the definition of LLSideTrayTab, we can implement
+// tab_cast.
+template <>
+LLPanel* tab_cast<LLPanel*>(LLSideTrayTab* tab) { return tab; }
+
//////////////////////////////////////////////////////////////////////////////
// LLSideTrayButton
// Side Tray tab button with "tear off" handling.
@@ -519,6 +539,8 @@ LLSideTray::LLSideTray(const Params& params)
// register handler function to process data from the xml.
// panel_name should be specified via "parameter" attribute.
commit.add("SideTray.ShowPanel", boost::bind(&LLSideTray::showPanel, this, _2, LLUUID::null));
+ commit.add("SideTray.Toggle", boost::bind(&LLSideTray::onToggleCollapse, this));
+ commit.add("SideTray.Collapse", boost::bind(&LLSideTray::collapseSideBar, this));
LLTransientFloaterMgr::getInstance()->addControlView(this);
LLView* side_bar_tabs = gViewerWindow->getRootView()->getChildView("side_bar_tabs");
if (side_bar_tabs != NULL)
diff --git a/indra/newview/llsidetray.h b/indra/newview/llsidetray.h
index 184d78845f..c4d3c2626c 100644
--- a/indra/newview/llsidetray.h
+++ b/indra/newview/llsidetray.h
@@ -33,6 +33,13 @@
class LLAccordionCtrl;
class LLSideTrayTab;
+// Deal with LLSideTrayTab being opaque. Generic do-nothing cast...
+template <class T>
+T tab_cast(LLSideTrayTab* tab) { return tab; }
+// specialized for implementation in presence of LLSideTrayTab definition
+template <>
+LLPanel* tab_cast<LLPanel*>(LLSideTrayTab* tab);
+
// added inheritance from LLDestroyClass<LLSideTray> to enable Side Tray perform necessary actions
// while disconnecting viewer in LLAppViewer::disconnectViewer().
// LLDestroyClassList::instance().fireCallbacks() calls destroyClass method. See EXT-245.
@@ -217,6 +224,9 @@ private:
}
private:
+ // Since we provide no public way to query mTabs and mDetachedTabs, give
+ // LLSideTrayListener friend access.
+ friend class LLSideTrayListener;
LLPanel* mButtonsPanel;
typedef std::map<std::string,LLButton*> button_map_t;
button_map_t mTabButtons;
diff --git a/indra/newview/llsidetraylistener.cpp b/indra/newview/llsidetraylistener.cpp
new file mode 100644
index 0000000000..6db13e517d
--- /dev/null
+++ b/indra/newview/llsidetraylistener.cpp
@@ -0,0 +1,162 @@
+/**
+ * @file llsidetraylistener.cpp
+ * @author Nat Goodspeed
+ * @date 2011-02-15
+ * @brief Implementation for llsidetraylistener.
+ *
+ * $LicenseInfo:firstyear=2011&license=lgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "llviewerprecompiledheaders.h"
+// associated header
+#include "llsidetraylistener.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "llsidetray.h"
+#include "llsdutil.h"
+
+LLSideTrayListener::LLSideTrayListener(const Getter& getter):
+ LLEventAPI("LLSideTray",
+ "Operations on side tray (e.g. query state, query tabs)"),
+ mGetter(getter)
+{
+ add("getCollapsed", "Send on [\"reply\"] an [\"open\"] Boolean",
+ &LLSideTrayListener::getCollapsed, LLSDMap("reply", LLSD()));
+ add("getTabs",
+ "Send on [\"reply\"] a map of tab names and info about them",
+ &LLSideTrayListener::getTabs, LLSDMap("reply", LLSD()));
+ add("getPanels",
+ "Send on [\"reply\"] data about panels available with SideTray.ShowPanel",
+ &LLSideTrayListener::getPanels, LLSDMap("reply", LLSD()));
+}
+
+void LLSideTrayListener::getCollapsed(const LLSD& event) const
+{
+ sendReply(LLSDMap("open", ! mGetter()->getCollapsed()), event);
+}
+
+void LLSideTrayListener::getTabs(const LLSD& event) const
+{
+ LLSD reply;
+
+ LLSideTray* tray = mGetter();
+ LLSD::Integer ord(0);
+ for (LLSideTray::child_list_const_iter_t chi(tray->beginChild()), chend(tray->endChild());
+ chi != chend; ++chi, ++ord)
+ {
+ LLView* child = *chi;
+ // How much info is important? Toss in as much as seems reasonable for
+ // each tab. But to me, at least for the moment, the most important
+ // item is the tab name.
+ LLSD info;
+ // I like the idea of returning a map keyed by tab name. But as
+ // compared to an array of maps, that loses sequence information.
+ // Address that by indicating the original order in each map entry.
+ info["ord"] = ord;
+ info["visible"] = bool(child->getVisible());
+ info["enabled"] = bool(child->getEnabled());
+ info["available"] = child->isAvailable();
+ reply[child->getName()] = info;
+ }
+
+ sendReply(reply, event);
+}
+
+static LLSD getTabInfo(LLPanel* tab)
+{
+ LLSD panels;
+ for (LLPanel::tree_iterator_t ti(tab->beginTreeDFS()), tend(tab->endTreeDFS());
+ ti != tend; ++ti)
+ {
+ // *ti is actually an LLView*, which had better not be NULL
+ LLView* view(*ti);
+ if (! view)
+ {
+ LL_ERRS("LLSideTrayListener") << "LLSideTrayTab '" << tab->getName()
+ << "' has a NULL child LLView*" << LL_ENDL;
+ }
+
+ // The logic we use to decide what "panel" names to return is heavily
+ // based on LLSideTray::showPanel(): the function that actually
+ // implements the "SideTray.ShowPanel" operation. showPanel(), in
+ // turn, depends on LLSideTray::openChildPanel(): when
+ // openChildPanel() returns non-NULL, showPanel() stops searching
+ // attached and detached LLSideTrayTab tabs.
+
+ // For each LLSideTrayTab, openChildPanel() first calls
+ // findChildView(panel_name, true). In other words, panel_name need
+ // not be a direct LLSideTrayTab child, it's sought recursively.
+ // That's why we use (begin|end)TreeDFS() in this loop.
+
+ // But this tree_iterator_t loop will actually traverse every widget
+ // in every panel. Returning all those names will not help our caller:
+ // passing most such names to openChildPanel() would not do what we
+ // want. Even though the code suggests that passing ANY valid
+ // side-panel widget name to openChildPanel() will open the tab
+ // containing that widget, results could get confusing since followup
+ // (onOpen()) logic wouldn't be invoked, and showPanel() wouldn't stop
+ // searching because openChildPanel() would return NULL.
+
+ // We must filter these LLView items, using logic that (sigh!) mirrors
+ // openChildPanel()'s own.
+
+ // openChildPanel() returns a non-NULL LLPanel* when either:
+ // - the LLView is a direct child of an LLSideTrayPanelContainer
+ // - the LLView is itself an LLPanel.
+ // But as LLSideTrayPanelContainer can directly contain LLView items
+ // that are NOT themselves LLPanels (e.g. "sidebar_me" contains an
+ // LLButton called "Jump Right Arrow"), we'd better focus only on
+ // LLSideTrayPanelContainer children that are themselves LLPanel
+ // items. Which means that the second test completely subsumes the
+ // first.
+ LLPanel* panel(dynamic_cast<LLPanel*>(view));
+ if (panel)
+ {
+ // Maybe it's overkill to construct an LLSD::Map for each panel, but
+ // the possibility remains that we might want to deliver more info
+ // about each panel than just its name.
+ panels.append(LLSDMap("name", panel->getName()));
+ }
+ }
+
+ return LLSDMap("panels", panels);
+}
+
+void LLSideTrayListener::getPanels(const LLSD& event) const
+{
+ LLSD reply;
+
+ LLSideTray* tray = mGetter();
+ // Iterate through the attached tabs.
+ LLSD::Integer ord(0);
+ for (LLSideTray::child_vector_t::const_iterator
+ ati(tray->mTabs.begin()), atend(tray->mTabs.end());
+ ati != atend; ++ati)
+ {
+ // We don't have access to LLSideTrayTab: the class definition is
+ // hidden in llsidetray.cpp. But as LLSideTrayTab isa LLPanel, use the
+ // LLPanel API. Unfortunately, without the LLSideTrayTab definition,
+ // the compiler doesn't even know this LLSideTrayTab* is an LLPanel*.
+ // Persuade it.
+ LLPanel* tab(tab_cast<LLPanel*>(*ati));
+ reply[tab->getName()] = getTabInfo(tab).with("attached", true).with("ord", ord);
+ }
+
+ // Now iterate over the detached tabs. These can also be opened via
+ // SideTray.ShowPanel.
+ ord = 0;
+ for (LLSideTray::child_vector_t::const_iterator
+ dti(tray->mDetachedTabs.begin()), dtend(tray->mDetachedTabs.end());
+ dti != dtend; ++dti)
+ {
+ LLPanel* tab(tab_cast<LLPanel*>(*dti));
+ reply[tab->getName()] = getTabInfo(tab).with("attached", false).with("ord", ord);
+ }
+
+ sendReply(reply, event);
+}
diff --git a/indra/newview/llsidetraylistener.h b/indra/newview/llsidetraylistener.h
new file mode 100644
index 0000000000..0dd2067433
--- /dev/null
+++ b/indra/newview/llsidetraylistener.h
@@ -0,0 +1,36 @@
+/**
+ * @file llsidetraylistener.h
+ * @author Nat Goodspeed
+ * @date 2011-02-15
+ * @brief
+ *
+ * $LicenseInfo:firstyear=2011&license=lgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLSIDETRAYLISTENER_H)
+#define LL_LLSIDETRAYLISTENER_H
+
+#include "lleventapi.h"
+#include <boost/function.hpp>
+
+class LLSideTray;
+class LLSD;
+
+class LLSideTrayListener: public LLEventAPI
+{
+ typedef boost::function<LLSideTray*()> Getter;
+
+public:
+ LLSideTrayListener(const Getter& getter);
+
+private:
+ void getCollapsed(const LLSD& event) const;
+ void getTabs(const LLSD& event) const;
+ void getPanels(const LLSD& event) const;
+
+ Getter mGetter;
+};
+
+#endif /* ! defined(LL_LLSIDETRAYLISTENER_H) */
diff --git a/indra/newview/llsimplestat.h b/indra/newview/llsimplestat.h
index a90e503adb..9d7780c4f9 100644
--- a/indra/newview/llsimplestat.h
+++ b/indra/newview/llsimplestat.h
@@ -2,31 +2,25 @@
* @file llsimplestat.h
* @brief Runtime statistics accumulation.
*
- * $LicenseInfo:firstyear=2010&license=viewergpl$
- *
- * Copyright (c) 2010, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 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.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 5cdf1706e6..18c3a3b87d 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -1,3011 +1,3011 @@
-/**
- * @file lltexturefetch.cpp
- * @brief Object which fetches textures from the cache and/or network
- *
- * $LicenseInfo:firstyear=2000&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$
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include <iostream>
-#include <map>
-
-#include "llstl.h"
-
-#include "lltexturefetch.h"
-
-#include "llcurl.h"
-#include "lldir.h"
-#include "llhttpclient.h"
-#include "llhttpstatuscodes.h"
-#include "llimage.h"
-#include "llimagej2c.h"
-#include "llimageworker.h"
-#include "llworkerthread.h"
-#include "message.h"
-
-#include "llagent.h"
-#include "lltexturecache.h"
-#include "llviewercontrol.h"
-#include "llviewertexturelist.h"
-#include "llviewertexture.h"
-#include "llviewerregion.h"
-#include "llviewerstats.h"
-#include "llviewerassetstats.h"
-#include "llworld.h"
-
-//////////////////////////////////////////////////////////////////////////////
-class LLTextureFetchWorker : public LLWorkerClass
-{
- friend class LLTextureFetch;
- friend class HTTPGetResponder;
-
-private:
- class CacheReadResponder : public LLTextureCache::ReadResponder
- {
- public:
- CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image)
- : mFetcher(fetcher), mID(id)
- {
- setImage(image);
- }
- virtual void completed(bool success)
- {
- LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
- if (worker)
- {
- worker->callbackCacheRead(success, mFormattedImage, mImageSize, mImageLocal);
- }
- }
- private:
- LLTextureFetch* mFetcher;
- LLUUID mID;
- };
-
- class CacheWriteResponder : public LLTextureCache::WriteResponder
- {
- public:
- CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id)
- : mFetcher(fetcher), mID(id)
- {
- }
- virtual void completed(bool success)
- {
- LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
- if (worker)
- {
- worker->callbackCacheWrite(success);
- }
- }
- private:
- LLTextureFetch* mFetcher;
- LLUUID mID;
- };
-
- class DecodeResponder : public LLImageDecodeThread::Responder
- {
- public:
- DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker)
- : mFetcher(fetcher), mID(id), mWorker(worker)
- {
- }
- virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
- {
- LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
- if (worker)
- {
- worker->callbackDecoded(success, raw, aux);
- }
- }
- private:
- LLTextureFetch* mFetcher;
- LLUUID mID;
- LLTextureFetchWorker* mWorker; // debug only (may get deleted from under us, use mFetcher/mID)
- };
-
- struct Compare
- {
- // lhs < rhs
- bool operator()(const LLTextureFetchWorker* lhs, const LLTextureFetchWorker* rhs) const
- {
- // greater priority is "less"
- const F32 lpriority = lhs->mImagePriority;
- const F32 rpriority = rhs->mImagePriority;
- if (lpriority > rpriority) // higher priority
- return true;
- else if (lpriority < rpriority)
- return false;
- else
- return lhs < rhs;
- }
- };
-
-public:
- /*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest()
- /*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD)
- /*virtual*/ bool deleteOK(); // called from update() (WORK THREAD)
-
- ~LLTextureFetchWorker();
- // void relese() { --mActiveCount; }
-
- S32 callbackHttpGet(const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer,
- bool partial, bool success);
- void callbackCacheRead(bool success, LLImageFormatted* image,
- S32 imagesize, BOOL islocal);
- void callbackCacheWrite(bool success);
- void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux);
-
- void setGetStatus(U32 status, const std::string& reason)
- {
- LLMutexLock lock(&mWorkMutex);
-
- mGetStatus = status;
- mGetReason = reason;
- }
-
- void setCanUseHTTP(bool can_use_http) { mCanUseHTTP = can_use_http; }
- bool getCanUseHTTP() const { return mCanUseHTTP; }
-
- LLTextureFetch & getFetcher() { return *mFetcher; }
-
-protected:
- LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host,
- F32 priority, S32 discard, S32 size);
-
-private:
- /*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD)
- /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
-
- void resetFormattedData();
-
- void setImagePriority(F32 priority);
- void setDesiredDiscard(S32 discard, S32 size);
- bool insertPacket(S32 index, U8* data, S32 size);
- void clearPackets();
- void setupPacketData();
- U32 calcWorkPriority();
- void removeFromCache();
- bool processSimulatorPackets();
- bool writeToCacheComplete();
-
- void lockWorkMutex() { mWorkMutex.lock(); }
- void unlockWorkMutex() { mWorkMutex.unlock(); }
-
-private:
- enum e_state // mState
- {
- // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack)
- INVALID = 0,
- INIT,
- LOAD_FROM_TEXTURE_CACHE,
- CACHE_POST,
- LOAD_FROM_NETWORK,
- LOAD_FROM_SIMULATOR,
- SEND_HTTP_REQ,
- WAIT_HTTP_REQ,
- DECODE_IMAGE,
- DECODE_IMAGE_UPDATE,
- WRITE_TO_CACHE,
- WAIT_ON_WRITE,
- DONE
- };
- enum e_request_state // mSentRequest
- {
- UNSENT = 0,
- QUEUED = 1,
- SENT_SIM = 2
- };
- enum e_write_to_cache_state //mWriteToCacheState
- {
- NOT_WRITE = 0,
- CAN_WRITE = 1,
- SHOULD_WRITE = 2
- };
- static const char* sStateDescs[];
- e_state mState;
- e_write_to_cache_state mWriteToCacheState;
- LLTextureFetch* mFetcher;
- LLPointer<LLImageFormatted> mFormattedImage;
- LLPointer<LLImageRaw> mRawImage;
- LLPointer<LLImageRaw> mAuxImage;
- LLUUID mID;
- LLHost mHost;
- std::string mUrl;
- U8 mType;
- F32 mImagePriority;
- U32 mWorkPriority;
- F32 mRequestedPriority;
- S32 mDesiredDiscard;
- S32 mSimRequestedDiscard;
- S32 mRequestedDiscard;
- S32 mLoadedDiscard;
- S32 mDecodedDiscard;
- LLFrameTimer mRequestedTimer;
- LLFrameTimer mFetchTimer;
- LLTextureCache::handle_t mCacheReadHandle;
- LLTextureCache::handle_t mCacheWriteHandle;
- U8* mBuffer;
- S32 mBufferSize;
- S32 mRequestedSize;
- S32 mDesiredSize;
- S32 mFileSize;
- S32 mCachedSize;
- e_request_state mSentRequest;
- handle_t mDecodeHandle;
- BOOL mLoaded;
- BOOL mDecoded;
- BOOL mWritten;
- BOOL mNeedsAux;
- BOOL mHaveAllData;
- BOOL mInLocalCache;
- bool mCanUseHTTP ;
- bool mCanUseNET ; //can get from asset server.
- S32 mHTTPFailCount;
- S32 mRetryAttempt;
- S32 mActiveCount;
- U32 mGetStatus;
- std::string mGetReason;
-
- // Work Data
- LLMutex mWorkMutex;
- struct PacketData
- {
- PacketData(U8* data, S32 size) { mData = data; mSize = size; }
- ~PacketData() { clearData(); }
- void clearData() { delete[] mData; mData = NULL; }
- U8* mData;
- U32 mSize;
- };
- std::vector<PacketData*> mPackets;
- S32 mFirstPacket;
- S32 mLastPacket;
- U16 mTotalPackets;
- U8 mImageCodec;
-
- LLViewerAssetStats::duration_t mMetricsStartTime;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-class HTTPGetResponder : public LLCurl::Responder
-{
- LOG_CLASS(HTTPGetResponder);
-public:
- HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset, bool redir)
- : mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset), mFollowRedir(redir)
- {
- }
- ~HTTPGetResponder()
- {
- }
-
- virtual void completedRaw(U32 status, const std::string& reason,
- const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer)
- {
- static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
- static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
- static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ;
-
- if (log_to_viewer_log || log_to_sim)
- {
- mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime);
- U64 timeNow = LLTimer::getTotalTime();
- mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
- mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize);
- mFetcher->mTextureInfo.setRequestOffset(mID, mOffset);
- mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow);
- }
-
- lldebugs << "HTTP COMPLETE: " << mID << llendl;
- LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
- if (worker)
- {
- bool success = false;
- bool partial = false;
- if (HTTP_OK <= status && status < HTTP_MULTIPLE_CHOICES)
- {
- success = true;
- if (HTTP_PARTIAL_CONTENT == status) // partial information
- {
- partial = true;
- }
- }
-
- if (!success)
- {
- worker->setGetStatus(status, reason);
-// llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl;
- }
-
- S32 data_size = worker->callbackHttpGet(channels, buffer, partial, success);
-
- if(log_texture_traffic && data_size > 0)
- {
- LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ;
- if(tex)
- {
- gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ;
- }
- }
-
- mFetcher->removeFromHTTPQueue(mID, data_size);
-
- if (worker->mMetricsStartTime)
- {
- LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
- true,
- LLImageBase::TYPE_AVATAR_BAKE == worker->mType,
- LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime);
- worker->mMetricsStartTime = 0;
- }
- LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
- true,
- LLImageBase::TYPE_AVATAR_BAKE == worker->mType);
- }
- else
- {
- mFetcher->removeFromHTTPQueue(mID);
- llwarns << "Worker not found: " << mID << llendl;
- }
- }
-
- virtual bool followRedir()
- {
- return mFollowRedir;
- }
-
-private:
- LLTextureFetch* mFetcher;
- LLUUID mID;
- U64 mStartTime;
- S32 mRequestedSize;
- U32 mOffset;
- bool mFollowRedir;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-// Cross-thread messaging for asset metrics.
-
-/**
- * @brief Base class for cross-thread requests made of the fetcher
- *
- * I believe the intent of the LLQueuedThread class was to
- * have these operations derived from LLQueuedThread::QueuedRequest
- * but the texture fetcher has elected to manage the queue
- * in its own manner. So these are free-standing objects which are
- * managed in simple FIFO order on the mCommands queue of the
- * LLTextureFetch object.
- *
- * What each represents is a simple command sent from an
- * outside thread into the TextureFetch thread to be processed
- * in order and in a timely fashion (though not an absolute
- * higher priority than other operations of the thread).
- * Each operation derives a new class from the base customizing
- * members, constructors and the doWork() method to effect
- * the command.
- *
- * The flow is one-directional. There are two global instances
- * of the LLViewerAssetStats collector, one for the main program's
- * thread pointed to by gViewerAssetStatsMain and one for the
- * TextureFetch thread pointed to by gViewerAssetStatsThread1.
- * Common operations has each thread recording metrics events
- * into the respective collector unconcerned with locking and
- * the state of any other thread. But when the agent moves into
- * a different region or the metrics timer expires and a report
- * needs to be sent back to the grid, messaging across threads
- * is required to distribute data and perform global actions.
- * In pseudo-UML, it looks like:
- *
- * Main Thread1
- * . .
- * . .
- * +-----+ .
- * | AM | .
- * +--+--+ .
- * +-------+ | .
- * | Main | +--+--+ .
- * | | | SRE |---. .
- * | Stats | +-----+ \ .
- * | | | \ (uuid) +-----+
- * | Coll. | +--+--+ `-------->| SR |
- * +-------+ | MSC | +--+--+
- * | ^ +-----+ |
- * | | (uuid) / . +-----+ (uuid)
- * | `--------' . | MSC |---------.
- * | . +-----+ |
- * | +-----+ . v
- * | | TE | . +-------+
- * | +--+--+ . | Thd1 |
- * | | . | |
- * | +-----+ . | Stats |
- * `--------->| RSC | . | |
- * +--+--+ . | Coll. |
- * | . +-------+
- * +--+--+ . |
- * | SME |---. . |
- * +-----+ \ . |
- * . \ (clone) +-----+ |
- * . `-------->| SM | |
- * . +--+--+ |
- * . | |
- * . +-----+ |
- * . | RSC |<--------'
- * . +-----+
- * . |
- * . +-----+
- * . | CP |--> HTTP POST
- * . +-----+
- * . .
- * . .
- *
- *
- * Key:
- *
- * SRE - Set Region Enqueued. Enqueue a 'Set Region' command in
- * the other thread providing the new UUID of the region.
- * TFReqSetRegion carries the data.
- * SR - Set Region. New region UUID is sent to the thread-local
- * collector.
- * SME - Send Metrics Enqueued. Enqueue a 'Send Metrics' command
- * including an ownership transfer of a cloned LLViewerAssetStats.
- * TFReqSendMetrics carries the data.
- * SM - Send Metrics. Global metrics reporting operation. Takes
- * the cloned stats from the command, merges it with the
- * thread's local stats, converts to LLSD and sends it on
- * to the grid.
- * AM - Agent Moved. Agent has completed some sort of move to a
- * new region.
- * TE - Timer Expired. Metrics timer has expired (on the order
- * of 10 minutes).
- * CP - CURL Post
- * MSC - Modify Stats Collector. State change in the thread-local
- * collector. Typically a region change which affects the
- * global pointers used to find the 'current stats'.
- * RSC - Read Stats Collector. Extract collector data cloning it
- * (i.e. deep copy) when necessary.
- *
- */
-class LLTextureFetch::TFRequest // : public LLQueuedThread::QueuedRequest
-{
-public:
- // Default ctors and assignment operator are correct.
-
- virtual ~TFRequest()
- {}
-
- // Patterned after QueuedRequest's method but expected behavior
- // is different. Always expected to complete on the first call
- // and work dispatcher will assume the same and delete the
- // request after invocation.
- virtual bool doWork(LLTextureFetch * fetcher) = 0;
-};
-
-namespace
-{
-
-/**
- * @brief Implements a 'Set Region' cross-thread command.
- *
- * When an agent moves to a new region, subsequent metrics need
- * to be binned into a new or existing stats collection in 1:1
- * relationship with the region. We communicate this region
- * change across the threads involved in the communication with
- * this message.
- *
- * Corresponds to LLTextureFetch::commandSetRegion()
- */
-class TFReqSetRegion : public LLTextureFetch::TFRequest
-{
-public:
- TFReqSetRegion(U64 region_handle)
- : LLTextureFetch::TFRequest(),
- mRegionHandle(region_handle)
- {}
- TFReqSetRegion & operator=(const TFReqSetRegion &); // Not defined
-
- virtual ~TFReqSetRegion()
- {}
-
- virtual bool doWork(LLTextureFetch * fetcher);
-
-public:
- const U64 mRegionHandle;
-};
-
-
-/**
- * @brief Implements a 'Send Metrics' cross-thread command.
- *
- * This is the big operation. The main thread gathers metrics
- * for a period of minutes into LLViewerAssetStats and other
- * objects then makes a snapshot of the data by cloning the
- * collector. This command transfers the clone, along with a few
- * additional arguments (UUIDs), handing ownership to the
- * TextureFetch thread. It then merges its own data into the
- * cloned copy, converts to LLSD and kicks off an HTTP POST of
- * the resulting data to the currently active metrics collector.
- *
- * Corresponds to LLTextureFetch::commandSendMetrics()
- */
-class TFReqSendMetrics : public LLTextureFetch::TFRequest
-{
-public:
- /**
- * Construct the 'Send Metrics' command to have the TextureFetch
- * thread add and log metrics data.
- *
- * @param caps_url URL of a "ViewerMetrics" Caps target
- * to receive the data. Does not have to
- * be associated with a particular region.
- *
- * @param session_id UUID of the agent's session.
- *
- * @param agent_id UUID of the agent. (Being pure here...)
- *
- * @param main_stats Pointer to a clone of the main thread's
- * LLViewerAssetStats data. Thread1 takes
- * ownership of the copy and disposes of it
- * when done.
- */
- TFReqSendMetrics(const std::string & caps_url,
- const LLUUID & session_id,
- const LLUUID & agent_id,
- LLViewerAssetStats * main_stats)
- : LLTextureFetch::TFRequest(),
- mCapsURL(caps_url),
- mSessionID(session_id),
- mAgentID(agent_id),
- mMainStats(main_stats)
- {}
- TFReqSendMetrics & operator=(const TFReqSendMetrics &); // Not defined
-
- virtual ~TFReqSendMetrics();
-
- virtual bool doWork(LLTextureFetch * fetcher);
-
-public:
- const std::string mCapsURL;
- const LLUUID mSessionID;
- const LLUUID mAgentID;
- LLViewerAssetStats * mMainStats;
-};
-
-/*
- * Examines the merged viewer metrics report and if found to be too long,
- * will attempt to truncate it in some reasonable fashion.
- *
- * @param max_regions Limit of regions allowed in report.
- *
- * @param metrics Full, merged viewer metrics report.
- *
- * @returns If data was truncated, returns true.
- */
-bool truncate_viewer_metrics(int max_regions, LLSD & metrics);
-
-} // end of anonymous namespace
-
-
-//////////////////////////////////////////////////////////////////////////////
-
-//static
-const char* LLTextureFetchWorker::sStateDescs[] = {
- "INVALID",
- "INIT",
- "LOAD_FROM_TEXTURE_CACHE",
- "CACHE_POST",
- "LOAD_FROM_NETWORK",
- "LOAD_FROM_SIMULATOR",
- "SEND_HTTP_REQ",
- "WAIT_HTTP_REQ",
- "DECODE_IMAGE",
- "DECODE_IMAGE_UPDATE",
- "WRITE_TO_CACHE",
- "WAIT_ON_WRITE",
- "DONE",
-};
-
-// static
-volatile bool LLTextureFetch::svMetricsDataBreak(true); // Start with a data break
-
-// called from MAIN THREAD
-
-LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
- const std::string& url, // Optional URL
- const LLUUID& id, // Image UUID
- const LLHost& host, // Simulator host
- F32 priority, // Priority
- S32 discard, // Desired discard
- S32 size) // Desired size
- : LLWorkerClass(fetcher, "TextureFetch"),
- mState(INIT),
- mWriteToCacheState(NOT_WRITE),
- mFetcher(fetcher),
- mID(id),
- mHost(host),
- mUrl(url),
- mImagePriority(priority),
- mWorkPriority(0),
- mRequestedPriority(0.f),
- mDesiredDiscard(-1),
- mSimRequestedDiscard(-1),
- mRequestedDiscard(-1),
- mLoadedDiscard(-1),
- mDecodedDiscard(-1),
- mCacheReadHandle(LLTextureCache::nullHandle()),
- mCacheWriteHandle(LLTextureCache::nullHandle()),
- mBuffer(NULL),
- mBufferSize(0),
- mRequestedSize(0),
- mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE),
- mFileSize(0),
- mCachedSize(0),
- mLoaded(FALSE),
- mSentRequest(UNSENT),
- mDecodeHandle(0),
- mDecoded(FALSE),
- mWritten(FALSE),
- mNeedsAux(FALSE),
- mHaveAllData(FALSE),
- mInLocalCache(FALSE),
- mCanUseHTTP(true),
- mHTTPFailCount(0),
- mRetryAttempt(0),
- mActiveCount(0),
- mGetStatus(0),
- mWorkMutex(NULL),
- mFirstPacket(0),
- mLastPacket(-1),
- mTotalPackets(0),
- mImageCodec(IMG_CODEC_INVALID),
- mMetricsStartTime(0)
-{
- mCanUseNET = mUrl.empty() ;
-
- calcWorkPriority();
- mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL;
-// llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl;
- if (!mFetcher->mDebugPause)
- {
- U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
- addWork(0, work_priority );
- }
- setDesiredDiscard(discard, size);
-}
-
-LLTextureFetchWorker::~LLTextureFetchWorker()
-{
-// llinfos << "Destroy: " << mID
-// << " Decoded=" << mDecodedDiscard
-// << " Requested=" << mRequestedDiscard
-// << " Desired=" << mDesiredDiscard << llendl;
- llassert_always(!haveWork());
- lockWorkMutex();
- if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache)
- {
- mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
- }
- if (mCacheWriteHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache)
- {
- mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
- }
- mFormattedImage = NULL;
- clearPackets();
- unlockWorkMutex();
- mFetcher->removeFromHTTPQueue(mID);
-}
-
-void LLTextureFetchWorker::clearPackets()
-{
- for_each(mPackets.begin(), mPackets.end(), DeletePointer());
- mPackets.clear();
- mTotalPackets = 0;
- mLastPacket = -1;
- mFirstPacket = 0;
-}
-
-void LLTextureFetchWorker::setupPacketData()
-{
- S32 data_size = 0;
- if (mFormattedImage.notNull())
- {
- data_size = mFormattedImage->getDataSize();
- }
- if (data_size > 0)
- {
- // Only used for simulator requests
- mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1;
- if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size)
- {
- llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl;
- removeFromCache();
- resetFormattedData();
- clearPackets();
- }
- else if (mFileSize > 0)
- {
- mLastPacket = mFirstPacket-1;
- mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1;
- }
- else
- {
- // This file was cached using HTTP so we have to refetch the first packet
- resetFormattedData();
- clearPackets();
- }
- }
-}
-
-U32 LLTextureFetchWorker::calcWorkPriority()
-{
- //llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerFetchedTexture::maxDecodePriority());
- static const F32 PRIORITY_SCALE = (F32)LLWorkerThread::PRIORITY_LOWBITS / LLViewerFetchedTexture::maxDecodePriority();
-
- mWorkPriority = llmin((U32)LLWorkerThread::PRIORITY_LOWBITS, (U32)(mImagePriority * PRIORITY_SCALE));
- return mWorkPriority;
-}
-
-// mWorkMutex is locked
-void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size)
-{
- bool prioritize = false;
- if (mDesiredDiscard != discard)
- {
- if (!haveWork())
- {
- calcWorkPriority();
- if (!mFetcher->mDebugPause)
- {
- U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
- addWork(0, work_priority);
- }
- }
- else if (mDesiredDiscard < discard)
- {
- prioritize = true;
- }
- mDesiredDiscard = discard;
- mDesiredSize = size;
- }
- else if (size > mDesiredSize)
- {
- mDesiredSize = size;
- prioritize = true;
- }
- mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE);
- if ((prioritize && mState == INIT) || mState == DONE)
- {
- mState = INIT;
- U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
- setPriority(work_priority);
- }
-}
-
-void LLTextureFetchWorker::setImagePriority(F32 priority)
-{
-// llassert_always(priority >= 0 && priority <= LLViewerTexture::maxDecodePriority());
- F32 delta = fabs(priority - mImagePriority);
- if (delta > (mImagePriority * .05f) || mState == DONE)
- {
- mImagePriority = priority;
- calcWorkPriority();
- U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS);
- setPriority(work_priority);
- }
-}
-
-void LLTextureFetchWorker::resetFormattedData()
-{
- delete[] mBuffer;
- mBuffer = NULL;
- mBufferSize = 0;
- if (mFormattedImage.notNull())
- {
- mFormattedImage->deleteData();
- }
- mHaveAllData = FALSE;
-}
-
-// Called from MAIN thread
-void LLTextureFetchWorker::startWork(S32 param)
-{
- llassert(mFormattedImage.isNull());
-}
-
-#include "llviewertexturelist.h" // debug
-
-// Called from LLWorkerThread::processRequest()
-bool LLTextureFetchWorker::doWork(S32 param)
-{
- LLMutexLock lock(&mWorkMutex);
-
- if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED)))
- {
- if (mState < DECODE_IMAGE)
- {
- return true; // abort
- }
- }
-
- if(mImagePriority < F_ALMOST_ZERO)
- {
- if (mState == INIT || mState == LOAD_FROM_NETWORK || mState == LOAD_FROM_SIMULATOR)
- {
- return true; // abort
- }
- }
- if(mState > CACHE_POST && !mCanUseNET && !mCanUseHTTP)
- {
- //nowhere to get data, abort.
- return true ;
- }
-
- if (mFetcher->mDebugPause)
- {
- return false; // debug: don't do any work
- }
- if (mID == mFetcher->mDebugID)
- {
- mFetcher->mDebugCount++; // for setting breakpoints
- }
-
- if (mState != DONE)
- {
- mFetchTimer.reset();
- }
-
- if (mState == INIT)
- {
- mRawImage = NULL ;
- mRequestedDiscard = -1;
- mLoadedDiscard = -1;
- mDecodedDiscard = -1;
- mRequestedSize = 0;
- mFileSize = 0;
- mCachedSize = 0;
- mLoaded = FALSE;
- mSentRequest = UNSENT;
- mDecoded = FALSE;
- mWritten = FALSE;
- delete[] mBuffer;
- mBuffer = NULL;
- mBufferSize = 0;
- mHaveAllData = FALSE;
- clearPackets(); // TODO: Shouldn't be necessary
- mCacheReadHandle = LLTextureCache::nullHandle();
- mCacheWriteHandle = LLTextureCache::nullHandle();
- mState = LOAD_FROM_TEXTURE_CACHE;
- mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE
- LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority)
- << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
- // fall through
- }
-
- if (mState == LOAD_FROM_TEXTURE_CACHE)
- {
- if (mCacheReadHandle == LLTextureCache::nullHandle())
- {
- U32 cache_priority = mWorkPriority;
- S32 offset = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
- S32 size = mDesiredSize - offset;
- if (size <= 0)
- {
- mState = CACHE_POST;
- return false;
- }
- mFileSize = 0;
- mLoaded = FALSE;
-
- if (mUrl.compare(0, 7, "file://") == 0)
- {
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
-
- // read file from local disk
- std::string filename = mUrl.substr(7, std::string::npos);
- CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
- mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority,
- offset, size, responder);
- }
- else if (mUrl.empty())
- {
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
-
- CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
- mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
- offset, size, responder);
- }
- else if(mCanUseHTTP)
- {
- if (!(mUrl.compare(0, 7, "http://") == 0))
- {
- // *TODO:?remove this warning
- llwarns << "Unknown URL Type: " << mUrl << llendl;
- }
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
- mState = SEND_HTTP_REQ;
- }
- else
- {
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
- mState = LOAD_FROM_NETWORK;
- }
- }
-
- if (mLoaded)
- {
- // Make sure request is complete. *TODO: make this auto-complete
- if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, false))
- {
- mCacheReadHandle = LLTextureCache::nullHandle();
- mState = CACHE_POST;
- // fall through
- }
- else
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
-
- if (mState == CACHE_POST)
- {
- mCachedSize = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
- // Successfully loaded
- if ((mCachedSize >= mDesiredSize) || mHaveAllData)
- {
- // we have enough data, decode it
- llassert_always(mFormattedImage->getDataSize() > 0);
- mLoadedDiscard = mDesiredDiscard;
- mState = DECODE_IMAGE;
- mWriteToCacheState = NOT_WRITE ;
- LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize()
- << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight())
- << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
- // fall through
- }
- else
- {
- if (mUrl.compare(0, 7, "file://") == 0)
- {
- // failed to load local file, we're done.
- return true;
- }
- // need more data
- else
- {
- LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL;
- mState = LOAD_FROM_NETWORK;
- }
- // fall through
- }
- }
-
- if (mState == LOAD_FROM_NETWORK)
- {
- static LLCachedControl<bool> use_http(gSavedSettings,"ImagePipelineUseHTTP");
-
-// if (mHost != LLHost::invalid) get_url = false;
- if ( use_http && mCanUseHTTP && mUrl.empty())//get http url.
- {
- LLViewerRegion* region = NULL;
- if (mHost == LLHost::invalid)
- region = gAgent.getRegion();
- else
- region = LLWorld::getInstance()->getRegion(mHost);
-
- if (region)
- {
- std::string http_url = region->getHttpUrl() ;
- if (!http_url.empty())
- {
- mUrl = http_url + "/?texture_id=" + mID.asString().c_str();
- mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id.
- }
- else
- {
- mCanUseHTTP = false ;
- }
- }
- else
- {
- // This will happen if not logged in or if a region deoes not have HTTP Texture enabled
- //llwarns << "Region not found for host: " << mHost << llendl;
- mCanUseHTTP = false;
- }
- }
- if (mCanUseHTTP && !mUrl.empty())
- {
- mState = LLTextureFetchWorker::SEND_HTTP_REQ;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
- if(mWriteToCacheState != NOT_WRITE)
- {
- mWriteToCacheState = CAN_WRITE ;
- }
- // don't return, fall through to next state
- }
- else if (mSentRequest == UNSENT && mCanUseNET)
- {
- // Add this to the network queue and sit here.
- // LLTextureFetch::update() will send off a request which will change our state
- mWriteToCacheState = CAN_WRITE ;
- mRequestedSize = mDesiredSize;
- mRequestedDiscard = mDesiredDiscard;
- mSentRequest = QUEUED;
- mFetcher->addToNetworkQueue(this);
- if (! mMetricsStartTime)
- {
- mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
- }
- LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
- false,
- LLImageBase::TYPE_AVATAR_BAKE == mType);
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
-
- return false;
- }
- else
- {
- // Shouldn't need to do anything here
- //llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end());
- // Make certain this is in the network queue
- //mFetcher->addToNetworkQueue(this);
- //if (! mMetricsStartTime)
- //{
- // mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
- //}
- //LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false,
- // LLImageBase::TYPE_AVATAR_BAKE == mType);
- //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- return false;
- }
- }
-
- if (mState == LOAD_FROM_SIMULATOR)
- {
- if (mFormattedImage.isNull())
- {
- mFormattedImage = new LLImageJ2C;
- }
- if (processSimulatorPackets())
- {
- LL_DEBUGS("Texture") << mID << ": Loaded from Sim. Bytes: " << mFormattedImage->getDataSize() << LL_ENDL;
- mFetcher->removeFromNetworkQueue(this, false);
- if (mFormattedImage.isNull() || !mFormattedImage->getDataSize())
- {
- // processSimulatorPackets() failed
-// llwarns << "processSimulatorPackets() failed to load buffer" << llendl;
- return true; // failed
- }
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
- mState = DECODE_IMAGE;
- mWriteToCacheState = SHOULD_WRITE;
-
- if (mMetricsStartTime)
- {
- LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
- false,
- LLImageBase::TYPE_AVATAR_BAKE == mType,
- LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime);
- mMetricsStartTime = 0;
- }
- LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
- false,
- LLImageBase::TYPE_AVATAR_BAKE == mType);
- }
- else
- {
- mFetcher->addToNetworkQueue(this); // failsafe
- if (! mMetricsStartTime)
- {
- mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
- }
- LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
- false,
- LLImageBase::TYPE_AVATAR_BAKE == mType);
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- }
- return false;
- }
-
- if (mState == SEND_HTTP_REQ)
- {
- if(mCanUseHTTP)
- {
- //NOTE:
- //control the number of the http requests issued for:
- //1, not openning too many file descriptors at the same time;
- //2, control the traffic of http so udp gets bandwidth.
- //
- static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 8 ;
- if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE)
- {
- return false ; //wait.
- }
-
- mFetcher->removeFromNetworkQueue(this, false);
-
- S32 cur_size = 0;
- if (mFormattedImage.notNull())
- {
- cur_size = mFormattedImage->getDataSize(); // amount of data we already have
- if (mFormattedImage->getDiscardLevel() == 0)
- {
- if(cur_size > 0)
- {
- // We already have all the data, just decode it
- mLoadedDiscard = mFormattedImage->getDiscardLevel();
- mState = DECODE_IMAGE;
- return false;
- }
- else
- {
- return true ; //abort.
- }
- }
- }
- mRequestedSize = mDesiredSize;
- mRequestedDiscard = mDesiredDiscard;
- mRequestedSize -= cur_size;
- S32 offset = cur_size;
- mBufferSize = cur_size; // This will get modified by callbackHttpGet()
-
- bool res = false;
- if (!mUrl.empty())
- {
- mLoaded = FALSE;
- mGetStatus = 0;
- mGetReason.clear();
- LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << offset
- << " Bytes: " << mRequestedSize
- << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth
- << LL_ENDL;
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- mState = WAIT_HTTP_REQ;
-
- mFetcher->addToHTTPQueue(mID);
- if (! mMetricsStartTime)
- {
- mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
- }
- LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
- true,
- LLImageBase::TYPE_AVATAR_BAKE == mType);
-
- // Will call callbackHttpGet when curl request completes
- std::vector<std::string> headers;
- headers.push_back("Accept: image/x-j2c");
- res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize,
- new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true));
- }
- if (!res)
- {
- llwarns << "HTTP GET request failed for " << mID << llendl;
- resetFormattedData();
- ++mHTTPFailCount;
- return true; // failed
- }
- // fall through
- }
- else //can not use http fetch.
- {
- return true ; //abort
- }
- }
-
- if (mState == WAIT_HTTP_REQ)
- {
- if (mLoaded)
- {
- S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
- if (mRequestedSize < 0)
- {
- S32 max_attempts;
- if (mGetStatus == HTTP_NOT_FOUND)
- {
- mHTTPFailCount = max_attempts = 1; // Don't retry
- llwarns << "Texture missing from server (404): " << mUrl << llendl;
-
- //roll back to try UDP
- if(mCanUseNET)
- {
- mState = INIT ;
- mCanUseHTTP = false ;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
- return false ;
- }
- }
- else if (mGetStatus == HTTP_SERVICE_UNAVAILABLE)
- {
- // *TODO: Should probably introduce a timer here to delay future HTTP requsts
- // for a short time (~1s) to ease server load? Ideally the server would queue
- // requests instead of returning 503... we already limit the number pending.
- ++mHTTPFailCount;
- max_attempts = mHTTPFailCount+1; // Keep retrying
- LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL;
- }
- else
- {
- const S32 HTTP_MAX_RETRY_COUNT = 3;
- max_attempts = HTTP_MAX_RETRY_COUNT + 1;
- ++mHTTPFailCount;
- llinfos << "HTTP GET failed for: " << mUrl
- << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'"
- << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl;
- }
-
- if (mHTTPFailCount >= max_attempts)
- {
- if (cur_size > 0)
- {
- // Use available data
- mLoadedDiscard = mFormattedImage->getDiscardLevel();
- mState = DECODE_IMAGE;
- return false;
- }
- else
- {
- resetFormattedData();
- mState = DONE;
- return true; // failed
- }
- }
- else
- {
- mState = SEND_HTTP_REQ;
- return false; // retry
- }
- }
-
- llassert_always(mBufferSize == cur_size + mRequestedSize);
- if(!mBufferSize)//no data received.
- {
- delete[] mBuffer;
- mBuffer = NULL;
-
- //abort.
- mState = DONE;
- return true;
- }
-
- if (mFormattedImage.isNull())
- {
- // For now, create formatted image based on extension
- std::string extension = gDirUtilp->getExtension(mUrl);
- mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension));
- if (mFormattedImage.isNull())
- {
- mFormattedImage = new LLImageJ2C; // default
- }
- }
-
- if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded.
- {
- mFileSize = mBufferSize;
- }
- else //the file size is unknown.
- {
- mFileSize = mBufferSize + 1 ; //flag the file is not fully loaded.
- }
-
- U8* buffer = new U8[mBufferSize];
- if (cur_size > 0)
- {
- memcpy(buffer, mFormattedImage->getData(), cur_size);
- }
- memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append
- // NOTE: setData releases current data and owns new data (buffer)
- mFormattedImage->setData(buffer, mBufferSize);
- // delete temp data
- delete[] mBuffer; // Note: not 'buffer' (assigned in setData())
- mBuffer = NULL;
- mBufferSize = 0;
- mLoadedDiscard = mRequestedDiscard;
- mState = DECODE_IMAGE;
- if(mWriteToCacheState != NOT_WRITE)
- {
- mWriteToCacheState = SHOULD_WRITE ;
- }
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
- return false;
- }
- else
- {
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- return false;
- }
- }
-
- if (mState == DECODE_IMAGE)
- {
- static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled");
- if(textures_decode_disabled)
- {
- // for debug use, don't decode
- mState = DONE;
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- return true;
- }
-
- if (mDesiredDiscard < 0)
- {
- // We aborted, don't decode
- mState = DONE;
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- return true;
- }
-
- if (mFormattedImage->getDataSize() <= 0)
- {
- //llerrs << "Decode entered with invalid mFormattedImage. ID = " << mID << llendl;
-
- //abort, don't decode
- mState = DONE;
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- return true;
- }
- if (mLoadedDiscard < 0)
- {
- //llerrs << "Decode entered with invalid mLoadedDiscard. ID = " << mID << llendl;
-
- //abort, don't decode
- mState = DONE;
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- return true;
- }
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
- mRawImage = NULL;
- mAuxImage = NULL;
- llassert_always(mFormattedImage.notNull());
- S32 discard = mHaveAllData ? 0 : mLoadedDiscard;
- U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority;
- mDecoded = FALSE;
- mState = DECODE_IMAGE_UPDATE;
- LL_DEBUGS("Texture") << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard
- << " All Data: " << mHaveAllData << LL_ENDL;
- mDecodeHandle = mFetcher->mImageDecodeThread->decodeImage(mFormattedImage, image_priority, discard, mNeedsAux,
- new DecodeResponder(mFetcher, mID, this));
- // fall though
- }
-
- if (mState == DECODE_IMAGE_UPDATE)
- {
- if (mDecoded)
- {
- if (mDecodedDiscard < 0)
- {
- LL_DEBUGS("Texture") << mID << ": Failed to Decode." << LL_ENDL;
- if (mCachedSize > 0 && !mInLocalCache && mRetryAttempt == 0)
- {
- // Cache file should be deleted, try again
-// llwarns << mID << ": Decode of cached file failed (removed), retrying" << llendl;
- llassert_always(mDecodeHandle == 0);
- mFormattedImage = NULL;
- ++mRetryAttempt;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
- mState = INIT;
- return false;
- }
- else
- {
-// llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl;
- mState = DONE; // failed
- }
- }
- else
- {
- llassert_always(mRawImage.notNull());
- LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard
- << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
- mState = WRITE_TO_CACHE;
- }
- // fall through
- }
- else
- {
- return false;
- }
- }
-
- if (mState == WRITE_TO_CACHE)
- {
- if (mWriteToCacheState != SHOULD_WRITE || mFormattedImage.isNull())
- {
- // If we're in a local cache or we didn't actually receive any new data,
- // or we failed to load anything, skip
- mState = DONE;
- return false;
- }
- S32 datasize = mFormattedImage->getDataSize();
- if(mFileSize < datasize)//This could happen when http fetching and sim fetching mixed.
- {
- if(mHaveAllData)
- {
- mFileSize = datasize ;
- }
- else
- {
- mFileSize = datasize + 1 ; //flag not fully loaded.
- }
- }
- llassert_always(datasize);
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
- U32 cache_priority = mWorkPriority;
- mWritten = FALSE;
- mState = WAIT_ON_WRITE;
- CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID);
- mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority,
- mFormattedImage->getData(), datasize,
- mFileSize, responder);
- // fall through
- }
-
- if (mState == WAIT_ON_WRITE)
- {
- if (writeToCacheComplete())
- {
- mState = DONE;
- // fall through
- }
- else
- {
- if (mDesiredDiscard < mDecodedDiscard)
- {
- // We're waiting for this write to complete before we can receive more data
- // (we can't touch mFormattedImage until the write completes)
- // Prioritize the write
- mFetcher->mTextureCache->prioritizeWrite(mCacheWriteHandle);
- }
- return false;
- }
- }
-
- if (mState == DONE)
- {
- if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard)
- {
- // More data was requested, return to INIT
- mState = INIT;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
- return false;
- }
- else
- {
- setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
- return true;
- }
- }
-
- return false;
-}
-
-// Called from MAIN thread
-void LLTextureFetchWorker::endWork(S32 param, bool aborted)
-{
- if (mDecodeHandle != 0)
- {
- mFetcher->mImageDecodeThread->abortRequest(mDecodeHandle, false);
- mDecodeHandle = 0;
- }
- mFormattedImage = NULL;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-// virtual
-void LLTextureFetchWorker::finishWork(S32 param, bool completed)
-{
- // The following are required in case the work was aborted
- if (mCacheReadHandle != LLTextureCache::nullHandle())
- {
- mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
- mCacheReadHandle = LLTextureCache::nullHandle();
- }
- if (mCacheWriteHandle != LLTextureCache::nullHandle())
- {
- mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
- mCacheWriteHandle = LLTextureCache::nullHandle();
- }
-}
-
-// virtual
-bool LLTextureFetchWorker::deleteOK()
-{
- bool delete_ok = true;
- // Allow any pending reads or writes to complete
- if (mCacheReadHandle != LLTextureCache::nullHandle())
- {
- if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true))
- {
- mCacheReadHandle = LLTextureCache::nullHandle();
- }
- else
- {
- delete_ok = false;
- }
- }
- if (mCacheWriteHandle != LLTextureCache::nullHandle())
- {
- if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
- {
- mCacheWriteHandle = LLTextureCache::nullHandle();
- }
- else
- {
- delete_ok = false;
- }
- }
-
- if ((haveWork() &&
- // not ok to delete from these states
- ((mState >= WRITE_TO_CACHE && mState <= WAIT_ON_WRITE))))
- {
- delete_ok = false;
- }
-
- return delete_ok;
-}
-
-void LLTextureFetchWorker::removeFromCache()
-{
- if (!mInLocalCache)
- {
- mFetcher->mTextureCache->removeFromCache(mID);
- }
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
-
-bool LLTextureFetchWorker::processSimulatorPackets()
-{
- if (mFormattedImage.isNull() || mRequestedSize < 0)
- {
- // not sure how we got here, but not a valid state, abort!
- llassert_always(mDecodeHandle == 0);
- mFormattedImage = NULL;
- return true;
- }
-
- if (mLastPacket >= mFirstPacket)
- {
- S32 buffer_size = mFormattedImage->getDataSize();
- for (S32 i = mFirstPacket; i<=mLastPacket; i++)
- {
- llassert_always(mPackets[i]);
- buffer_size += mPackets[i]->mSize;
- }
- bool have_all_data = mLastPacket >= mTotalPackets-1;
- if (mRequestedSize <= 0)
- {
- // We received a packed but haven't requested anything yet (edge case)
- // Return true (we're "done") since we didn't request anything
- return true;
- }
- if (buffer_size >= mRequestedSize || have_all_data)
- {
- /// We have enough (or all) data
- if (have_all_data)
- {
- mHaveAllData = TRUE;
- }
- S32 cur_size = mFormattedImage->getDataSize();
- if (buffer_size > cur_size)
- {
- /// We have new data
- U8* buffer = new U8[buffer_size];
- S32 offset = 0;
- if (cur_size > 0 && mFirstPacket > 0)
- {
- memcpy(buffer, mFormattedImage->getData(), cur_size);
- offset = cur_size;
- }
- for (S32 i=mFirstPacket; i<=mLastPacket; i++)
- {
- memcpy(buffer + offset, mPackets[i]->mData, mPackets[i]->mSize);
- offset += mPackets[i]->mSize;
- }
- // NOTE: setData releases current data
- mFormattedImage->setData(buffer, buffer_size);
- }
- mLoadedDiscard = mRequestedDiscard;
- return true;
- }
- }
- return false;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels,
- const LLIOPipe::buffer_ptr_t& buffer,
- bool partial, bool success)
-{
- S32 data_size = 0 ;
-
- LLMutexLock lock(&mWorkMutex);
-
- if (mState != WAIT_HTTP_REQ)
- {
- llwarns << "callbackHttpGet for unrequested fetch worker: " << mID
- << " req=" << mSentRequest << " state= " << mState << llendl;
- return data_size;
- }
- if (mLoaded)
- {
- llwarns << "Duplicate callback for " << mID.asString() << llendl;
- return data_size ; // ignore duplicate callback
- }
- if (success)
- {
- // get length of stream:
- data_size = buffer->countAfter(channels.in(), NULL);
-
- LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL;
- if (data_size > 0)
- {
- // *TODO: set the formatted image data here directly to avoid the copy
- mBuffer = new U8[data_size];
- buffer->readAfter(channels.in(), NULL, mBuffer, data_size);
- mBufferSize += data_size;
- if (data_size < mRequestedSize && mRequestedDiscard == 0)
- {
- mHaveAllData = TRUE;
- }
- else if (data_size > mRequestedSize)
- {
- // *TODO: This shouldn't be happening any more
- llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl;
- mHaveAllData = TRUE;
- llassert_always(mDecodeHandle == 0);
- mFormattedImage = NULL; // discard any previous data we had
- mBufferSize = data_size;
- }
- }
- else
- {
- // We requested data but received none (and no error),
- // so presumably we have all of it
- mHaveAllData = TRUE;
- }
- mRequestedSize = data_size;
- }
- else
- {
- mRequestedSize = -1; // error
- }
- mLoaded = TRUE;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-
- return data_size ;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image,
- S32 imagesize, BOOL islocal)
-{
- LLMutexLock lock(&mWorkMutex);
- if (mState != LOAD_FROM_TEXTURE_CACHE)
- {
-// llwarns << "Read callback for " << mID << " with state = " << mState << llendl;
- return;
- }
- if (success)
- {
- llassert_always(imagesize >= 0);
- mFileSize = imagesize;
- mFormattedImage = image;
- mImageCodec = image->getCodec();
- mInLocalCache = islocal;
- if (mFileSize != 0 && mFormattedImage->getDataSize() >= mFileSize)
- {
- mHaveAllData = TRUE;
- }
- }
- mLoaded = TRUE;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-}
-
-void LLTextureFetchWorker::callbackCacheWrite(bool success)
-{
- LLMutexLock lock(&mWorkMutex);
- if (mState != WAIT_ON_WRITE)
- {
-// llwarns << "Write callback for " << mID << " with state = " << mState << llendl;
- return;
- }
- mWritten = TRUE;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux)
-{
- LLMutexLock lock(&mWorkMutex);
- if (mDecodeHandle == 0)
- {
- return; // aborted, ignore
- }
- if (mState != DECODE_IMAGE_UPDATE)
- {
-// llwarns << "Decode callback for " << mID << " with state = " << mState << llendl;
- mDecodeHandle = 0;
- return;
- }
- llassert_always(mFormattedImage.notNull());
-
- mDecodeHandle = 0;
- if (success)
- {
- llassert_always(raw);
- mRawImage = raw;
- mAuxImage = aux;
- mDecodedDiscard = mFormattedImage->getDiscardLevel();
- LL_DEBUGS("Texture") << mID << ": Decode Finished. Discard: " << mDecodedDiscard
- << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
- }
- else
- {
- llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl;
- removeFromCache();
- mDecodedDiscard = -1; // Redundant, here for clarity and paranoia
- }
- mDecoded = TRUE;
-// llinfos << mID << " : DECODE COMPLETE " << llendl;
- setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-bool LLTextureFetchWorker::writeToCacheComplete()
-{
- // Complete write to cache
- if (mCacheWriteHandle != LLTextureCache::nullHandle())
- {
- if (!mWritten)
- {
- return false;
- }
- if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
- {
- mCacheWriteHandle = LLTextureCache::nullHandle();
- }
- else
- {
- return false;
- }
- }
- return true;
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-// public
-
-LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode)
- : LLWorkerThread("TextureFetch", threaded),
- mDebugCount(0),
- mDebugPause(FALSE),
- mPacketCount(0),
- mBadPacketCount(0),
- mQueueMutex(getAPRPool()),
- mNetworkQueueMutex(getAPRPool()),
- mTextureCache(cache),
- mImageDecodeThread(imagedecodethread),
- mTextureBandwidth(0),
- mHTTPTextureBits(0),
- mTotalHTTPRequests(0),
- mCurlGetRequest(NULL),
- mQAMode(qa_mode)
-{
- mCurlPOSTRequestCount = 0;
- mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
- mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
-}
-
-LLTextureFetch::~LLTextureFetch()
-{
- clearDeleteList() ;
-
- while (! mCommands.empty())
- {
- TFRequest * req(mCommands.front());
- mCommands.erase(mCommands.begin());
- delete req;
- }
-
- // ~LLQueuedThread() called here
-}
-
-bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority,
- S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux, bool can_use_http)
-{
- if (mDebugPause)
- {
- return false;
- }
-
- LLTextureFetchWorker* worker = getWorker(id) ;
- if (worker)
- {
- if (worker->mHost != host)
- {
- llwarns << "LLTextureFetch::createRequest " << id << " called with multiple hosts: "
- << host << " != " << worker->mHost << llendl;
- removeRequest(worker, true);
- worker = NULL;
- return false;
- }
- }
-
- S32 desired_size;
- std::string exten = gDirUtilp->getExtension(url);
- if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C))
- {
- // Only do partial requests for J2C at the moment
- desired_size = MAX_IMAGE_DATA_SIZE;
- desired_discard = 0;
- }
- else if (desired_discard == 0)
- {
- // if we want the entire image, and we know its size, then get it all
- // (calcDataSizeJ2C() below makes assumptions about how the image
- // was compressed - this code ensures that when we request the entire image,
- // we really do get it.)
- desired_size = MAX_IMAGE_DATA_SIZE;
- }
- else if (w*h*c > 0)
- {
- // If the requester knows the dimensions of the image,
- // this will calculate how much data we need without having to parse the header
-
- desired_size = LLImageJ2C::calcDataSizeJ2C(w, h, c, desired_discard);
- }
- else
- {
- desired_size = TEXTURE_CACHE_ENTRY_SIZE;
- desired_discard = MAX_DISCARD_LEVEL;
- }
-
-
- if (worker)
- {
- if (worker->wasAborted())
- {
- return false; // need to wait for previous aborted request to complete
- }
- worker->lockWorkMutex();
- worker->mActiveCount++;
- worker->mNeedsAux = needs_aux;
- worker->setImagePriority(priority);
- worker->setDesiredDiscard(desired_discard, desired_size);
- worker->setCanUseHTTP(can_use_http) ;
- if (!worker->haveWork())
- {
- worker->mState = LLTextureFetchWorker::INIT;
- worker->unlockWorkMutex();
-
- worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
- }
- else
- {
- worker->unlockWorkMutex();
- }
- }
- else
- {
- worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size);
- lockQueue() ;
- mRequestMap[id] = worker;
- unlockQueue() ;
-
- worker->lockWorkMutex();
- worker->mActiveCount++;
- worker->mNeedsAux = needs_aux;
- worker->setCanUseHTTP(can_use_http) ;
- worker->unlockWorkMutex();
- }
-
-// llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl;
- return true;
-}
-
-// protected
-void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker)
-{
- lockQueue() ;
- bool in_request_map = (mRequestMap.find(worker->mID) != mRequestMap.end()) ;
- unlockQueue() ;
-
- LLMutexLock lock(&mNetworkQueueMutex);
- if (in_request_map)
- {
- // only add to the queue if in the request map
- // i.e. a delete has not been requested
- mNetworkQueue.insert(worker->mID);
- }
- for (cancel_queue_t::iterator iter1 = mCancelQueue.begin();
- iter1 != mCancelQueue.end(); ++iter1)
- {
- iter1->second.erase(worker->mID);
- }
-}
-
-void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel)
-{
- LLMutexLock lock(&mNetworkQueueMutex);
- size_t erased = mNetworkQueue.erase(worker->mID);
- if (cancel && erased > 0)
- {
- mCancelQueue[worker->mHost].insert(worker->mID);
- }
-}
-
-// protected
-void LLTextureFetch::addToHTTPQueue(const LLUUID& id)
-{
- LLMutexLock lock(&mNetworkQueueMutex);
- mHTTPTextureQueue.insert(id);
- mTotalHTTPRequests++;
-}
-
-void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size)
-{
- LLMutexLock lock(&mNetworkQueueMutex);
- mHTTPTextureQueue.erase(id);
- mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits
-}
-
-void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel)
-{
- lockQueue() ;
- LLTextureFetchWorker* worker = getWorkerAfterLock(id);
- if (worker)
- {
- size_t erased_1 = mRequestMap.erase(worker->mID);
- unlockQueue() ;
-
- llassert_always(erased_1 > 0) ;
-
- removeFromNetworkQueue(worker, cancel);
- llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ;
-
- worker->scheduleDelete();
- }
- else
- {
- unlockQueue() ;
- }
-}
-
-void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel)
-{
- lockQueue() ;
- size_t erased_1 = mRequestMap.erase(worker->mID);
- unlockQueue() ;
-
- llassert_always(erased_1 > 0) ;
- removeFromNetworkQueue(worker, cancel);
- llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ;
-
- worker->scheduleDelete();
-}
-
-S32 LLTextureFetch::getNumRequests()
-{
- lockQueue() ;
- S32 size = (S32)mRequestMap.size();
- unlockQueue() ;
-
- return size ;
-}
-
-S32 LLTextureFetch::getNumHTTPRequests()
-{
- mNetworkQueueMutex.lock() ;
- S32 size = (S32)mHTTPTextureQueue.size();
- mNetworkQueueMutex.unlock() ;
-
- return size ;
-}
-
-U32 LLTextureFetch::getTotalNumHTTPRequests()
-{
- mNetworkQueueMutex.lock() ;
- U32 size = mTotalHTTPRequests ;
- mNetworkQueueMutex.unlock() ;
-
- return size ;
-}
-
-// call lockQueue() first!
-LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id)
-{
- LLTextureFetchWorker* res = NULL;
- map_t::iterator iter = mRequestMap.find(id);
- if (iter != mRequestMap.end())
- {
- res = iter->second;
- }
- return res;
-}
-
-LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id)
-{
- LLMutexLock lock(&mQueueMutex) ;
-
- return getWorkerAfterLock(id) ;
-}
-
-
-bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
- LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux)
-{
- bool res = false;
- LLTextureFetchWorker* worker = getWorker(id);
- if (worker)
- {
- if (worker->wasAborted())
- {
- res = true;
- }
- else if (!worker->haveWork())
- {
- // Should only happen if we set mDebugPause...
- if (!mDebugPause)
- {
-// llwarns << "Adding work for inactive worker: " << id << llendl;
- worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
- }
- }
- else if (worker->checkWork())
- {
- worker->lockWorkMutex();
- discard_level = worker->mDecodedDiscard;
- raw = worker->mRawImage;
- aux = worker->mAuxImage;
- res = true;
- LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL;
- worker->unlockWorkMutex();
- }
- else
- {
- worker->lockWorkMutex();
- if ((worker->mDecodedDiscard >= 0) &&
- (worker->mDecodedDiscard < discard_level || discard_level < 0) &&
- (worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE))
- {
- // Not finished, but data is ready
- discard_level = worker->mDecodedDiscard;
- raw = worker->mRawImage;
- aux = worker->mAuxImage;
- }
- worker->unlockWorkMutex();
- }
- }
- else
- {
- res = true;
- }
- return res;
-}
-
-bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority)
-{
- bool res = false;
- LLTextureFetchWorker* worker = getWorker(id);
- if (worker)
- {
- worker->lockWorkMutex();
- worker->setImagePriority(priority);
- worker->unlockWorkMutex();
- res = true;
- }
- return res;
-}
-
-// Replicates and expands upon the base class's
-// getPending() implementation. getPending() and
-// runCondition() replicate one another's logic to
-// an extent and are sometimes used for the same
-// function (deciding whether or not to sleep/pause
-// a thread). So the implementations need to stay
-// in step, at least until this can be refactored and
-// the redundancy eliminated.
-//
-// May be called from any thread
-
-//virtual
-S32 LLTextureFetch::getPending()
-{
- S32 res;
- lockData();
- {
- LLMutexLock lock(&mQueueMutex);
-
- res = mRequestQueue.size();
- res += mCurlPOSTRequestCount;
- res += mCommands.size();
- }
- unlockData();
- return res;
-}
-
-// virtual
-bool LLTextureFetch::runCondition()
-{
- // Caller is holding the lock on LLThread's condition variable.
-
- // LLQueuedThread, unlike its base class LLThread, makes this a
- // private method which is unfortunate. I want to use it directly
- // but I'm going to have to re-implement the logic here (or change
- // declarations, which I don't want to do right now).
- //
- // Changes here may need to be reflected in getPending().
-
- bool have_no_commands(false);
- {
- LLMutexLock lock(&mQueueMutex);
-
- have_no_commands = mCommands.empty();
- }
-
- bool have_no_curl_requests(0 == mCurlPOSTRequestCount);
-
- return ! (have_no_commands
- && have_no_curl_requests
- && (mRequestQueue.empty() && mIdleThread)); // From base class
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs)
-void LLTextureFetch::commonUpdate()
-{
- // Run a cross-thread command, if any.
- cmdDoWork();
-
- // Update Curl on same thread as mCurlGetRequest was constructed
- S32 processed = mCurlGetRequest->process();
- if (processed > 0)
- {
- lldebugs << "processed: " << processed << " messages." << llendl;
- }
-}
-
-
-// MAIN THREAD
-//virtual
-S32 LLTextureFetch::update(U32 max_time_ms)
-{
- static LLCachedControl<F32> band_width(gSavedSettings,"ThrottleBandwidthKBPS");
-
- {
- mNetworkQueueMutex.lock() ;
- mMaxBandwidth = band_width ;
-
- gTextureList.sTextureBits += mHTTPTextureBits ;
- mHTTPTextureBits = 0 ;
-
- mNetworkQueueMutex.unlock() ;
- }
-
- S32 res = LLWorkerThread::update(max_time_ms);
-
- if (!mDebugPause)
- {
- sendRequestListToSimulators();
- }
-
- if (!mThreaded)
- {
- commonUpdate();
- }
-
- return res;
-}
-
-//called in the MAIN thread after the TextureCacheThread shuts down.
-void LLTextureFetch::shutDownTextureCacheThread()
-{
- if(mTextureCache)
- {
- llassert_always(mTextureCache->isQuitting() || mTextureCache->isStopped()) ;
- mTextureCache = NULL ;
- }
-}
-
-//called in the MAIN thread after the ImageDecodeThread shuts down.
-void LLTextureFetch::shutDownImageDecodeThread()
-{
- if(mImageDecodeThread)
- {
- llassert_always(mImageDecodeThread->isQuitting() || mImageDecodeThread->isStopped()) ;
- mImageDecodeThread = NULL ;
- }
-}
-
-// WORKER THREAD
-void LLTextureFetch::startThread()
-{
- // Construct mCurlGetRequest from Worker Thread
- mCurlGetRequest = new LLCurlRequest();
-}
-
-// WORKER THREAD
-void LLTextureFetch::endThread()
-{
- // Destroy mCurlGetRequest from Worker Thread
- delete mCurlGetRequest;
- mCurlGetRequest = NULL;
-}
-
-// WORKER THREAD
-void LLTextureFetch::threadedUpdate()
-{
- llassert_always(mCurlGetRequest);
-
- // Limit update frequency
- const F32 PROCESS_TIME = 0.05f;
- static LLFrameTimer process_timer;
- if (process_timer.getElapsedTimeF32() < PROCESS_TIME)
- {
- return;
- }
- process_timer.reset();
-
- commonUpdate();
-
-#if 0
- const F32 INFO_TIME = 1.0f;
- static LLFrameTimer info_timer;
- if (info_timer.getElapsedTimeF32() >= INFO_TIME)
- {
- S32 q = mCurlGetRequest->getQueued();
- if (q > 0)
- {
- llinfos << "Queued gets: " << q << llendl;
- info_timer.reset();
- }
- }
-#endif
-
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-void LLTextureFetch::sendRequestListToSimulators()
-{
- // All requests
- const F32 REQUEST_DELTA_TIME = 0.10f; // 10 fps
-
- // Sim requests
- const S32 IMAGES_PER_REQUEST = 50;
- const F32 SIM_LAZY_FLUSH_TIMEOUT = 10.0f; // temp
- const F32 MIN_REQUEST_TIME = 1.0f;
- const F32 MIN_DELTA_PRIORITY = 1000.f;
-
- // Periodically, gather the list of textures that need data from the network
- // And send the requests out to the simulators
- static LLFrameTimer timer;
- if (timer.getElapsedTimeF32() < REQUEST_DELTA_TIME)
- {
- return;
- }
- timer.reset();
-
- // Send requests
- typedef std::set<LLTextureFetchWorker*,LLTextureFetchWorker::Compare> request_list_t;
- typedef std::map< LLHost, request_list_t > work_request_map_t;
- work_request_map_t requests;
- {
- LLMutexLock lock2(&mNetworkQueueMutex);
- for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); )
- {
- queue_t::iterator curiter = iter++;
- LLTextureFetchWorker* req = getWorker(*curiter);
- if (!req)
- {
- mNetworkQueue.erase(curiter);
- continue; // paranoia
- }
- if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) &&
- (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR))
- {
- // We already received our URL, remove from the queue
- llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl;
- mNetworkQueue.erase(curiter);
- continue;
- }
- if (req->mID == mDebugID)
- {
- mDebugCount++; // for setting breakpoints
- }
- if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM &&
- req->mTotalPackets > 0 &&
- req->mLastPacket >= req->mTotalPackets-1)
- {
- // We have all the packets... make sure this is high priority
-// req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority);
- continue;
- }
- F32 elapsed = req->mRequestedTimer.getElapsedTimeF32();
- {
- F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority);
- if ((req->mSimRequestedDiscard != req->mDesiredDiscard) ||
- (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) ||
- (elapsed >= SIM_LAZY_FLUSH_TIMEOUT))
- {
- requests[req->mHost].insert(req);
- }
- }
- }
- }
-
- for (work_request_map_t::iterator iter1 = requests.begin();
- iter1 != requests.end(); ++iter1)
- {
- LLHost host = iter1->first;
- // invalid host = use agent host
- if (host == LLHost::invalid)
- {
- host = gAgent.getRegionHost();
- }
-
- S32 sim_request_count = 0;
-
- for (request_list_t::iterator iter2 = iter1->second.begin();
- iter2 != iter1->second.end(); ++iter2)
- {
- LLTextureFetchWorker* req = *iter2;
- if (gMessageSystem)
- {
- if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM)
- {
- // Initialize packet data based on data read from cache
- req->lockWorkMutex();
- req->setupPacketData();
- req->unlockWorkMutex();
- }
- if (0 == sim_request_count)
- {
- gMessageSystem->newMessageFast(_PREHASH_RequestImage);
- gMessageSystem->nextBlockFast(_PREHASH_AgentData);
- gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- }
- S32 packet = req->mLastPacket + 1;
- gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
- gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID);
- gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, (S8)req->mDesiredDiscard);
- gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mImagePriority);
- gMessageSystem->addU32Fast(_PREHASH_Packet, packet);
- gMessageSystem->addU8Fast(_PREHASH_Type, req->mType);
-// llinfos << "IMAGE REQUEST: " << req->mID << " Discard: " << req->mDesiredDiscard
-// << " Packet: " << packet << " Priority: " << req->mImagePriority << llendl;
-
- static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
- static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
- if (log_to_viewer_log || log_to_sim)
- {
- mTextureInfo.setRequestStartTime(req->mID, LLTimer::getTotalTime());
- mTextureInfo.setRequestOffset(req->mID, 0);
- mTextureInfo.setRequestSize(req->mID, 0);
- mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP);
- }
-
- req->lockWorkMutex();
- req->mSentRequest = LLTextureFetchWorker::SENT_SIM;
- req->mSimRequestedDiscard = req->mDesiredDiscard;
- req->mRequestedPriority = req->mImagePriority;
- req->mRequestedTimer.reset();
- req->unlockWorkMutex();
- sim_request_count++;
- if (sim_request_count >= IMAGES_PER_REQUEST)
- {
-// llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
-
- gMessageSystem->sendSemiReliable(host, NULL, NULL);
- sim_request_count = 0;
- }
- }
- }
- if (gMessageSystem && sim_request_count > 0 && sim_request_count < IMAGES_PER_REQUEST)
- {
-// llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
- gMessageSystem->sendSemiReliable(host, NULL, NULL);
- sim_request_count = 0;
- }
- }
-
- // Send cancelations
- {
- LLMutexLock lock2(&mNetworkQueueMutex);
- if (gMessageSystem && !mCancelQueue.empty())
- {
- for (cancel_queue_t::iterator iter1 = mCancelQueue.begin();
- iter1 != mCancelQueue.end(); ++iter1)
- {
- LLHost host = iter1->first;
- if (host == LLHost::invalid)
- {
- host = gAgent.getRegionHost();
- }
- S32 request_count = 0;
- for (queue_t::iterator iter2 = iter1->second.begin();
- iter2 != iter1->second.end(); ++iter2)
- {
- if (0 == request_count)
- {
- gMessageSystem->newMessageFast(_PREHASH_RequestImage);
- gMessageSystem->nextBlockFast(_PREHASH_AgentData);
- gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- }
- gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
- gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2);
- gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1);
- gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0);
- gMessageSystem->addU32Fast(_PREHASH_Packet, 0);
- gMessageSystem->addU8Fast(_PREHASH_Type, 0);
-// llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl;
-
- request_count++;
- if (request_count >= IMAGES_PER_REQUEST)
- {
- gMessageSystem->sendSemiReliable(host, NULL, NULL);
- request_count = 0;
- }
- }
- if (request_count > 0 && request_count < IMAGES_PER_REQUEST)
- {
- gMessageSystem->sendSemiReliable(host, NULL, NULL);
- }
- }
- mCancelQueue.clear();
- }
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size)
-{
- mRequestedTimer.reset();
- if (index >= mTotalPackets)
- {
-// llwarns << "Received Image Packet " << index << " > max: " << mTotalPackets << " for image: " << mID << llendl;
- return false;
- }
- if (index > 0 && index < mTotalPackets-1 && size != MAX_IMG_PACKET_SIZE)
- {
-// llwarns << "Received bad sized packet: " << index << ", " << size << " != " << MAX_IMG_PACKET_SIZE << " for image: " << mID << llendl;
- return false;
- }
-
- if (index >= (S32)mPackets.size())
- {
- mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers
- }
- else if (mPackets[index] != NULL)
- {
-// llwarns << "Received duplicate packet: " << index << " for image: " << mID << llendl;
- return false;
- }
-
- mPackets[index] = new PacketData(data, size);
- while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL)
- {
- ++mLastPacket;
- }
- return true;
-}
-
-bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes,
- U16 data_size, U8* data)
-{
- LLTextureFetchWorker* worker = getWorker(id);
- bool res = true;
-
- ++mPacketCount;
-
- if (!worker)
- {
-// llwarns << "Received header for non active worker: " << id << llendl;
- res = false;
- }
- else if (worker->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK ||
- worker->mSentRequest != LLTextureFetchWorker::SENT_SIM)
- {
-// llwarns << "receiveImageHeader for worker: " << id
-// << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState]
-// << " sent: " << worker->mSentRequest << llendl;
- res = false;
- }
- else if (worker->mLastPacket != -1)
- {
- // check to see if we've gotten this packet before
-// llwarns << "Received duplicate header for: " << id << llendl;
- res = false;
- }
- else if (!data_size)
- {
-// llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl;
- res = false;
- }
- if (!res)
- {
- ++mBadPacketCount;
- mNetworkQueueMutex.lock() ;
- mCancelQueue[host].insert(id);
- mNetworkQueueMutex.unlock() ;
- return false;
- }
-
- worker->lockWorkMutex();
-
- // Copy header data into image object
- worker->mImageCodec = codec;
- worker->mTotalPackets = packets;
- worker->mFileSize = (S32)totalbytes;
- llassert_always(totalbytes > 0);
- llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize);
- res = worker->insertPacket(0, data, data_size);
- worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
- worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
- worker->unlockWorkMutex();
- return res;
-}
-
-bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data)
-{
- LLTextureFetchWorker* worker = getWorker(id);
- bool res = true;
-
- ++mPacketCount;
-
- if (!worker)
- {
-// llwarns << "Received packet " << packet_num << " for non active worker: " << id << llendl;
- res = false;
- }
- else if (worker->mLastPacket == -1)
- {
-// llwarns << "Received packet " << packet_num << " before header for: " << id << llendl;
- res = false;
- }
- else if (!data_size)
- {
-// llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl;
- res = false;
- }
- if (!res)
- {
- ++mBadPacketCount;
- mNetworkQueueMutex.lock() ;
- mCancelQueue[host].insert(id);
- mNetworkQueueMutex.unlock() ;
- return false;
- }
-
- worker->lockWorkMutex();
-
- res = worker->insertPacket(packet_num, data, data_size);
-
- if ((worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) ||
- (worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK))
- {
- worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
- worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
- }
- else
- {
-// llwarns << "receiveImagePacket " << packet_num << "/" << worker->mLastPacket << " for worker: " << id
-// << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl;
- removeFromNetworkQueue(worker, true); // failsafe
- }
-
- if(packet_num >= (worker->mTotalPackets - 1))
- {
- static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
- static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
-
- if (log_to_viewer_log || log_to_sim)
- {
- U64 timeNow = LLTimer::getTotalTime();
- mTextureInfo.setRequestSize(id, worker->mFileSize);
- mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow);
- }
- }
- worker->unlockWorkMutex();
-
- return res;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id)
-{
- BOOL from_cache = FALSE ;
-
- LLTextureFetchWorker* worker = getWorker(id);
- if (worker)
- {
- worker->lockWorkMutex() ;
- from_cache = worker->mInLocalCache ;
- worker->unlockWorkMutex() ;
- }
-
- return from_cache ;
-}
-
-S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p,
- U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http)
-{
- S32 state = LLTextureFetchWorker::INVALID;
- F32 data_progress = 0.0f;
- F32 requested_priority = 0.0f;
- F32 fetch_dtime = 999999.f;
- F32 request_dtime = 999999.f;
- U32 fetch_priority = 0;
-
- LLTextureFetchWorker* worker = getWorker(id);
- if (worker && worker->haveWork())
- {
- worker->lockWorkMutex();
- state = worker->mState;
- fetch_dtime = worker->mFetchTimer.getElapsedTimeF32();
- request_dtime = worker->mRequestedTimer.getElapsedTimeF32();
- if (worker->mFileSize > 0)
- {
- if (state == LLTextureFetchWorker::LOAD_FROM_SIMULATOR)
- {
- S32 data_size = FIRST_PACKET_SIZE + (worker->mLastPacket-1) * MAX_IMG_PACKET_SIZE;
- data_size = llmax(data_size, 0);
- data_progress = (F32)data_size / (F32)worker->mFileSize;
- }
- else if (worker->mFormattedImage.notNull())
- {
- data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize;
- }
- }
- if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::WAIT_HTTP_REQ)
- {
- requested_priority = worker->mRequestedPriority;
- }
- else
- {
- requested_priority = worker->mImagePriority;
- }
- fetch_priority = worker->getPriority();
- can_use_http = worker->getCanUseHTTP() ;
- worker->unlockWorkMutex();
- }
- data_progress_p = data_progress;
- requested_priority_p = requested_priority;
- fetch_priority_p = fetch_priority;
- fetch_dtime_p = fetch_dtime;
- request_dtime_p = request_dtime;
- return state;
-}
-
-void LLTextureFetch::dump()
-{
- llinfos << "LLTextureFetch REQUESTS:" << llendl;
- for (request_queue_t::iterator iter = mRequestQueue.begin();
- iter != mRequestQueue.end(); ++iter)
- {
- LLQueuedThread::QueuedRequest* qreq = *iter;
- LLWorkerThread::WorkRequest* wreq = (LLWorkerThread::WorkRequest*)qreq;
- LLTextureFetchWorker* worker = (LLTextureFetchWorker*)wreq->getWorkerClass();
- llinfos << " ID: " << worker->mID
- << " PRI: " << llformat("0x%08x",wreq->getPriority())
- << " STATE: " << worker->sStateDescs[worker->mState]
- << llendl;
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-// cross-thread command methods
-
-void LLTextureFetch::commandSetRegion(U64 region_handle)
-{
- TFReqSetRegion * req = new TFReqSetRegion(region_handle);
-
- cmdEnqueue(req);
-}
-
-void LLTextureFetch::commandSendMetrics(const std::string & caps_url,
- const LLUUID & session_id,
- const LLUUID & agent_id,
- LLViewerAssetStats * main_stats)
-{
- TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, main_stats);
-
- cmdEnqueue(req);
-}
-
-void LLTextureFetch::commandDataBreak()
-{
- // The pedantically correct way to implement this is to create a command
- // request object in the above fashion and enqueue it. However, this is
- // simple data of an advisorial not operational nature and this case
- // of shared-write access is tolerable.
-
- LLTextureFetch::svMetricsDataBreak = true;
-}
-
-void LLTextureFetch::cmdEnqueue(TFRequest * req)
-{
- lockQueue();
- mCommands.push_back(req);
- unlockQueue();
-
- unpause();
-}
-
-LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue()
-{
- TFRequest * ret = 0;
-
- lockQueue();
- if (! mCommands.empty())
- {
- ret = mCommands.front();
- mCommands.erase(mCommands.begin());
- }
- unlockQueue();
-
- return ret;
-}
-
-void LLTextureFetch::cmdDoWork()
-{
- if (mDebugPause)
- {
- return; // debug: don't do any work
- }
-
- TFRequest * req = cmdDequeue();
- if (req)
- {
- // One request per pass should really be enough for this.
- req->doWork(this);
- delete req;
- }
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
-
-// Private (anonymous) class methods implementing the command scheme.
-
-namespace
-{
-
-/**
- * Implements the 'Set Region' command.
- *
- * Thread: Thread1 (TextureFetch)
- */
-bool
-TFReqSetRegion::doWork(LLTextureFetch *)
-{
- LLViewerAssetStatsFF::set_region_thread1(mRegionHandle);
-
- return true;
-}
-
-
-TFReqSendMetrics::~TFReqSendMetrics()
-{
- delete mMainStats;
- mMainStats = 0;
-}
-
-
-/**
- * Implements the 'Send Metrics' command. Takes over
- * ownership of the passed LLViewerAssetStats pointer.
- *
- * Thread: Thread1 (TextureFetch)
- */
-bool
-TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
-{
- /*
- * HTTP POST responder. Doesn't do much but tries to
- * detect simple breaks in recording the metrics stream.
- *
- * The 'volatile' modifiers don't indicate signals,
- * mmap'd memory or threads, really. They indicate that
- * the referenced data is part of a pseudo-closure for
- * this responder rather than being required for correct
- * operation.
- *
- * We don't try very hard with the POST request. We give
- * it one shot and that's more-or-less it. With a proper
- * refactoring of the LLQueuedThread usage, these POSTs
- * could be put in a request object and made more reliable.
- */
- class lcl_responder : public LLCurl::Responder
- {
- public:
- lcl_responder(LLTextureFetch * fetcher,
- S32 expected_sequence,
- volatile const S32 & live_sequence,
- volatile bool & reporting_break,
- volatile bool & reporting_started)
- : LLCurl::Responder(),
- mFetcher(fetcher),
- mExpectedSequence(expected_sequence),
- mLiveSequence(live_sequence),
- mReportingBreak(reporting_break),
- mReportingStarted(reporting_started)
- {
- mFetcher->incrCurlPOSTCount();
- }
-
- ~lcl_responder()
- {
- mFetcher->decrCurlPOSTCount();
- }
-
- // virtual
- void error(U32 status_num, const std::string & reason)
- {
- if (mLiveSequence == mExpectedSequence)
- {
- mReportingBreak = true;
- }
- LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service. Reason: "
- << reason << LL_ENDL;
- }
-
- // virtual
- void result(const LLSD & content)
- {
- if (mLiveSequence == mExpectedSequence)
- {
- mReportingBreak = false;
- mReportingStarted = true;
- }
- }
-
- private:
- LLTextureFetch * mFetcher;
- S32 mExpectedSequence;
- volatile const S32 & mLiveSequence;
- volatile bool & mReportingBreak;
- volatile bool & mReportingStarted;
-
- }; // class lcl_responder
-
- if (! gViewerAssetStatsThread1)
- return true;
-
- static volatile bool reporting_started(false);
- static volatile S32 report_sequence(0);
-
- // We've taken over ownership of the stats copy at this
- // point. Get a working reference to it for merging here
- // but leave it in 'this'. Destructor will rid us of it.
- LLViewerAssetStats & main_stats = *mMainStats;
-
- // Merge existing stats into those from main, convert to LLSD
- main_stats.merge(*gViewerAssetStatsThread1);
- LLSD merged_llsd = main_stats.asLLSD(true);
-
- // Add some additional meta fields to the content
- merged_llsd["session_id"] = mSessionID;
- merged_llsd["agent_id"] = mAgentID;
- merged_llsd["message"] = "ViewerAssetMetrics"; // Identifies the type of metrics
- merged_llsd["sequence"] = report_sequence; // Sequence number
- merged_llsd["initial"] = ! reporting_started; // Initial data from viewer
- merged_llsd["break"] = LLTextureFetch::svMetricsDataBreak; // Break in data prior to this report
-
- // Update sequence number
- if (S32_MAX == ++report_sequence)
- report_sequence = 0;
-
- // Limit the size of the stats report if necessary.
- merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd);
-
- if (! mCapsURL.empty())
- {
- LLCurlRequest::headers_t headers;
- fetcher->getCurlRequest().post(mCapsURL,
- headers,
- merged_llsd,
- new lcl_responder(fetcher,
- report_sequence,
- report_sequence,
- LLTextureFetch::svMetricsDataBreak,
- reporting_started));
- }
- else
- {
- LLTextureFetch::svMetricsDataBreak = true;
- }
-
- // In QA mode, Metrics submode, log the result for ease of testing
- if (fetcher->isQAMode())
- {
- LL_INFOS("Textures") << merged_llsd << LL_ENDL;
- }
-
- gViewerAssetStatsThread1->reset();
-
- return true;
-}
-
-
-bool
-truncate_viewer_metrics(int max_regions, LLSD & metrics)
-{
- static const LLSD::String reg_tag("regions");
- static const LLSD::String duration_tag("duration");
-
- LLSD & reg_map(metrics[reg_tag]);
- if (reg_map.size() <= max_regions)
- {
- return false;
- }
-
- // Build map of region hashes ordered by duration
- typedef std::multimap<LLSD::Real, int> reg_ordered_list_t;
- reg_ordered_list_t regions_by_duration;
-
- int ind(0);
- LLSD::array_const_iterator it_end(reg_map.endArray());
- for (LLSD::array_const_iterator it(reg_map.beginArray()); it_end != it; ++it, ++ind)
- {
- LLSD::Real duration = (*it)[duration_tag].asReal();
- regions_by_duration.insert(reg_ordered_list_t::value_type(duration, ind));
- }
-
- // Build a replacement regions array with the longest-persistence regions
- LLSD new_region(LLSD::emptyArray());
- reg_ordered_list_t::const_reverse_iterator it2_end(regions_by_duration.rend());
- reg_ordered_list_t::const_reverse_iterator it2(regions_by_duration.rbegin());
- for (int i(0); i < max_regions && it2_end != it2; ++i, ++it2)
- {
- new_region.append(reg_map[it2->second]);
- }
- reg_map = new_region;
-
- return true;
-}
-
-} // end of anonymous namespace
-
-
-
+/**
+ * @file lltexturefetch.cpp
+ * @brief Object which fetches textures from the cache and/or network
+ *
+ * $LicenseInfo:firstyear=2000&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$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <iostream>
+#include <map>
+
+#include "llstl.h"
+
+#include "lltexturefetch.h"
+
+#include "llcurl.h"
+#include "lldir.h"
+#include "llhttpclient.h"
+#include "llhttpstatuscodes.h"
+#include "llimage.h"
+#include "llimagej2c.h"
+#include "llimageworker.h"
+#include "llworkerthread.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "lltexturecache.h"
+#include "llviewercontrol.h"
+#include "llviewertexturelist.h"
+#include "llviewertexture.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerassetstats.h"
+#include "llworld.h"
+
+//////////////////////////////////////////////////////////////////////////////
+class LLTextureFetchWorker : public LLWorkerClass
+{
+ friend class LLTextureFetch;
+ friend class HTTPGetResponder;
+
+private:
+ class CacheReadResponder : public LLTextureCache::ReadResponder
+ {
+ public:
+ CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image)
+ : mFetcher(fetcher), mID(id)
+ {
+ setImage(image);
+ }
+ virtual void completed(bool success)
+ {
+ LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+ if (worker)
+ {
+ worker->callbackCacheRead(success, mFormattedImage, mImageSize, mImageLocal);
+ }
+ }
+ private:
+ LLTextureFetch* mFetcher;
+ LLUUID mID;
+ };
+
+ class CacheWriteResponder : public LLTextureCache::WriteResponder
+ {
+ public:
+ CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id)
+ : mFetcher(fetcher), mID(id)
+ {
+ }
+ virtual void completed(bool success)
+ {
+ LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+ if (worker)
+ {
+ worker->callbackCacheWrite(success);
+ }
+ }
+ private:
+ LLTextureFetch* mFetcher;
+ LLUUID mID;
+ };
+
+ class DecodeResponder : public LLImageDecodeThread::Responder
+ {
+ public:
+ DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker)
+ : mFetcher(fetcher), mID(id), mWorker(worker)
+ {
+ }
+ virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
+ {
+ LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+ if (worker)
+ {
+ worker->callbackDecoded(success, raw, aux);
+ }
+ }
+ private:
+ LLTextureFetch* mFetcher;
+ LLUUID mID;
+ LLTextureFetchWorker* mWorker; // debug only (may get deleted from under us, use mFetcher/mID)
+ };
+
+ struct Compare
+ {
+ // lhs < rhs
+ bool operator()(const LLTextureFetchWorker* lhs, const LLTextureFetchWorker* rhs) const
+ {
+ // greater priority is "less"
+ const F32 lpriority = lhs->mImagePriority;
+ const F32 rpriority = rhs->mImagePriority;
+ if (lpriority > rpriority) // higher priority
+ return true;
+ else if (lpriority < rpriority)
+ return false;
+ else
+ return lhs < rhs;
+ }
+ };
+
+public:
+ /*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest()
+ /*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD)
+ /*virtual*/ bool deleteOK(); // called from update() (WORK THREAD)
+
+ ~LLTextureFetchWorker();
+ // void relese() { --mActiveCount; }
+
+ S32 callbackHttpGet(const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer,
+ bool partial, bool success);
+ void callbackCacheRead(bool success, LLImageFormatted* image,
+ S32 imagesize, BOOL islocal);
+ void callbackCacheWrite(bool success);
+ void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux);
+
+ void setGetStatus(U32 status, const std::string& reason)
+ {
+ LLMutexLock lock(&mWorkMutex);
+
+ mGetStatus = status;
+ mGetReason = reason;
+ }
+
+ void setCanUseHTTP(bool can_use_http) { mCanUseHTTP = can_use_http; }
+ bool getCanUseHTTP() const { return mCanUseHTTP; }
+
+ LLTextureFetch & getFetcher() { return *mFetcher; }
+
+protected:
+ LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host,
+ F32 priority, S32 discard, S32 size);
+
+private:
+ /*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD)
+ /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
+
+ void resetFormattedData();
+
+ void setImagePriority(F32 priority);
+ void setDesiredDiscard(S32 discard, S32 size);
+ bool insertPacket(S32 index, U8* data, S32 size);
+ void clearPackets();
+ void setupPacketData();
+ U32 calcWorkPriority();
+ void removeFromCache();
+ bool processSimulatorPackets();
+ bool writeToCacheComplete();
+
+ void lockWorkMutex() { mWorkMutex.lock(); }
+ void unlockWorkMutex() { mWorkMutex.unlock(); }
+
+private:
+ enum e_state // mState
+ {
+ // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack)
+ INVALID = 0,
+ INIT,
+ LOAD_FROM_TEXTURE_CACHE,
+ CACHE_POST,
+ LOAD_FROM_NETWORK,
+ LOAD_FROM_SIMULATOR,
+ SEND_HTTP_REQ,
+ WAIT_HTTP_REQ,
+ DECODE_IMAGE,
+ DECODE_IMAGE_UPDATE,
+ WRITE_TO_CACHE,
+ WAIT_ON_WRITE,
+ DONE
+ };
+ enum e_request_state // mSentRequest
+ {
+ UNSENT = 0,
+ QUEUED = 1,
+ SENT_SIM = 2
+ };
+ enum e_write_to_cache_state //mWriteToCacheState
+ {
+ NOT_WRITE = 0,
+ CAN_WRITE = 1,
+ SHOULD_WRITE = 2
+ };
+ static const char* sStateDescs[];
+ e_state mState;
+ e_write_to_cache_state mWriteToCacheState;
+ LLTextureFetch* mFetcher;
+ LLPointer<LLImageFormatted> mFormattedImage;
+ LLPointer<LLImageRaw> mRawImage;
+ LLPointer<LLImageRaw> mAuxImage;
+ LLUUID mID;
+ LLHost mHost;
+ std::string mUrl;
+ U8 mType;
+ F32 mImagePriority;
+ U32 mWorkPriority;
+ F32 mRequestedPriority;
+ S32 mDesiredDiscard;
+ S32 mSimRequestedDiscard;
+ S32 mRequestedDiscard;
+ S32 mLoadedDiscard;
+ S32 mDecodedDiscard;
+ LLFrameTimer mRequestedTimer;
+ LLFrameTimer mFetchTimer;
+ LLTextureCache::handle_t mCacheReadHandle;
+ LLTextureCache::handle_t mCacheWriteHandle;
+ U8* mBuffer;
+ S32 mBufferSize;
+ S32 mRequestedSize;
+ S32 mDesiredSize;
+ S32 mFileSize;
+ S32 mCachedSize;
+ e_request_state mSentRequest;
+ handle_t mDecodeHandle;
+ BOOL mLoaded;
+ BOOL mDecoded;
+ BOOL mWritten;
+ BOOL mNeedsAux;
+ BOOL mHaveAllData;
+ BOOL mInLocalCache;
+ bool mCanUseHTTP ;
+ bool mCanUseNET ; //can get from asset server.
+ S32 mHTTPFailCount;
+ S32 mRetryAttempt;
+ S32 mActiveCount;
+ U32 mGetStatus;
+ std::string mGetReason;
+
+ // Work Data
+ LLMutex mWorkMutex;
+ struct PacketData
+ {
+ PacketData(U8* data, S32 size) { mData = data; mSize = size; }
+ ~PacketData() { clearData(); }
+ void clearData() { delete[] mData; mData = NULL; }
+ U8* mData;
+ U32 mSize;
+ };
+ std::vector<PacketData*> mPackets;
+ S32 mFirstPacket;
+ S32 mLastPacket;
+ U16 mTotalPackets;
+ U8 mImageCodec;
+
+ LLViewerAssetStats::duration_t mMetricsStartTime;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class HTTPGetResponder : public LLCurl::Responder
+{
+ LOG_CLASS(HTTPGetResponder);
+public:
+ HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset, bool redir)
+ : mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset), mFollowRedir(redir)
+ {
+ }
+ ~HTTPGetResponder()
+ {
+ }
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
+ static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
+ static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ;
+
+ if (log_to_viewer_log || log_to_sim)
+ {
+ mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime);
+ U64 timeNow = LLTimer::getTotalTime();
+ mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
+ mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize);
+ mFetcher->mTextureInfo.setRequestOffset(mID, mOffset);
+ mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow);
+ }
+
+ lldebugs << "HTTP COMPLETE: " << mID << llendl;
+ LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+ if (worker)
+ {
+ bool success = false;
+ bool partial = false;
+ if (HTTP_OK <= status && status < HTTP_MULTIPLE_CHOICES)
+ {
+ success = true;
+ if (HTTP_PARTIAL_CONTENT == status) // partial information
+ {
+ partial = true;
+ }
+ }
+
+ if (!success)
+ {
+ worker->setGetStatus(status, reason);
+// llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl;
+ }
+
+ S32 data_size = worker->callbackHttpGet(channels, buffer, partial, success);
+
+ if(log_texture_traffic && data_size > 0)
+ {
+ LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ;
+ if(tex)
+ {
+ gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ;
+ }
+ }
+
+ mFetcher->removeFromHTTPQueue(mID, data_size);
+
+ if (worker->mMetricsStartTime)
+ {
+ LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
+ true,
+ LLImageBase::TYPE_AVATAR_BAKE == worker->mType,
+ LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime);
+ worker->mMetricsStartTime = 0;
+ }
+ LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
+ true,
+ LLImageBase::TYPE_AVATAR_BAKE == worker->mType);
+ }
+ else
+ {
+ mFetcher->removeFromHTTPQueue(mID);
+ llwarns << "Worker not found: " << mID << llendl;
+ }
+ }
+
+ virtual bool followRedir()
+ {
+ return mFollowRedir;
+ }
+
+private:
+ LLTextureFetch* mFetcher;
+ LLUUID mID;
+ U64 mStartTime;
+ S32 mRequestedSize;
+ U32 mOffset;
+ bool mFollowRedir;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Cross-thread messaging for asset metrics.
+
+/**
+ * @brief Base class for cross-thread requests made of the fetcher
+ *
+ * I believe the intent of the LLQueuedThread class was to
+ * have these operations derived from LLQueuedThread::QueuedRequest
+ * but the texture fetcher has elected to manage the queue
+ * in its own manner. So these are free-standing objects which are
+ * managed in simple FIFO order on the mCommands queue of the
+ * LLTextureFetch object.
+ *
+ * What each represents is a simple command sent from an
+ * outside thread into the TextureFetch thread to be processed
+ * in order and in a timely fashion (though not an absolute
+ * higher priority than other operations of the thread).
+ * Each operation derives a new class from the base customizing
+ * members, constructors and the doWork() method to effect
+ * the command.
+ *
+ * The flow is one-directional. There are two global instances
+ * of the LLViewerAssetStats collector, one for the main program's
+ * thread pointed to by gViewerAssetStatsMain and one for the
+ * TextureFetch thread pointed to by gViewerAssetStatsThread1.
+ * Common operations has each thread recording metrics events
+ * into the respective collector unconcerned with locking and
+ * the state of any other thread. But when the agent moves into
+ * a different region or the metrics timer expires and a report
+ * needs to be sent back to the grid, messaging across threads
+ * is required to distribute data and perform global actions.
+ * In pseudo-UML, it looks like:
+ *
+ * Main Thread1
+ * . .
+ * . .
+ * +-----+ .
+ * | AM | .
+ * +--+--+ .
+ * +-------+ | .
+ * | Main | +--+--+ .
+ * | | | SRE |---. .
+ * | Stats | +-----+ \ .
+ * | | | \ (uuid) +-----+
+ * | Coll. | +--+--+ `-------->| SR |
+ * +-------+ | MSC | +--+--+
+ * | ^ +-----+ |
+ * | | (uuid) / . +-----+ (uuid)
+ * | `--------' . | MSC |---------.
+ * | . +-----+ |
+ * | +-----+ . v
+ * | | TE | . +-------+
+ * | +--+--+ . | Thd1 |
+ * | | . | |
+ * | +-----+ . | Stats |
+ * `--------->| RSC | . | |
+ * +--+--+ . | Coll. |
+ * | . +-------+
+ * +--+--+ . |
+ * | SME |---. . |
+ * +-----+ \ . |
+ * . \ (clone) +-----+ |
+ * . `-------->| SM | |
+ * . +--+--+ |
+ * . | |
+ * . +-----+ |
+ * . | RSC |<--------'
+ * . +-----+
+ * . |
+ * . +-----+
+ * . | CP |--> HTTP POST
+ * . +-----+
+ * . .
+ * . .
+ *
+ *
+ * Key:
+ *
+ * SRE - Set Region Enqueued. Enqueue a 'Set Region' command in
+ * the other thread providing the new UUID of the region.
+ * TFReqSetRegion carries the data.
+ * SR - Set Region. New region UUID is sent to the thread-local
+ * collector.
+ * SME - Send Metrics Enqueued. Enqueue a 'Send Metrics' command
+ * including an ownership transfer of a cloned LLViewerAssetStats.
+ * TFReqSendMetrics carries the data.
+ * SM - Send Metrics. Global metrics reporting operation. Takes
+ * the cloned stats from the command, merges it with the
+ * thread's local stats, converts to LLSD and sends it on
+ * to the grid.
+ * AM - Agent Moved. Agent has completed some sort of move to a
+ * new region.
+ * TE - Timer Expired. Metrics timer has expired (on the order
+ * of 10 minutes).
+ * CP - CURL Post
+ * MSC - Modify Stats Collector. State change in the thread-local
+ * collector. Typically a region change which affects the
+ * global pointers used to find the 'current stats'.
+ * RSC - Read Stats Collector. Extract collector data cloning it
+ * (i.e. deep copy) when necessary.
+ *
+ */
+class LLTextureFetch::TFRequest // : public LLQueuedThread::QueuedRequest
+{
+public:
+ // Default ctors and assignment operator are correct.
+
+ virtual ~TFRequest()
+ {}
+
+ // Patterned after QueuedRequest's method but expected behavior
+ // is different. Always expected to complete on the first call
+ // and work dispatcher will assume the same and delete the
+ // request after invocation.
+ virtual bool doWork(LLTextureFetch * fetcher) = 0;
+};
+
+namespace
+{
+
+/**
+ * @brief Implements a 'Set Region' cross-thread command.
+ *
+ * When an agent moves to a new region, subsequent metrics need
+ * to be binned into a new or existing stats collection in 1:1
+ * relationship with the region. We communicate this region
+ * change across the threads involved in the communication with
+ * this message.
+ *
+ * Corresponds to LLTextureFetch::commandSetRegion()
+ */
+class TFReqSetRegion : public LLTextureFetch::TFRequest
+{
+public:
+ TFReqSetRegion(U64 region_handle)
+ : LLTextureFetch::TFRequest(),
+ mRegionHandle(region_handle)
+ {}
+ TFReqSetRegion & operator=(const TFReqSetRegion &); // Not defined
+
+ virtual ~TFReqSetRegion()
+ {}
+
+ virtual bool doWork(LLTextureFetch * fetcher);
+
+public:
+ const U64 mRegionHandle;
+};
+
+
+/**
+ * @brief Implements a 'Send Metrics' cross-thread command.
+ *
+ * This is the big operation. The main thread gathers metrics
+ * for a period of minutes into LLViewerAssetStats and other
+ * objects then makes a snapshot of the data by cloning the
+ * collector. This command transfers the clone, along with a few
+ * additional arguments (UUIDs), handing ownership to the
+ * TextureFetch thread. It then merges its own data into the
+ * cloned copy, converts to LLSD and kicks off an HTTP POST of
+ * the resulting data to the currently active metrics collector.
+ *
+ * Corresponds to LLTextureFetch::commandSendMetrics()
+ */
+class TFReqSendMetrics : public LLTextureFetch::TFRequest
+{
+public:
+ /**
+ * Construct the 'Send Metrics' command to have the TextureFetch
+ * thread add and log metrics data.
+ *
+ * @param caps_url URL of a "ViewerMetrics" Caps target
+ * to receive the data. Does not have to
+ * be associated with a particular region.
+ *
+ * @param session_id UUID of the agent's session.
+ *
+ * @param agent_id UUID of the agent. (Being pure here...)
+ *
+ * @param main_stats Pointer to a clone of the main thread's
+ * LLViewerAssetStats data. Thread1 takes
+ * ownership of the copy and disposes of it
+ * when done.
+ */
+ TFReqSendMetrics(const std::string & caps_url,
+ const LLUUID & session_id,
+ const LLUUID & agent_id,
+ LLViewerAssetStats * main_stats)
+ : LLTextureFetch::TFRequest(),
+ mCapsURL(caps_url),
+ mSessionID(session_id),
+ mAgentID(agent_id),
+ mMainStats(main_stats)
+ {}
+ TFReqSendMetrics & operator=(const TFReqSendMetrics &); // Not defined
+
+ virtual ~TFReqSendMetrics();
+
+ virtual bool doWork(LLTextureFetch * fetcher);
+
+public:
+ const std::string mCapsURL;
+ const LLUUID mSessionID;
+ const LLUUID mAgentID;
+ LLViewerAssetStats * mMainStats;
+};
+
+/*
+ * Examines the merged viewer metrics report and if found to be too long,
+ * will attempt to truncate it in some reasonable fashion.
+ *
+ * @param max_regions Limit of regions allowed in report.
+ *
+ * @param metrics Full, merged viewer metrics report.
+ *
+ * @returns If data was truncated, returns true.
+ */
+bool truncate_viewer_metrics(int max_regions, LLSD & metrics);
+
+} // end of anonymous namespace
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+//static
+const char* LLTextureFetchWorker::sStateDescs[] = {
+ "INVALID",
+ "INIT",
+ "LOAD_FROM_TEXTURE_CACHE",
+ "CACHE_POST",
+ "LOAD_FROM_NETWORK",
+ "LOAD_FROM_SIMULATOR",
+ "SEND_HTTP_REQ",
+ "WAIT_HTTP_REQ",
+ "DECODE_IMAGE",
+ "DECODE_IMAGE_UPDATE",
+ "WRITE_TO_CACHE",
+ "WAIT_ON_WRITE",
+ "DONE",
+};
+
+// static
+volatile bool LLTextureFetch::svMetricsDataBreak(true); // Start with a data break
+
+// called from MAIN THREAD
+
+LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
+ const std::string& url, // Optional URL
+ const LLUUID& id, // Image UUID
+ const LLHost& host, // Simulator host
+ F32 priority, // Priority
+ S32 discard, // Desired discard
+ S32 size) // Desired size
+ : LLWorkerClass(fetcher, "TextureFetch"),
+ mState(INIT),
+ mWriteToCacheState(NOT_WRITE),
+ mFetcher(fetcher),
+ mID(id),
+ mHost(host),
+ mUrl(url),
+ mImagePriority(priority),
+ mWorkPriority(0),
+ mRequestedPriority(0.f),
+ mDesiredDiscard(-1),
+ mSimRequestedDiscard(-1),
+ mRequestedDiscard(-1),
+ mLoadedDiscard(-1),
+ mDecodedDiscard(-1),
+ mCacheReadHandle(LLTextureCache::nullHandle()),
+ mCacheWriteHandle(LLTextureCache::nullHandle()),
+ mBuffer(NULL),
+ mBufferSize(0),
+ mRequestedSize(0),
+ mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE),
+ mFileSize(0),
+ mCachedSize(0),
+ mLoaded(FALSE),
+ mSentRequest(UNSENT),
+ mDecodeHandle(0),
+ mDecoded(FALSE),
+ mWritten(FALSE),
+ mNeedsAux(FALSE),
+ mHaveAllData(FALSE),
+ mInLocalCache(FALSE),
+ mCanUseHTTP(true),
+ mHTTPFailCount(0),
+ mRetryAttempt(0),
+ mActiveCount(0),
+ mGetStatus(0),
+ mWorkMutex(NULL),
+ mFirstPacket(0),
+ mLastPacket(-1),
+ mTotalPackets(0),
+ mImageCodec(IMG_CODEC_INVALID),
+ mMetricsStartTime(0)
+{
+ mCanUseNET = mUrl.empty() ;
+
+ calcWorkPriority();
+ mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL;
+// llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl;
+ if (!mFetcher->mDebugPause)
+ {
+ U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
+ addWork(0, work_priority );
+ }
+ setDesiredDiscard(discard, size);
+}
+
+LLTextureFetchWorker::~LLTextureFetchWorker()
+{
+// llinfos << "Destroy: " << mID
+// << " Decoded=" << mDecodedDiscard
+// << " Requested=" << mRequestedDiscard
+// << " Desired=" << mDesiredDiscard << llendl;
+ llassert_always(!haveWork());
+ lockWorkMutex();
+ if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache)
+ {
+ mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
+ }
+ if (mCacheWriteHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache)
+ {
+ mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
+ }
+ mFormattedImage = NULL;
+ clearPackets();
+ unlockWorkMutex();
+ mFetcher->removeFromHTTPQueue(mID);
+}
+
+void LLTextureFetchWorker::clearPackets()
+{
+ for_each(mPackets.begin(), mPackets.end(), DeletePointer());
+ mPackets.clear();
+ mTotalPackets = 0;
+ mLastPacket = -1;
+ mFirstPacket = 0;
+}
+
+void LLTextureFetchWorker::setupPacketData()
+{
+ S32 data_size = 0;
+ if (mFormattedImage.notNull())
+ {
+ data_size = mFormattedImage->getDataSize();
+ }
+ if (data_size > 0)
+ {
+ // Only used for simulator requests
+ mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1;
+ if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size)
+ {
+ llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl;
+ removeFromCache();
+ resetFormattedData();
+ clearPackets();
+ }
+ else if (mFileSize > 0)
+ {
+ mLastPacket = mFirstPacket-1;
+ mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1;
+ }
+ else
+ {
+ // This file was cached using HTTP so we have to refetch the first packet
+ resetFormattedData();
+ clearPackets();
+ }
+ }
+}
+
+U32 LLTextureFetchWorker::calcWorkPriority()
+{
+ //llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerFetchedTexture::maxDecodePriority());
+ static const F32 PRIORITY_SCALE = (F32)LLWorkerThread::PRIORITY_LOWBITS / LLViewerFetchedTexture::maxDecodePriority();
+
+ mWorkPriority = llmin((U32)LLWorkerThread::PRIORITY_LOWBITS, (U32)(mImagePriority * PRIORITY_SCALE));
+ return mWorkPriority;
+}
+
+// mWorkMutex is locked
+void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size)
+{
+ bool prioritize = false;
+ if (mDesiredDiscard != discard)
+ {
+ if (!haveWork())
+ {
+ calcWorkPriority();
+ if (!mFetcher->mDebugPause)
+ {
+ U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
+ addWork(0, work_priority);
+ }
+ }
+ else if (mDesiredDiscard < discard)
+ {
+ prioritize = true;
+ }
+ mDesiredDiscard = discard;
+ mDesiredSize = size;
+ }
+ else if (size > mDesiredSize)
+ {
+ mDesiredSize = size;
+ prioritize = true;
+ }
+ mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE);
+ if ((prioritize && mState == INIT) || mState == DONE)
+ {
+ mState = INIT;
+ U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
+ setPriority(work_priority);
+ }
+}
+
+void LLTextureFetchWorker::setImagePriority(F32 priority)
+{
+// llassert_always(priority >= 0 && priority <= LLViewerTexture::maxDecodePriority());
+ F32 delta = fabs(priority - mImagePriority);
+ if (delta > (mImagePriority * .05f) || mState == DONE)
+ {
+ mImagePriority = priority;
+ calcWorkPriority();
+ U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS);
+ setPriority(work_priority);
+ }
+}
+
+void LLTextureFetchWorker::resetFormattedData()
+{
+ delete[] mBuffer;
+ mBuffer = NULL;
+ mBufferSize = 0;
+ if (mFormattedImage.notNull())
+ {
+ mFormattedImage->deleteData();
+ }
+ mHaveAllData = FALSE;
+}
+
+// Called from MAIN thread
+void LLTextureFetchWorker::startWork(S32 param)
+{
+ llassert(mFormattedImage.isNull());
+}
+
+#include "llviewertexturelist.h" // debug
+
+// Called from LLWorkerThread::processRequest()
+bool LLTextureFetchWorker::doWork(S32 param)
+{
+ LLMutexLock lock(&mWorkMutex);
+
+ if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED)))
+ {
+ if (mState < DECODE_IMAGE)
+ {
+ return true; // abort
+ }
+ }
+
+ if(mImagePriority < F_ALMOST_ZERO)
+ {
+ if (mState == INIT || mState == LOAD_FROM_NETWORK || mState == LOAD_FROM_SIMULATOR)
+ {
+ return true; // abort
+ }
+ }
+ if(mState > CACHE_POST && !mCanUseNET && !mCanUseHTTP)
+ {
+ //nowhere to get data, abort.
+ return true ;
+ }
+
+ if (mFetcher->mDebugPause)
+ {
+ return false; // debug: don't do any work
+ }
+ if (mID == mFetcher->mDebugID)
+ {
+ mFetcher->mDebugCount++; // for setting breakpoints
+ }
+
+ if (mState != DONE)
+ {
+ mFetchTimer.reset();
+ }
+
+ if (mState == INIT)
+ {
+ mRawImage = NULL ;
+ mRequestedDiscard = -1;
+ mLoadedDiscard = -1;
+ mDecodedDiscard = -1;
+ mRequestedSize = 0;
+ mFileSize = 0;
+ mCachedSize = 0;
+ mLoaded = FALSE;
+ mSentRequest = UNSENT;
+ mDecoded = FALSE;
+ mWritten = FALSE;
+ delete[] mBuffer;
+ mBuffer = NULL;
+ mBufferSize = 0;
+ mHaveAllData = FALSE;
+ clearPackets(); // TODO: Shouldn't be necessary
+ mCacheReadHandle = LLTextureCache::nullHandle();
+ mCacheWriteHandle = LLTextureCache::nullHandle();
+ mState = LOAD_FROM_TEXTURE_CACHE;
+ mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE
+ LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority)
+ << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
+ // fall through
+ }
+
+ if (mState == LOAD_FROM_TEXTURE_CACHE)
+ {
+ if (mCacheReadHandle == LLTextureCache::nullHandle())
+ {
+ U32 cache_priority = mWorkPriority;
+ S32 offset = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
+ S32 size = mDesiredSize - offset;
+ if (size <= 0)
+ {
+ mState = CACHE_POST;
+ return false;
+ }
+ mFileSize = 0;
+ mLoaded = FALSE;
+
+ if (mUrl.compare(0, 7, "file://") == 0)
+ {
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
+
+ // read file from local disk
+ std::string filename = mUrl.substr(7, std::string::npos);
+ CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
+ mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority,
+ offset, size, responder);
+ }
+ else if (mUrl.empty())
+ {
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
+
+ CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
+ mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
+ offset, size, responder);
+ }
+ else if(mCanUseHTTP)
+ {
+ if (!(mUrl.compare(0, 7, "http://") == 0))
+ {
+ // *TODO:?remove this warning
+ llwarns << "Unknown URL Type: " << mUrl << llendl;
+ }
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ mState = SEND_HTTP_REQ;
+ }
+ else
+ {
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ mState = LOAD_FROM_NETWORK;
+ }
+ }
+
+ if (mLoaded)
+ {
+ // Make sure request is complete. *TODO: make this auto-complete
+ if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, false))
+ {
+ mCacheReadHandle = LLTextureCache::nullHandle();
+ mState = CACHE_POST;
+ // fall through
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if (mState == CACHE_POST)
+ {
+ mCachedSize = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
+ // Successfully loaded
+ if ((mCachedSize >= mDesiredSize) || mHaveAllData)
+ {
+ // we have enough data, decode it
+ llassert_always(mFormattedImage->getDataSize() > 0);
+ mLoadedDiscard = mDesiredDiscard;
+ mState = DECODE_IMAGE;
+ mWriteToCacheState = NOT_WRITE ;
+ LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize()
+ << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight())
+ << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
+ // fall through
+ }
+ else
+ {
+ if (mUrl.compare(0, 7, "file://") == 0)
+ {
+ // failed to load local file, we're done.
+ return true;
+ }
+ // need more data
+ else
+ {
+ LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL;
+ mState = LOAD_FROM_NETWORK;
+ }
+ // fall through
+ }
+ }
+
+ if (mState == LOAD_FROM_NETWORK)
+ {
+ static LLCachedControl<bool> use_http(gSavedSettings,"ImagePipelineUseHTTP");
+
+// if (mHost != LLHost::invalid) get_url = false;
+ if ( use_http && mCanUseHTTP && mUrl.empty())//get http url.
+ {
+ LLViewerRegion* region = NULL;
+ if (mHost == LLHost::invalid)
+ region = gAgent.getRegion();
+ else
+ region = LLWorld::getInstance()->getRegion(mHost);
+
+ if (region)
+ {
+ std::string http_url = region->getHttpUrl() ;
+ if (!http_url.empty())
+ {
+ mUrl = http_url + "/?texture_id=" + mID.asString().c_str();
+ mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id.
+ }
+ else
+ {
+ mCanUseHTTP = false ;
+ }
+ }
+ else
+ {
+ // This will happen if not logged in or if a region deoes not have HTTP Texture enabled
+ //llwarns << "Region not found for host: " << mHost << llendl;
+ mCanUseHTTP = false;
+ }
+ }
+ if (mCanUseHTTP && !mUrl.empty())
+ {
+ mState = LLTextureFetchWorker::SEND_HTTP_REQ;
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ if(mWriteToCacheState != NOT_WRITE)
+ {
+ mWriteToCacheState = CAN_WRITE ;
+ }
+ // don't return, fall through to next state
+ }
+ else if (mSentRequest == UNSENT && mCanUseNET)
+ {
+ // Add this to the network queue and sit here.
+ // LLTextureFetch::update() will send off a request which will change our state
+ mWriteToCacheState = CAN_WRITE ;
+ mRequestedSize = mDesiredSize;
+ mRequestedDiscard = mDesiredDiscard;
+ mSentRequest = QUEUED;
+ mFetcher->addToNetworkQueue(this);
+ if (! mMetricsStartTime)
+ {
+ mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+ }
+ LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
+ false,
+ LLImageBase::TYPE_AVATAR_BAKE == mType);
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+
+ return false;
+ }
+ else
+ {
+ // Shouldn't need to do anything here
+ //llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end());
+ // Make certain this is in the network queue
+ //mFetcher->addToNetworkQueue(this);
+ //if (! mMetricsStartTime)
+ //{
+ // mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+ //}
+ //LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false,
+ // LLImageBase::TYPE_AVATAR_BAKE == mType);
+ //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ return false;
+ }
+ }
+
+ if (mState == LOAD_FROM_SIMULATOR)
+ {
+ if (mFormattedImage.isNull())
+ {
+ mFormattedImage = new LLImageJ2C;
+ }
+ if (processSimulatorPackets())
+ {
+ LL_DEBUGS("Texture") << mID << ": Loaded from Sim. Bytes: " << mFormattedImage->getDataSize() << LL_ENDL;
+ mFetcher->removeFromNetworkQueue(this, false);
+ if (mFormattedImage.isNull() || !mFormattedImage->getDataSize())
+ {
+ // processSimulatorPackets() failed
+// llwarns << "processSimulatorPackets() failed to load buffer" << llendl;
+ return true; // failed
+ }
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ mState = DECODE_IMAGE;
+ mWriteToCacheState = SHOULD_WRITE;
+
+ if (mMetricsStartTime)
+ {
+ LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
+ false,
+ LLImageBase::TYPE_AVATAR_BAKE == mType,
+ LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime);
+ mMetricsStartTime = 0;
+ }
+ LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
+ false,
+ LLImageBase::TYPE_AVATAR_BAKE == mType);
+ }
+ else
+ {
+ mFetcher->addToNetworkQueue(this); // failsafe
+ if (! mMetricsStartTime)
+ {
+ mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+ }
+ LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
+ false,
+ LLImageBase::TYPE_AVATAR_BAKE == mType);
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ }
+ return false;
+ }
+
+ if (mState == SEND_HTTP_REQ)
+ {
+ if(mCanUseHTTP)
+ {
+ //NOTE:
+ //control the number of the http requests issued for:
+ //1, not openning too many file descriptors at the same time;
+ //2, control the traffic of http so udp gets bandwidth.
+ //
+ static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 8 ;
+ if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE)
+ {
+ return false ; //wait.
+ }
+
+ mFetcher->removeFromNetworkQueue(this, false);
+
+ S32 cur_size = 0;
+ if (mFormattedImage.notNull())
+ {
+ cur_size = mFormattedImage->getDataSize(); // amount of data we already have
+ if (mFormattedImage->getDiscardLevel() == 0)
+ {
+ if(cur_size > 0)
+ {
+ // We already have all the data, just decode it
+ mLoadedDiscard = mFormattedImage->getDiscardLevel();
+ mState = DECODE_IMAGE;
+ return false;
+ }
+ else
+ {
+ return true ; //abort.
+ }
+ }
+ }
+ mRequestedSize = mDesiredSize;
+ mRequestedDiscard = mDesiredDiscard;
+ mRequestedSize -= cur_size;
+ S32 offset = cur_size;
+ mBufferSize = cur_size; // This will get modified by callbackHttpGet()
+
+ bool res = false;
+ if (!mUrl.empty())
+ {
+ mLoaded = FALSE;
+ mGetStatus = 0;
+ mGetReason.clear();
+ LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << offset
+ << " Bytes: " << mRequestedSize
+ << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth
+ << LL_ENDL;
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ mState = WAIT_HTTP_REQ;
+
+ mFetcher->addToHTTPQueue(mID);
+ if (! mMetricsStartTime)
+ {
+ mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+ }
+ LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
+ true,
+ LLImageBase::TYPE_AVATAR_BAKE == mType);
+
+ // Will call callbackHttpGet when curl request completes
+ std::vector<std::string> headers;
+ headers.push_back("Accept: image/x-j2c");
+ res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize,
+ new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true));
+ }
+ if (!res)
+ {
+ llwarns << "HTTP GET request failed for " << mID << llendl;
+ resetFormattedData();
+ ++mHTTPFailCount;
+ return true; // failed
+ }
+ // fall through
+ }
+ else //can not use http fetch.
+ {
+ return true ; //abort
+ }
+ }
+
+ if (mState == WAIT_HTTP_REQ)
+ {
+ if (mLoaded)
+ {
+ S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
+ if (mRequestedSize < 0)
+ {
+ S32 max_attempts;
+ if (mGetStatus == HTTP_NOT_FOUND)
+ {
+ mHTTPFailCount = max_attempts = 1; // Don't retry
+ llwarns << "Texture missing from server (404): " << mUrl << llendl;
+
+ //roll back to try UDP
+ if(mCanUseNET)
+ {
+ mState = INIT ;
+ mCanUseHTTP = false ;
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ return false ;
+ }
+ }
+ else if (mGetStatus == HTTP_SERVICE_UNAVAILABLE)
+ {
+ // *TODO: Should probably introduce a timer here to delay future HTTP requsts
+ // for a short time (~1s) to ease server load? Ideally the server would queue
+ // requests instead of returning 503... we already limit the number pending.
+ ++mHTTPFailCount;
+ max_attempts = mHTTPFailCount+1; // Keep retrying
+ LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL;
+ }
+ else
+ {
+ const S32 HTTP_MAX_RETRY_COUNT = 3;
+ max_attempts = HTTP_MAX_RETRY_COUNT + 1;
+ ++mHTTPFailCount;
+ llinfos << "HTTP GET failed for: " << mUrl
+ << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'"
+ << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl;
+ }
+
+ if (mHTTPFailCount >= max_attempts)
+ {
+ if (cur_size > 0)
+ {
+ // Use available data
+ mLoadedDiscard = mFormattedImage->getDiscardLevel();
+ mState = DECODE_IMAGE;
+ return false;
+ }
+ else
+ {
+ resetFormattedData();
+ mState = DONE;
+ return true; // failed
+ }
+ }
+ else
+ {
+ mState = SEND_HTTP_REQ;
+ return false; // retry
+ }
+ }
+
+ llassert_always(mBufferSize == cur_size + mRequestedSize);
+ if(!mBufferSize)//no data received.
+ {
+ delete[] mBuffer;
+ mBuffer = NULL;
+
+ //abort.
+ mState = DONE;
+ return true;
+ }
+
+ if (mFormattedImage.isNull())
+ {
+ // For now, create formatted image based on extension
+ std::string extension = gDirUtilp->getExtension(mUrl);
+ mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension));
+ if (mFormattedImage.isNull())
+ {
+ mFormattedImage = new LLImageJ2C; // default
+ }
+ }
+
+ if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded.
+ {
+ mFileSize = mBufferSize;
+ }
+ else //the file size is unknown.
+ {
+ mFileSize = mBufferSize + 1 ; //flag the file is not fully loaded.
+ }
+
+ U8* buffer = new U8[mBufferSize];
+ if (cur_size > 0)
+ {
+ memcpy(buffer, mFormattedImage->getData(), cur_size);
+ }
+ memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append
+ // NOTE: setData releases current data and owns new data (buffer)
+ mFormattedImage->setData(buffer, mBufferSize);
+ // delete temp data
+ delete[] mBuffer; // Note: not 'buffer' (assigned in setData())
+ mBuffer = NULL;
+ mBufferSize = 0;
+ mLoadedDiscard = mRequestedDiscard;
+ mState = DECODE_IMAGE;
+ if(mWriteToCacheState != NOT_WRITE)
+ {
+ mWriteToCacheState = SHOULD_WRITE ;
+ }
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ return false;
+ }
+ else
+ {
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ return false;
+ }
+ }
+
+ if (mState == DECODE_IMAGE)
+ {
+ static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled");
+ if(textures_decode_disabled)
+ {
+ // for debug use, don't decode
+ mState = DONE;
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ return true;
+ }
+
+ if (mDesiredDiscard < 0)
+ {
+ // We aborted, don't decode
+ mState = DONE;
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ return true;
+ }
+
+ if (mFormattedImage->getDataSize() <= 0)
+ {
+ //llerrs << "Decode entered with invalid mFormattedImage. ID = " << mID << llendl;
+
+ //abort, don't decode
+ mState = DONE;
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ return true;
+ }
+ if (mLoadedDiscard < 0)
+ {
+ //llerrs << "Decode entered with invalid mLoadedDiscard. ID = " << mID << llendl;
+
+ //abort, don't decode
+ mState = DONE;
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ return true;
+ }
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
+ mRawImage = NULL;
+ mAuxImage = NULL;
+ llassert_always(mFormattedImage.notNull());
+ S32 discard = mHaveAllData ? 0 : mLoadedDiscard;
+ U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority;
+ mDecoded = FALSE;
+ mState = DECODE_IMAGE_UPDATE;
+ LL_DEBUGS("Texture") << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard
+ << " All Data: " << mHaveAllData << LL_ENDL;
+ mDecodeHandle = mFetcher->mImageDecodeThread->decodeImage(mFormattedImage, image_priority, discard, mNeedsAux,
+ new DecodeResponder(mFetcher, mID, this));
+ // fall though
+ }
+
+ if (mState == DECODE_IMAGE_UPDATE)
+ {
+ if (mDecoded)
+ {
+ if (mDecodedDiscard < 0)
+ {
+ LL_DEBUGS("Texture") << mID << ": Failed to Decode." << LL_ENDL;
+ if (mCachedSize > 0 && !mInLocalCache && mRetryAttempt == 0)
+ {
+ // Cache file should be deleted, try again
+// llwarns << mID << ": Decode of cached file failed (removed), retrying" << llendl;
+ llassert_always(mDecodeHandle == 0);
+ mFormattedImage = NULL;
+ ++mRetryAttempt;
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ mState = INIT;
+ return false;
+ }
+ else
+ {
+// llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl;
+ mState = DONE; // failed
+ }
+ }
+ else
+ {
+ llassert_always(mRawImage.notNull());
+ LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard
+ << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ mState = WRITE_TO_CACHE;
+ }
+ // fall through
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if (mState == WRITE_TO_CACHE)
+ {
+ if (mWriteToCacheState != SHOULD_WRITE || mFormattedImage.isNull())
+ {
+ // If we're in a local cache or we didn't actually receive any new data,
+ // or we failed to load anything, skip
+ mState = DONE;
+ return false;
+ }
+ S32 datasize = mFormattedImage->getDataSize();
+ if(mFileSize < datasize)//This could happen when http fetching and sim fetching mixed.
+ {
+ if(mHaveAllData)
+ {
+ mFileSize = datasize ;
+ }
+ else
+ {
+ mFileSize = datasize + 1 ; //flag not fully loaded.
+ }
+ }
+ llassert_always(datasize);
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
+ U32 cache_priority = mWorkPriority;
+ mWritten = FALSE;
+ mState = WAIT_ON_WRITE;
+ CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID);
+ mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority,
+ mFormattedImage->getData(), datasize,
+ mFileSize, responder);
+ // fall through
+ }
+
+ if (mState == WAIT_ON_WRITE)
+ {
+ if (writeToCacheComplete())
+ {
+ mState = DONE;
+ // fall through
+ }
+ else
+ {
+ if (mDesiredDiscard < mDecodedDiscard)
+ {
+ // We're waiting for this write to complete before we can receive more data
+ // (we can't touch mFormattedImage until the write completes)
+ // Prioritize the write
+ mFetcher->mTextureCache->prioritizeWrite(mCacheWriteHandle);
+ }
+ return false;
+ }
+ }
+
+ if (mState == DONE)
+ {
+ if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard)
+ {
+ // More data was requested, return to INIT
+ mState = INIT;
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+ return false;
+ }
+ else
+ {
+ setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Called from MAIN thread
+void LLTextureFetchWorker::endWork(S32 param, bool aborted)
+{
+ if (mDecodeHandle != 0)
+ {
+ mFetcher->mImageDecodeThread->abortRequest(mDecodeHandle, false);
+ mDecodeHandle = 0;
+ }
+ mFormattedImage = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// virtual
+void LLTextureFetchWorker::finishWork(S32 param, bool completed)
+{
+ // The following are required in case the work was aborted
+ if (mCacheReadHandle != LLTextureCache::nullHandle())
+ {
+ mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
+ mCacheReadHandle = LLTextureCache::nullHandle();
+ }
+ if (mCacheWriteHandle != LLTextureCache::nullHandle())
+ {
+ mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
+ mCacheWriteHandle = LLTextureCache::nullHandle();
+ }
+}
+
+// virtual
+bool LLTextureFetchWorker::deleteOK()
+{
+ bool delete_ok = true;
+ // Allow any pending reads or writes to complete
+ if (mCacheReadHandle != LLTextureCache::nullHandle())
+ {
+ if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true))
+ {
+ mCacheReadHandle = LLTextureCache::nullHandle();
+ }
+ else
+ {
+ delete_ok = false;
+ }
+ }
+ if (mCacheWriteHandle != LLTextureCache::nullHandle())
+ {
+ if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
+ {
+ mCacheWriteHandle = LLTextureCache::nullHandle();
+ }
+ else
+ {
+ delete_ok = false;
+ }
+ }
+
+ if ((haveWork() &&
+ // not ok to delete from these states
+ ((mState >= WRITE_TO_CACHE && mState <= WAIT_ON_WRITE))))
+ {
+ delete_ok = false;
+ }
+
+ return delete_ok;
+}
+
+void LLTextureFetchWorker::removeFromCache()
+{
+ if (!mInLocalCache)
+ {
+ mFetcher->mTextureCache->removeFromCache(mID);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::processSimulatorPackets()
+{
+ if (mFormattedImage.isNull() || mRequestedSize < 0)
+ {
+ // not sure how we got here, but not a valid state, abort!
+ llassert_always(mDecodeHandle == 0);
+ mFormattedImage = NULL;
+ return true;
+ }
+
+ if (mLastPacket >= mFirstPacket)
+ {
+ S32 buffer_size = mFormattedImage->getDataSize();
+ for (S32 i = mFirstPacket; i<=mLastPacket; i++)
+ {
+ llassert_always(mPackets[i]);
+ buffer_size += mPackets[i]->mSize;
+ }
+ bool have_all_data = mLastPacket >= mTotalPackets-1;
+ if (mRequestedSize <= 0)
+ {
+ // We received a packed but haven't requested anything yet (edge case)
+ // Return true (we're "done") since we didn't request anything
+ return true;
+ }
+ if (buffer_size >= mRequestedSize || have_all_data)
+ {
+ /// We have enough (or all) data
+ if (have_all_data)
+ {
+ mHaveAllData = TRUE;
+ }
+ S32 cur_size = mFormattedImage->getDataSize();
+ if (buffer_size > cur_size)
+ {
+ /// We have new data
+ U8* buffer = new U8[buffer_size];
+ S32 offset = 0;
+ if (cur_size > 0 && mFirstPacket > 0)
+ {
+ memcpy(buffer, mFormattedImage->getData(), cur_size);
+ offset = cur_size;
+ }
+ for (S32 i=mFirstPacket; i<=mLastPacket; i++)
+ {
+ memcpy(buffer + offset, mPackets[i]->mData, mPackets[i]->mSize);
+ offset += mPackets[i]->mSize;
+ }
+ // NOTE: setData releases current data
+ mFormattedImage->setData(buffer, buffer_size);
+ }
+ mLoadedDiscard = mRequestedDiscard;
+ return true;
+ }
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer,
+ bool partial, bool success)
+{
+ S32 data_size = 0 ;
+
+ LLMutexLock lock(&mWorkMutex);
+
+ if (mState != WAIT_HTTP_REQ)
+ {
+ llwarns << "callbackHttpGet for unrequested fetch worker: " << mID
+ << " req=" << mSentRequest << " state= " << mState << llendl;
+ return data_size;
+ }
+ if (mLoaded)
+ {
+ llwarns << "Duplicate callback for " << mID.asString() << llendl;
+ return data_size ; // ignore duplicate callback
+ }
+ if (success)
+ {
+ // get length of stream:
+ data_size = buffer->countAfter(channels.in(), NULL);
+
+ LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL;
+ if (data_size > 0)
+ {
+ // *TODO: set the formatted image data here directly to avoid the copy
+ mBuffer = new U8[data_size];
+ buffer->readAfter(channels.in(), NULL, mBuffer, data_size);
+ mBufferSize += data_size;
+ if (data_size < mRequestedSize && mRequestedDiscard == 0)
+ {
+ mHaveAllData = TRUE;
+ }
+ else if (data_size > mRequestedSize)
+ {
+ // *TODO: This shouldn't be happening any more
+ llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl;
+ mHaveAllData = TRUE;
+ llassert_always(mDecodeHandle == 0);
+ mFormattedImage = NULL; // discard any previous data we had
+ mBufferSize = data_size;
+ }
+ }
+ else
+ {
+ // We requested data but received none (and no error),
+ // so presumably we have all of it
+ mHaveAllData = TRUE;
+ }
+ mRequestedSize = data_size;
+ }
+ else
+ {
+ mRequestedSize = -1; // error
+ }
+ mLoaded = TRUE;
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+
+ return data_size ;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image,
+ S32 imagesize, BOOL islocal)
+{
+ LLMutexLock lock(&mWorkMutex);
+ if (mState != LOAD_FROM_TEXTURE_CACHE)
+ {
+// llwarns << "Read callback for " << mID << " with state = " << mState << llendl;
+ return;
+ }
+ if (success)
+ {
+ llassert_always(imagesize >= 0);
+ mFileSize = imagesize;
+ mFormattedImage = image;
+ mImageCodec = image->getCodec();
+ mInLocalCache = islocal;
+ if (mFileSize != 0 && mFormattedImage->getDataSize() >= mFileSize)
+ {
+ mHaveAllData = TRUE;
+ }
+ }
+ mLoaded = TRUE;
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+}
+
+void LLTextureFetchWorker::callbackCacheWrite(bool success)
+{
+ LLMutexLock lock(&mWorkMutex);
+ if (mState != WAIT_ON_WRITE)
+ {
+// llwarns << "Write callback for " << mID << " with state = " << mState << llendl;
+ return;
+ }
+ mWritten = TRUE;
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux)
+{
+ LLMutexLock lock(&mWorkMutex);
+ if (mDecodeHandle == 0)
+ {
+ return; // aborted, ignore
+ }
+ if (mState != DECODE_IMAGE_UPDATE)
+ {
+// llwarns << "Decode callback for " << mID << " with state = " << mState << llendl;
+ mDecodeHandle = 0;
+ return;
+ }
+ llassert_always(mFormattedImage.notNull());
+
+ mDecodeHandle = 0;
+ if (success)
+ {
+ llassert_always(raw);
+ mRawImage = raw;
+ mAuxImage = aux;
+ mDecodedDiscard = mFormattedImage->getDiscardLevel();
+ LL_DEBUGS("Texture") << mID << ": Decode Finished. Discard: " << mDecodedDiscard
+ << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
+ }
+ else
+ {
+ llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl;
+ removeFromCache();
+ mDecodedDiscard = -1; // Redundant, here for clarity and paranoia
+ }
+ mDecoded = TRUE;
+// llinfos << mID << " : DECODE COMPLETE " << llendl;
+ setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::writeToCacheComplete()
+{
+ // Complete write to cache
+ if (mCacheWriteHandle != LLTextureCache::nullHandle())
+ {
+ if (!mWritten)
+ {
+ return false;
+ }
+ if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
+ {
+ mCacheWriteHandle = LLTextureCache::nullHandle();
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+// public
+
+LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode)
+ : LLWorkerThread("TextureFetch", threaded),
+ mDebugCount(0),
+ mDebugPause(FALSE),
+ mPacketCount(0),
+ mBadPacketCount(0),
+ mQueueMutex(getAPRPool()),
+ mNetworkQueueMutex(getAPRPool()),
+ mTextureCache(cache),
+ mImageDecodeThread(imagedecodethread),
+ mTextureBandwidth(0),
+ mHTTPTextureBits(0),
+ mTotalHTTPRequests(0),
+ mCurlGetRequest(NULL),
+ mQAMode(qa_mode)
+{
+ mCurlPOSTRequestCount = 0;
+ mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
+ mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
+}
+
+LLTextureFetch::~LLTextureFetch()
+{
+ clearDeleteList() ;
+
+ while (! mCommands.empty())
+ {
+ TFRequest * req(mCommands.front());
+ mCommands.erase(mCommands.begin());
+ delete req;
+ }
+
+ // ~LLQueuedThread() called here
+}
+
+bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority,
+ S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux, bool can_use_http)
+{
+ if (mDebugPause)
+ {
+ return false;
+ }
+
+ LLTextureFetchWorker* worker = getWorker(id) ;
+ if (worker)
+ {
+ if (worker->mHost != host)
+ {
+ llwarns << "LLTextureFetch::createRequest " << id << " called with multiple hosts: "
+ << host << " != " << worker->mHost << llendl;
+ removeRequest(worker, true);
+ worker = NULL;
+ return false;
+ }
+ }
+
+ S32 desired_size;
+ std::string exten = gDirUtilp->getExtension(url);
+ if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C))
+ {
+ // Only do partial requests for J2C at the moment
+ desired_size = MAX_IMAGE_DATA_SIZE;
+ desired_discard = 0;
+ }
+ else if (desired_discard == 0)
+ {
+ // if we want the entire image, and we know its size, then get it all
+ // (calcDataSizeJ2C() below makes assumptions about how the image
+ // was compressed - this code ensures that when we request the entire image,
+ // we really do get it.)
+ desired_size = MAX_IMAGE_DATA_SIZE;
+ }
+ else if (w*h*c > 0)
+ {
+ // If the requester knows the dimensions of the image,
+ // this will calculate how much data we need without having to parse the header
+
+ desired_size = LLImageJ2C::calcDataSizeJ2C(w, h, c, desired_discard);
+ }
+ else
+ {
+ desired_size = TEXTURE_CACHE_ENTRY_SIZE;
+ desired_discard = MAX_DISCARD_LEVEL;
+ }
+
+
+ if (worker)
+ {
+ if (worker->wasAborted())
+ {
+ return false; // need to wait for previous aborted request to complete
+ }
+ worker->lockWorkMutex();
+ worker->mActiveCount++;
+ worker->mNeedsAux = needs_aux;
+ worker->setImagePriority(priority);
+ worker->setDesiredDiscard(desired_discard, desired_size);
+ worker->setCanUseHTTP(can_use_http) ;
+ if (!worker->haveWork())
+ {
+ worker->mState = LLTextureFetchWorker::INIT;
+ worker->unlockWorkMutex();
+
+ worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
+ }
+ else
+ {
+ worker->unlockWorkMutex();
+ }
+ }
+ else
+ {
+ worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size);
+ lockQueue() ;
+ mRequestMap[id] = worker;
+ unlockQueue() ;
+
+ worker->lockWorkMutex();
+ worker->mActiveCount++;
+ worker->mNeedsAux = needs_aux;
+ worker->setCanUseHTTP(can_use_http) ;
+ worker->unlockWorkMutex();
+ }
+
+// llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl;
+ return true;
+}
+
+// protected
+void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker)
+{
+ lockQueue() ;
+ bool in_request_map = (mRequestMap.find(worker->mID) != mRequestMap.end()) ;
+ unlockQueue() ;
+
+ LLMutexLock lock(&mNetworkQueueMutex);
+ if (in_request_map)
+ {
+ // only add to the queue if in the request map
+ // i.e. a delete has not been requested
+ mNetworkQueue.insert(worker->mID);
+ }
+ for (cancel_queue_t::iterator iter1 = mCancelQueue.begin();
+ iter1 != mCancelQueue.end(); ++iter1)
+ {
+ iter1->second.erase(worker->mID);
+ }
+}
+
+void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel)
+{
+ LLMutexLock lock(&mNetworkQueueMutex);
+ size_t erased = mNetworkQueue.erase(worker->mID);
+ if (cancel && erased > 0)
+ {
+ mCancelQueue[worker->mHost].insert(worker->mID);
+ }
+}
+
+// protected
+void LLTextureFetch::addToHTTPQueue(const LLUUID& id)
+{
+ LLMutexLock lock(&mNetworkQueueMutex);
+ mHTTPTextureQueue.insert(id);
+ mTotalHTTPRequests++;
+}
+
+void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size)
+{
+ LLMutexLock lock(&mNetworkQueueMutex);
+ mHTTPTextureQueue.erase(id);
+ mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits
+}
+
+void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel)
+{
+ lockQueue() ;
+ LLTextureFetchWorker* worker = getWorkerAfterLock(id);
+ if (worker)
+ {
+ size_t erased_1 = mRequestMap.erase(worker->mID);
+ unlockQueue() ;
+
+ llassert_always(erased_1 > 0) ;
+
+ removeFromNetworkQueue(worker, cancel);
+ llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ;
+
+ worker->scheduleDelete();
+ }
+ else
+ {
+ unlockQueue() ;
+ }
+}
+
+void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel)
+{
+ lockQueue() ;
+ size_t erased_1 = mRequestMap.erase(worker->mID);
+ unlockQueue() ;
+
+ llassert_always(erased_1 > 0) ;
+ removeFromNetworkQueue(worker, cancel);
+ llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ;
+
+ worker->scheduleDelete();
+}
+
+S32 LLTextureFetch::getNumRequests()
+{
+ lockQueue() ;
+ S32 size = (S32)mRequestMap.size();
+ unlockQueue() ;
+
+ return size ;
+}
+
+S32 LLTextureFetch::getNumHTTPRequests()
+{
+ mNetworkQueueMutex.lock() ;
+ S32 size = (S32)mHTTPTextureQueue.size();
+ mNetworkQueueMutex.unlock() ;
+
+ return size ;
+}
+
+U32 LLTextureFetch::getTotalNumHTTPRequests()
+{
+ mNetworkQueueMutex.lock() ;
+ U32 size = mTotalHTTPRequests ;
+ mNetworkQueueMutex.unlock() ;
+
+ return size ;
+}
+
+// call lockQueue() first!
+LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id)
+{
+ LLTextureFetchWorker* res = NULL;
+ map_t::iterator iter = mRequestMap.find(id);
+ if (iter != mRequestMap.end())
+ {
+ res = iter->second;
+ }
+ return res;
+}
+
+LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id)
+{
+ LLMutexLock lock(&mQueueMutex) ;
+
+ return getWorkerAfterLock(id) ;
+}
+
+
+bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
+ LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux)
+{
+ bool res = false;
+ LLTextureFetchWorker* worker = getWorker(id);
+ if (worker)
+ {
+ if (worker->wasAborted())
+ {
+ res = true;
+ }
+ else if (!worker->haveWork())
+ {
+ // Should only happen if we set mDebugPause...
+ if (!mDebugPause)
+ {
+// llwarns << "Adding work for inactive worker: " << id << llendl;
+ worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
+ }
+ }
+ else if (worker->checkWork())
+ {
+ worker->lockWorkMutex();
+ discard_level = worker->mDecodedDiscard;
+ raw = worker->mRawImage;
+ aux = worker->mAuxImage;
+ res = true;
+ LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL;
+ worker->unlockWorkMutex();
+ }
+ else
+ {
+ worker->lockWorkMutex();
+ if ((worker->mDecodedDiscard >= 0) &&
+ (worker->mDecodedDiscard < discard_level || discard_level < 0) &&
+ (worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE))
+ {
+ // Not finished, but data is ready
+ discard_level = worker->mDecodedDiscard;
+ raw = worker->mRawImage;
+ aux = worker->mAuxImage;
+ }
+ worker->unlockWorkMutex();
+ }
+ }
+ else
+ {
+ res = true;
+ }
+ return res;
+}
+
+bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority)
+{
+ bool res = false;
+ LLTextureFetchWorker* worker = getWorker(id);
+ if (worker)
+ {
+ worker->lockWorkMutex();
+ worker->setImagePriority(priority);
+ worker->unlockWorkMutex();
+ res = true;
+ }
+ return res;
+}
+
+// Replicates and expands upon the base class's
+// getPending() implementation. getPending() and
+// runCondition() replicate one another's logic to
+// an extent and are sometimes used for the same
+// function (deciding whether or not to sleep/pause
+// a thread). So the implementations need to stay
+// in step, at least until this can be refactored and
+// the redundancy eliminated.
+//
+// May be called from any thread
+
+//virtual
+S32 LLTextureFetch::getPending()
+{
+ S32 res;
+ lockData();
+ {
+ LLMutexLock lock(&mQueueMutex);
+
+ res = mRequestQueue.size();
+ res += mCurlPOSTRequestCount;
+ res += mCommands.size();
+ }
+ unlockData();
+ return res;
+}
+
+// virtual
+bool LLTextureFetch::runCondition()
+{
+ // Caller is holding the lock on LLThread's condition variable.
+
+ // LLQueuedThread, unlike its base class LLThread, makes this a
+ // private method which is unfortunate. I want to use it directly
+ // but I'm going to have to re-implement the logic here (or change
+ // declarations, which I don't want to do right now).
+ //
+ // Changes here may need to be reflected in getPending().
+
+ bool have_no_commands(false);
+ {
+ LLMutexLock lock(&mQueueMutex);
+
+ have_no_commands = mCommands.empty();
+ }
+
+ bool have_no_curl_requests(0 == mCurlPOSTRequestCount);
+
+ return ! (have_no_commands
+ && have_no_curl_requests
+ && (mRequestQueue.empty() && mIdleThread)); // From base class
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs)
+void LLTextureFetch::commonUpdate()
+{
+ // Run a cross-thread command, if any.
+ cmdDoWork();
+
+ // Update Curl on same thread as mCurlGetRequest was constructed
+ S32 processed = mCurlGetRequest->process();
+ if (processed > 0)
+ {
+ lldebugs << "processed: " << processed << " messages." << llendl;
+ }
+}
+
+
+// MAIN THREAD
+//virtual
+S32 LLTextureFetch::update(U32 max_time_ms)
+{
+ static LLCachedControl<F32> band_width(gSavedSettings,"ThrottleBandwidthKBPS");
+
+ {
+ mNetworkQueueMutex.lock() ;
+ mMaxBandwidth = band_width ;
+
+ gTextureList.sTextureBits += mHTTPTextureBits ;
+ mHTTPTextureBits = 0 ;
+
+ mNetworkQueueMutex.unlock() ;
+ }
+
+ S32 res = LLWorkerThread::update(max_time_ms);
+
+ if (!mDebugPause)
+ {
+ sendRequestListToSimulators();
+ }
+
+ if (!mThreaded)
+ {
+ commonUpdate();
+ }
+
+ return res;
+}
+
+//called in the MAIN thread after the TextureCacheThread shuts down.
+void LLTextureFetch::shutDownTextureCacheThread()
+{
+ if(mTextureCache)
+ {
+ llassert_always(mTextureCache->isQuitting() || mTextureCache->isStopped()) ;
+ mTextureCache = NULL ;
+ }
+}
+
+//called in the MAIN thread after the ImageDecodeThread shuts down.
+void LLTextureFetch::shutDownImageDecodeThread()
+{
+ if(mImageDecodeThread)
+ {
+ llassert_always(mImageDecodeThread->isQuitting() || mImageDecodeThread->isStopped()) ;
+ mImageDecodeThread = NULL ;
+ }
+}
+
+// WORKER THREAD
+void LLTextureFetch::startThread()
+{
+ // Construct mCurlGetRequest from Worker Thread
+ mCurlGetRequest = new LLCurlRequest();
+}
+
+// WORKER THREAD
+void LLTextureFetch::endThread()
+{
+ // Destroy mCurlGetRequest from Worker Thread
+ delete mCurlGetRequest;
+ mCurlGetRequest = NULL;
+}
+
+// WORKER THREAD
+void LLTextureFetch::threadedUpdate()
+{
+ llassert_always(mCurlGetRequest);
+
+ // Limit update frequency
+ const F32 PROCESS_TIME = 0.05f;
+ static LLFrameTimer process_timer;
+ if (process_timer.getElapsedTimeF32() < PROCESS_TIME)
+ {
+ return;
+ }
+ process_timer.reset();
+
+ commonUpdate();
+
+#if 0
+ const F32 INFO_TIME = 1.0f;
+ static LLFrameTimer info_timer;
+ if (info_timer.getElapsedTimeF32() >= INFO_TIME)
+ {
+ S32 q = mCurlGetRequest->getQueued();
+ if (q > 0)
+ {
+ llinfos << "Queued gets: " << q << llendl;
+ info_timer.reset();
+ }
+ }
+#endif
+
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetch::sendRequestListToSimulators()
+{
+ // All requests
+ const F32 REQUEST_DELTA_TIME = 0.10f; // 10 fps
+
+ // Sim requests
+ const S32 IMAGES_PER_REQUEST = 50;
+ const F32 SIM_LAZY_FLUSH_TIMEOUT = 10.0f; // temp
+ const F32 MIN_REQUEST_TIME = 1.0f;
+ const F32 MIN_DELTA_PRIORITY = 1000.f;
+
+ // Periodically, gather the list of textures that need data from the network
+ // And send the requests out to the simulators
+ static LLFrameTimer timer;
+ if (timer.getElapsedTimeF32() < REQUEST_DELTA_TIME)
+ {
+ return;
+ }
+ timer.reset();
+
+ // Send requests
+ typedef std::set<LLTextureFetchWorker*,LLTextureFetchWorker::Compare> request_list_t;
+ typedef std::map< LLHost, request_list_t > work_request_map_t;
+ work_request_map_t requests;
+ {
+ LLMutexLock lock2(&mNetworkQueueMutex);
+ for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); )
+ {
+ queue_t::iterator curiter = iter++;
+ LLTextureFetchWorker* req = getWorker(*curiter);
+ if (!req)
+ {
+ mNetworkQueue.erase(curiter);
+ continue; // paranoia
+ }
+ if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) &&
+ (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR))
+ {
+ // We already received our URL, remove from the queue
+ llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl;
+ mNetworkQueue.erase(curiter);
+ continue;
+ }
+ if (req->mID == mDebugID)
+ {
+ mDebugCount++; // for setting breakpoints
+ }
+ if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM &&
+ req->mTotalPackets > 0 &&
+ req->mLastPacket >= req->mTotalPackets-1)
+ {
+ // We have all the packets... make sure this is high priority
+// req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority);
+ continue;
+ }
+ F32 elapsed = req->mRequestedTimer.getElapsedTimeF32();
+ {
+ F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority);
+ if ((req->mSimRequestedDiscard != req->mDesiredDiscard) ||
+ (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) ||
+ (elapsed >= SIM_LAZY_FLUSH_TIMEOUT))
+ {
+ requests[req->mHost].insert(req);
+ }
+ }
+ }
+ }
+
+ for (work_request_map_t::iterator iter1 = requests.begin();
+ iter1 != requests.end(); ++iter1)
+ {
+ LLHost host = iter1->first;
+ // invalid host = use agent host
+ if (host == LLHost::invalid)
+ {
+ host = gAgent.getRegionHost();
+ }
+
+ S32 sim_request_count = 0;
+
+ for (request_list_t::iterator iter2 = iter1->second.begin();
+ iter2 != iter1->second.end(); ++iter2)
+ {
+ LLTextureFetchWorker* req = *iter2;
+ if (gMessageSystem)
+ {
+ if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM)
+ {
+ // Initialize packet data based on data read from cache
+ req->lockWorkMutex();
+ req->setupPacketData();
+ req->unlockWorkMutex();
+ }
+ if (0 == sim_request_count)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_RequestImage);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ }
+ S32 packet = req->mLastPacket + 1;
+ gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
+ gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID);
+ gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, (S8)req->mDesiredDiscard);
+ gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mImagePriority);
+ gMessageSystem->addU32Fast(_PREHASH_Packet, packet);
+ gMessageSystem->addU8Fast(_PREHASH_Type, req->mType);
+// llinfos << "IMAGE REQUEST: " << req->mID << " Discard: " << req->mDesiredDiscard
+// << " Packet: " << packet << " Priority: " << req->mImagePriority << llendl;
+
+ static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
+ static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
+ if (log_to_viewer_log || log_to_sim)
+ {
+ mTextureInfo.setRequestStartTime(req->mID, LLTimer::getTotalTime());
+ mTextureInfo.setRequestOffset(req->mID, 0);
+ mTextureInfo.setRequestSize(req->mID, 0);
+ mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP);
+ }
+
+ req->lockWorkMutex();
+ req->mSentRequest = LLTextureFetchWorker::SENT_SIM;
+ req->mSimRequestedDiscard = req->mDesiredDiscard;
+ req->mRequestedPriority = req->mImagePriority;
+ req->mRequestedTimer.reset();
+ req->unlockWorkMutex();
+ sim_request_count++;
+ if (sim_request_count >= IMAGES_PER_REQUEST)
+ {
+// llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
+
+ gMessageSystem->sendSemiReliable(host, NULL, NULL);
+ sim_request_count = 0;
+ }
+ }
+ }
+ if (gMessageSystem && sim_request_count > 0 && sim_request_count < IMAGES_PER_REQUEST)
+ {
+// llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
+ gMessageSystem->sendSemiReliable(host, NULL, NULL);
+ sim_request_count = 0;
+ }
+ }
+
+ // Send cancelations
+ {
+ LLMutexLock lock2(&mNetworkQueueMutex);
+ if (gMessageSystem && !mCancelQueue.empty())
+ {
+ for (cancel_queue_t::iterator iter1 = mCancelQueue.begin();
+ iter1 != mCancelQueue.end(); ++iter1)
+ {
+ LLHost host = iter1->first;
+ if (host == LLHost::invalid)
+ {
+ host = gAgent.getRegionHost();
+ }
+ S32 request_count = 0;
+ for (queue_t::iterator iter2 = iter1->second.begin();
+ iter2 != iter1->second.end(); ++iter2)
+ {
+ if (0 == request_count)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_RequestImage);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ }
+ gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
+ gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2);
+ gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1);
+ gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0);
+ gMessageSystem->addU32Fast(_PREHASH_Packet, 0);
+ gMessageSystem->addU8Fast(_PREHASH_Type, 0);
+// llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl;
+
+ request_count++;
+ if (request_count >= IMAGES_PER_REQUEST)
+ {
+ gMessageSystem->sendSemiReliable(host, NULL, NULL);
+ request_count = 0;
+ }
+ }
+ if (request_count > 0 && request_count < IMAGES_PER_REQUEST)
+ {
+ gMessageSystem->sendSemiReliable(host, NULL, NULL);
+ }
+ }
+ mCancelQueue.clear();
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size)
+{
+ mRequestedTimer.reset();
+ if (index >= mTotalPackets)
+ {
+// llwarns << "Received Image Packet " << index << " > max: " << mTotalPackets << " for image: " << mID << llendl;
+ return false;
+ }
+ if (index > 0 && index < mTotalPackets-1 && size != MAX_IMG_PACKET_SIZE)
+ {
+// llwarns << "Received bad sized packet: " << index << ", " << size << " != " << MAX_IMG_PACKET_SIZE << " for image: " << mID << llendl;
+ return false;
+ }
+
+ if (index >= (S32)mPackets.size())
+ {
+ mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers
+ }
+ else if (mPackets[index] != NULL)
+ {
+// llwarns << "Received duplicate packet: " << index << " for image: " << mID << llendl;
+ return false;
+ }
+
+ mPackets[index] = new PacketData(data, size);
+ while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL)
+ {
+ ++mLastPacket;
+ }
+ return true;
+}
+
+bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes,
+ U16 data_size, U8* data)
+{
+ LLTextureFetchWorker* worker = getWorker(id);
+ bool res = true;
+
+ ++mPacketCount;
+
+ if (!worker)
+ {
+// llwarns << "Received header for non active worker: " << id << llendl;
+ res = false;
+ }
+ else if (worker->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK ||
+ worker->mSentRequest != LLTextureFetchWorker::SENT_SIM)
+ {
+// llwarns << "receiveImageHeader for worker: " << id
+// << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState]
+// << " sent: " << worker->mSentRequest << llendl;
+ res = false;
+ }
+ else if (worker->mLastPacket != -1)
+ {
+ // check to see if we've gotten this packet before
+// llwarns << "Received duplicate header for: " << id << llendl;
+ res = false;
+ }
+ else if (!data_size)
+ {
+// llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl;
+ res = false;
+ }
+ if (!res)
+ {
+ ++mBadPacketCount;
+ mNetworkQueueMutex.lock() ;
+ mCancelQueue[host].insert(id);
+ mNetworkQueueMutex.unlock() ;
+ return false;
+ }
+
+ worker->lockWorkMutex();
+
+ // Copy header data into image object
+ worker->mImageCodec = codec;
+ worker->mTotalPackets = packets;
+ worker->mFileSize = (S32)totalbytes;
+ llassert_always(totalbytes > 0);
+ llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize);
+ res = worker->insertPacket(0, data, data_size);
+ worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
+ worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
+ worker->unlockWorkMutex();
+ return res;
+}
+
+bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data)
+{
+ LLTextureFetchWorker* worker = getWorker(id);
+ bool res = true;
+
+ ++mPacketCount;
+
+ if (!worker)
+ {
+// llwarns << "Received packet " << packet_num << " for non active worker: " << id << llendl;
+ res = false;
+ }
+ else if (worker->mLastPacket == -1)
+ {
+// llwarns << "Received packet " << packet_num << " before header for: " << id << llendl;
+ res = false;
+ }
+ else if (!data_size)
+ {
+// llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl;
+ res = false;
+ }
+ if (!res)
+ {
+ ++mBadPacketCount;
+ mNetworkQueueMutex.lock() ;
+ mCancelQueue[host].insert(id);
+ mNetworkQueueMutex.unlock() ;
+ return false;
+ }
+
+ worker->lockWorkMutex();
+
+ res = worker->insertPacket(packet_num, data, data_size);
+
+ if ((worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) ||
+ (worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK))
+ {
+ worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
+ worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
+ }
+ else
+ {
+// llwarns << "receiveImagePacket " << packet_num << "/" << worker->mLastPacket << " for worker: " << id
+// << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl;
+ removeFromNetworkQueue(worker, true); // failsafe
+ }
+
+ if(packet_num >= (worker->mTotalPackets - 1))
+ {
+ static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
+ static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
+
+ if (log_to_viewer_log || log_to_sim)
+ {
+ U64 timeNow = LLTimer::getTotalTime();
+ mTextureInfo.setRequestSize(id, worker->mFileSize);
+ mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow);
+ }
+ }
+ worker->unlockWorkMutex();
+
+ return res;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id)
+{
+ BOOL from_cache = FALSE ;
+
+ LLTextureFetchWorker* worker = getWorker(id);
+ if (worker)
+ {
+ worker->lockWorkMutex() ;
+ from_cache = worker->mInLocalCache ;
+ worker->unlockWorkMutex() ;
+ }
+
+ return from_cache ;
+}
+
+S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p,
+ U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http)
+{
+ S32 state = LLTextureFetchWorker::INVALID;
+ F32 data_progress = 0.0f;
+ F32 requested_priority = 0.0f;
+ F32 fetch_dtime = 999999.f;
+ F32 request_dtime = 999999.f;
+ U32 fetch_priority = 0;
+
+ LLTextureFetchWorker* worker = getWorker(id);
+ if (worker && worker->haveWork())
+ {
+ worker->lockWorkMutex();
+ state = worker->mState;
+ fetch_dtime = worker->mFetchTimer.getElapsedTimeF32();
+ request_dtime = worker->mRequestedTimer.getElapsedTimeF32();
+ if (worker->mFileSize > 0)
+ {
+ if (state == LLTextureFetchWorker::LOAD_FROM_SIMULATOR)
+ {
+ S32 data_size = FIRST_PACKET_SIZE + (worker->mLastPacket-1) * MAX_IMG_PACKET_SIZE;
+ data_size = llmax(data_size, 0);
+ data_progress = (F32)data_size / (F32)worker->mFileSize;
+ }
+ else if (worker->mFormattedImage.notNull())
+ {
+ data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize;
+ }
+ }
+ if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::WAIT_HTTP_REQ)
+ {
+ requested_priority = worker->mRequestedPriority;
+ }
+ else
+ {
+ requested_priority = worker->mImagePriority;
+ }
+ fetch_priority = worker->getPriority();
+ can_use_http = worker->getCanUseHTTP() ;
+ worker->unlockWorkMutex();
+ }
+ data_progress_p = data_progress;
+ requested_priority_p = requested_priority;
+ fetch_priority_p = fetch_priority;
+ fetch_dtime_p = fetch_dtime;
+ request_dtime_p = request_dtime;
+ return state;
+}
+
+void LLTextureFetch::dump()
+{
+ llinfos << "LLTextureFetch REQUESTS:" << llendl;
+ for (request_queue_t::iterator iter = mRequestQueue.begin();
+ iter != mRequestQueue.end(); ++iter)
+ {
+ LLQueuedThread::QueuedRequest* qreq = *iter;
+ LLWorkerThread::WorkRequest* wreq = (LLWorkerThread::WorkRequest*)qreq;
+ LLTextureFetchWorker* worker = (LLTextureFetchWorker*)wreq->getWorkerClass();
+ llinfos << " ID: " << worker->mID
+ << " PRI: " << llformat("0x%08x",wreq->getPriority())
+ << " STATE: " << worker->sStateDescs[worker->mState]
+ << llendl;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// cross-thread command methods
+
+void LLTextureFetch::commandSetRegion(U64 region_handle)
+{
+ TFReqSetRegion * req = new TFReqSetRegion(region_handle);
+
+ cmdEnqueue(req);
+}
+
+void LLTextureFetch::commandSendMetrics(const std::string & caps_url,
+ const LLUUID & session_id,
+ const LLUUID & agent_id,
+ LLViewerAssetStats * main_stats)
+{
+ TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, main_stats);
+
+ cmdEnqueue(req);
+}
+
+void LLTextureFetch::commandDataBreak()
+{
+ // The pedantically correct way to implement this is to create a command
+ // request object in the above fashion and enqueue it. However, this is
+ // simple data of an advisorial not operational nature and this case
+ // of shared-write access is tolerable.
+
+ LLTextureFetch::svMetricsDataBreak = true;
+}
+
+void LLTextureFetch::cmdEnqueue(TFRequest * req)
+{
+ lockQueue();
+ mCommands.push_back(req);
+ unlockQueue();
+
+ unpause();
+}
+
+LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue()
+{
+ TFRequest * ret = 0;
+
+ lockQueue();
+ if (! mCommands.empty())
+ {
+ ret = mCommands.front();
+ mCommands.erase(mCommands.begin());
+ }
+ unlockQueue();
+
+ return ret;
+}
+
+void LLTextureFetch::cmdDoWork()
+{
+ if (mDebugPause)
+ {
+ return; // debug: don't do any work
+ }
+
+ TFRequest * req = cmdDequeue();
+ if (req)
+ {
+ // One request per pass should really be enough for this.
+ req->doWork(this);
+ delete req;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Private (anonymous) class methods implementing the command scheme.
+
+namespace
+{
+
+/**
+ * Implements the 'Set Region' command.
+ *
+ * Thread: Thread1 (TextureFetch)
+ */
+bool
+TFReqSetRegion::doWork(LLTextureFetch *)
+{
+ LLViewerAssetStatsFF::set_region_thread1(mRegionHandle);
+
+ return true;
+}
+
+
+TFReqSendMetrics::~TFReqSendMetrics()
+{
+ delete mMainStats;
+ mMainStats = 0;
+}
+
+
+/**
+ * Implements the 'Send Metrics' command. Takes over
+ * ownership of the passed LLViewerAssetStats pointer.
+ *
+ * Thread: Thread1 (TextureFetch)
+ */
+bool
+TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
+{
+ /*
+ * HTTP POST responder. Doesn't do much but tries to
+ * detect simple breaks in recording the metrics stream.
+ *
+ * The 'volatile' modifiers don't indicate signals,
+ * mmap'd memory or threads, really. They indicate that
+ * the referenced data is part of a pseudo-closure for
+ * this responder rather than being required for correct
+ * operation.
+ *
+ * We don't try very hard with the POST request. We give
+ * it one shot and that's more-or-less it. With a proper
+ * refactoring of the LLQueuedThread usage, these POSTs
+ * could be put in a request object and made more reliable.
+ */
+ class lcl_responder : public LLCurl::Responder
+ {
+ public:
+ lcl_responder(LLTextureFetch * fetcher,
+ S32 expected_sequence,
+ volatile const S32 & live_sequence,
+ volatile bool & reporting_break,
+ volatile bool & reporting_started)
+ : LLCurl::Responder(),
+ mFetcher(fetcher),
+ mExpectedSequence(expected_sequence),
+ mLiveSequence(live_sequence),
+ mReportingBreak(reporting_break),
+ mReportingStarted(reporting_started)
+ {
+ mFetcher->incrCurlPOSTCount();
+ }
+
+ ~lcl_responder()
+ {
+ mFetcher->decrCurlPOSTCount();
+ }
+
+ // virtual
+ void error(U32 status_num, const std::string & reason)
+ {
+ if (mLiveSequence == mExpectedSequence)
+ {
+ mReportingBreak = true;
+ }
+ LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service. Reason: "
+ << reason << LL_ENDL;
+ }
+
+ // virtual
+ void result(const LLSD & content)
+ {
+ if (mLiveSequence == mExpectedSequence)
+ {
+ mReportingBreak = false;
+ mReportingStarted = true;
+ }
+ }
+
+ private:
+ LLTextureFetch * mFetcher;
+ S32 mExpectedSequence;
+ volatile const S32 & mLiveSequence;
+ volatile bool & mReportingBreak;
+ volatile bool & mReportingStarted;
+
+ }; // class lcl_responder
+
+ if (! gViewerAssetStatsThread1)
+ return true;
+
+ static volatile bool reporting_started(false);
+ static volatile S32 report_sequence(0);
+
+ // We've taken over ownership of the stats copy at this
+ // point. Get a working reference to it for merging here
+ // but leave it in 'this'. Destructor will rid us of it.
+ LLViewerAssetStats & main_stats = *mMainStats;
+
+ // Merge existing stats into those from main, convert to LLSD
+ main_stats.merge(*gViewerAssetStatsThread1);
+ LLSD merged_llsd = main_stats.asLLSD(true);
+
+ // Add some additional meta fields to the content
+ merged_llsd["session_id"] = mSessionID;
+ merged_llsd["agent_id"] = mAgentID;
+ merged_llsd["message"] = "ViewerAssetMetrics"; // Identifies the type of metrics
+ merged_llsd["sequence"] = report_sequence; // Sequence number
+ merged_llsd["initial"] = ! reporting_started; // Initial data from viewer
+ merged_llsd["break"] = LLTextureFetch::svMetricsDataBreak; // Break in data prior to this report
+
+ // Update sequence number
+ if (S32_MAX == ++report_sequence)
+ report_sequence = 0;
+
+ // Limit the size of the stats report if necessary.
+ merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd);
+
+ if (! mCapsURL.empty())
+ {
+ LLCurlRequest::headers_t headers;
+ fetcher->getCurlRequest().post(mCapsURL,
+ headers,
+ merged_llsd,
+ new lcl_responder(fetcher,
+ report_sequence,
+ report_sequence,
+ LLTextureFetch::svMetricsDataBreak,
+ reporting_started));
+ }
+ else
+ {
+ LLTextureFetch::svMetricsDataBreak = true;
+ }
+
+ // In QA mode, Metrics submode, log the result for ease of testing
+ if (fetcher->isQAMode())
+ {
+ LL_INFOS("Textures") << merged_llsd << LL_ENDL;
+ }
+
+ gViewerAssetStatsThread1->reset();
+
+ return true;
+}
+
+
+bool
+truncate_viewer_metrics(int max_regions, LLSD & metrics)
+{
+ static const LLSD::String reg_tag("regions");
+ static const LLSD::String duration_tag("duration");
+
+ LLSD & reg_map(metrics[reg_tag]);
+ if (reg_map.size() <= max_regions)
+ {
+ return false;
+ }
+
+ // Build map of region hashes ordered by duration
+ typedef std::multimap<LLSD::Real, int> reg_ordered_list_t;
+ reg_ordered_list_t regions_by_duration;
+
+ int ind(0);
+ LLSD::array_const_iterator it_end(reg_map.endArray());
+ for (LLSD::array_const_iterator it(reg_map.beginArray()); it_end != it; ++it, ++ind)
+ {
+ LLSD::Real duration = (*it)[duration_tag].asReal();
+ regions_by_duration.insert(reg_ordered_list_t::value_type(duration, ind));
+ }
+
+ // Build a replacement regions array with the longest-persistence regions
+ LLSD new_region(LLSD::emptyArray());
+ reg_ordered_list_t::const_reverse_iterator it2_end(regions_by_duration.rend());
+ reg_ordered_list_t::const_reverse_iterator it2(regions_by_duration.rbegin());
+ for (int i(0); i < max_regions && it2_end != it2; ++i, ++it2)
+ {
+ new_region.append(reg_map[it2->second]);
+ }
+ reg_map = new_region;
+
+ return true;
+}
+
+} // end of anonymous namespace
+
+
+
diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp
index 3f7dc24ade..fa91f129b8 100644
--- a/indra/newview/lltoastnotifypanel.cpp
+++ b/indra/newview/lltoastnotifypanel.cpp
@@ -563,7 +563,17 @@ void LLIMToastNotifyPanel::reshape(S32 width, S32 height, BOOL called_from_paren
height = rc.getHeight();
width = rc.getWidth();
+ bool is_width_changed = width != getRect().getWidth();
+
LLToastPanel::reshape(width, height, called_from_parent);
+
+ // Notification height required to display the text message depends on
+ // the width of the text box thus if panel width is changed the text box
+ // width is also changed then reshape() is called to adjust proper height.
+ if (is_width_changed)
+ {
+ reshape(width, height, called_from_parent);
+ }
}
// EOF
diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp
index 8ccfdb071b..011aa47e31 100644
--- a/indra/newview/lltranslate.cpp
+++ b/indra/newview/lltranslate.cpp
@@ -2,33 +2,27 @@
* @file lltranslate.cpp
* @brief Functions for translating text via Google Translate.
*
-* $LicenseInfo:firstyear=2009&license=viewergpl$
-*
-* Copyright (c) 2009-2010, Linden Research, Inc.
-*
-* Second Life Viewer Source Code
-* The source code in this file ("Source Code") is provided by Linden Lab
-* to you under the terms of the GNU General Public License, version 2.0
-* ("GPL"), unless you have obtained a separate licensing agreement
-* ("Other License"), formally executed by you and Linden Lab. Terms of
-* the GPL can be found in doc/GPL-license.txt in this distribution, or
-* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
-*
-* There are special exceptions to the terms and conditions of the GPL as
-* it is applied to this Source Code. View the full text of the exception
-* in the file doc/FLOSS-exception.txt in this software distribution, or
-* online at
-* http://secondlifegrid.net/programs/open_source/licensing/flossexception
-*
-* By copying, modifying or distributing this software, you acknowledge
-* that you have read and understood your obligations described above,
-* and agree to abide by those obligations.
-*
-* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
-* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
-* COMPLETENESS OR PERFORMANCE.
-* $/LicenseInfo$
-*/
+ * $LicenseInfo:firstyear=2009&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$
+ */
#include "llviewerprecompiledheaders.h"
diff --git a/indra/newview/lltranslate.h b/indra/newview/lltranslate.h
index 0786dc0ca3..e85a42e878 100644
--- a/indra/newview/lltranslate.h
+++ b/indra/newview/lltranslate.h
@@ -2,33 +2,27 @@
* @file lltranslate.h
* @brief Human language translation class and JSON response receiver.
*
-* $LicenseInfo:firstyear=2009&license=viewergpl$
-*
-* Copyright (c) 2009-2010, Linden Research, Inc.
-*
-* Second Life Viewer Source Code
-* The source code in this file ("Source Code") is provided by Linden Lab
-* to you under the terms of the GNU General Public License, version 2.0
-* ("GPL"), unless you have obtained a separate licensing agreement
-* ("Other License"), formally executed by you and Linden Lab. Terms of
-* the GPL can be found in doc/GPL-license.txt in this distribution, or
-* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
-*
-* There are special exceptions to the terms and conditions of the GPL as
-* it is applied to this Source Code. View the full text of the exception
-* in the file doc/FLOSS-exception.txt in this software distribution, or
-* online at
-* http://secondlifegrid.net/programs/open_source/licensing/flossexception
-*
-* By copying, modifying or distributing this software, you acknowledge
-* that you have read and understood your obligations described above,
-* and agree to abide by those obligations.
-*
-* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
-* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
-* COMPLETENESS OR PERFORMANCE.
-* $/LicenseInfo$
-*/
+ * $LicenseInfo:firstyear=2009&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$
+ */
#ifndef LL_LLTRANSLATE_H
#define LL_LLTRANSLATE_H
diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp
index 5ad7725b3e..7024b2c785 100644
--- a/indra/newview/llviewerassetstats.cpp
+++ b/indra/newview/llviewerassetstats.cpp
@@ -2,31 +2,25 @@
* @file llviewerassetstats.cpp
* @brief
*
- * $LicenseInfo:firstyear=2010&license=viewergpl$
- *
- * Copyright (c) 2010, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 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.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h
index 905ceefad5..73ec5974b2 100644
--- a/indra/newview/llviewerassetstats.h
+++ b/indra/newview/llviewerassetstats.h
@@ -2,31 +2,25 @@
* @file llviewerassetstats.h
* @brief Client-side collection of asset request statistics
*
- * $LicenseInfo:firstyear=2010&license=viewergpl$
- *
- * Copyright (c) 2010, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 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.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 3e13537113..de8e37b572 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -647,8 +647,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
LLGLState::checkTextureChannels();
LLGLState::checkClientArrays();
- BOOL to_texture = !for_snapshot &&
- gPipeline.canUseVertexShaders() &&
+ BOOL to_texture = gPipeline.canUseVertexShaders() &&
LLPipeline::sRenderGlow;
LLAppViewer::instance()->pingMainloopTimeout("Display:Swap");
@@ -709,7 +708,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
- if (!for_snapshot)
+ //if (!for_snapshot)
{
LLMemType mt_gw(LLMemType::MTYPE_DISPLAY_GEN_REFLECTION);
LLAppViewer::instance()->pingMainloopTimeout("Display:Imagery");
@@ -1043,8 +1042,7 @@ LLRect get_whole_screen_region()
S32 tile_height = llround((F32)gViewerWindow->getWorldViewHeightScaled() / zoom_factor);
int tile_y = sub_region / num_horizontal_tiles;
int tile_x = sub_region - (tile_y * num_horizontal_tiles);
- glh::matrix4f mat;
-
+
whole_screen.setLeftTopAndSize(tile_x * tile_width, gViewerWindow->getWorldViewHeightScaled() - (tile_y * tile_height), tile_width, tile_height);
}
return whole_screen;
@@ -1124,10 +1122,14 @@ void render_ui(F32 zoom_factor, int subfield)
LLMemType mt_ru(LLMemType::MTYPE_DISPLAY_RENDER_UI);
LLGLState::checkStates();
- glPushMatrix();
- glLoadMatrixd(gGLLastModelView);
glh::matrix4f saved_view = glh_get_current_modelview();
- glh_set_current_modelview(glh_copy_matrix(gGLLastModelView));
+
+ if (!gSnapshot)
+ {
+ glPushMatrix();
+ glLoadMatrixd(gGLLastModelView);
+ glh_set_current_modelview(glh_copy_matrix(gGLLastModelView));
+ }
{
BOOL to_texture = gPipeline.canUseVertexShaders() &&
@@ -1178,8 +1180,11 @@ void render_ui(F32 zoom_factor, int subfield)
LLVertexBuffer::unbind();
}
- glh_set_current_modelview(saved_view);
- glPopMatrix();
+ if (!gSnapshot)
+ {
+ glh_set_current_modelview(saved_view);
+ glPopMatrix();
+ }
if (gDisplaySwapBuffers)
{
@@ -1321,7 +1326,7 @@ void render_ui_2d()
// render outline for HUD
if (isAgentAvatarValid() && gAgentCamera.mHUDCurZoom < 0.98f)
{
- glPushMatrix();
+ gGL.pushMatrix();
S32 half_width = (gViewerWindow->getWorldViewWidthScaled() / 2);
S32 half_height = (gViewerWindow->getWorldViewHeightScaled() / 2);
glScalef(LLUI::sGLScaleFactor.mV[0], LLUI::sGLScaleFactor.mV[1], 1.f);
@@ -1330,7 +1335,7 @@ void render_ui_2d()
glScalef(zoom,zoom,1.f);
gGL.color4fv(LLColor4::white.mV);
gl_rect_2d(-half_width, half_height, half_width, -half_height, FALSE);
- glPopMatrix();
+ gGL.popMatrix();
stop_glerror();
}
@@ -1378,8 +1383,7 @@ void render_ui_2d()
gGL.setColorMask(true, false);
LLUI::sDirtyRect = t_rect;
-
- }
+ }
LLGLDisable cull(GL_CULL_FACE);
LLGLDisable blend(GL_BLEND);
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index ed18d67b62..900081b8c6 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -53,7 +53,7 @@
#include "llwebsharing.h" // For LLWebSharing::setOpenIDCookie(), *TODO: find a better way to do this!
#include "llfilepicker.h"
#include "llnotifications.h"
-
+#include "lldir.h"
#include "llevent.h" // LLSimpleListener
#include "llnotificationsutil.h"
#include "lluuid.h"
@@ -1766,7 +1766,8 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
media_source->setTarget(target);
- if (media_source->init(launcher_name, plugin_name, gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins")))
+ const std::string plugin_dir = gDirUtilp->getLLPluginDir();
+ if (media_source->init(launcher_name, plugin_dir, plugin_name, gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins")))
{
return media_source;
}
@@ -1832,16 +1833,17 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type)
media_source->ignore_ssl_cert_errors(true);
}
- // start by assuming the default CA file will be used
- std::string ca_path = gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "lindenlab.pem" );
-
- // default turned off so pick up the user specified path
- if( ! gSavedSettings.getBOOL("BrowserUseDefaultCAFile"))
- {
- ca_path = gSavedSettings.getString("BrowserCAFilePath");
- }
- // set the path to the CA.pem file
- media_source->addCertificateFilePath( ca_path );
+ // NOTE: Removed as per STORM-927 - SSL handshake failed - setting local self-signed certs like this
+ // seems to screw things up big time. For now, devs will need to add these certs locally and Qt will pick them up.
+// // start by assuming the default CA file will be used
+// std::string ca_path = gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "lindenlab.pem" );
+// // default turned off so pick up the user specified path
+// if( ! gSavedSettings.getBOOL("BrowserUseDefaultCAFile"))
+// {
+// ca_path = gSavedSettings.getString("BrowserCAFilePath");
+// }
+// // set the path to the CA.pem file
+// media_source->addCertificateFilePath( ca_path );
media_source->proxy_setup(gSavedSettings.getBOOL("BrowserProxyEnabled"), gSavedSettings.getString("BrowserProxyAddress"), gSavedSettings.getS32("BrowserProxyPort"));
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 7e8fbf7345..852911ceb7 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -2711,6 +2711,14 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
LLSD args;
args["slurl"] = location;
args["type"] = LLNotificationsUI::NT_NEARBYCHAT;
+
+ // Look for IRC-style emotes here so object name formatting is correct
+ std::string prefix = message.substr(0, 4);
+ if (prefix == "/me " || prefix == "/me'")
+ {
+ chat.mChatStyle = CHAT_STYLE_IRC;
+ }
+
LLNotificationsUI::LLNotificationManager::instance().onChat(chat, args);
}
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index c60bdf31c9..e8828e63a9 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -472,7 +472,6 @@ void LLViewerObject::initVOClasses()
llinfos << "Viewer Object size: " << sizeof(LLViewerObject) << llendl;
LLVOGrass::initClass();
LLVOWater::initClass();
- LLVOSky::initClass();
LLVOVolume::initClass();
}
@@ -2998,6 +2997,8 @@ void LLViewerObject::setScale(const LLVector3 &scale, BOOL damped)
{
if (!mOnMap)
{
+ llassert_always(LLWorld::getInstance()->getRegionFromHandle(getRegion()->getHandle()));
+
gObjectList.addToMap(this);
mOnMap = TRUE;
}
@@ -3525,8 +3526,8 @@ void LLViewerObject::setPositionParent(const LLVector3 &pos_parent, BOOL damped)
// Set position relative to parent, if no parent, relative to region
if (!isRoot())
{
- LLViewerObject::setPosition(pos_parent);
- updateDrawable(damped);
+ LLViewerObject::setPosition(pos_parent, damped);
+ //updateDrawable(damped);
}
else
{
@@ -3567,6 +3568,7 @@ void LLViewerObject::setPositionEdit(const LLVector3 &pos_edit, BOOL damped)
LLVector3 position_offset = getPosition() * getParent()->getRotation();
((LLViewerObject *)getParent())->setPositionEdit(pos_edit - position_offset);
+ updateDrawable(damped);
}
else if (isJointChild())
{
@@ -3575,15 +3577,14 @@ void LLViewerObject::setPositionEdit(const LLVector3 &pos_edit, BOOL damped)
LLQuaternion inv_parent_rot = parent->getRotation();
inv_parent_rot.transQuat();
LLVector3 pos_parent = (pos_edit - parent->getPositionRegion()) * inv_parent_rot;
- LLViewerObject::setPosition(pos_parent);
+ LLViewerObject::setPosition(pos_parent, damped);
}
else
{
- LLViewerObject::setPosition(pos_edit);
+ LLViewerObject::setPosition(pos_edit, damped);
mPositionRegion = pos_edit;
mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
- }
- updateDrawable(damped);
+ }
}
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index d112b3490e..80d8c8e332 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -1121,6 +1121,22 @@ void LLViewerObjectList::shiftObjects(const LLVector3 &offset)
LLWorld::getInstance()->shiftRegions(offset);
}
+//debug code
+bool LLViewerObjectList::hasMapObjectInRegion(LLViewerRegion* regionp)
+{
+ for (vobj_list_t::iterator iter = mMapObjects.begin(); iter != mMapObjects.end(); ++iter)
+ {
+ LLViewerObject* objectp = *iter;
+
+ if(objectp->isDead() || objectp->getRegion() == regionp)
+ {
+ return true ;
+ }
+ }
+
+ return false ;
+}
+
void LLViewerObjectList::renderObjectsForMap(LLNetMap &netmap)
{
LLColor4 above_water_color = LLUIColorTable::instance().getColor( "NetMapOtherOwnAboveWater" );
@@ -1139,6 +1155,9 @@ void LLViewerObjectList::renderObjectsForMap(LLNetMap &netmap)
for (vobj_list_t::iterator iter = mMapObjects.begin(); iter != mMapObjects.end(); ++iter)
{
LLViewerObject* objectp = *iter;
+
+ llassert_always(!objectp->isDead());
+
if (!objectp->getRegion() || objectp->isOrphaned() || objectp->isAttachment())
{
continue;
@@ -1426,7 +1445,7 @@ LLViewerObject *LLViewerObjectList::replaceObject(const LLUUID &id, const LLPCod
LLViewerObject *old_instance = findObject(id);
if (old_instance)
{
- cleanupReferences(old_instance);
+ //cleanupReferences(old_instance);
old_instance->markDead();
return createObject(pcode, regionp, id, old_instance->getLocalID(), LLHost());
diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h
index fda3d6899d..8cff8e988a 100644
--- a/indra/newview/llviewerobjectlist.h
+++ b/indra/newview/llviewerobjectlist.h
@@ -87,6 +87,7 @@ public:
void shiftObjects(const LLVector3 &offset);
+ bool hasMapObjectInRegion(LLViewerRegion* regionp) ;
void renderObjectsForMap(LLNetMap &netmap);
void renderObjectBounds(const LLVector3 &center);
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 435da72622..336915ac8c 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -351,6 +351,14 @@ public:
addText(xpos, ypos, llformat("Time: %d:%02d:%02d", hours,mins,secs)); ypos += y_inc;
}
+#if LL_WINDOWS
+ if (gSavedSettings.getBOOL("DebugShowMemory"))
+ {
+ addText(xpos, ypos, llformat("Memory: %d (KB)", LLMemory::getWorkingSetSize() / 1024));
+ ypos += y_inc;
+ }
+#endif
+
if (gDisplayCameraPos)
{
std::string camera_view_text;
@@ -3978,18 +3986,26 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei
LLPipeline::sShowHUDAttachments = FALSE;
}
+ // if not showing ui, use full window to render world view
+ updateWorldViewRect(!show_ui);
+
// Copy screen to a buffer
// crop sides or top and bottom, if taking a snapshot of different aspect ratio
// from window
- S32 snapshot_width = mWindowRectRaw.getWidth();
- S32 snapshot_height = mWindowRectRaw.getHeight();
+ LLRect window_rect = show_ui ? getWindowRectRaw() : getWorldViewRectRaw();
+
+ S32 snapshot_width = window_rect.getWidth();
+ S32 snapshot_height = window_rect.getHeight();
// SNAPSHOT
- S32 window_width = mWindowRectRaw.getWidth();
- S32 window_height = mWindowRectRaw.getHeight();
- LLRect window_rect = mWindowRectRaw;
- BOOL use_fbo = FALSE;
+ S32 window_width = snapshot_width;
+ S32 window_height = snapshot_height;
+
+ if (show_ui)
+ {
+ image_width = llmin(image_width, window_width);
+ image_height = llmin(image_height, window_height);
+ }
- LLRenderTarget target;
F32 scale_factor = 1.0f ;
if(!keep_window_aspect) //image cropping
{
@@ -4002,45 +4018,24 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei
{
if(image_width > window_width || image_height > window_height) //need to enlarge the scene
{
- if (!LLPipeline::sRenderDeferred && gGLManager.mHasFramebufferObject && !show_ui)
- {
- GLint max_size = 0;
- glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &max_size);
-
- if (image_width <= max_size && image_height <= max_size) //re-project the scene
- {
- use_fbo = TRUE;
-
- snapshot_width = image_width;
- snapshot_height = image_height;
- target.allocate(snapshot_width, snapshot_height, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, TRUE);
- window_width = snapshot_width;
- window_height = snapshot_height;
- scale_factor = 1.f;
- mWindowRectRaw.set(0, snapshot_height, snapshot_width, 0);
- target.bindTarget();
- }
- }
-
- if(!use_fbo) //no re-projection, so tiling the scene
- {
- F32 ratio = llmin( (F32)window_width / image_width , (F32)window_height / image_height) ;
- snapshot_width = (S32)(ratio * image_width) ;
- snapshot_height = (S32)(ratio * image_height) ;
- scale_factor = llmax(1.0f, 1.0f / ratio) ;
- }
+ F32 ratio = llmin( (F32)window_width / image_width , (F32)window_height / image_height) ;
+ snapshot_width = (S32)(ratio * image_width) ;
+ snapshot_height = (S32)(ratio * image_height) ;
+ scale_factor = llmax(1.0f, 1.0f / ratio) ;
}
- //else: keep the current scene scale, re-scale it if necessary after reading out.
}
- // if not showing ui, use full window to render world view
- updateWorldViewRect(!show_ui);
+ if (show_ui && scale_factor > 1.f)
+ {
+ llwarns << "over scaling UI not supported." << llendl;
+ }
S32 buffer_x_offset = llfloor(((window_width - snapshot_width) * scale_factor) / 2.f);
S32 buffer_y_offset = llfloor(((window_height - snapshot_height) * scale_factor) / 2.f);
S32 image_buffer_x = llfloor(snapshot_width*scale_factor) ;
S32 image_buffer_y = llfloor(snapshot_height *scale_factor) ;
+
if(image_buffer_x > max_size || image_buffer_y > max_size) //boundary check to avoid memory overflow
{
scale_factor *= llmin((F32)max_size / image_buffer_x, (F32)max_size / image_buffer_y) ;
@@ -4049,7 +4044,7 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei
}
if(image_buffer_x > 0 && image_buffer_y > 0)
{
- raw->resize(image_buffer_x, image_buffer_y, 3);
+ raw->resize(image_buffer_x, image_buffer_y, 3);
}
else
{
@@ -4061,12 +4056,13 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei
}
BOOL high_res = scale_factor >= 2.f; // Font scaling is slow, only do so if rez is much higher
- if (high_res)
+ if (high_res && show_ui)
{
- send_agent_pause();
+ llwarns << "High res UI snapshot not supported. " << llendl;
+ /*send_agent_pause();
//rescale fonts
initFonts(scale_factor);
- LLHUDObject::reshapeAll();
+ LLHUDObject::reshapeAll();*/
}
S32 output_buffer_offset_y = 0;
@@ -4162,12 +4158,6 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei
output_buffer_offset_y += subimage_y_offset;
}
- if (use_fbo)
- {
- mWindowRectRaw = window_rect;
- target.flush();
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
- }
gDisplaySwapBuffers = FALSE;
gDepthDirty = TRUE;
@@ -4182,11 +4172,11 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei
LLPipeline::sShowHUDAttachments = TRUE;
}
- if (high_res)
+ /*if (high_res)
{
initFonts(1.f);
LLHUDObject::reshapeAll();
- }
+ }*/
// Pre-pad image to number of pixels such that the line length is a multiple of 4 bytes (for BMP encoding)
// Note: this formula depends on the number of components being 3. Not obvious, but it's correct.
diff --git a/indra/newview/llviewerwindowlistener.cpp b/indra/newview/llviewerwindowlistener.cpp
index 0b52948680..1fe5fc9800 100644
--- a/indra/newview/llviewerwindowlistener.cpp
+++ b/indra/newview/llviewerwindowlistener.cpp
@@ -65,7 +65,6 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow):
void LLViewerWindowListener::saveSnapshot(const LLSD& event) const
{
- LLReqID reqid(event);
typedef std::map<LLSD::String, LLViewerWindow::ESnapshotType> TypeMap;
TypeMap types;
#define tp(name) types[#name] = LLViewerWindow::SNAPSHOT_TYPE_##name
@@ -98,9 +97,7 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const
type = found->second;
}
bool ok = mViewerWindow->saveSnapshot(event["filename"], width, height, showui, rebuild, type);
- LLSD response(reqid.makeResponse());
- response["ok"] = ok;
- LLEventPumps::instance().obtain(event["reply"]).post(response);
+ sendReply(LLSDMap("ok", ok), event);
}
void LLViewerWindowListener::requestReshape(LLSD const & event_data) const
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 26b595c923..38d2437405 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -599,16 +599,16 @@ F32 LLVOAvatar::sRenderDistance = 256.f;
S32 LLVOAvatar::sNumVisibleAvatars = 0;
S32 LLVOAvatar::sNumLODChangesThisFrame = 0;
-const LLUUID LLVOAvatar::sStepSoundOnLand = LLUUID("e8af4a28-aa83-4310-a7c4-c047e15ea0df");
+const LLUUID LLVOAvatar::sStepSoundOnLand("e8af4a28-aa83-4310-a7c4-c047e15ea0df");
const LLUUID LLVOAvatar::sStepSounds[LL_MCODE_END] =
{
- LLUUID(SND_STONE_RUBBER),
- LLUUID(SND_METAL_RUBBER),
- LLUUID(SND_GLASS_RUBBER),
- LLUUID(SND_WOOD_RUBBER),
- LLUUID(SND_FLESH_RUBBER),
- LLUUID(SND_RUBBER_PLASTIC),
- LLUUID(SND_RUBBER_RUBBER)
+ SND_STONE_RUBBER,
+ SND_METAL_RUBBER,
+ SND_GLASS_RUBBER,
+ SND_WOOD_RUBBER,
+ SND_FLESH_RUBBER,
+ SND_RUBBER_PLASTIC,
+ SND_RUBBER_RUBBER
};
S32 LLVOAvatar::sRenderName = RENDER_NAME_ALWAYS;
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index b3312db4a0..a933500706 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -651,7 +651,7 @@ void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::voca
void LLVOCache::purgeEntries(U32 size)
{
- while(mHeaderEntryQueue.size() >= size)
+ while(mHeaderEntryQueue.size() > size)
{
header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ;
HeaderEntryInfo* entry = *iter ;
diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp
index 7ae8c2c07d..80f43e51d2 100644
--- a/indra/newview/llvosky.cpp
+++ b/indra/newview/llvosky.cpp
@@ -77,9 +77,6 @@ static const LLVector2 TEX11 = LLVector2(1.f, 1.f);
LLUUID gSunTextureID = IMG_SUN;
LLUUID gMoonTextureID = IMG_MOON;
-//static
-LLColor3 LLHaze::sAirScaSeaLevel;
-
class LLFastLn
{
public:
@@ -182,6 +179,23 @@ inline void color_gamma_correct(LLColor3 &col)
}
}
+static LLColor3 calc_air_sca_sea_level()
+{
+ static LLColor3 WAVE_LEN(675, 520, 445);
+ static LLColor3 refr_ind = refr_ind_calc(WAVE_LEN);
+ static LLColor3 n21 = refr_ind * refr_ind - LLColor3(1, 1, 1);
+ static LLColor3 n4 = n21 * n21;
+ static LLColor3 wl2 = WAVE_LEN * WAVE_LEN * 1e-6f;
+ static LLColor3 wl4 = wl2 * wl2;
+ static LLColor3 mult_const = fsigma * 2.0f/ 3.0f * 1e24f * (F_PI * F_PI) * n4;
+ static F32 dens_div_N = F32( ATM_SEA_LEVEL_NDENS / Ndens2);
+ return dens_div_N * color_div ( mult_const, wl4 );
+}
+
+// static constants.
+LLColor3 const LLHaze::sAirScaSeaLevel = calc_air_sca_sea_level();
+F32 const LLHaze::sAirScaIntense = color_intens(LLHaze::sAirScaSeaLevel);
+F32 const LLHaze::sAirScaAvg = LLHaze::sAirScaIntense / 3.f;
/***************************************
@@ -394,12 +408,6 @@ LLVOSky::~LLVOSky()
mCubeMap = NULL;
}
-void LLVOSky::initClass()
-{
- LLHaze::initClass();
-}
-
-
void LLVOSky::init()
{
const F32 haze_int = color_intens(mHaze.calcSigSca(0));
@@ -2147,17 +2155,8 @@ void LLVOSky::updateFog(const F32 distance)
stop_glerror();
}
-// static
-void LLHaze::initClass()
-{
- sAirScaSeaLevel = LLHaze::calcAirScaSeaLevel();
-}
-
-
// Functions used a lot.
-
-
F32 color_norm_pow(LLColor3& col, F32 e, BOOL postmultiply)
{
F32 mv = color_max(col);
diff --git a/indra/newview/llvosky.h b/indra/newview/llvosky.h
index 6b3e7873a1..d3a42583ea 100644
--- a/indra/newview/llvosky.h
+++ b/indra/newview/llvosky.h
@@ -292,23 +292,6 @@ LL_FORCE_INLINE LLColor3 refr_ind_calc(const LLColor3 &wave_length)
}
-LL_FORCE_INLINE LLColor3 calc_air_sca_sea_level()
-{
- const static LLColor3 WAVE_LEN(675, 520, 445);
- const static LLColor3 refr_ind = refr_ind_calc(WAVE_LEN);
- const static LLColor3 n21 = refr_ind * refr_ind - LLColor3(1, 1, 1);
- const static LLColor3 n4 = n21 * n21;
- const static LLColor3 wl2 = WAVE_LEN * WAVE_LEN * 1e-6f;
- const static LLColor3 wl4 = wl2 * wl2;
- const static LLColor3 mult_const = fsigma * 2.0f/ 3.0f * 1e24f * (F_PI * F_PI) * n4;
- const static F32 dens_div_N = F32( ATM_SEA_LEVEL_NDENS / Ndens2);
- return dens_div_N * color_div ( mult_const, wl4 );
-}
-
-const LLColor3 gAirScaSeaLevel = calc_air_sca_sea_level();
-const F32 AIR_SCA_INTENS = color_intens(gAirScaSeaLevel);
-const F32 AIR_SCA_AVG = AIR_SCA_INTENS / 3.f;
-
class LLHaze
{
public:
@@ -316,18 +299,15 @@ public:
LLHaze(const F32 g, const LLColor3& sca, const F32 fo = 2.f) :
mG(g), mSigSca(0.25f/F_PI * sca), mFalloff(fo), mAbsCoef(0.f)
{
- mAbsCoef = color_intens(mSigSca) / AIR_SCA_INTENS;
+ mAbsCoef = color_intens(mSigSca) / sAirScaIntense;
}
LLHaze(const F32 g, const F32 sca, const F32 fo = 2.f) : mG(g),
mSigSca(0.25f/F_PI * LLColor3(sca, sca, sca)), mFalloff(fo)
{
- mAbsCoef = 0.01f * sca / AIR_SCA_AVG;
+ mAbsCoef = 0.01f * sca / sAirScaAvg;
}
- static void initClass();
-
-
F32 getG() const { return mG; }
void setG(const F32 g)
@@ -343,12 +323,12 @@ public:
void setSigSca(const LLColor3& s)
{
mSigSca = s;
- mAbsCoef = 0.01f * color_intens(mSigSca) / AIR_SCA_INTENS;
+ mAbsCoef = 0.01f * color_intens(mSigSca) / sAirScaIntense;
}
void setSigSca(const F32 s0, const F32 s1, const F32 s2)
{
- mSigSca = AIR_SCA_AVG * LLColor3 (s0, s1, s2);
+ mSigSca = sAirScaAvg * LLColor3 (s0, s1, s2);
mAbsCoef = 0.01f * (s0 + s1 + s2) / 3;
}
@@ -392,10 +372,11 @@ public:
static inline LLColor3 calcAirSca(const F32 h);
static inline void calcAirSca(const F32 h, LLColor3 &result);
- static LLColor3 calcAirScaSeaLevel() { return gAirScaSeaLevel; }
- static const LLColor3 &getAirScaSeaLevel() { return sAirScaSeaLevel; }
-public:
- static LLColor3 sAirScaSeaLevel;
+
+private:
+ static LLColor3 const sAirScaSeaLevel;
+ static F32 const sAirScaIntense;
+ static F32 const sAirScaAvg;
protected:
F32 mG;
@@ -473,7 +454,6 @@ public:
LLVOSky(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
// Initialize/delete data that's only inited once per class.
- static void initClass();
void init();
void initCubeMap();
void initEmpty();
@@ -654,14 +634,12 @@ F32 color_norm_pow(LLColor3& col, F32 e, BOOL postmultiply = FALSE);
inline LLColor3 LLHaze::calcAirSca(const F32 h)
{
- static const LLColor3 air_sca_sea_level = calcAirScaSeaLevel();
- return calcFalloff(h) * air_sca_sea_level;
+ return calcFalloff(h) * sAirScaSeaLevel;
}
inline void LLHaze::calcAirSca(const F32 h, LLColor3 &result)
{
- static const LLColor3 air_sca_sea_level = calcAirScaSeaLevel();
- result = air_sca_sea_level;
+ result = sAirScaSeaLevel;
result *= calcFalloff(h);
}
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index f67e3a9770..a207d3e050 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -2165,7 +2165,7 @@ void LLVOVolume::removeMediaImpl(S32 texture_index)
}
//make the face referencing to mMediaImplList[texture_index] to point back to the old texture.
- if(mDrawable)
+ if(mDrawable && texture_index < mDrawable->getNumFaces())
{
LLFace* facep = mDrawable->getFace(texture_index) ;
if(facep)
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
index 7f3a8deb47..d9ec41e73a 100644
--- a/indra/newview/llworld.cpp
+++ b/indra/newview/llworld.cpp
@@ -276,6 +276,8 @@ void LLWorld::removeRegion(const LLHost &host)
delete regionp;
updateWaterObjects();
+
+ llassert_always(!gObjectList.hasMapObjectInRegion(regionp)) ;
}
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index b4ea455318..90929b5aa1 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -5428,7 +5428,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
gGL.setColorMask(true, true);
glClearColor(0,0,0,0);
- if (for_snapshot)
+ /*if (for_snapshot)
{
gGL.getTexUnit(0)->bind(&mGlow[1]);
{
@@ -5439,14 +5439,21 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
// If the snapshot is constructed from tiles, calculate which
// tile we're in.
- const S32 num_horizontal_tiles = llceil(zoom_factor);
- const LLVector2 tile(subfield % num_horizontal_tiles,
- (S32)(subfield / num_horizontal_tiles));
- llassert(zoom_factor > 0.0); // Non-zero, non-negative.
- const F32 tile_size = 1.0/zoom_factor;
-
- tc1 = tile*tile_size; // Top left texture coordinates
- tc2 = (tile+LLVector2(1,1))*tile_size; // Bottom right texture coordinates
+
+ //from LLViewerCamera::setPerpsective
+ if (zoom_factor > 1.f)
+ {
+ int pos_y = subfield / llceil(zoom_factor);
+ int pos_x = subfield - (pos_y*llceil(zoom_factor));
+ F32 size = 1.f/zoom_factor;
+
+ tc1.set(pos_x*size, pos_y*size);
+ tc2 = tc1 + LLVector2(size,size);
+ }
+ else
+ {
+ tc2.set(1,1);
+ }
LLGLEnable blend(GL_BLEND);
gGL.setSceneBlendType(LLRender::BT_ADD);
@@ -5479,7 +5486,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
glPopMatrix();
return;
- }
+ }*/
{
{
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 2c00120177..7b3cc7bdfa 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -107,6 +107,7 @@ with the same filename but different name
<texture name="ComboButton_Selected" file_name="widgets/ComboButton_Selected.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />
<texture name="ComboButton_UpSelected" file_name="widgets/ComboButton_UpSelected.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />
<texture name="ComboButton_Up_On_Selected" file_name="widgets/ComboButton_Up_On_Selected.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />
+ <texture name="ComboButton_On" file_name="widgets/ComboButton_On.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />
<texture name="ComboButton_Off" file_name="widgets/ComboButton_Off.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />
<texture name="ComboButton_UpOff" file_name="widgets/ComboButton_UpOff.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />
<texture name="Container" file_name="containers/Container.png" preload="false" />
diff --git a/indra/newview/skins/default/xui/da/floater_web_content.xml b/indra/newview/skins/default/xui/da/floater_web_content.xml
new file mode 100644
index 0000000000..74092e88ec
--- /dev/null
+++ b/indra/newview/skins/default/xui/da/floater_web_content.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="floater_web_content" title="">
+ <layout_stack name="stack1">
+ <layout_panel name="nav_controls">
+ <button name="back" tool_tip="Navigér tilbage"/>
+ <button name="forward" tool_tip="Navigér frem"/>
+ <button name="stop" tool_tip="Stop navigering"/>
+ <button name="reload" tool_tip="Genindlæs side"/>
+ <combo_box name="address" tool_tip="Indtast URL her"/>
+ <icon name="media_secure_lock_flag" tool_tip="Sikker browsing"/>
+ <button name="popexternal" tool_tip="Åben denne URL i din normale browser"/>
+ </layout_panel>
+ </layout_stack>
+</floater>
diff --git a/indra/newview/skins/default/xui/da/menu_login.xml b/indra/newview/skins/default/xui/da/menu_login.xml
index 1231c4c08d..0b7a5040ae 100644
--- a/indra/newview/skins/default/xui/da/menu_login.xml
+++ b/indra/newview/skins/default/xui/da/menu_login.xml
@@ -16,7 +16,8 @@
<menu_item_call label="Sæt vinduesstørrelse" name="Set Window Size..."/>
<menu_item_call label="Vis betingelser" name="TOS"/>
<menu_item_call label="Vis vigtig besked" name="Critical"/>
- <menu_item_call label="Test i web browser" name="Web Browser Test"/>
+ <menu_item_call label="Media Browser Test" name="Web Browser Test"/>
+ <menu_item_call label="Web Content Floater Test" name="Web Content Floater Test"/>
<menu_item_check label="Vis gitter vælger" name="Show Grid Picker"/>
<menu_item_call label="Vis notifikationskonsol" name="Show Notifications Console"/>
</menu>
diff --git a/indra/newview/skins/default/xui/da/menu_mini_map.xml b/indra/newview/skins/default/xui/da/menu_mini_map.xml
index 9dcce49708..186dbd476a 100644
--- a/indra/newview/skins/default/xui/da/menu_mini_map.xml
+++ b/indra/newview/skins/default/xui/da/menu_mini_map.xml
@@ -3,6 +3,7 @@
<menu_item_call label="Zoom tæt" name="Zoom Close"/>
<menu_item_call label="Zoom mellem" name="Zoom Medium"/>
<menu_item_call label="Zoom langt" name="Zoom Far"/>
+ <menu_item_call label="Zoom standard" name="Zoom Default"/>
<menu_item_check label="Rotér kort" name="Rotate Map"/>
<menu_item_check label="Auto centrér" name="Auto Center"/>
<menu_item_call label="Fjern ref." name="Stop Tracking"/>
diff --git a/indra/newview/skins/default/xui/da/menu_viewer.xml b/indra/newview/skins/default/xui/da/menu_viewer.xml
index a3dcfdf4cc..fc32be9dc9 100644
--- a/indra/newview/skins/default/xui/da/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/da/menu_viewer.xml
@@ -119,13 +119,15 @@
<menu_item_call label="Animation (L$[COST])..." name="Upload Animation"/>
<menu_item_call label="Mange (L$[COST] pr. fil)..." name="Bulk Upload"/>
</menu>
+ <menu_item_call label="Fortyd" name="Undo"/>
+ <menu_item_call label="Gendan" name="Redo"/>
</menu>
<menu label="Hjælp" name="Help">
<menu_item_call label="[SECOND_LIFE] Help" name="Second Life Help"/>
+ <menu_item_check label="Aktiver tips" name="Enable Hints"/>
<menu_item_call label="Rapporter misbrug" name="Report Abuse"/>
<menu_item_call label="Rapportér fejl" name="Report Bug"/>
<menu_item_call label="Om [APP_NAME]" name="About Second Life"/>
- <menu_item_check label="Aktiver tips" name="Enable Hints"/>
</menu>
<menu label="Avanceret" name="Advanced">
<menu_item_call label="Gendan teksturer" name="Rebake Texture"/>
@@ -266,7 +268,8 @@
<menu_item_call label="Dump Region Object Cache" name="Dump Region Object Cache"/>
</menu>
<menu label="UI (brugerflade)" name="UI">
- <menu_item_call label="Test web browser" name="Web Browser Test"/>
+ <menu_item_call label="Media browser test" name="Web Browser Test"/>
+ <menu_item_call label="Browser med webindhold" name="Web Content Browser"/>
<menu_item_call label="Print info om valgt objekt" name="Print Selected Object Info"/>
<menu_item_call label="Hukommelse statistik" name="Memory Stats"/>
<menu_item_check label="Debug konsol for region" name="Region Debug Console"/>
diff --git a/indra/newview/skins/default/xui/da/notifications.xml b/indra/newview/skins/default/xui/da/notifications.xml
index 27024f4eaa..a3c4897ee1 100644
--- a/indra/newview/skins/default/xui/da/notifications.xml
+++ b/indra/newview/skins/default/xui/da/notifications.xml
@@ -109,6 +109,10 @@ Vælg kun en genstand, og prøv igen.
&apos;Ikke-venner&apos; vil ikke vide, at du har valgt at ignorere deres opkald og personlige beskeder (IM)
<usetemplate name="okbutton" yestext="OK"/>
</notification>
+ <notification name="FavoritesOnLogin">
+ Bemærk: Når du aktiverer dette valg, kan enhver der bruger denne computer se dine favorit lokationer.
+ <usetemplate name="okbutton" yestext="OK"/>
+ </notification>
<notification name="GrantModifyRights">
Tildeling af ændre-rettigheder til andre beboere, tillader dem at ændre, slette eller tage ETHVERT objekt du måtte have. Vær MEGET forsigtig ved tildeling af denne rettighed.
Ønsker du at give ændre-rettgheder til [NAME]?
@@ -416,7 +420,7 @@ Tilbyd venskab til [NAME]?
<input name="message">
[DESC] (ny)
</input>
- <button name="Offer" text="OK"/>
+ <button name="OK" text="OK"/>
<button name="Cancel" text="Annullér"/>
</form>
</notification>
@@ -426,7 +430,7 @@ Tilbyd venskab til [NAME]?
<input name="message">
[DESC] (ny)
</input>
- <button name="Offer" text="OK"/>
+ <button name="OK" text="OK"/>
<button name="Cancel" text="Annullér"/>
</form>
</notification>
@@ -436,7 +440,7 @@ Tilbyd venskab til [NAME]?
<input name="new_name">
[NAME]
</input>
- <button name="Offer" text="OK"/>
+ <button name="OK" text="OK"/>
<button name="Cancel" text="Annullér"/>
</form>
</notification>
@@ -598,9 +602,41 @@ Hent og installér venligst den nyeste version fra
http://secondlife.com/download.
<usetemplate name="okbutton" yestext="OK"/>
</notification>
- <notification name="DownloadBackground">
- En opdateret version af [APP_NAME] er hentet.
-Den vil blive anvendt næste gang du genstarter [APP_NAME]
+ <notification name="FailedRequiredUpdateInstall">
+ Vi kunne ikke installere en påkrævet opdatering.
+Du kan ikke logge på før [APP_NAME] er blevet opdateret.
+
+Hent og installer venligst den nyeste klien fra
+http://secondlife.com/download.
+ <usetemplate name="okbutton" yestext="Afslut"/>
+ </notification>
+ <notification name="UpdaterServiceNotRunning">
+ Dette er en påkrævet opdatering af din Second Life installation.
+
+Du kan downloade opdateringen fra http://www.secondlife.com/downloads
+eller du kan installere den nu.
+ <usetemplate name="okcancelbuttons" notext="Afslut Second Life" yestext="Hent og installér nu"/>
+ </notification>
+ <notification name="DownloadBackgroundTip">
+ Vi har hentet en opdatering til din [APP_NAME] installation.
+Version [VERSION] [[RELEASE_NOTES_FULL_URL] Information about this update]
+ <usetemplate name="okcancelbuttons" notext="Senere..." yestext="Installér nu og genstart [APP_NAME]"/>
+ </notification>
+ <notification name="DownloadBackgroundDialog">
+ Vi har hentet en opdatering til din [APP_NAME] installation.
+Version [VERSION] [[RELEASE_NOTES_FULL_URL] Information about this update]
+ <usetemplate name="okcancelbuttons" notext="Senere..." yestext="Installér nu og genstart [APP_NAME]"/>
+ </notification>
+ <notification name="RequiredUpdateDownloadedVerboseDialog">
+ Vi har hentet en påkrævet opdatering.
+Version [VERSION]
+
+Du skal genstarte [APP_NAME] for at installere denne opdatering.
+ <usetemplate name="okbutton" yestext="OK"/>
+ </notification>
+ <notification name="RequiredUpdateDownloadedDialog">
+ Du skal genstarte [APP_NAME] for at installere opdateringen.
+ <usetemplate name="okbutton" yestext="OK"/>
</notification>
<notification name="DeedObjectToGroup">
<usetemplate ignoretext="Bekræft før jeg dedikerer et objekt til en gruppe" name="okcancelignore" notext="Cancel" yestext="Deed"/>
@@ -1123,14 +1159,6 @@ Prøv at vælge mindre stykker land.
<notification name="NoContentToSearch">
Vælg venligst mindst en indholdstype for at søge (PG, Mature, or Adult).
</notification>
- <notification name="GroupVote">
- [NAME] har forslået at stemme for:
-[MESSAGE]
- <form name="form">
- <button name="VoteNow" text="Stem nu"/>
- <button name="Later" text="Senere"/>
- </form>
- </notification>
<notification name="SystemMessage">
[MESSAGE]
</notification>
@@ -1682,9 +1710,7 @@ Avatar &apos;[NAME]&apos; har forladt udseende modus.
<notification name="NoConnect">
Vi har problemer med at oprette forbindelse via [PROTOCOL] [HOSTID].
Check venligst din netværks- og firewallsetup.
- <form name="form">
- <button name="OK" text="OK"/>
- </form>
+ <usetemplate name="okbutton" yestext="OK"/>
</notification>
<notification name="NoVoiceConnect">
Vi har problemer med at oprette forbindelse til din stemme server:
@@ -1693,9 +1719,7 @@ Check venligst din netværks- og firewallsetup.
Stemme kommunikation vil ikke være tilgængelig.
Check venligst din netværks- og firewall setup.
- <form name="form">
- <button name="OK" text="OK"/>
- </form>
+ <usetemplate name="okbutton" yestext="OK"/>
</notification>
<notification name="AvatarRezLeftNotification">
( [EXISTENCE] sekunder i live )
@@ -1731,6 +1755,9 @@ Sluk for alles lyd?
<notification label="Undersøg verden" name="HintDestinationGuide">
Destinationsguiden indeholder tusinder af nye steder der kan opleves. Vælg venligst et sted og vælg Teleport for at komme derhen.
</notification>
+ <notification label="Ændre dit udseende" name="HintAvatarPicker">
+ Kunne du tænke dig at prøve et nyt udseende? Klik på knappen nedenfor for at se flere avatarer.
+ </notification>
<notification label="Side panel" name="HintSidePanel">
Få hurtig tilgang til din beholdning, sæt, profiler og andet i dette side panel.
</notification>
@@ -1740,6 +1767,12 @@ Sluk for alles lyd?
<notification label="Visningsnavn" name="HintDisplayName">
Angiv dit konfigurérbare visningsnavn her. Dette er i tillæg til dit unikke brugernavn, som ikke kan ændres. Du kan ændre hvordan du ser andre beboeres navne i dine indstillinger.
</notification>
+ <notification label="Flyt" name="HintMoveArrows">
+ For at gå, brug piletasterne på tastaturet. Du kan løbe ved at trykke to gange på Pil-Op
+ </notification>
+ <notification label="Se" name="HintView">
+ For at ændre dit kamera-view, benyt kredsløbs og panoreringskontrollerne. Nulstil view ved at trykke Esc eller ved at gå.
+ </notification>
<notification label="Beholdning" name="HintInventory">
Undersøg din beholdning for at finde ting. Nyeste genstand findes lettes under fanen &quot;Nye ting&quot;
</notification>
@@ -1753,6 +1786,15 @@ Sluk for alles lyd?
<button name="open" text="Åben pop-up vindue"/>
</form>
</notification>
+ <notification name="AuthRequest">
+ Hjemmesiden på &apos;&lt;nolink&gt;[HOST_NAME]&lt;/nolink&gt;&apos; in realm &apos;[REALM]&apos; kræver et brugernavn og password.
+ <form name="form">
+ <input name="username" text="Brugernavn"/>
+ <input name="password" text="Password"/>
+ <button name="ok" text="Send"/>
+ <button name="cancel" text="Annullér"/>
+ </form>
+ </notification>
<global name="UnsupportedGLRequirements">
Det ser ikke ud til at din hardware opfylder minimumskravene til [APP_NAME]. [APP_NAME] kræver et OpenGL grafikkort som understøter &apos;multitexture&apos;. Check eventuelt om du har de nyeste drivere for grafikkortet, og de nyeste service-packs og patches til dit operativsystem.
diff --git a/indra/newview/skins/default/xui/da/panel_avatar_list_item.xml b/indra/newview/skins/default/xui/da/panel_avatar_list_item.xml
index df1173a0a0..890f4a2f0a 100644
--- a/indra/newview/skins/default/xui/da/panel_avatar_list_item.xml
+++ b/indra/newview/skins/default/xui/da/panel_avatar_list_item.xml
@@ -21,7 +21,7 @@
<string name="FormatYears">
[COUNT]å
</string>
- <text name="avatar_name" value="Ukendt"/>
+ <text name="avatar_name" value="(henter)"/>
<icon name="permission_edit_theirs_icon" tool_tip="Du kan redigere denne vens objekter"/>
<icon name="permission_edit_mine_icon" tool_tip="Denne ven kan redigere, slette eller tage dine objekter"/>
<icon name="permission_map_icon" tool_tip="Denne ven kan finde dig på kortet"/>
diff --git a/indra/newview/skins/default/xui/da/panel_edit_alpha.xml b/indra/newview/skins/default/xui/da/panel_edit_alpha.xml
index 3826e8a228..0f60a6df51 100644
--- a/indra/newview/skins/default/xui/da/panel_edit_alpha.xml
+++ b/indra/newview/skins/default/xui/da/panel_edit_alpha.xml
@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<panel name="edit_alpha_panel">
- <panel name="avatar_alpha_color_panel">
- <texture_picker label="Alpha - nedre" name="Lower Alpha" tool_tip="Klik for at vælge et billede"/>
- <texture_picker label="Alpha - øvre" name="Upper Alpha" tool_tip="Klik for at vælge et billede"/>
- <texture_picker label="Alpha - hoved" name="Head Alpha" tool_tip="Klik for at vælge et billede"/>
- <texture_picker label="Alpha - øje" name="Eye Alpha" tool_tip="Klik for at vælge et billede"/>
- <texture_picker label="Alpha - hår" name="Hair Alpha" tool_tip="Klik for at vælge et billede"/>
- </panel>
+ <scroll_container name="avatar_alpha_color_panel_scroll">
+ <panel name="avatar_alpha_color_panel">
+ <texture_picker label="Nedre alpha" name="Lower Alpha" tool_tip="Klik for at vælge et billede"/>
+ <texture_picker label="Øverste alpha" name="Upper Alpha" tool_tip="Klik for at vælge et billede"/>
+ <texture_picker label="Hovede alpha" name="Head Alpha" tool_tip="Klik for at vælge et billede"/>
+ <texture_picker label="Øje alpha" name="Eye Alpha" tool_tip="Klik for at vælge et billede"/>
+ <texture_picker label="Hår alpha" name="Hair Alpha" tool_tip="Klik for at vælge et billede"/>
+ </panel>
+ </scroll_container>
</panel>
diff --git a/indra/newview/skins/default/xui/da/panel_login.xml b/indra/newview/skins/default/xui/da/panel_login.xml
index 268f138185..dc8d9bc432 100644
--- a/indra/newview/skins/default/xui/da/panel_login.xml
+++ b/indra/newview/skins/default/xui/da/panel_login.xml
@@ -3,9 +3,6 @@
<panel.string name="create_account_url">
http://join.secondlife.com/
</panel.string>
- <panel.string name="real_url">
- http://secondlife.com/app/login/
- </panel.string>
<panel.string name="forgot_password_url">
http://secondlife.com/account/request.php
</panel.string>
@@ -14,7 +11,7 @@
<text name="username_text">
Brugernavn:
</text>
- <line_editor label="bobsmith12 eller Steller Sunshine" name="username_edit" tool_tip="Det brugernavn du valgte da du registrerede, som f.eks. bobsmith12 eller Steller Sunshine"/>
+ <combo_box name="username_combo" tool_tip="Brugernavnet du valgte da du registrerde dig, som f.eks. bobsmith12 or Steller Sunshine"/>
<text name="password_text">
Password:
</text>
diff --git a/indra/newview/skins/default/xui/da/panel_my_profile.xml b/indra/newview/skins/default/xui/da/panel_my_profile.xml
index 2db4b278d7..94da58389f 100644
--- a/indra/newview/skins/default/xui/da/panel_my_profile.xml
+++ b/indra/newview/skins/default/xui/da/panel_my_profile.xml
@@ -5,30 +5,27 @@
<string name="RegisterDateFormat">
[REG_DATE] ([AGE])
</string>
+ <string name="name_text_args">
+ [NAME]
+ </string>
+ <string name="display_name_text_args">
+ [DISPLAY_NAME]
+ </string>
<layout_stack name="layout">
<layout_panel name="profile_stack">
<scroll_container name="profile_scroll">
<panel name="scroll_content_panel">
<panel name="second_life_image_panel">
- <icon label="" name="2nd_life_edit_icon" tool_tip="Klik på Redigér profil knappen forneden for at ændre billede"/>
- <text name="title_sl_descr_text" value="[SECOND_LIFE]:"/>
- </panel>
- <panel name="first_life_image_panel">
- <icon label="" name="real_world_edit_icon" tool_tip="Klik på Redigér profil knappen forneden for at ændre billede"/>
- <text name="title_rw_descr_text" value="Real World:"/>
- </panel>
- <text name="title_member_text" value="Beboer siden:"/>
- <text name="title_acc_status_text" value="Konto status:"/>
- <text name="title_partner_text" value="Partner:"/>
- <panel name="partner_data_panel">
- <name_box initial_value="(henter)" name="partner_text"/>
+ <text name="display_name_descr_text">
+ Brugernavn
+ </text>
+ <text name="name_descr_text">
+ Visningsnavn
+ </text>
+ <button label="Profil" name="see_profile_btn" tool_tip="Se profil for denne avatar"/>
</panel>
- <text name="title_groups_text" value="Grupper:"/>
</panel>
</scroll_container>
</layout_panel>
</layout_stack>
- <panel name="profile_me_buttons_panel">
- <button label="Redigér profil" name="edit_profile_btn" tool_tip="Redigér din personlige information"/>
- </panel>
</panel>
diff --git a/indra/newview/skins/default/xui/da/panel_notify_textbox.xml b/indra/newview/skins/default/xui/da/panel_notify_textbox.xml
index 949ff1a058..30ad4ff9f6 100644
--- a/indra/newview/skins/default/xui/da/panel_notify_textbox.xml
+++ b/indra/newview/skins/default/xui/da/panel_notify_textbox.xml
@@ -3,8 +3,9 @@
<string name="message_max_lines_count" value="7"/>
<panel label="info_panel" name="info_panel">
<text_editor name="message" value="besked"/>
- parse_urls=&quot;false&quot;
+ </panel>
+ <panel label="control_panel" name="control_panel">
<button label="Send" name="btn_submit"/>
+ <button label="Ignorér" name="ignore_btn"/>
</panel>
- <panel label="control_panel" name="control_panel"/>
</panel>
diff --git a/indra/newview/skins/default/xui/da/panel_people.xml b/indra/newview/skins/default/xui/da/panel_people.xml
index 599686d360..b85a33279a 100644
--- a/indra/newview/skins/default/xui/da/panel_people.xml
+++ b/indra/newview/skins/default/xui/da/panel_people.xml
@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- Side tray panel -->
<panel label="Personer" name="people_panel">
- <string name="no_recent_people" value="Ingen tidligere personer. Leder du efter nogen at være sammen med? Prøv [secondlife:///app/search/people Search] eller [secondlife:///app/worldmap World Map]."/>
- <string name="no_filtered_recent_people" value="Fandt du ikke det du søgte? Prøv [secondlife:///app/search/people/[SEARCH_TERM] Search]."/>
- <string name="no_one_near" value="Ingen i nærheden. Leder du efter nogen at være sammen med? Prøv [secondlife:///app/search/people Search] eller [secondlife:///app/worldmap World Map]."/>
- <string name="no_one_filtered_near" value="Fandt du ikke det du søgte? Prøv [secondlife:///app/search/people/[SEARCH_TERM] Search]."/>
+ <string name="no_recent_people" value="Ingen tidligere personer. Leder du efter nogen at være sammen med? Prøv [secondlife:///app/search/people Søg] eller [secondlife:///app/worldmap Verdenskort]."/>
+ <string name="no_filtered_recent_people" value="Fandt du ikke det du søgte? Prøv [secondlife:///app/search/people/[SEARCH_TERM] Søg]."/>
+ <string name="no_one_near" value="Ingen i nærheden. Leder du efter nogen at være sammen med? Prøv [secondlife:///app/search/people Søg] eller [secondlife:///app/worldmap Verdenskort]."/>
+ <string name="no_one_filtered_near" value="Fandt du ikke det du søgte? Prøv [secondlife:///app/search/people/[SEARCH_TERM] Søg]."/>
<string name="no_friends_online" value="Ingen venner online"/>
<string name="no_friends" value="Ingen venner"/>
<string name="no_friends_msg">
- Find venner via [secondlife:///app/search/people Search] eller højre-klik på en beboer og tilføj dem som venner.
-Leder du efter nogen at være sammen med? Prøv [secondlife:///app/worldmap World Map].
+ Find venner via [secondlife:///app/search/people Søg] eller højre-klik på en beboer og tilføj dem som venner.
+Leder du efter nogen at være sammen med? Prøv [secondlife:///app/worldmap Verdenskort].
</string>
<string name="no_filtered_friends_msg">
- Fandt du ikke det du søgte? Prøv [secondlife:///app/search/people/[SEARCH_TERM] Search].
+ Fandt du ikke det du søgte? Prøv [secondlife:///app/search/people/[SEARCH_TERM] Søg].
</string>
<string name="people_filter_label" value="Filtrér personer"/>
<string name="groups_filter_label" value="Filtrér grupper"/>
- <string name="no_filtered_groups_msg" value="Fandt du ikke det du søgte? Prøv [secondlife:///app/search/groups/[SEARCH_TERM] Search]."/>
- <string name="no_groups_msg" value="Leder du efter grupper at være med i? Prøv [secondlife:///app/search/groups Search]."/>
+ <string name="no_filtered_groups_msg" value="Fandt du ikke det du søgte? Prøv [secondlife:///app/search/groups/[SEARCH_TERM] Søg]."/>
+ <string name="no_groups_msg" value="Leder du efter grupper at være med i? Prøv [secondlife:///app/search/groups Søg]."/>
<filter_editor label="Filtrér" name="filter_input"/>
<tab_container name="tabs">
<panel label="TÆT PÅ" name="nearby_panel">
diff --git a/indra/newview/skins/default/xui/da/panel_preferences_colors.xml b/indra/newview/skins/default/xui/da/panel_preferences_colors.xml
index 604a00e0b4..b2b00db769 100644
--- a/indra/newview/skins/default/xui/da/panel_preferences_colors.xml
+++ b/indra/newview/skins/default/xui/da/panel_preferences_colors.xml
@@ -29,10 +29,10 @@
URL&apos;er
</text>
<text name="bubble_chat">
- Chat-boble baggrund:
+ Baggrundsfarve til navne-skilt (berører også Bubble Chat):
</text>
- <color_swatch name="background" tool_tip="Vælg farve til chat-boble"/>
- <slider label="Uigennemsigtighed:" name="bubble_chat_opacity"/>
+ <color_swatch name="background" tool_tip="Vælg farve til navne-skilt"/>
+ <slider label="Uigennemsigtighed:" name="bubble_chat_opacity" tool_tip="Vælg gennemsigtighed for navneskilt"/>
<text name="floater_opacity">
Vindue uigennemsigtighed:
</text>
diff --git a/indra/newview/skins/default/xui/da/panel_preferences_privacy.xml b/indra/newview/skins/default/xui/da/panel_preferences_privacy.xml
index 2843f0d339..0df330b016 100644
--- a/indra/newview/skins/default/xui/da/panel_preferences_privacy.xml
+++ b/indra/newview/skins/default/xui/da/panel_preferences_privacy.xml
@@ -7,9 +7,11 @@
<text name="cache_size_label_l">
(Lokationer, billeder, web, søge historik)
</text>
+ <check_box label="Vis dig selv i søgeresultater" name="online_searchresults"/>
<check_box label="Kun venner og grupper ved jeg er online" name="online_visibility"/>
<check_box label="Kun venner og grupper kan sende besked til mig" name="voice_call_friends_only_check"/>
<check_box label="Slå mikrofon fra når opkald slutter" name="auto_disengage_mic_check"/>
+ <check_box label="Vis mine favorit landemærker ved login (via &quot;Start ved&quot; menuen)" name="favorites_on_login_check"/>
<text name="Logs:">
Chat Logs:
</text>
diff --git a/indra/newview/skins/default/xui/da/panel_preferences_setup.xml b/indra/newview/skins/default/xui/da/panel_preferences_setup.xml
index 332b5ed1c4..479e98817e 100644
--- a/indra/newview/skins/default/xui/da/panel_preferences_setup.xml
+++ b/indra/newview/skins/default/xui/da/panel_preferences_setup.xml
@@ -39,5 +39,11 @@
</text>
<line_editor name="web_proxy_editor" tool_tip="Angiv navn eller IP addresse på den proxy du ønsker at anvende"/>
<spinner label="Port nummer:" name="web_proxy_port"/>
- <check_box initial_value="sand" label="Hent og installer automatisk [APP_NAME] opdateringer" name="updater_service_active"/>
+ <text name="Software updates:">
+ Software opdateringer:
+ </text>
+ <combo_box name="updater_service_combobox">
+ <combo_box.item label="Installér automatisk" name="Install_automatically"/>
+ <combo_box.item label="Hent og installér opdateringer manuelt" name="Install_manual"/>
+ </combo_box>
</panel>
diff --git a/indra/newview/skins/default/xui/da/panel_preferences_sound.xml b/indra/newview/skins/default/xui/da/panel_preferences_sound.xml
index 75600a93f6..5810cc21e7 100644
--- a/indra/newview/skins/default/xui/da/panel_preferences_sound.xml
+++ b/indra/newview/skins/default/xui/da/panel_preferences_sound.xml
@@ -9,7 +9,7 @@
<slider label="Omgivelser" name="Wind Volume"/>
<slider label="Lyd effekter" name="SFX Volume"/>
<slider label="Musik" name="Music Volume"/>
- <check_box label="Aktiveret" name="music_enabled"/>
+ <check_box label="Aktiveret" name="enable_music"/>
<slider label="Media" name="Media Volume"/>
<check_box label="Aktiveret" name="enable_media"/>
<slider label="Stemme chat" name="Voice Volume"/>
diff --git a/indra/newview/skins/default/xui/da/panel_status_bar.xml b/indra/newview/skins/default/xui/da/panel_status_bar.xml
index 8633f12d24..6e7bdfc188 100644
--- a/indra/newview/skins/default/xui/da/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/da/panel_status_bar.xml
@@ -22,7 +22,7 @@
L$ [AMT]
</panel.string>
<panel name="balance_bg">
- <text name="balance" tool_tip="Min status" value="L$20"/>
+ <text name="balance" tool_tip="Klik for at opdaterer din L$ balance" value="L$20"/>
<button label="KØB L$" name="buyL" tool_tip="Klik for at købe flere L$"/>
</panel>
<text name="TimeText" tool_tip="Nuværende tid (Pacific)">
diff --git a/indra/newview/skins/default/xui/da/strings.xml b/indra/newview/skins/default/xui/da/strings.xml
index 6f891b8d1b..aa02fc14e5 100644
--- a/indra/newview/skins/default/xui/da/strings.xml
+++ b/indra/newview/skins/default/xui/da/strings.xml
@@ -1843,34 +1843,34 @@ Forventet .wav, .tga, .bmp, .jpg, .jpeg, or .bvh
<string name="PDT">
PDT
</string>
- <string name="Forward">
+ <string name="Direction_Forward">
Fremad
</string>
- <string name="Left">
+ <string name="Direction_Left">
Venstre
</string>
- <string name="Right">
+ <string name="Direction_Right">
Højre
</string>
- <string name="Back">
+ <string name="Direction_Back">
Bagud
</string>
- <string name="North">
+ <string name="Direction_North">
Nord
</string>
- <string name="South">
+ <string name="Direction_South">
Syd
</string>
- <string name="West">
+ <string name="Direction_West">
Vest
</string>
- <string name="East">
+ <string name="Direction_East">
Øst
</string>
- <string name="Up">
+ <string name="Direction_Up">
Op
</string>
- <string name="Down">
+ <string name="Direction_Down">
Ned
</string>
<string name="Any Category">
diff --git a/indra/newview/skins/default/xui/de/panel_preferences_sound.xml b/indra/newview/skins/default/xui/de/panel_preferences_sound.xml
index 26674ea594..0f029d8664 100644
--- a/indra/newview/skins/default/xui/de/panel_preferences_sound.xml
+++ b/indra/newview/skins/default/xui/de/panel_preferences_sound.xml
@@ -9,7 +9,7 @@
<slider label="Umgebung" name="Wind Volume"/>
<slider label="Soundeffekte" name="SFX Volume"/>
<slider label="Musik wird gestreamt" name="Music Volume"/>
- <check_box label="Aktiviert" name="music_enabled"/>
+ <check_box label="Aktiviert" name="enable_music"/>
<slider label="Medien" name="Media Volume"/>
<check_box label="Aktiviert" name="enable_media"/>
<slider label="Voice-Chat" name="Voice Volume"/>
diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml
index e4676194aa..7284e40be2 100644
--- a/indra/newview/skins/default/xui/de/strings.xml
+++ b/indra/newview/skins/default/xui/de/strings.xml
@@ -1888,34 +1888,34 @@ Gültige Formate: .wav, .tga, .bmp, .jpg, .jpeg oder .bvh
<string name="PDT">
PDT
</string>
- <string name="Forward">
+ <string name="Direction_Forward">
Vorwärts
</string>
- <string name="Left">
+ <string name="Direction_Left">
Links
</string>
- <string name="Right">
+ <string name="Direction_Right">
Rechts
</string>
- <string name="Back">
+ <string name="Direction_Back">
Hinten
</string>
- <string name="North">
+ <string name="Direction_North">
Norden
</string>
- <string name="South">
+ <string name="Direction_South">
Süden
</string>
- <string name="West">
+ <string name="Direction_West">
Westen
</string>
- <string name="East">
+ <string name="Direction_East">
Osten
</string>
- <string name="Up">
+ <string name="Direction_Up">
Nach oben
</string>
- <string name="Down">
+ <string name="Direction_Down">
Nach unten
</string>
<string name="Any Category">
diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml
index 3dd6c60095..937a97797d 100644
--- a/indra/newview/skins/default/xui/en/floater_about_land.xml
+++ b/indra/newview/skins/default/xui/en/floater_about_land.xml
@@ -1905,7 +1905,7 @@ Only large parcels can be listed in search.
</panel.string>
<panel.string
name="allow_public_access">
- Allow Public Access ([MATURITY])
+ Allow Public Access ([MATURITY]) (Note: Unchecking this will create ban lines)
</panel.string>
<panel.string
name="estate_override">
@@ -1932,7 +1932,7 @@ Only large parcels can be listed in search.
name="public_access"
top_pad="5"
label_text.valign="center"
- label_text.v_pad="-7"
+ label_text.v_pad="-2"
width="278" />
<text
type="string"
diff --git a/indra/newview/skins/default/xui/en/floater_map.xml b/indra/newview/skins/default/xui/en/floater_map.xml
index 6370ff9243..ae99fa8dd5 100644
--- a/indra/newview/skins/default/xui/en/floater_map.xml
+++ b/indra/newview/skins/default/xui/en/floater_map.xml
@@ -22,7 +22,11 @@
name="ToolTipMsg">
[REGION](Double-click to open Map, shift-drag to pan)
</floater.string>
- <floater.string name="mini_map_caption">
+ <floater.string
+ name="AltToolTipMsg">
+ [REGION](Double-click to teleport, shift-drag to pan)
+ </floater.string>
+ <floater.string name="mini_map_caption">
MINIMAP
</floater.string>
<net_map
diff --git a/indra/newview/skins/default/xui/en/floater_region_debug_console.xml b/indra/newview/skins/default/xui/en/floater_region_debug_console.xml
index cf95257b0a..7c7ee2df4c 100644
--- a/indra/newview/skins/default/xui/en/floater_region_debug_console.xml
+++ b/indra/newview/skins/default/xui/en/floater_region_debug_console.xml
@@ -2,6 +2,7 @@
<floater
name="region_debug_console"
title="Region Debug"
+ can_resize="true"
layout="topleft"
min_height="300"
min_width="300"
@@ -12,7 +13,7 @@
left="10"
type="string"
length="1"
- follows="left|top|right|bottom"
+ follows="left|right|bottom"
font="Monospace"
height="366"
width="576"
diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml
index 1808fea445..b16124cb7e 100644
--- a/indra/newview/skins/default/xui/en/floater_tools.xml
+++ b/indra/newview/skins/default/xui/en/floater_tools.xml
@@ -878,7 +878,7 @@
top_delta="0"
width="190"
word_wrap="true"
- use_ellipses="ture">
+ use_ellipses="true">
Mrs. Esbee Linden (esbee.linden)
</text>
<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 6ec063cd26..e04a72cbc0 100644
--- a/indra/newview/skins/default/xui/en/floater_web_content.xml
+++ b/indra/newview/skins/default/xui/en/floater_web_content.xml
@@ -12,7 +12,7 @@
auto_tile="true"
title=""
initial_mime_type="text/html"
- width="735">
+ width="780">
<layout_stack
bottom="775"
follows="left|right|top|bottom"
@@ -21,7 +21,7 @@
name="stack1"
orientation="vertical"
top="20"
- width="725">
+ width="770">
<layout_panel
auto_resize="false"
default_tab_group="1"
@@ -32,7 +32,7 @@
name="nav_controls"
top="400"
user_resize="false"
- width="725">
+ width="770">
<button
image_overlay="Arrow_Left_Off"
image_disabled="PushButton_Disabled"
@@ -115,7 +115,7 @@
combo_editor.select_on_focus="true"
tool_tip="Enter URL here"
top_delta="0"
- width="627">
+ width="672">
<combo_box.commit_callback
function="WebContent.EnterAddress" />
</combo_box>
@@ -125,7 +125,7 @@
follows="top|right"
image_name="Lock2"
layout="topleft"
- left_delta="575"
+ left_delta="620"
top_delta="2"
visible="false"
tool_tip="Secured Browsing"
@@ -142,7 +142,7 @@
height="22"
layout="topleft"
name="popexternal"
- right="725"
+ right="770"
top_delta="-2"
width="22">
<button.commit_callback
@@ -156,7 +156,7 @@
name="external_controls"
top_delta="0"
user_resize="false"
- width="540">
+ width="585">
<web_browser
bottom="-22"
follows="all"
@@ -175,7 +175,7 @@
parse_urls="false"
text_color="0.4 0.4 0.4 1"
top_pad="5"
- width="520"/>
+ width="495"/>
<progress_bar
color_bar="0.3 1.0 0.3 1"
follows="bottom|right"
diff --git a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml
index 58d58a6ca9..76b188220d 100644
--- a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml
+++ b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml
@@ -78,7 +78,7 @@
<menu_item_call.on_click
function="InspectAvatar.Freeze"/>
<menu_item_call.on_visible
- function="InspectAvatar.VisibleFreezeEject"/>
+ function="InspectAvatar.VisibleFreeze"/>
</menu_item_call>
<menu_item_call
label="Eject"
@@ -86,7 +86,23 @@
<menu_item_call.on_click
function="InspectAvatar.Eject"/>
<menu_item_call.on_visible
- function="InspectAvatar.VisibleFreezeEject"/>
+ function="InspectAvatar.VisibleEject"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Kick"
+ name="kick">
+ <menu_item_call.on_click
+ function="InspectAvatar.Kick"/>
+ <menu_item_call.on_visible
+ function="InspectAvatar.EnableGod"/>
+ </menu_item_call>
+ <menu_item_call
+ label="CSR"
+ name="csr">
+ <menu_item_call.on_click
+ function="InspectAvatar.CSR" />
+ <menu_item_call.on_visible
+ function="InspectAvatar.EnableGod" />
</menu_item_call>
<menu_item_call
label="Debug Textures"
diff --git a/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml b/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml
index 7fa4cd840a..d2519a5aa4 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml
@@ -39,6 +39,17 @@
parameter="sort_by_recent" />
</menu_item_check>
<menu_item_check
+ label="Sort Folders Always by Name"
+ layout="topleft"
+ name="sort_folders_by_name">
+ <on_click
+ function="Inventory.GearDefault.Custom.Action"
+ parameter="sort_folders_by_name" />
+ <on_check
+ function="Inventory.GearDefault.Check"
+ parameter="sort_folders_by_name" />
+ </menu_item_check>
+ <menu_item_check
label="Sort System Folders to Top"
layout="topleft"
name="sort_system_folders_to_top">
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index c2735c85e4..606ff69599 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -1990,6 +1990,16 @@
function="ToggleControl"
parameter="DebugShowColor" />
</menu_item_check>
+ <menu_item_check
+ label="Show Memory"
+ name="Show Memory">
+ <menu_item_check.on_check
+ function="CheckControl"
+ parameter="DebugShowMemory" />
+ <menu_item_check.on_click
+ function="ToggleControl"
+ parameter="DebugShowMemory" />
+ </menu_item_check>
<menu_item_separator/>
@@ -2737,18 +2747,6 @@
function="Floater.Toggle"
parameter="region_debug_console" />
</menu_item_check>
- <menu_item_check
- label="Region Debug Console"
- name="Region Debug Console"
- shortcut="control|shift|`"
- use_mac_ctrl="true">
- <menu_item_check.on_check
- function="Floater.Visible"
- parameter="region_debug_console" />
- <menu_item_check.on_click
- function="Floater.Toggle"
- parameter="region_debug_console" />
- </menu_item_check>
<menu_item_separator />
diff --git a/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml
index 5871eb0654..21c627cdfb 100644
--- a/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml
+++ b/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml
@@ -45,9 +45,9 @@
left_pad="4"
image_disabled="ComboButton_UpOff"
image_unselected="ComboButton_UpOff"
- image_selected="ComboButton_Up_On_Selected"
+ image_selected="ComboButton_On"
image_pressed="ComboButton_UpSelected"
- image_pressed_selected="ComboButton_Up_On_Selected"
+ image_pressed_selected="ComboButton_Selected"
height="23"
name="show_nearby_chat"
tool_tip="Shows/hides nearby chat log">
diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml
index 6a8bf87bc5..43431ea7c1 100644
--- a/indra/newview/skins/default/xui/en/panel_people.xml
+++ b/indra/newview/skins/default/xui/en/panel_people.xml
@@ -54,7 +54,13 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M
<string
name="no_groups_msg"
value="Looking for Groups to join? Try [secondlife:///app/search/groups Search]." />
- <filter_editor
+ <string
+ name="MiniMapToolTipMsg"
+ value="[REGION](Double-click to open Map, shift-drag to pan)"/>
+ <string
+ name="AltMiniMapToolTipMsg"
+ value="[REGION](Double-click to teleport, shift-drag to pan)"/>
+ <filter_editor
follows="left|top|right"
height="23"
layout="topleft"
@@ -93,16 +99,26 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M
name="nearby_panel"
top="0"
width="313">
- <avatar_list
+ <net_map
+ bg_color="NetMapBackgroundColor"
+ follows="top|left|right"
+ layout="topleft"
+ left="3"
+ mouse_opaque="false"
+ name="Net Map"
+ width="307"
+ height="140"
+ top="0"/>
+ <avatar_list
allow_select="true"
- follows="all"
- height="356"
+ follows="top|left|bottom|right"
+ height="216"
ignore_online_status="true"
layout="topleft"
left="3"
multi_select="true"
name="avatar_list"
- top="0"
+ top="145"
width="307" />
<panel
background_visible="true"
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml
index f0ce8b849a..26af8dc29d 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml
@@ -198,9 +198,12 @@
label="Enabled"
layout="topleft"
left_pad="5"
- name="music_enabled"
+ name="enable_music"
top_delta="2"
- width="350"/>
+ width="350">
+ <check_box.commit_callback
+ function="Pref.updateMediaAutoPlayCheckbox"/>
+ </check_box>
<slider
control_name="AudioLevelMedia"
disabled_control="MuteAudio"
@@ -245,7 +248,10 @@
top_delta="2"
left_pad="5"
name="enable_media"
- width="110"/>
+ width="110">
+ <check_box.commit_callback
+ function="Pref.updateMediaAutoPlayCheckbox"/>
+ </check_box>
<slider
control_name="AudioLevelVoice"
disabled_control="MuteAudio"
diff --git a/indra/newview/skins/default/xui/en/panel_profile.xml b/indra/newview/skins/default/xui/en/panel_profile.xml
index 61e3bb354f..d36220385d 100644
--- a/indra/newview/skins/default/xui/en/panel_profile.xml
+++ b/indra/newview/skins/default/xui/en/panel_profile.xml
@@ -34,6 +34,14 @@
name="RegisterDateFormat">
[REG_DATE] ([AGE])
</string>
+ <string
+ name="name_text_args">
+ [NAME]
+ </string>
+ <string
+ name="display_name_text_args">
+ [DISPLAY_NAME]
+ </string>
<layout_stack
name="layout"
orientation="vertical"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 752bb6ed3a..70a40960a1 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -1948,7 +1948,7 @@ Requests name of an avatar. When data is available the dataserver event will be
<!-- Gestures labels -->
<!-- use value="" because they have preceding spaces -->
- <string name="Chat" value=" Chat : " />
+ <string name="Chat Message" value=" Chat : " />
<string name="Sound" value=" Sound : " />
<string name="Wait" value=" --- Wait : " />
<string name="AnimFlagStop" value=" Stop Animation : " />
@@ -2321,9 +2321,6 @@ Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh
<string name="accel-win-alt">Alt+</string>
<string name="accel-win-shift">Shift+</string>
- <string name="Esc">Esc</string>
- <string name="Home">Home</string>
-
<!-- Previews -->
<string name="FileSaved">File Saved</string>
<string name="Receiving">Receiving</string>
@@ -2335,16 +2332,16 @@ Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh
<string name="PDT">PDT</string>
<!-- Directions, HUD -->
- <string name="Forward">Forward</string>
- <string name="Left">Left</string>
- <string name="Right">Right</string>
- <string name="Back">Back</string>
- <string name="North">North</string>
- <string name="South">South</string>
- <string name="West">West</string>
- <string name="East">East</string>
- <string name="Up">Up</string>
- <string name="Down">Down</string>
+ <string name="Direction_Forward">Forward</string>
+ <string name="Direction_Left">Left</string>
+ <string name="Direction_Right">Right</string>
+ <string name="Direction_Back">Back</string>
+ <string name="Direction_North">North</string>
+ <string name="Direction_South">South</string>
+ <string name="Direction_West">West</string>
+ <string name="Direction_East">East</string>
+ <string name="Direction_Up">Up</string>
+ <string name="Direction_Down">Down</string>
<!-- Search Category Strings -->
<string name="Any Category">Any Category</string>
@@ -3313,4 +3310,119 @@ Abuse Report</string>
<string name="EmptyOutfitText">There are no items in this outfit</string>
+ <!-- Key names begin -->
+ <string name="Esc">Esc</string>
+ <string name="Space">Space</string>
+ <string name="Enter">Enter</string>
+ <string name="Tab">Tab</string>
+ <string name="Ins">Ins</string>
+ <string name="Del">Del</string>
+ <string name="Backsp">Backsp</string>
+ <string name="Shift">Shift</string>
+ <string name="Ctrl">Ctrl</string>
+ <string name="Alt">Alt</string>
+ <string name="CapsLock">CapsLock</string>
+ <string name="Left">Left</string>
+ <string name="Right">Right</string>
+ <string name="Up">Up</string>
+ <string name="Down">Down</string>
+ <string name="Home">Home</string>
+ <string name="End">End</string>
+ <string name="PgUp">PgUp</string>
+ <string name="PgDn">PgDn</string>
+
+ <string name="F1">F1</string>
+ <string name="F2">F2</string>
+ <string name="F3">F3</string>
+ <string name="F4">F4</string>
+ <string name="F5">F5</string>
+ <string name="F6">F6</string>
+ <string name="F7">F7</string>
+ <string name="F8">F8</string>
+ <string name="F9">F9</string>
+ <string name="F10">F10</string>
+ <string name="F11">F11</string>
+ <string name="F12">F12</string>
+
+ <string name="Add">Add</string>
+ <string name="Subtract">Subtract</string>
+ <string name="Multiply">Multiply</string>
+ <string name="Divide">Divide</string>
+ <string name="PAD_DIVIDE">PAD_DIVIDE</string>
+ <string name="PAD_LEFT">PAD_LEFT</string>
+ <string name="PAD_RIGHT">PAD_RIGHT</string>
+ <string name="PAD_DOWN">PAD_DOWN</string>
+ <string name="PAD_UP">PAD_UP</string>
+ <string name="PAD_HOME">PAD_HOME</string>
+ <string name="PAD_END">PAD_END</string>
+ <string name="PAD_PGUP">PAD_PGUP</string>
+ <string name="PAD_PGDN">PAD_PGDN</string>
+ <string name="PAD_CENTER">PAD_CENTER</string>
+ <string name="PAD_INS">PAD_INS</string>
+ <string name="PAD_DEL">PAD_DEL</string>
+ <string name="PAD_Enter">PAD_Enter</string>
+ <string name="PAD_BUTTON0">PAD_BUTTON0</string>
+ <string name="PAD_BUTTON1">PAD_BUTTON1</string>
+ <string name="PAD_BUTTON2">PAD_BUTTON2</string>
+ <string name="PAD_BUTTON3">PAD_BUTTON3</string>
+ <string name="PAD_BUTTON4">PAD_BUTTON4</string>
+ <string name="PAD_BUTTON5">PAD_BUTTON5</string>
+ <string name="PAD_BUTTON6">PAD_BUTTON6</string>
+ <string name="PAD_BUTTON7">PAD_BUTTON7</string>
+ <string name="PAD_BUTTON8">PAD_BUTTON8</string>
+ <string name="PAD_BUTTON9">PAD_BUTTON9</string>
+ <string name="PAD_BUTTON10">PAD_BUTTON10</string>
+ <string name="PAD_BUTTON11">PAD_BUTTON11</string>
+ <string name="PAD_BUTTON12">PAD_BUTTON12</string>
+ <string name="PAD_BUTTON13">PAD_BUTTON13</string>
+ <string name="PAD_BUTTON14">PAD_BUTTON14</string>
+ <string name="PAD_BUTTON15">PAD_BUTTON15</string>
+
+ <string name="-">-</string>
+ <string name="=">=</string>
+ <string name="`">`</string>
+ <string name=";">;</string>
+ <string name="[">[</string>
+ <string name="]">]</string>
+ <string name="\">\</string>
+
+ <string name="0">0</string>
+ <string name="1">1</string>
+ <string name="2">2</string>
+ <string name="3">3</string>
+ <string name="4">4</string>
+ <string name="5">5</string>
+ <string name="6">6</string>
+ <string name="7">7</string>
+ <string name="8">8</string>
+ <string name="9">9</string>
+
+ <string name="A">A</string>
+ <string name="B">B</string>
+ <string name="C">C</string>
+ <string name="D">D</string>
+ <string name="E">E</string>
+ <string name="F">F</string>
+ <string name="G">G</string>
+ <string name="H">H</string>
+ <string name="I">I</string>
+ <string name="J">J</string>
+ <string name="K">K</string>
+ <string name="L">L</string>
+ <string name="M">M</string>
+ <string name="N">N</string>
+ <string name="O">O</string>
+ <string name="P">P</string>
+ <string name="Q">Q</string>
+ <string name="R">R</string>
+ <string name="S">S</string>
+ <string name="T">T</string>
+ <string name="U">U</string>
+ <string name="V">V</string>
+ <string name="W">W</string>
+ <string name="X">X</string>
+ <string name="Y">Y</string>
+ <string name="Z">Z</string>
+ <!-- Key names end -->
+
</strings>
diff --git a/indra/newview/skins/default/xui/en/widgets/talk_button.xml b/indra/newview/skins/default/xui/en/widgets/talk_button.xml
index a7e271a1ff..d792e9f29c 100644
--- a/indra/newview/skins/default/xui/en/widgets/talk_button.xml
+++ b/indra/newview/skins/default/xui/en/widgets/talk_button.xml
@@ -23,11 +23,11 @@
bottom="0"
tab_stop="false"
is_toggle="true"
- image_selected="SegmentedBtn_Right_Selected_Press"
- image_unselected="SegmentedBtn_Right_Off"
- image_pressed="SegmentedBtn_Right_Press"
- image_pressed_selected="SegmentedBtn_Right_Selected_Press"
- image_overlay="Arrow_Small_Up"
+ image_disabled="ComboButton_UpOff"
+ image_unselected="ComboButton_UpOff"
+ image_selected="ComboButton_On"
+ image_pressed="ComboButton_UpSelected"
+ image_pressed_selected="ComboButton_Selected"
/>
<monitor
follows="right"
diff --git a/indra/newview/skins/default/xui/es/panel_preferences_sound.xml b/indra/newview/skins/default/xui/es/panel_preferences_sound.xml
index 7989100c09..6c4ab0f14f 100644
--- a/indra/newview/skins/default/xui/es/panel_preferences_sound.xml
+++ b/indra/newview/skins/default/xui/es/panel_preferences_sound.xml
@@ -9,7 +9,7 @@
<slider label="Ambiental" name="Wind Volume"/>
<slider label="Efectos de sonido" name="SFX Volume"/>
<slider label="Música en streaming" name="Music Volume"/>
- <check_box label="Activada" name="music_enabled"/>
+ <check_box label="Activada" name="enable_music"/>
<slider label="Multimedia" name="Media Volume"/>
<check_box label="Activada" name="enable_media"/>
<slider label="Chat de voz" name="Voice Volume"/>
diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml
index 810b1630dd..19adf29d29 100644
--- a/indra/newview/skins/default/xui/es/strings.xml
+++ b/indra/newview/skins/default/xui/es/strings.xml
@@ -1846,34 +1846,34 @@ Se esperaba .wav, .tga, .bmp, .jpg, .jpeg, o .bvh
<string name="PDT">
PDT
</string>
- <string name="Forward">
+ <string name="Direction_Forward">
Adelante
</string>
- <string name="Left">
+ <string name="Direction_Left">
Izquierda
</string>
- <string name="Right">
+ <string name="Direction_Right">
Derecha
</string>
- <string name="Back">
+ <string name="Direction_Back">
Atrás
</string>
- <string name="North">
+ <string name="Direction_North">
Norte
</string>
- <string name="South">
+ <string name="Direction_South">
Sur
</string>
- <string name="West">
+ <string name="Direction_West">
Oeste
</string>
- <string name="East">
+ <string name="Direction_East">
Este
</string>
- <string name="Up">
+ <string name="Direction_Up">
Arriba
</string>
- <string name="Down">
+ <string name="Direction_Down">
Abajo
</string>
<string name="Any Category">
diff --git a/indra/newview/skins/default/xui/fr/floater_preview_gesture.xml b/indra/newview/skins/default/xui/fr/floater_preview_gesture.xml
index 7828d2df97..6287f10f3e 100644
--- a/indra/newview/skins/default/xui/fr/floater_preview_gesture.xml
+++ b/indra/newview/skins/default/xui/fr/floater_preview_gesture.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<floater name="gesture_preview">
<floater.string name="step_anim">
- Animation à jouer :
+ Animation à exécuter :
</floater.string>
<floater.string name="step_sound">
Son à lire :
diff --git a/indra/newview/skins/default/xui/fr/menu_hide_navbar.xml b/indra/newview/skins/default/xui/fr/menu_hide_navbar.xml
index 86a2ddd185..20af901ddc 100644
--- a/indra/newview/skins/default/xui/fr/menu_hide_navbar.xml
+++ b/indra/newview/skins/default/xui/fr/menu_hide_navbar.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<menu name="hide_navbar_menu">
<menu_item_check label="Afficher la barre de navigation" name="ShowNavbarNavigationPanel"/>
- <menu_item_check label="Afficher la barre des Favoris" name="ShowNavbarFavoritesPanel"/>
+ <menu_item_check label="Afficher la barre des favoris" name="ShowNavbarFavoritesPanel"/>
<menu_item_check label="Afficher la mini-barre d&apos;emplacement" name="ShowMiniLocationPanel"/>
</menu>
diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_sound.xml b/indra/newview/skins/default/xui/fr/panel_preferences_sound.xml
index 654d40e2f9..48630918d7 100644
--- a/indra/newview/skins/default/xui/fr/panel_preferences_sound.xml
+++ b/indra/newview/skins/default/xui/fr/panel_preferences_sound.xml
@@ -9,13 +9,13 @@
<slider label="Ambiant" name="Wind Volume"/>
<slider label="Effets sonores" name="SFX Volume"/>
<slider label="Flux musical" name="Music Volume"/>
- <check_box label="Activé" name="music_enabled"/>
+ <check_box label="Activé" name="enable_music"/>
<slider label="Média" name="Media Volume"/>
<check_box label="Activé" name="enable_media"/>
<slider label="Chat vocal" name="Voice Volume"/>
<check_box label="Activé" name="enable_voice_check"/>
<check_box label="Autoriser la lecture automatique du média" name="media_auto_play_btn" tool_tip="Cochez pour autoriser la lecture automatique du média" value="true"/>
- <check_box label="Jouer le média aux autres avatars" name="media_show_on_others_btn" tool_tip="Décochez pour masquer le média aux autres avatars près de vous" value="true"/>
+ <check_box label="Lecture du média aux autres avatars" name="media_show_on_others_btn" tool_tip="Décochez pour masquer le média aux autres avatars près de vous" value="true"/>
<text name="voice_chat_settings">
Paramètres du chat vocal
</text>
diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml
index d75f6c731d..74c1fd8622 100644
--- a/indra/newview/skins/default/xui/fr/strings.xml
+++ b/indra/newview/skins/default/xui/fr/strings.xml
@@ -1888,34 +1888,34 @@
<string name="PDT">
PDT
</string>
- <string name="Forward">
+ <string name="Direction_Forward">
Vers l&apos;avant
</string>
- <string name="Left">
+ <string name="Direction_Left">
Gauche
</string>
- <string name="Right">
+ <string name="Direction_Right">
Droite
</string>
- <string name="Back">
+ <string name="Direction_Back">
Arrière
</string>
- <string name="North">
+ <string name="Direction_North">
Nord
</string>
- <string name="South">
+ <string name="Direction_South">
Sud
</string>
- <string name="West">
+ <string name="Direction_West">
Ouest
</string>
- <string name="East">
+ <string name="Direction_East">
Est
</string>
- <string name="Up">
+ <string name="Direction_Up">
Haut
</string>
- <string name="Down">
+ <string name="Direction_Down">
Bas
</string>
<string name="Any Category">
diff --git a/indra/newview/skins/default/xui/it/panel_preferences_sound.xml b/indra/newview/skins/default/xui/it/panel_preferences_sound.xml
index 2ddb226020..6e70a314c5 100644
--- a/indra/newview/skins/default/xui/it/panel_preferences_sound.xml
+++ b/indra/newview/skins/default/xui/it/panel_preferences_sound.xml
@@ -6,7 +6,7 @@
<slider label="Ambiente" name="Wind Volume"/>
<slider label="Effetti sonori" name="SFX Volume"/>
<slider label="Musica in streaming" name="Music Volume"/>
- <check_box label="Abilitato" name="music_enabled"/>
+ <check_box label="Abilitato" name="enable_music"/>
<slider label="Multimediale" name="Media Volume"/>
<check_box label="Abilitato" name="enable_media"/>
<slider label="Chat vocale" name="Voice Volume"/>
diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml
index dfe635182e..37dc90d056 100644
--- a/indra/newview/skins/default/xui/it/strings.xml
+++ b/indra/newview/skins/default/xui/it/strings.xml
@@ -1846,34 +1846,34 @@ Tipi conosciuti .wav, .tga, .bmp, .jpg, .jpeg, or .bvh
<string name="PDT">
Ora legale Pacifico
</string>
- <string name="Forward">
+ <string name="Direction_Forward">
Avanti
</string>
- <string name="Left">
+ <string name="Direction_Left">
Sinistra
</string>
- <string name="Right">
+ <string name="Direction_Right">
Destra
</string>
- <string name="Back">
+ <string name="Direction_Back">
Indietro
</string>
- <string name="North">
+ <string name="Direction_North">
Nord
</string>
- <string name="South">
+ <string name="Direction_South">
Sud
</string>
- <string name="West">
+ <string name="Direction_West">
Ovest
</string>
- <string name="East">
+ <string name="Direction_East">
Est
</string>
- <string name="Up">
+ <string name="Direction_Up">
Su
</string>
- <string name="Down">
+ <string name="Direction_Down">
Giù
</string>
<string name="Any Category">
diff --git a/indra/newview/skins/default/xui/ja/panel_preferences_sound.xml b/indra/newview/skins/default/xui/ja/panel_preferences_sound.xml
index 4f29ae7b44..9fbbd46220 100644
--- a/indra/newview/skins/default/xui/ja/panel_preferences_sound.xml
+++ b/indra/newview/skins/default/xui/ja/panel_preferences_sound.xml
@@ -6,7 +6,7 @@
<slider label="風" name="Wind Volume"/>
<slider label="効果音" name="SFX Volume"/>
<slider label="ストリーミング音楽" name="Music Volume"/>
- <check_box label="有効" name="music_enabled"/>
+ <check_box label="有効" name="enable_music"/>
<slider label="メディア" name="Media Volume"/>
<check_box label="有効" name="enable_media"/>
<slider label="ボイスチャット" name="Voice Volume"/>
diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml
index 187f21257a..75cb126874 100644
--- a/indra/newview/skins/default/xui/ja/strings.xml
+++ b/indra/newview/skins/default/xui/ja/strings.xml
@@ -1882,34 +1882,34 @@
<string name="PDT">
太平洋夏時間
</string>
- <string name="Forward">
+ <string name="Direction_Forward">
</string>
- <string name="Left">
+ <string name="Direction_Left">
</string>
- <string name="Right">
+ <string name="Direction_Right">
</string>
- <string name="Back">
+ <string name="Direction_Back">
後ろ
</string>
- <string name="North">
+ <string name="Direction_North">
</string>
- <string name="South">
+ <string name="Direction_South">
</string>
- <string name="West">
+ <string name="Direction_West">
西
</string>
- <string name="East">
+ <string name="Direction_East">
</string>
- <string name="Up">
+ <string name="Direction_Up">
</string>
- <string name="Down">
+ <string name="Direction_Down">
</string>
<string name="Any Category">
diff --git a/indra/newview/skins/default/xui/nl/strings.xml b/indra/newview/skins/default/xui/nl/strings.xml
index 07265d2716..87e3638a49 100644
--- a/indra/newview/skins/default/xui/nl/strings.xml
+++ b/indra/newview/skins/default/xui/nl/strings.xml
@@ -1441,34 +1441,34 @@ Verwacht .wav, .tga, .bmp, .jpg, .jpeg, or .bvh
<string name="PDT">
PDT
</string>
- <string name="Forward">
+ <string name="Direction_Forward">
Vooruit
</string>
- <string name="Left">
+ <string name="Direction_Left">
Links
</string>
- <string name="Right">
+ <string name="Direction_Right">
Rechts
</string>
- <string name="Back">
+ <string name="Direction_Back">
Achteruit
</string>
- <string name="North">
+ <string name="Direction_North">
Noord
</string>
- <string name="South">
+ <string name="Direction_South">
Zuid
</string>
- <string name="West">
+ <string name="Direction_West">
West
</string>
- <string name="East">
+ <string name="Direction_East">
Oost
</string>
- <string name="Up">
+ <string name="Direction_Up">
Omhoog
</string>
- <string name="Down">
+ <string name="Direction_Down">
Omlaag
</string>
<string name="Any Category">
diff --git a/indra/newview/skins/default/xui/pl/panel_preferences_sound.xml b/indra/newview/skins/default/xui/pl/panel_preferences_sound.xml
index c708cc0b99..ac93949a1b 100644
--- a/indra/newview/skins/default/xui/pl/panel_preferences_sound.xml
+++ b/indra/newview/skins/default/xui/pl/panel_preferences_sound.xml
@@ -9,7 +9,7 @@
<slider label="Otoczenie" name="Wind Volume"/>
<slider label="Efekty dźwiękowe" name="SFX Volume"/>
<slider label="Muzyka strumieniowa" name="Music Volume"/>
- <check_box label="Odtwarzaj media audio" name="music_enabled"/>
+ <check_box label="Odtwarzaj media audio" name="enable_music"/>
<slider label="Media" name="Media Volume"/>
<check_box label="Odtwarzaj media" name="enable_media"/>
<slider label="Komunikacja głosowa" name="Voice Volume"/>
diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml
index d1fb382a2d..e6019bf66d 100644
--- a/indra/newview/skins/default/xui/pl/strings.xml
+++ b/indra/newview/skins/default/xui/pl/strings.xml
@@ -1843,34 +1843,34 @@ Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh
<string name="PDT">
PDT
</string>
- <string name="Forward">
+ <string name="Direction_Forward">
Do przodu
</string>
- <string name="Left">
+ <string name="Direction_Left">
W lewo
</string>
- <string name="Right">
+ <string name="Direction_Right">
W prawo
</string>
- <string name="Back">
+ <string name="Direction_Back">
Wróć
</string>
- <string name="North">
+ <string name="Direction_North">
Północ
</string>
- <string name="South">
+ <string name="Direction_South">
Południe
</string>
- <string name="West">
+ <string name="Direction_West">
Zachód
</string>
- <string name="East">
+ <string name="Direction_East">
Wschód
</string>
- <string name="Up">
+ <string name="Direction_Up">
W górę
</string>
- <string name="Down">
+ <string name="Direction_Down">
W dół
</string>
<string name="Any Category">
diff --git a/indra/newview/skins/default/xui/pt/panel_preferences_sound.xml b/indra/newview/skins/default/xui/pt/panel_preferences_sound.xml
index 60f51c33e5..3846bfb377 100644
--- a/indra/newview/skins/default/xui/pt/panel_preferences_sound.xml
+++ b/indra/newview/skins/default/xui/pt/panel_preferences_sound.xml
@@ -9,7 +9,7 @@
<slider label="Ambiente" name="Wind Volume"/>
<slider label="Efeitos sonoros" name="SFX Volume"/>
<slider label="Streaming de música" name="Music Volume"/>
- <check_box label="Ativado" name="music_enabled"/>
+ <check_box label="Ativado" name="enable_music"/>
<slider label="Mídia" name="Media Volume"/>
<check_box label="Ativado" name="enable_media"/>
<slider label="Bate-papo de voz" name="Voice Volume"/>
diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml
index ce2c2ddaa1..06123e0118 100644
--- a/indra/newview/skins/default/xui/pt/strings.xml
+++ b/indra/newview/skins/default/xui/pt/strings.xml
@@ -1846,34 +1846,34 @@ Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh
<string name="PDT">
PDT
</string>
- <string name="Forward">
+ <string name="Direction_Forward">
Avante
</string>
- <string name="Left">
+ <string name="Direction_Left">
Esquerda
</string>
- <string name="Right">
+ <string name="Direction_Right">
Direita
</string>
- <string name="Back">
+ <string name="Direction_Back">
Atrás
</string>
- <string name="North">
+ <string name="Direction_North">
Norte
</string>
- <string name="South">
+ <string name="Direction_South">
Sul
</string>
- <string name="West">
+ <string name="Direction_West">
Oeste
</string>
- <string name="East">
+ <string name="Direction_East">
Leste
</string>
- <string name="Up">
+ <string name="Direction_Up">
Acima
</string>
- <string name="Down">
+ <string name="Direction_Down">
Abaixo
</string>
<string name="Any Category">
diff --git a/indra/newview/tests/llsimplestat_test.cpp b/indra/newview/tests/llsimplestat_test.cpp
index 60a8cac995..b556941f4a 100644
--- a/indra/newview/tests/llsimplestat_test.cpp
+++ b/indra/newview/tests/llsimplestat_test.cpp
@@ -3,31 +3,25 @@
* @date 2010-10-22
* @brief Test cases for some of llsimplestat.h
*
- * $LicenseInfo:firstyear=2010&license=viewergpl$
- *
- * Copyright (c) 2010, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 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.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp
index 1bb4fb7c0c..3faddc13c1 100644
--- a/indra/newview/tests/llviewerassetstats_test.cpp
+++ b/indra/newview/tests/llviewerassetstats_test.cpp
@@ -3,31 +3,25 @@
* @date 2010-10-28
* @brief Test cases for some of newview/llviewerassetstats.cpp
*
- * $LicenseInfo:firstyear=2010&license=viewergpl$
- *
- * Copyright (c) 2010, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 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.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/test_apps/llplugintest/llmediaplugintest.cpp b/indra/test_apps/llplugintest/llmediaplugintest.cpp
index 4a2272032b..e9d4d99753 100644
--- a/indra/test_apps/llplugintest/llmediaplugintest.cpp
+++ b/indra/test_apps/llplugintest/llmediaplugintest.cpp
@@ -1620,7 +1620,7 @@ mediaPanel* LLMediaPluginTest::addMediaPanel( std::string url )
std::string user_data_path = std::string( cwd ) + "/";
#endif
media_source->setUserDataPath(user_data_path);
- media_source->init( launcher_name, plugin_name, false );
+ media_source->init( launcher_name, user_data_path, plugin_name, false );
media_source->setDisableTimeout(mDisableTimeout);
// make a new panel and save parameters
@@ -1860,7 +1860,7 @@ mediaPanel* LLMediaPluginTest::replaceMediaPanel( mediaPanel* panel, std::string
#endif
media_source->setUserDataPath(user_data_path);
- media_source->init( launcher_name, plugin_name, false );
+ media_source->init( launcher_name, user_data_path, plugin_name, false );
media_source->setDisableTimeout(mDisableTimeout);
// make a new panel and save parameters
diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp
index ea242f45cd..1888f191e2 100644
--- a/indra/viewer_components/updater/llupdaterservice.cpp
+++ b/indra/viewer_components/updater/llupdaterservice.cpp
@@ -373,9 +373,8 @@ void LLUpdaterServiceImpl::optionalUpdate(std::string const & newVersion,
stopTimer();
mNewVersion = newVersion;
mIsDownloading = true;
- mUpdateDownloader.download(uri, hash, newVersion, false);
-
setState(LLUpdaterService::DOWNLOADING);
+ mUpdateDownloader.download(uri, hash, newVersion, false);
}
void LLUpdaterServiceImpl::requiredUpdate(std::string const & newVersion,
@@ -385,9 +384,8 @@ void LLUpdaterServiceImpl::requiredUpdate(std::string const & newVersion,
stopTimer();
mNewVersion = newVersion;
mIsDownloading = true;
- mUpdateDownloader.download(uri, hash, newVersion, true);
-
setState(LLUpdaterService::DOWNLOADING);
+ mUpdateDownloader.download(uri, hash, newVersion, true);
}
void LLUpdaterServiceImpl::upToDate(void)