From 111c671a21f5ad498c1ae8ecf114b38d477f61eb Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 8 May 2011 15:44:06 +0200 Subject: [PATCH 01/21] Also strip libraries on x86_64, which uses lib32 and lib64 directory names --- indra/newview/viewer_manifest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 9ca3b4704..a663389ea 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -723,8 +723,8 @@ class LinuxManifest(ViewerManifest): if self.args['buildtype'].lower() in ['release', 'releasesse2']: print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build" # makes some small assumptions about our packaged dir structure - self.run_command("find %(d)r/bin %(d)r/lib -type f | xargs --no-run-if-empty strip --strip-unneeded" % {'d': self.get_dst_prefix()} ) - self.run_command("find %(d)r/bin %(d)r/lib -type f -not -name \\*.so | xargs --no-run-if-empty strip -s" % {'d': self.get_dst_prefix()} ) + self.run_command("find %(d)r/bin %(d)r/lib* -type f | xargs --no-run-if-empty strip --strip-unneeded" % {'d': self.get_dst_prefix()} ) + self.run_command("find %(d)r/bin %(d)r/lib* -type f -not -name \\*.so | xargs --no-run-if-empty strip -s" % {'d': self.get_dst_prefix()} ) # Fix access permissions self.run_command(""" From 3a1d753344196a5bfb87a47c02743bb63a57312a Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 8 May 2011 15:10:51 +0200 Subject: [PATCH 02/21] Use LL_DEBUG_TERMINAL_COMMAND and LL_DEBUG_GDB_PATH Upgraded the code that attaches gdb to a plugin for linux to the code that I wrote for viewer 2. --- doc/contributions.txt | 1 + indra/llplugin/llpluginprocessparent.cpp | 48 +++++++++++++++--------- indra/newview/app_settings/settings.xml | 2 +- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/doc/contributions.txt b/doc/contributions.txt index 258e139bc..f70336a94 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -74,6 +74,7 @@ Aleric Inglewood VWR-24312 VWR-24320 VWR-24333 + VWR-24334 SNOW-47 SNOW-84 SNOW-86 diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index 2cb6b2832..2be917be6 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -38,6 +38,9 @@ #include "llpluginprocessparent.h" #include "llpluginmessagepipe.h" #include "llpluginmessageclasses.h" +#if LL_LINUX +#include +#endif #include "llapr.h" @@ -373,14 +376,12 @@ void LLPluginProcessParent::idle(void) { if(mDebug) { - #if LL_DARWIN // If we're set to debug, start up a gdb instance in a new terminal window and have it attach to the plugin process and continue. - + std::stringstream cmd; + +#if LL_DARWIN // The command we're constructing would look like this on the command line: // osascript -e 'tell application "Terminal"' -e 'set win to do script "gdb -pid 12345"' -e 'do script "continue" in win' -e 'end tell' - - std::stringstream cmd; - mDebugger.setExecutable("/usr/bin/osascript"); mDebugger.addArgument("-e"); mDebugger.addArgument("tell application \"Terminal\""); @@ -392,19 +393,32 @@ void LLPluginProcessParent::idle(void) mDebugger.addArgument("-e"); mDebugger.addArgument("end tell"); mDebugger.launch(); - - #elif LL_LINUX - - std::stringstream cmd; - - mDebugger.setExecutable("/usr/bin/gnome-terminal"); - mDebugger.addArgument("--geometry=165x24-0+0"); - mDebugger.addArgument("-e"); - cmd << "/usr/bin/gdb -n /proc/" << mProcess.getProcessID() << "/exe " << mProcess.getProcessID(); - mDebugger.addArgument(cmd.str()); +#elif LL_LINUX + // The command we're constructing would look like this on the command line: + // /usr/bin/xterm -geometry 160x24-0+0 -e '/usr/bin/gdb -n /proc/12345/exe 12345' + // This can be changed by setting the following environment variables, for example: + // export LL_DEBUG_TERMINAL_COMMAND="/usr/bin/gnome-terminal --geometry=165x24-0+0 -e %s" + // export LL_DEBUG_GDB_PATH=/usr/bin/gdb + char const* env; + std::string const terminal_command = (env = getenv("LL_DEBUG_TERMINAL_COMMAND")) ? env : "/usr/bin/xterm -geometry 160x24+0+0 -e %s"; + char const* const gdb_path = (env = getenv("LL_DEBUG_GDB_PATH")) ? env : "/usr/bin/gdb"; + cmd << gdb_path << " -n /proc/" << mProcess.getProcessID() << "/exe " << mProcess.getProcessID(); + std::vector tokens = boost::program_options::split_unix(terminal_command, " "); + std::vector::iterator token = tokens.begin(); + mDebugger.setExecutable(*token); + while (++token != tokens.end()) + { + if (*token == "%s") + { + mDebugger.addArgument(cmd.str()); + } + else + { + mDebugger.addArgument(*token); + } + } mDebugger.launch(); - - #endif +#endif } // This will allow us to time out if the process never starts. diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 038090663..b0a04a3bd 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1935,7 +1935,7 @@ PluginAttachDebuggerToPlugins Comment - TODO: understand what this actually does -.-sg + Opens a terminal window with a debugger and attach it, every time a new SLPlugin process is started. Persist 1 Type From 474acdbff9d3a4d6fd60f9437135654e0b1a69f6 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 8 May 2011 16:16:11 +0200 Subject: [PATCH 03/21] Add support for libcwd. This patch has no influence if you don't have libcwd installed. Note that libcwd (http://libcwd.sourceforge.net/) is only available for linux. A default compile of libcwd does memory allocation tracking, which is too slow for everyday usage of the viewer (usable, but notably slower) and we don't need that. Configure your libcwd as follows: ./configure --prefix=/sl/usr --disable-alloc --enable-optimize Or whatever prefix you prefer (add --enable-maintainer-mode if you're compiling it from the SVN repository), add --disable-nonthreading to compile twice as fast. If you have it installed you can activate it's use by setting a few environment variables: CXXFLAGS="$(pkg-config --cflags libcwd_r)" LDFLAGS="$(pkg-config --libs libcwd_r) -lpthread" and then reconfiguring the viewer. The -lpthread is needed when using ld.gold, however, if you leave it out you might get an LDFLAGS that ends on trailing whitespaces, which doesn't work for some reason. Also, if you installed it in a non-standard place (/sl/usr above) then you need this to run the viewer (and tests): export LD_LIBRARY_PATH="/sl/usr/lib" --- indra/CMakeLists.txt | 1 + indra/cmake/Cwdebug.cmake | 1 + indra/cmake/LLAddBuildTest.cmake | 1 + indra/cmake/LLCommon.cmake | 1 + indra/cwdebug/CMakeLists.txt | 33 ++ indra/cwdebug/cwdebug.h | 9 + indra/cwdebug/debug.cc | 412 +++++++++++++++++++++ indra/cwdebug/debug.h | 256 +++++++++++++ indra/cwdebug/debug_ostream_operators.h | 1 + indra/cwdebug/sys.h | 9 + indra/llcommon/CMakeLists.txt | 7 + indra/llcommon/linden_common.h | 2 + indra/llcommon/llassettype.h | 5 + indra/llcommon/llerror.cpp | 10 + indra/llcommon/llindraconfigfile.h | 2 +- indra/llcommon/llthread.cpp | 4 + indra/llcrashlogger/llcrashlogger.cpp | 4 +- indra/llcrashlogger/llcrashlogger.h | 4 +- indra/llimage/tests/llimageworker_test.cpp | 1 + indra/llinventory/llsaleinfo.cpp | 2 +- indra/llmessage/llnetcanary.cpp | 2 +- indra/llmessage/llsocks5.cpp | 3 +- indra/llplugin/slplugin/slplugin.cpp | 7 + indra/newview/llappviewerlinux.cpp | 3 + 24 files changed, 772 insertions(+), 8 deletions(-) create mode 100644 indra/cmake/Cwdebug.cmake create mode 100644 indra/cwdebug/CMakeLists.txt create mode 100644 indra/cwdebug/cwdebug.h create mode 100644 indra/cwdebug/debug.cc create mode 100644 indra/cwdebug/debug.h create mode 100644 indra/cwdebug/debug_ostream_operators.h create mode 100644 indra/cwdebug/sys.h diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index e81510b2c..f88aa1fa0 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -44,6 +44,7 @@ endif(NOT STANDALONE) add_custom_target(prepare DEPENDS ${prepare_depends}) add_subdirectory(cmake) +add_subdirectory(${LIBS_OPEN_PREFIX}cwdebug) add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) diff --git a/indra/cmake/Cwdebug.cmake b/indra/cmake/Cwdebug.cmake new file mode 100644 index 000000000..9568a625f --- /dev/null +++ b/indra/cmake/Cwdebug.cmake @@ -0,0 +1 @@ +set(CWDEBUG_LIBRARIES cwdebug) diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index f9b1ef96f..74f2e0cb1 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -3,6 +3,7 @@ INCLUDE(APR) INCLUDE(LLMath) INCLUDE(Tut) +INCLUDE(Cwdebug) MACRO(ADD_BUILD_TEST_NO_COMMON name parent) # MESSAGE("${CMAKE_CURRENT_SOURCE_DIR}/tests/${name}_test.cpp") diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index 2862ba7f0..efb3d004c 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -6,6 +6,7 @@ include(EXPAT) include(ZLIB) set(LLCOMMON_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/cwdebug ${LIBS_OPEN_DIR}/llcommon ${APR_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} diff --git a/indra/cwdebug/CMakeLists.txt b/indra/cwdebug/CMakeLists.txt new file mode 100644 index 000000000..b291957e7 --- /dev/null +++ b/indra/cwdebug/CMakeLists.txt @@ -0,0 +1,33 @@ +# -*- cmake -*- + +project(cwdebug) + +include(00-Common) +include(LLCommon) +include(LLMath) +include(LLMessage) +include(LLVFS) + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +set(cwdebug_SOURCE_FILES + debug.cc + ) + +set(cwdebug_HEADER_FILES + CMakeLists.txt + + cwdebug.h + sys.h + debug.h + debug_ostream_operators.h + ) + +set_source_files_properties(${cwdebug_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +add_definitions(-fPIC) + +list(APPEND cwdebug_SOURCE_FILES ${cwdebug_HEADER_FILES}) + +add_library (cwdebug ${cwdebug_SOURCE_FILES}) diff --git a/indra/cwdebug/cwdebug.h b/indra/cwdebug/cwdebug.h new file mode 100644 index 000000000..37415df3f --- /dev/null +++ b/indra/cwdebug/cwdebug.h @@ -0,0 +1,9 @@ +// We support compiling C++ source files that are not +// thread-safe, but in that case we assume that they +// will not be linked with libcwd_r. +#if !defined(_REENTRANT) || !defined(__linux__) +#undef CWDEBUG +#endif + +#include "sys.h" +#include "debug.h" diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc new file mode 100644 index 000000000..3066c2845 --- /dev/null +++ b/indra/cwdebug/debug.cc @@ -0,0 +1,412 @@ +// slviewer -- Second Life Viewer Source Code +// +//! @file debug.cc +//! @brief This file contains the definitions of debug related objects and functions. +// +// Copyright (C) 2008, by +// +// Carlo Wood, Run on IRC +// RSA-1024 0x624ACAD5 1997-01-26 Sign & Encrypt +// Fingerprint16 = 32 EC A7 B6 AC DB 65 A6 F6 F6 55 DD 1C DC FF 61 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef USE_PCH +#include "sys.h" // Needed for platform-specific code +#endif + +#ifdef CWDEBUG + +#ifndef USE_PCH +#include // Needed for std::isprint +#include // Needed for setfill and setw +#include +#include +#include +#include +#include "debug.h" +#ifdef USE_LIBCW +#include // memleak_filter +#endif +#include +#include +#endif // USE_PCH + +#define BACKTRACE_AQUIRE_LOCK libcwd::_private_::mutex_tct::lock() +#define BACKTRACE_RELEASE_LOCK libcwd::_private_::mutex_tct::unlock() + +namespace debug { + +#if CWDEBUG_LOCATION +void BackTrace::dump_backtrace(void) const +{ + for (int frame = 0; frame < frames(); ++frame) + { + libcwd::location_ct loc((char*)buffer()[frame] + libcwd::builtin_return_address_offset); + Dout(dc::notice|continued_cf, '#' << std::left << std::setw(3) << frame << + std::left << std::setw(16) << buffer()[frame] << ' ' << loc << "\n in "); + char const* mangled_function_name = loc.mangled_function_name(); + if (mangled_function_name != libcwd::unknown_function_c) + { + std::string demangled_function_name; + libcwd::demangle_symbol(mangled_function_name, demangled_function_name); + Dout(dc::finish, demangled_function_name); + } + else + Dout(dc::finish, mangled_function_name); + } +} +#endif // CWDEBUG_LOCATION + +#if CWDEBUG_ALLOC && CWDEBUG_LOCATION +typedef std::map, libcwd::_private_::internal_allocator> backtrace_map_t; +backtrace_map_t* backtrace_map; +static int total_calls = 0; +static int number_of_stack_traces = 0; + +void my_backtrace_hook(void** buffer, int frames LIBCWD_COMMA_TSD_PARAM) +{ + ++total_calls; + + backtrace_map_t::iterator iter; + + set_alloc_checking_off(__libcwd_tsd); + { + BackTrace backtrace(buffer, frames); + std::pair res = backtrace_map->insert(backtrace_map_t::value_type(backtrace, 0)); + if (res.second) + ++number_of_stack_traces; + ++res.first->second; + iter = res.first; + } + set_alloc_checking_on(__libcwd_tsd); +#if 0 + // Dump the stack trace. + iter->first.dump_backtrace(); +#endif +} + +void start_recording_backtraces(void) +{ + BACKTRACE_AQUIRE_LOCK; + libcwd::backtrace_hook = my_backtrace_hook; + BACKTRACE_RELEASE_LOCK; + //Debug(dc::malloc.on()); + LIBCWD_TSD_DECLARATION; + set_alloc_checking_off(__libcwd_tsd); + backtrace_map = new backtrace_map_t; + set_alloc_checking_on(__libcwd_tsd); +} + +struct Compare { + bool operator()(backtrace_map_t::const_iterator const& iter1, backtrace_map_t::const_iterator const& iter2) + { + return iter1->second > iter2->second; + } +}; + +void stop_recording_backtraces(void) +{ + //Debug(dc::malloc.off()); + BACKTRACE_AQUIRE_LOCK; + libcwd::backtrace_hook = NULL; + + if (!backtrace_map) + { + Dout(dc::notice, "Not recording; call cwdebug_start() first."); + return; + } + + Dout(dc::notice, "Total number of calls: " << total_calls); + Dout(dc::notice, "Number of different stack traces: " << number_of_stack_traces); + Dout(dc::notice, "First 10 stack traces:"); + std::list entries; + for (backtrace_map_t::const_iterator iter = backtrace_map->begin(); iter != backtrace_map->end(); ++iter) + entries.push_back(iter); + entries.sort(Compare()); + int count = 0; + for (std::list::iterator iter = entries.begin(); iter != entries.end(); ++iter, ++count) + { + Dout(dc::notice, "Used: " << (*iter)->second); + // Dump the stack trace. + (*iter)->first.dump_backtrace(); + if (count == 10) + break; + } + + // Clear all data. + LIBCWD_TSD_DECLARATION; + set_alloc_checking_off(__libcwd_tsd); + delete backtrace_map; + set_alloc_checking_on(__libcwd_tsd); + backtrace_map = NULL; + total_calls = 0; + number_of_stack_traces = 0; + + BACKTRACE_RELEASE_LOCK; +} +#endif // CWDEBUG_ALLOC && CWDEBUG_LOCATION + + namespace channels { // namespace DEBUGCHANNELS + namespace dc { + +#ifndef DOXYGEN +#define DDCN(x) (x) +#endif + // Add new debug channels here. + + channel_ct viewer DDCN("VIEWER"); //!< This debug channel is used for the normal debugging out of the viewer. + channel_ct primbackup DDCN("PRIMBACKUP"); //!< This debug channel is used for output related to primbackup. + channel_ct gtk DDCN("GTK"); //!< This debug channel is used for output related to gtk. + channel_ct sdl DDCN("SDL"); //!< This debug channel is used for output related to sdl locking. + channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces. + + } // namespace dc + } // namespace DEBUGCHANNELS + + // Anonymous namespace, this map and its initialization functions are private to this file + // for Thead-safeness reasons. + namespace { + + /*! @brief The type of rcfile_dc_states. + * @internal + */ + typedef std::map rcfile_dc_states_type; + + /*! @brief Map containing the default debug channel states used at the start of each new thread. + * @internal + * + * The first thread calls main, which calls debug::init which will initialize this + * map with all debug channel labels and whether or not they were turned on in the + * rcfile or not. + */ + rcfile_dc_states_type rcfile_dc_states; + + /*! @brief Set the default state of debug channel \a dc_label. + * @internal + * + * This function is called once for each debug channel. + */ + void set_state(char const* dc_label, bool is_on) + { + std::pair res = + rcfile_dc_states.insert(rcfile_dc_states_type::value_type(std::string(dc_label), is_on)); + if (!res.second) + Dout(dc::warning, "Calling set_state() more than once for the same label!"); + return; + } + + /*! @brief Save debug channel states. + * @internal + * + * One time initialization function of rcfile_dc_state. + * This must be called from debug::init after reading the rcfile. + */ + void save_dc_states(void) + { + // We may only call this function once: it reflects the states as stored + // in the rcfile and that won't change. Therefore it is not needed to + // lock `rcfile_dc_states', it is only written to by the first thread + // (once, via main -> init) when there are no other threads yet. + static bool second_time = false; + if (second_time) + { + Dout(dc::warning, "Calling save_dc_states() more than once!"); + return; + } + second_time = true; + ForAllDebugChannels( set_state(debugChannel.get_label(), debugChannel.is_on()) ); + } + + } // anonymous namespace + + /*! @brief Returns the the original state of a debug channel. + * @internal + * + * For a given \a dc_label, which must be the exact name (channel_ct::get_label) of an + * existing debug channel, this function returns \c true when the corresponding debug channel was + * on at the startup of the application, directly after reading the libcwd runtime + * configuration file (.libcwdrc). + * + * If the label/channel did not exist at the start of the application, it will return \c false + * (note that libcwd disallows adding debug channels to modules - so this would probably + * a bug). + */ + bool is_on_in_rcfile(char const* dc_label) + { + rcfile_dc_states_type::const_iterator iter = rcfile_dc_states.find(std::string(dc_label)); + if (iter == rcfile_dc_states.end()) + { + Dout(dc::warning, "is_on_in_rcfile(\"" << dc_label << "\"): \"" << dc_label << "\" is an unknown label!"); + return false; + } + return iter->second; + } + +#if LIBCWD_THREAD_SAFE + pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + // I can cause I'm the maintainer of libcwd ;). + libcwd::_private_::pthread_lock_interface_ct cout_mutex(&mutex); + libcwd::_private_::lock_interface_base_ct* cout_mutex_ptr(&cout_mutex); +#endif + + /*! @brief Initialize debugging code from new threads. + * + * This function needs to be called at the start of each new thread, + * because a new thread starts in a completely reset state. + * + * The function turns on all debug channels that were turned on + * after reading the rcfile at the start of the application. + * Furthermore it initializes the debug ostream, its mutex and the + * margin of the default debug object (Dout). + */ + void init_thread(void) + { + // Turn on all debug channels that are turned on as per rcfile configuration. + ForAllDebugChannels( + if (!debugChannel.is_on() && is_on_in_rcfile(debugChannel.get_label())) + debugChannel.on(); + ); + + // Turn on debug output. + Debug( libcw_do.on() ); + static std::ofstream debug_file; + static bool debug_out_opened = false; + if (!debug_out_opened) + { + debug_out_opened = true; // Thread-safe, cause the main thread calls this first while it's still alone. + std::ostringstream os; + os << "debug.out." << getpid(); + debug_file.open(os.str().c_str()); + } + static debug::TeeStream debug_stream(std::cout, debug_file); +#if LIBCWD_THREAD_SAFE + Debug( libcw_do.set_ostream(&debug_stream, cout_mutex_ptr) ); +#else + Debug( libcw_do.set_ostream(&debug_stream) ); +#endif + + static bool first_thread = true; + if (!first_thread) // So far, the application has only one thread. So don't add a thread id. + { + // Set the thread id in the margin. + char margin[18]; + sprintf(margin, "0x%-14lx ", pthread_self()); + Debug( libcw_do.margin().assign(margin, 17) ); + } + first_thread = false; + } + + /*! @brief Initialize debugging code from main. + * + * This function initializes the debug code. + */ + void init(void) + { +#if CWDEBUG_ALLOC && defined(USE_LIBCW) + // Tell the memory leak detector which parts of the code are + // expected to leak so that we won't get an alarm for those. + { + std::vector > hide_list; + hide_list.push_back(std::pair("libdl.so.2", "_dlerror_run")); + hide_list.push_back(std::pair("libstdc++.so.6", "__cxa_get_globals")); + // The following is actually necessary because of a bug in glibc + // (see http://sources.redhat.com/bugzilla/show_bug.cgi?id=311). + hide_list.push_back(std::pair("libc.so.6", "dl_open_worker")); + memleak_filter().hide_functions_matching(hide_list); + } + { + std::vector hide_list; + // Also because of http://sources.redhat.com/bugzilla/show_bug.cgi?id=311 + hide_list.push_back(std::string("ld-linux.so.2")); + memleak_filter().hide_objectfiles_matching(hide_list); + } + memleak_filter().set_flags(libcwd::show_objectfile|libcwd::show_function); +#endif + + // The following call allocated the filebuf's of cin, cout, cerr, wcin, wcout and wcerr. + // Because this causes a memory leak being reported, make them invisible. + Debug(set_invisible_on()); + + // You want this, unless you mix streams output with C output. + // Read http://gcc.gnu.org/onlinedocs/libstdc++/27_io/howto.html#8 for an explanation. + //std::ios::sync_with_stdio(false); + + // Cancel previous call to set_invisible_on. + Debug(set_invisible_off()); + + // This will warn you when you are using header files that do not belong to the + // shared libcwd object that you linked with. + Debug( check_configuration() ); + + Debug( + libcw_do.on(); // Show which rcfile we are reading! + ForAllDebugChannels( + while (debugChannel.is_on()) + debugChannel.off() // Print as little as possible though. + ); + read_rcfile(); // Put 'silent = on' in the rcfile to suppress most of the output here. + libcw_do.off() + ); + save_dc_states(); + + init_thread(); + } + +#if CWDEBUG_LOCATION + /*! @brief Return call location. + * + * @param return_addr The return address of the call. + */ + std::string call_location(void const* return_addr) + { + libcwd::location_ct loc((char*)return_addr + libcwd::builtin_return_address_offset); + std::ostringstream convert; + convert << loc; + return convert.str(); + } + + std::vector __attribute__ ((visibility("default"))) backtraces; + pthread_mutex_t __attribute__ ((visibility("default"))) backtrace_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +} // namespace debug + +#if CWDEBUG_ALLOC +// These can be called from gdb. +void cwdebug_start() +{ + debug::start_recording_backtraces(); +} + +void cwdebug_stop() +{ + debug::stop_recording_backtraces(); +} +#endif + +#if CWDEBUG_LOCATION +void cwdebug_backtrace(int n) +{ + if (0 < n && n <= debug::backtraces.size()) + { + Dout(dc::backtrace, "Backtrace #" << n << ":"); + debug::backtraces[n - 1].dump_backtrace(); + } + else + std::cout << "No such backtrace nr. Max is " << debug::backtraces.size() << std::endl; +} +#endif + +#endif // CWDEBUG diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h new file mode 100644 index 000000000..66408ad72 --- /dev/null +++ b/indra/cwdebug/debug.h @@ -0,0 +1,256 @@ +// slviewer -- Second Life Viewer Source Code +// +//! @file debug.h +//! @brief This file contains the declaration of debug related macros, objects and functions. +// +// Copyright (C) 2008, by +// +// Carlo Wood, Run on IRC +// RSA-1024 0x624ACAD5 1997-01-26 Sign & Encrypt +// Fingerprint16 = 32 EC A7 B6 AC DB 65 A6 F6 F6 55 DD 1C DC FF 61 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef DEBUG_H +#define DEBUG_H + +#ifndef CWDEBUG + +#ifndef DOXYGEN // No need to document this. See http://libcwd.sourceforge.net/ for more info. + +#include +#include // std::exit, EXIT_FAILURE + +#define AllocTag1(p) +#define AllocTag2(p, desc) +#define AllocTag_dynamic_description(p, x) +#define AllocTag(p, x) +#define Debug(x) +#define Dout(a, b) +#define DoutEntering(a, b) +#define DoutFatal(a, b) LibcwDoutFatal(::std, , a, b) +#define ForAllDebugChannels(STATEMENT) +#define ForAllDebugObjects(STATEMENT) +#define LibcwDebug(dc_namespace, x) +#define LibcwDout(a, b, c, d) +#define LibcwDoutFatal(a, b, c, d) do { ::std::cerr << d << ::std::endl; ::std::exit(EXIT_FAILURE); } while (1) +#define NEW(x) new x +#define CWDEBUG_ALLOC 0 +#define CWDEBUG_MAGIC 0 +#define CWDEBUG_LOCATION 0 +#define CWDEBUG_LIBBFD 0 +#define CWDEBUG_DEBUG 0 +#define CWDEBUG_DEBUGOUTPUT 0 +#define CWDEBUG_DEBUGM 0 +#define CWDEBUG_DEBUGT 0 +#define CWDEBUG_MARKER 0 + +#define BACKTRACE do { } while(0) + +#endif // !DOXYGEN + +#include +#ifdef DEBUG +#define ASSERT(x) assert(x) +#else +#define ASSERT(x) +#endif + +#else // CWDEBUG + +//! Assert \a x, if debugging is turned on. +#define ASSERT(x) LIBCWD_ASSERT(x) +#define builtin_return_address(addr) ((char*)__builtin_return_address(addr) + libcwd::builtin_return_address_offset) + +#ifndef DEBUGCHANNELS +//! @brief The namespace in which the \c dc namespace is declared. +// +// Libcwd demands that this macro is defined +// before is included and must be the name of the namespace containing +// the \c dc (Debug Channels) namespace. +// +// @sa debug::channels::dc + +#define DEBUGCHANNELS ::debug::channels +#endif +#include +#include +#if CWDEBUG_LOCATION +#include // Needed for 'backtrace'. +#endif + +#define CWD_API __attribute__ ((visibility("default"))) + +//! Debug specific code. +namespace debug { + +void CWD_API init(void); // Initialize debugging code, called once from main. +void CWD_API init_thread(void); // Initialize debugging code, called once for each thread. + +//! @brief Debug Channels (dc) namespace. +// +// @sa debug::channels::dc +namespace channels { // namespace DEBUGCHANNELS + +//! The namespace containing the actual debug channels. +namespace dc { +using namespace libcwd::channels::dc; +using libcwd::channel_ct; + +#ifndef DOXYGEN // Doxygen bug causes a warning here. +// Add the declaration of new debug channels here +// and add their definition in a custom debug.cc file. +extern CWD_API channel_ct viewer; // The normal logging output of the viewer (normally to stderr). +extern CWD_API channel_ct primbackup; +extern CWD_API channel_ct gtk; +extern CWD_API channel_ct sdl; +extern CWD_API channel_ct backtrace; + +#endif + +} // namespace dc +} // namespace DEBUGCHANNELS + +#if CWDEBUG_LOCATION +std::string call_location(void const* return_addr); +#endif + +//! @brief Interface for marking scopes of invisible memory allocations. +// +// Creation of the object does nothing, you have to explicitly call +// InvisibleAllocations::on. Destruction of the object automatically +// cancels any call to \c on of this object. This makes it exception- +// (stack unwinding) and recursive-safe. +struct InvisibleAllocations { + int M_on; //!< The number of times that InvisibleAllocations::on() was called. + //! Constructor. + InvisibleAllocations() : M_on(0) { } + //! Destructor. + ~InvisibleAllocations() { while (M_on > 0) off(); } + //! Set invisible allocations on. Can be called recursively. + void on(void) { libcwd::set_invisible_on(); ++M_on; } + //! Cancel one call to on(). + void off(void) { assert(M_on > 0); --M_on; libcwd::set_invisible_off(); } +}; + +//! @brief Interface for marking scopes with indented debug output. +// +// Creation of the object increments the debug indentation. Destruction +// of the object automatically decrements the indentation again. +struct Indent { + int M_indent; //!< The extra number of spaces that were added to the indentation. + //! Construct an Indent object. + Indent(int indent) : M_indent(indent) { if (M_indent > 0) libcwd::libcw_do.inc_indent(M_indent); } + //! Destructor. + ~Indent() { if (M_indent > 0) libcwd::libcw_do.dec_indent(M_indent); } +}; + +// A class streambuf that splits what is written to two streambufs. +class TeeBuf : public std::streambuf { + private: + std::streambuf* M_sb1; + std::streambuf* M_sb2; + + public: + TeeBuf(std::streambuf* sb1, std::streambuf* sb2) : M_sb1(sb1), M_sb2(sb2) { } + + protected: + virtual int sync(void) { M_sb2->pubsync(); return M_sb1->pubsync(); } + virtual std::streamsize xsputn(char_type const* p, std::streamsize n) { M_sb2->sputn(p, n); return M_sb1->sputn(p, n); } + virtual int_type overflow(int_type c = traits_type::eof()) { M_sb2->sputc(c); return M_sb1->sputc(c); } +}; + +// An ostream that passes what is written to it on to two other ostreams. +class TeeStream : public std::ostream { + private: + TeeBuf M_teebuf; + public: + TeeStream(std::ostream& os1, std::ostream& os2) : std::ostream(&M_teebuf), M_teebuf(os1.rdbuf(), os2.rdbuf()) { } +}; + +#if CWDEBUG_LOCATION +class BackTrace { + private: + boost::shared_array M_buffer; + int M_frames; + public: + BackTrace(void** buffer, int frames) : M_buffer(new void* [frames]), M_frames(frames) { std::memcpy(M_buffer.get(), buffer, sizeof(void*) * frames); } + + friend bool operator<(BackTrace const& bt1, BackTrace const& bt2) + { + if (bt1.M_frames != bt2.M_frames) + return bt1.M_frames < bt2.M_frames; + for (int frame = 0; frame < bt1.M_frames; ++frame) + if (bt1.M_buffer[frame] < bt2.M_buffer[frame]) + return true; + else if (bt1.M_buffer[frame] > bt2.M_buffer[frame]) + return true; + return false; + } + + void dump_backtrace(void) const; + + int frames(void) const { return M_frames; } + boost::shared_array const& buffer(void) const { return M_buffer; } +}; +extern std::vector backtraces; +extern pthread_mutex_t backtrace_mutex; +#define BACKTRACE do { \ + using namespace debug; \ + void* buffer[32]; \ + int frames = backtrace(buffer, 32); \ + size_t size; \ + { \ + pthread_mutex_lock(&backtrace_mutex); \ + backtraces.push_back(BackTrace(buffer, frames)); \ + size = backtraces.size(); \ + pthread_mutex_unlock(&backtrace_mutex); \ + } \ + Dout(dc::backtrace, "Stored backtrace #" << size); \ + } while(0) +#else +#define BACKTRACE do { } while(0) +#endif // CWDEBUG_LOCATION + +} // namespace debug + +//! Debugging macro. +// +// Print "Entering " << \a data to channel \a cntrl and increment +// debugging output indentation until the end of the current scope. +#define DoutEntering(cntrl, data) \ + int __slviewer_debug_indentation = 2; \ + { \ + LIBCWD_TSD_DECLARATION; \ + if (LIBCWD_DO_TSD_MEMBER_OFF(::libcwd::libcw_do) < 0) \ + { \ + ::libcwd::channel_set_bootstrap_st __libcwd_channel_set(LIBCWD_DO_TSD(::libcwd::libcw_do) LIBCWD_COMMA_TSD); \ + bool on; \ + { \ + using namespace LIBCWD_DEBUGCHANNELS; \ + on = (__libcwd_channel_set|cntrl).on; \ + } \ + if (on) \ + Dout(cntrl, "Entering " << data); \ + else \ + __slviewer_debug_indentation = 0; \ + } \ + } \ + debug::Indent __slviewer_debug_indent(__slviewer_debug_indentation); + +#endif // CWDEBUG + +#include "debug_ostream_operators.h" + +#endif // DEBUG_H diff --git a/indra/cwdebug/debug_ostream_operators.h b/indra/cwdebug/debug_ostream_operators.h new file mode 100644 index 000000000..4459e9fab --- /dev/null +++ b/indra/cwdebug/debug_ostream_operators.h @@ -0,0 +1 @@ +// Empty so far... diff --git a/indra/cwdebug/sys.h b/indra/cwdebug/sys.h new file mode 100644 index 000000000..c18df1701 --- /dev/null +++ b/indra/cwdebug/sys.h @@ -0,0 +1,9 @@ +// The following is the libcwd related mandatory part. +// It must be included before any system header file is included. +#ifdef CWDEBUG +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#endif + diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 7f352269b..f1325163c 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -2,6 +2,7 @@ project(llcommon) +include(Cwdebug) include(00-Common) include(LLCommon) include(Linking) @@ -214,5 +215,11 @@ target_link_libraries( ${EXPAT_LIBRARIES} ${ZLIB_LIBRARIES} ${WINDOWS_LIBRARIES} + ${CWDEBUG_LIBRARIES} ) +if (LINUX) + # When linking with llcommon later, we do not want to link with cwdebug.a again. + set_property(TARGET llcommon PROPERTY LINK_INTERFACE_LIBRARIES "-lapr-1 -laprutil-1 -lz") +endif (LINUX) + diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h index 6e66e9916..d486359c7 100644 --- a/indra/llcommon/linden_common.h +++ b/indra/llcommon/linden_common.h @@ -33,6 +33,8 @@ #ifndef LL_LINDEN_COMMON_H #define LL_LINDEN_COMMON_H +#include "cwdebug.h" + #if defined(LL_WINDOWS) && defined(_DEBUG) # if _MSC_VER >= 1400 // Visual C++ 2005 or later # define _CRTDBG_MAP_ALLOC diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index a769e165c..9912ecba2 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -195,4 +195,9 @@ private: static const char* mAssetTypeHumanNames[]; }; +#ifdef CWDEBUG +#include +inline std::ostream& operator<<(std::ostream& os, LLAssetType::EType type) { return os << LLAssetType::getDesc(type); } +#endif + #endif // LL_LLASSETTYPE diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 4cdfe097d..a9c4380a1 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -162,7 +162,15 @@ namespace { break; } } +#ifdef CWDEBUG + // Include normal logging in libcwd's message processing. + // This takes care of prefixing with thread ID's, locking + // and allows us to (temporarily) turn off normal logging + // output. + Dout(dc::viewer, message); +#else fprintf(stderr, "%s\n", message.c_str()); +#endif #if LL_WINDOWS fflush(stderr); //Now using a buffer. flush is required. #endif @@ -1218,6 +1226,8 @@ namespace LLError #endif void crashAndLoop(const std::string& message) { + DoutFatal(dc::core, message); + // Now, we go kaboom! int* make_me_crash = NULL; diff --git a/indra/llcommon/llindraconfigfile.h b/indra/llcommon/llindraconfigfile.h index 661f79bd6..17eda906e 100644 --- a/indra/llcommon/llindraconfigfile.h +++ b/indra/llcommon/llindraconfigfile.h @@ -33,8 +33,8 @@ #ifndef LL_LLINDRACONFIGFILE_H #define LL_LLINDRACONFIGFILE_H -#include #include "linden_common.h" +#include #include "lllivefile.h" #include "llsd.h" diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 7ced30939..8bd341457 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -67,6 +67,10 @@ // void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap) { +#ifdef CWDEBUG + debug::init_thread(); +#endif + LLThread *threadp = (LLThread *)datap; // Set thread state to running diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index 3dc41e897..f955ccdfd 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -29,13 +29,15 @@ * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ + +#include "linden_common.h" + #include #include #include #include #include "llcrashlogger.h" -#include "linden_common.h" #include "llstring.h" #include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME #include "llerror.h" diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h index f8abe2059..72c1f3fb8 100644 --- a/indra/llcrashlogger/llcrashlogger.h +++ b/indra/llcrashlogger/llcrashlogger.h @@ -32,10 +32,10 @@ #ifndef LLCRASHLOGGER_H #define LLCRASHLOGGER_H -#include - #include "linden_common.h" +#include + #include "llapp.h" #include "llsd.h" #include "llcontrol.h" diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp index cc44696a4..2bca81434 100644 --- a/indra/llimage/tests/llimageworker_test.cpp +++ b/indra/llimage/tests/llimageworker_test.cpp @@ -32,6 +32,7 @@ */ // Precompiled header: almost always required for newview cpp files +#include "../llcommon/linden_common.h" #include #include #include diff --git a/indra/llinventory/llsaleinfo.cpp b/indra/llinventory/llsaleinfo.cpp index b7afb28ad..e51e35e6e 100644 --- a/indra/llinventory/llsaleinfo.cpp +++ b/indra/llinventory/llsaleinfo.cpp @@ -30,8 +30,8 @@ * $/LicenseInfo$ */ -#include #include "linden_common.h" +#include #include "llsaleinfo.h" diff --git a/indra/llmessage/llnetcanary.cpp b/indra/llmessage/llnetcanary.cpp index a714c60f2..57ee6d3a6 100644 --- a/indra/llmessage/llnetcanary.cpp +++ b/indra/llmessage/llnetcanary.cpp @@ -1,6 +1,6 @@ // -#include "llnetcanary.h" #include "linden_common.h" +#include "llnetcanary.h" #include "llerror.h" #ifdef _MSC_VER #include diff --git a/indra/llmessage/llsocks5.cpp b/indra/llmessage/llsocks5.cpp index 7326e806b..d21412f86 100644 --- a/indra/llmessage/llsocks5.cpp +++ b/indra/llmessage/llsocks5.cpp @@ -30,13 +30,12 @@ * $/LicenseInfo$ */ -#include - #include "linden_common.h" #include "net.h" #include "llhost.h" #include "message.h" #include "llsocks5.h" +#include // Static class variable instances diff --git a/indra/llplugin/slplugin/slplugin.cpp b/indra/llplugin/slplugin/slplugin.cpp index cca8ead8f..febcfea84 100644 --- a/indra/llplugin/slplugin/slplugin.cpp +++ b/indra/llplugin/slplugin/slplugin.cpp @@ -183,6 +183,13 @@ int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdL int main(int argc, char **argv) #endif { +#ifdef CWDEBUG + Debug( libcw_do.margin().assign("SLPlugin ", 9) ); + Debug(debug::init()); + // Uncomment this to automatically open a terminal with gdb. Requires SNOW-173. + //Debug(attach_gdb()); +#endif + // Set up llerror logging { LLError::initForApplication("."); diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index a19ad5658..4e4bbcb43 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -98,6 +98,9 @@ static void exceptionTerminateHandler() int main( int argc, char **argv ) { + Debug(debug::init()); + Debug(libcw_do.on()); + LLMemType mt1(LLMemType::MTYPE_STARTUP); #if LL_SOLARIS && defined(__sparc) From f6b57d956d2f395d87e83f8097f94d9566308ae7 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 5 May 2011 16:34:38 +0200 Subject: [PATCH 04/21] Added base class AIStateMachine. This is the skeleton needed to implement classes that can be reused and work together, which can perform asynchronous tasks (read: need to wait for certain events before they can continue). An example would be the task of waiting for a given inventory folder to be read. This could then be used to improve the builtin AO (automatically reading that folder when a notecard is dropped, and continuing when the whole folder is read). It's first use will be communication with a filepicker that runs in a plugin. --- doc/contributions.txt | 1 + indra/cwdebug/debug.cc | 1 + indra/cwdebug/debug.h | 1 + indra/llcommon/llfasttimer.h | 1 + indra/newview/CMakeLists.txt | 9 + indra/newview/app_settings/settings.xml | 11 + indra/newview/llcallbacklist.cpp | 40 +- indra/newview/llcallbacklist.h | 4 +- indra/newview/llfasttimerview.cpp | 1 + indra/newview/llviewercontrol.cpp | 7 + indra/newview/statemachine/aistatemachine.cpp | 343 +++++++++++++++++ indra/newview/statemachine/aistatemachine.h | 344 ++++++++++++++++++ 12 files changed, 757 insertions(+), 6 deletions(-) create mode 100644 indra/newview/statemachine/aistatemachine.cpp create mode 100644 indra/newview/statemachine/aistatemachine.h diff --git a/doc/contributions.txt b/doc/contributions.txt index f70336a94..ea148abd9 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -114,6 +114,7 @@ Aleric Inglewood IMP-670 IMP-701 IMP-734 + IMP-735 Alissa Sabre VWR-81 VWR-83 diff --git a/indra/cwdebug/debug.cc b/indra/cwdebug/debug.cc index 3066c2845..48392beae 100644 --- a/indra/cwdebug/debug.cc +++ b/indra/cwdebug/debug.cc @@ -171,6 +171,7 @@ void stop_recording_backtraces(void) channel_ct gtk DDCN("GTK"); //!< This debug channel is used for output related to gtk. channel_ct sdl DDCN("SDL"); //!< This debug channel is used for output related to sdl locking. channel_ct backtrace DDCN("BACKTRACE"); //!< This debug channel is used for backtraces. + channel_ct statemachine DDCN("STATEMACHINE"); //!< This debug channel is used class AIStateMachine. } // namespace dc } // namespace DEBUGCHANNELS diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index 66408ad72..e91eab92e 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -116,6 +116,7 @@ extern CWD_API channel_ct primbackup; extern CWD_API channel_ct gtk; extern CWD_API channel_ct sdl; extern CWD_API channel_ct backtrace; +extern CWD_API channel_ct statemachine; #endif diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 898ed496b..726274c23 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -177,6 +177,7 @@ public: FTM_REFRESH, FTM_SORT, FTM_PICK, + FTM_STATEMACHINE, // Temp FTM_TEMP1, diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 57a6163d7..628ee9ec4 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1005,6 +1005,15 @@ set(viewer_HEADER_FILES source_group("CMake Rules" FILES ViewerInstall.cmake) +set(statemachine_SOURCE_FILES + statemachine/aistatemachine.cpp + ) +set(statemachine_HEADER_FILES + statemachine/aistatemachine.h + ) +list(APPEND viewer_SOURCE_FILES ${statemachine_SOURCE_FILES}) +list(APPEND viewer_HEADER_FILES ${statemachine_HEADER_FILES}) + if (DARWIN) LIST(APPEND viewer_SOURCE_FILES llappviewermacosx.cpp) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index b0a04a3bd..1406c4e96 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -11382,6 +11382,17 @@ Value 0 + StateMachineMaxTime + + Comment + Maximum time per frame spent executing AIStateMachine objects, in miliseconds + Persist + 1 + Type + U32 + Value + 20 + StatsAutoRun Comment diff --git a/indra/newview/llcallbacklist.cpp b/indra/newview/llcallbacklist.cpp index bba4d021c..6063995cf 100644 --- a/indra/newview/llcallbacklist.cpp +++ b/indra/newview/llcallbacklist.cpp @@ -47,7 +47,7 @@ LLCallbackList gIdleCallbacks; // Member functions // -LLCallbackList::LLCallbackList() +LLCallbackList::LLCallbackList() : mLoopingOverCallbackList(false) { // nothing } @@ -96,7 +96,15 @@ BOOL LLCallbackList::deleteFunction( callback_t func, void *data) callback_list_t::iterator iter = std::find(mCallbackList.begin(), mCallbackList.end(), t); if (iter != mCallbackList.end()) { - mCallbackList.erase(iter); + if (mLoopingOverCallbackList) + { + iter->first = NULL; // Mark for removal later (when we return to LLCallbackList::callFunctions). + mNeedErase = true; + } + else + { + mCallbackList.erase(iter); + } return TRUE; } else @@ -108,16 +116,38 @@ BOOL LLCallbackList::deleteFunction( callback_t func, void *data) void LLCallbackList::deleteAllFunctions() { + llassert(!mLoopingOverCallbackList); // Only called from unit tests. mCallbackList.clear(); } void LLCallbackList::callFunctions() { - for (callback_list_t::iterator iter = mCallbackList.begin(); iter != mCallbackList.end(); ) + llassert(!mLoopingOverCallbackList); + mLoopingOverCallbackList = true; + mNeedErase = false; + for (callback_list_t::iterator iter = mCallbackList.begin(); iter != mCallbackList.end(); ++iter) { - callback_list_t::iterator curiter = iter++; - curiter->first(curiter->second); + if (iter->first) // Not pending removal? + { + iter->first(iter->second); // This can theorectically set any iter->first to NULL, which means the entry should be erased. + } + } + mLoopingOverCallbackList = false; + if (mNeedErase) + { + callback_list_t::iterator iter = mCallbackList.begin(); + while (iter != mCallbackList.end()) + { + if (!iter->first) + { + iter = mCallbackList.erase(iter); + } + else + { + ++iter; + } + } } } diff --git a/indra/newview/llcallbacklist.h b/indra/newview/llcallbacklist.h index be0fe9f5c..240346478 100644 --- a/indra/newview/llcallbacklist.h +++ b/indra/newview/llcallbacklist.h @@ -53,9 +53,11 @@ public: protected: // Use a list so that the callbacks are ordered in case that matters - typedef std::pair callback_pair_t; + typedef std::pair callback_pair_t; // callback_t is a (function) pointer. If it is NULL it means that the entry should be considered deleted. typedef std::list callback_list_t; callback_list_t mCallbackList; + bool mLoopingOverCallbackList; // True while looping over mCallbackList and calling the callback_t functions (see callFunctions). + bool mNeedErase; // True when deleteFunction was called while mLoopingOverCallbackList was true. }; extern LLCallbackList gIdleCallbacks; diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp index 27661567e..418ea1bf0 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -83,6 +83,7 @@ static struct ft_display_info ft_display_table[] = { LLFastTimer::FTM_KEYHANDLER, " Keyboard", &LLColor4::grey1, 0 }, { LLFastTimer::FTM_SLEEP, " Sleep", &LLColor4::grey2, 0 }, { LLFastTimer::FTM_IDLE, " Idle", &blue0, 0 }, + { LLFastTimer::FTM_STATEMACHINE, " State Machines", &LLColor4::yellow1, 0 }, { LLFastTimer::FTM_PUMP, " Pump", &LLColor4::magenta2, 1 }, { LLFastTimer::FTM_CURL, " Curl", &LLColor4::magenta3, 0 }, { LLFastTimer::FTM_PUMPIO, " PumpIO", &LLColor4::magenta1, 0 }, diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 23c9535fa..de65b97af 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -75,6 +75,7 @@ #include "llnetmap.h" #include "llrender.h" #include "llfloaterchat.h" +#include "statemachine/aistatemachine.h" #include "aithreadsafe.h" #include "llviewerobjectlist.h" #include "lldrawpoolbump.h" @@ -119,6 +120,11 @@ static bool handleTerrainDetailChanged(const LLSD& newvalue) return true; } +bool handleStateMachineMaxTimeChanged(const LLSD& newvalue) +{ + AIStateMachine::updateSettings(); + return true; +} static bool handleSetShaderChanged(const LLSD& newvalue) { @@ -707,6 +713,7 @@ void settings_setup_listeners() gSavedSettings.getControl("AudioLevelMic")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1)); gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1)); gSavedSettings.getControl("TranslateChat")->getSignal()->connect(boost::bind(&handleTranslateChatPrefsChanged, _1)); + gSavedSettings.getControl("StateMachineMaxTime")->getSignal()->connect(boost::bind(&handleStateMachineMaxTimeChanged, _1)); gSavedSettings.getControl("CloudsEnabled")->getSignal()->connect(boost::bind(&handleCloudSettingsChanged, _1)); gSavedSettings.getControl("SkyUseClassicClouds")->getSignal()->connect(boost::bind(&handleCloudSettingsChanged, _1)); diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp new file mode 100644 index 000000000..ca1b5c61e --- /dev/null +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -0,0 +1,343 @@ +/** + * @file aistatemachine.cpp + * @brief Implementation of AIStateMachine + * + * Copyright (c) 2010, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * 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. + * + * CHANGELOG + * and additional copyright holders. + * + * 01/03/2010 + * Initial version, written by Aleric Inglewood @ SL + */ + +#include "../llviewerprecompiledheaders.h" + +#include + +#include "../llcallbacklist.h" +#include "../llviewercontrol.h" + +#include "llfasttimer.h" +#include "aithreadsafe.h" +#include "aistatemachine.h" + +// Local variables. +namespace { + struct QueueElementComp; + + class QueueElement { + private: + AIStateMachine* mStateMachine; + U64 mRuntime; + + public: + QueueElement(AIStateMachine* statemachine) : mStateMachine(statemachine), mRuntime(0) { } + friend bool operator==(QueueElement const& e1, QueueElement const& e2) { return e1.mStateMachine == e2.mStateMachine; } + friend struct QueueElementComp; + + AIStateMachine& statemachine(void) const { return *mStateMachine; } + void add(U64 count) { mRuntime += count; } + }; + + struct QueueElementComp { + bool operator()(QueueElement const& e1, QueueElement const& e2) const { return e1.mRuntime < e2.mRuntime; } + }; + + typedef std::vector active_statemachines_type; + static active_statemachines_type active_statemachines; + typedef std::vector continued_statemachines_type; + struct cscm_type + { + continued_statemachines_type continued_statemachines; + bool calling_mainloop; + }; + static AITHREADSAFE(cscm_type, continued_statemachines_and_calling_mainloop, ); +} + +// static +AITHREADSAFESIMPLE(U64, AIStateMachine::sMaxCount, ); + +void AIStateMachine::updateSettings(void) +{ + Dout(dc::statemachine, "Initializing AIStateMachine::sMaxCount"); + *AIAccess(sMaxCount) = LLFastTimer::countsPerSecond() * gSavedSettings.getU32("StateMachineMaxTime") / 1000; +} + +//---------------------------------------------------------------------------- +// +// Public methods +// + +void AIStateMachine::run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent) +{ + DoutEntering(dc::statemachine, "AIStateMachine::run(" << (void*)parent << ", " << (parent ? parent->state_str(new_parent_state) : "NA") << ", " << abort_parent << ") [" << (void*)this << "]"); + // Must be the first time we're being run. + llassert(!mParent); + llassert(!mCallback); + // Can only be run when in this state. + llassert(mState == bs_initialize); + // If a parent is provided, it must be running. + llassert(!parent || parent->mState == bs_run); + + mParent = parent; + mNewParentState = new_parent_state; + mAbortParent = abort_parent; + + cont(); +} + +void AIStateMachine::run(callback_type::signal_type::slot_type const& slot) +{ + DoutEntering(dc::statemachine, "AIStateMachine::run() [" << (void*)this << "]"); + // Must be the first time we're being run. + llassert(!mParent); + llassert(!mCallback); + // Can only be run when in this state. + llassert(mState == bs_initialize); + + mCallback = new callback_type(slot); + + cont(); +} + +void AIStateMachine::idle(void) +{ + DoutEntering(dc::statemachine, "AIStateMachine::idle() [" << (void*)this << "]"); + llassert(!mIdle); + mIdle = true; + mSleep = 0; +} + +void AIStateMachine::cont(void) +{ + DoutEntering(dc::statemachine, "AIStateMachine::cont() [" << (void*)this << "]"); + llassert(mIdle); + mIdle = false; + AIWriteAccess cscm_w(continued_statemachines_and_calling_mainloop); + cscm_w->continued_statemachines.push_back(this); + if (!cscm_w->calling_mainloop) + { + Dout(dc::statemachine, "Adding AIStateMachine::mainloop to gIdleCallbacks"); + cscm_w->calling_mainloop = true; + gIdleCallbacks.addFunction(&AIStateMachine::mainloop); + } +} + +void AIStateMachine::set_state(state_type state) +{ + DoutEntering(dc::statemachine, "AIStateMachine::set_state(" << state_str(state) << ") [" << (void*)this << "]"); + llassert(mState == bs_run); + if (mRunState != state) + { + mRunState = state; + Dout(dc::statemachine, "mRunState set to " << state_str(mRunState)); + } + if (mIdle) + cont(); +} + +void AIStateMachine::abort(void) +{ + DoutEntering(dc::statemachine, "AIStateMachine::abort() [" << (void*)this << "]"); + llassert(mState == bs_run); + mState = bs_abort; + abort_impl(); + mAborted = true; + finish(); +} + +void AIStateMachine::finish(void) +{ + DoutEntering(dc::statemachine, "AIStateMachine::finish() [" << (void*)this << "]"); + llassert(mState == bs_run || mState == bs_abort); + mState = bs_finish; + finish_impl(); + if (mParent) + { + // It is possible that the parent is not running when the parent is in fact aborting and called + // abort on this object from it's abort_impl function. It that case we don't want to recursively + // call abort again (or change it's state). + if (mParent->running()) + { + if (mAborted && mAbortParent) + { + mParent->abort(); + } + else + { + mParent->set_state(mNewParentState); + } + } + mParent = NULL; + } + // Set this already to bs_initialize now, so that (bool)*this evaluates to true. + mState = bs_initialize; + // It is possible that mIdle is false when abort or finish was called from + // outside multiplex_impl. However, that only may be done by the main thread. + llassert(!mIdle || is_main_thread()); + if (!mIdle) + idle(); + if (mCallback) + { + mCallback->callback(!mAborted); // This can/may call deleteMe(), in which case the whole AIStateMachine will be deleted from the mainloop. + delete mCallback; + mCallback = NULL; + } +} + +void AIStateMachine::deleteMe(void) +{ + llassert(mIdle && mState == bs_initialize); + mState = bs_deleted; +} + +// Return stringified 'state'. +char const* AIStateMachine::state_str(state_type state) +{ + if (state >= min_state && state < max_state) + { + switch (state) + { + AI_CASE_RETURN(bs_initialize); + AI_CASE_RETURN(bs_run); + AI_CASE_RETURN(bs_abort); + AI_CASE_RETURN(bs_finish); + AI_CASE_RETURN(bs_deleted); + } + } + return state_str_impl(state); +} + +//---------------------------------------------------------------------------- +// +// Private methods +// + +void AIStateMachine::multiplex(U64 current_time) +{ + // Return immediately when this state machine is sleeping. + // A negative value of mSleep means we're counting frames, + // a positive value means we're waiting till a certain + // amount of time has passed. + if (mSleep != 0) + { + if (mSleep < 0) + { + if (++mSleep) + return; + } + else + { + if (current_time < mSleep) + return; + mSleep = 0; + } + } + + DoutEntering(dc::statemachine, "AIStateMachine::multiplex() [" << (void*)this << "] [with state: " << state_str(mState == bs_run ? mRunState : mState) << "]"); + llassert(mState == bs_initialize || mState == bs_run); + + // Real state machine starts here. + if (mState == bs_initialize) + { + mAborted = false; + mState = bs_run; + initialize_impl(); + if (mAborted || mState != bs_run) + return; + } + multiplex_impl(); +} + +// static +void AIStateMachine::mainloop(void*) +{ + LLFastTimer t(LLFastTimer::FTM_STATEMACHINE); + // Add continued state machines. + { + AIReadAccess cscm_r(continued_statemachines_and_calling_mainloop); + bool nonempty = false; + for (continued_statemachines_type::const_iterator iter = cscm_r->continued_statemachines.begin(); iter != cscm_r->continued_statemachines.end(); ++iter) + { + nonempty = true; + active_statemachines.push_back(QueueElement(*iter)); + Dout(dc::statemachine, "Adding " << (void*)*iter << " to active_statemachines"); + } + if (nonempty) + AIWriteAccess(cscm_r)->continued_statemachines.clear(); + } + llassert(!active_statemachines.empty()); + // Run one or more state machines. + U64 total_clocks = 0; + U64 max_count = *AIAccess(sMaxCount); + for (active_statemachines_type::iterator iter = active_statemachines.begin(); iter != active_statemachines.end(); ++iter) + { + AIStateMachine& statemachine(iter->statemachine()); + if (!statemachine.mIdle) + { + U64 start = get_cpu_clock_count(); + iter->statemachine().multiplex(start); + U64 delta = get_cpu_clock_count() - start; + iter->add(delta); + total_clocks += delta; + if (total_clocks >= max_count) + { +#ifndef LL_RELEASE_FOR_DOWNLOAD + llwarns << "AIStateMachine::mainloop did run for " << (total_clocks * 1000 / LLFastTimer::countsPerSecond()) << " ms." << llendl; +#endif + std::sort(active_statemachines.begin(), active_statemachines.end(), QueueElementComp()); + break; + } + } + } + // Remove idle state machines from the loop. + active_statemachines_type::iterator iter = active_statemachines.begin(); + while (iter != active_statemachines.end()) + { + AIStateMachine& statemachine(iter->statemachine()); + if (statemachine.mIdle) + { + Dout(dc::statemachine, "Erasing " << (void*)&statemachine << " from active_statemachines"); + iter = active_statemachines.erase(iter); + if (statemachine.mState == bs_deleted) + { + Dout(dc::statemachine, "Deleting " << (void*)&statemachine); + delete &statemachine; + } + } + else + { + llassert(statemachine.mState == bs_run); + ++iter; + } + } + if (active_statemachines.empty()) + { + // If this was the last state machine, remove mainloop from the IdleCallbacks. + AIReadAccess cscm_r(continued_statemachines_and_calling_mainloop); + if (cscm_r->continued_statemachines.empty() && cscm_r->calling_mainloop) + { + Dout(dc::statemachine, "Removing AIStateMachine::mainloop from gIdleCallbacks"); + AIWriteAccess(cscm_r)->calling_mainloop = false; + gIdleCallbacks.deleteFunction(&AIStateMachine::mainloop); + } + } +} diff --git a/indra/newview/statemachine/aistatemachine.h b/indra/newview/statemachine/aistatemachine.h new file mode 100644 index 000000000..4b25b957d --- /dev/null +++ b/indra/newview/statemachine/aistatemachine.h @@ -0,0 +1,344 @@ +/** + * @file aistatemachine.h + * @brief State machine base class + * + * Copyright (c) 2010, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * 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. + * + * CHANGELOG + * and additional copyright holders. + * + * 01/03/2010 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AISTATEMACHINE_H +#define AISTATEMACHINE_H + +#include "aithreadsafe.h" +#include "llfasttimer.h" + +//! +// A AIStateMachine is a base class that allows derived classes to +// go through asynchronous states, while the code still appears to +// be more or less sequential. +// +// These state machine objects can be reused to build more complex +// objects. +// +// It is important to note that each state has a duality: the object +// can have a state that will cause a corresponding function to be +// called; and often that function will end with changing the state +// again, to signal that it was handled. It is easy to confuse the +// function of a state with the state at the end of the function. +// For example, the state "initialize" could cause the member +// function 'init()' to be called, and at the end one would be +// inclined to set the state to "initialized". However, this is the +// wrong approach: the correct use of state names does reflect the +// functions that will be called, never the function that just was +// called. +// +// Each (derived) class goes through a series of states as follows: +// +// Creation +// | +// v +// (idle) <----. Idle until run() is called. +// | | +// Initialize | Calls initialize_impl(). +// | | +// | (idle) | Idle until cont() or set_state() is called. +// | | ^ | +// v v | | +// .-------. | | +// | Run |_, | Call multiplex_impl() until idle(), abort() or finish() is called. +// '-------' | +// | | | +// v | | +// Abort | | Calls abort_impl(). +// | | | +// v v | +// Finish | Calls finish_impl(). +// | | +// `---------' +// +// Each state causes corresponding code to be called. +// Finish cleans up whatever is done by Initialize. +// Abort should clean up additional things done by Run. +// +// The Run state is entered by calling run(). +// +// While the base class is in the Run state, it is the derived class +// that goes through different states. The state variable of the derived +// class is only valid while the base class is in the state Run. +// +// A derived class can exit the Run state by calling one of two methods: +// abort() in case of failure, or finish() in case of success. +// Respectively these set the state to Abort and Finish. +// +// State machines are run from the "idle" part of the viewer main loop. +// Often a state machine has nothing to do however. In that case it can +// call the method idle(). This will stop the state machine until +// external code changes it's state (by calling set_state()), or calls +// cont() to continue with the last state. +// +// The methods of the derived class call set_state() to change their +// own state within the bs_run state, or by calling either abort() +// or finish(). +// +// Restarting a finished state machine can also be done by calling run(), +// which will cause a re-initialize. +// +// Derived classes should implement the following constants: +// +// static state_type const min_state = first_state; +// static state_type const max_state = last_state + 1; +// +// Where first_state should be equal to BaseClass::max_state. +// These should represent the minimum and (one past) the maximum +// values of mRunState. +// +// virtual void initialize_impl(void) +// +// Initializes the derived class. +// +// virtual void multiplex_impl(void); +// +// This method should handle mRunState in a switch. +// For example: +// +// switch(mRunState) +// { +// case foo: +// handle_foo(); +// break; +// case wait_state: +// if (still_waiting()) +// { +// idle(); +// break; +// } +// set_state(working); +// /*fall-through*/ +// case working: +// do_work(); +// if (failure()) +// abort(); +// break; +// case etc: +// etc(); +// finish(); +// break; +// } +// +// virtual void abort_impl(void); +// +// A call to this method should bring the object to a state +// where finish_impl() can be called. +// +// virtual void finish_impl(void); +// +// Should cleanup whatever init_impl() did, or any of the +// states of the object where multiplex_impl() calls finish(). +// +// virtual char const* state_str_impl(state_type run_state); +// +// Should return a stringified value of run_state. +// +class AIStateMachine { + //! The type of mState + enum base_state_type { + bs_initialize, + bs_run, + bs_abort, + bs_finish, + bs_deleted + }; + + public: + typedef U32 state_type; //!< The type of mRunState + + //! Integral value equal to the state with the lowest value. + static state_type const min_state = bs_initialize; + //! Integral value one more than the state with the highest value. + static state_type const max_state = bs_deleted + 1; + + private: + base_state_type mState; //!< State of the base class. + bool mIdle; //!< True if this state machine is not running. + bool mAborted; //!< True after calling abort() and before calling run(). + U64 mSleep; //!< Non-zero while the state machine is sleeping. + + // Callback facilities. + // From within an other state machine: + AIStateMachine* mParent; //!< The parent object that started this state machine, or NULL if there isn't any. + state_type mNewParentState; //!< The state at which the parent should continue upon a successful finish. + bool mAbortParent; //!< If true, abort parent on abort(). Otherwise continue as normal. + // From outside a state machine: + struct callback_type { + typedef boost::signal signal_type; + callback_type(signal_type::slot_type const& slot) { connection = signal.connect(slot); } + ~callback_type() { connection.disconnect(); } + void callback(bool success) const { signal(success); } + private: + boost::signals::connection connection; + signal_type signal; + }; + callback_type* mCallback; //!< Pointer to signal/connection, or NULL when not connected. + + static AIThreadSafeSimple sMaxCount; //!< Number of cpu clocks below which we start a new state machine within the same frame. + + protected: + //! State of the derived class. Only valid if mState == bs_run. Call set_state to change. + state_type mRunState; + + public: + //! Create a non-running state machine. + AIStateMachine(void) : mState(bs_initialize), mIdle(true), mAborted(true), mSleep(0), mParent(NULL), mCallback(NULL) { updateSettings(); } + + protected: + //! The user should call 'deleteMe()', not delete a AIStateMachine (derived) directly. + virtual ~AIStateMachine() { llassert(mState == bs_deleted); } + + public: + //! Halt the state machine until cont() is called. + void idle(void); + + //! Temporarily halt the state machine. + void yield_frame(unsigned int frames) { mSleep = -frames; } + + //! Temporarily halt the state machine. + void yield_ms(unsigned int ms) { mSleep = get_cpu_clock_count() + LLFastTimer::countsPerSecond() * ms / 1000; } + + //! Continue running after calling idle. + void cont(void); + + //--------------------------------------- + // Changing the state. + + //! Change state to bs_run. May only be called after creation or after returning from finish(). + // If parent is non-NULL, change the parent state machine's state to new_parent_state + // upon finish, or in the case of an abort and when abort_parent is true, call parent->abort() instead. + void run(AIStateMachine* parent, state_type new_parent_state, bool abort_parent = true); + + //! Change state to 'bs_run'. May only be called after creation or after returning from finish(). + // Does not cause a callback. + void run(void) { run(NULL, 0, false); } + + //! The same as above, but pass the result of a boost::bind with _1. + // + // Here _1, if present, will be replaced with a bool indicating success. + // + // For example: + // + // + // struct Foo { void callback(AIStateMachineDerived* ptr, bool success); }; + // ... + // AIStateMachineDerived* magic = new AIStateMachineDerived; // Deleted by callback + // // Call foo_ptr->callback(magic, _1) on finish. + // state_machine->run(boost::bind(&Foo::callback, foo_ptr, magic, _1)); + // + // + // or + // + // + // struct Foo { void callback(bool success, AIStateMachineDerived const& magic); }; + // ... + // AIStateMachineDerived magic; + // // Call foo_ptr->callback(_1, magic) on finish. + // magic.run(boost::bind(&Foo::callback, foo_ptr, _1, magic)); + // + // + // or + // + // + // static void callback(void* userdata); + // ... + // AIStateMachineDerived magic; + // // Call callback(userdata) on finish. + // magic.run(boost::bind(&callback, userdata)); + // + void run(callback_type::signal_type::slot_type const& slot); + + //! Change state to 'bs_abort'. May only be called while in the bs_run state. + void abort(void); + + //! Change state to 'bs_finish'. May only be called while in the bs_run state. + void finish(void); + + //! Refine state while in the bs_run state. May only be called while in the bs_run state. + void set_state(state_type run_state); + + //! Change state to 'bs_deleted'. May only be called while in the bs_finish state. + void deleteMe(void); + + //--------------------------------------- + // Other. + + //! Called whenever the StateMachineMaxTime setting is changed. + static void updateSettings(void); + + //--------------------------------------- + // Accessors. + + //! Return true if state machine was aborted (can be used in finish_impl). + bool aborted(void) const { return mAborted; } + + //! Return true if the derived class is running (also when we are idle). + bool running(void) const { return mState == bs_run; } + + //! Return true if the derived class is running but idle. + bool waiting(void) const { return mState == bs_run && mIdle; } + + // Use some safebool idiom (http://www.artima.com/cppsource/safebool.html) rather than operator bool. + typedef state_type AIStateMachine::* const bool_type; + //! Return true if state machine successfully finished. + operator bool_type() const { return (mState == bs_initialize && !mAborted) ? &AIStateMachine::mRunState : 0; } + + //! Return a stringified state, for debugging purposes. + char const* state_str(state_type state); + + private: + static void mainloop(void*); + void multiplex(U64 current_time); + + protected: + //--------------------------------------- + // Derived class implementations. + + // Handle initializing the object. + virtual void initialize_impl(void) = 0; + + // Handle mRunState. + virtual void multiplex_impl(void) = 0; + + // Handle aborting from current bs_run state. + virtual void abort_impl(void) = 0; + + // Handle cleaning up from initialization (or post abort) state. + virtual void finish_impl(void) = 0; + + // Implemenation of state_str for run states. + virtual char const* state_str_impl(state_type run_state) const = 0; +}; + +// This case be used in state_str_impl. +#define AI_CASE_RETURN(x) do { case x: return #x; } while(0) + +#endif From e89d6d9d667a3fb7eb5d56cdfa1e00ed0d134098 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 6 May 2011 14:39:16 +0200 Subject: [PATCH 05/21] Split plugin classes and derive AIFilePicker from BasicPluginBase (part 1). This commit contains all changes, except those in indra/media_plugins (which was renamed to indra/plugins) and indra/llplugin. However, it does contain the (new) file indra/plugins/filepicker/basic_plugin_filepicker.cpp --- doc/contributions.txt | 1 + indra/CMakeLists.txt | 4 +- indra/cmake/BasicPluginBase.cmake | 8 + indra/cmake/MediaPluginBase.cmake | 8 +- indra/newview/CMakeLists.txt | 7 + indra/newview/lldirpicker.cpp | 28 +- indra/newview/lldirpicker.h | 16 +- indra/newview/llfilepicker.cpp | 64 ++- indra/newview/llfilepicker.h | 30 +- indra/newview/llfloatersnapshot.cpp | 12 +- indra/newview/llmediactrl.h | 2 +- indra/newview/llviewermedia.cpp | 338 +++++++--------- indra/newview/llviewermedia.h | 33 +- indra/newview/llviewermediaeventemitter.cpp | 85 ++++ indra/newview/llviewermediaeventemitter.h | 56 +++ indra/newview/llviewermediaobserver.cpp | 47 +++ indra/newview/llviewermenufile.cpp | 2 - indra/newview/llviewerparcelmedia.h | 2 +- indra/newview/llviewerpluginmanager.cpp | 56 +++ indra/newview/llviewerpluginmanager.h | 115 ++++++ indra/newview/llviewerwindow.cpp | 55 ++- indra/newview/llviewerwindow.h | 9 +- indra/newview/statemachine/aifilepicker.cpp | 369 ++++++++++++++++++ indra/newview/statemachine/aifilepicker.h | 160 ++++++++ indra/newview/viewer_manifest.py | 19 +- .../filepicker/basic_plugin_filepicker.cpp | 243 ++++++++++++ 26 files changed, 1432 insertions(+), 337 deletions(-) create mode 100644 indra/cmake/BasicPluginBase.cmake create mode 100644 indra/newview/llviewermediaeventemitter.cpp create mode 100644 indra/newview/llviewermediaeventemitter.h create mode 100644 indra/newview/llviewermediaobserver.cpp create mode 100644 indra/newview/llviewerpluginmanager.cpp create mode 100644 indra/newview/llviewerpluginmanager.h create mode 100644 indra/newview/statemachine/aifilepicker.cpp create mode 100644 indra/newview/statemachine/aifilepicker.h create mode 100644 indra/plugins/filepicker/basic_plugin_filepicker.cpp diff --git a/doc/contributions.txt b/doc/contributions.txt index ea148abd9..53ab212d8 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -113,6 +113,7 @@ Aleric Inglewood IMP-664 IMP-670 IMP-701 + IMP-702 IMP-734 IMP-735 Alissa Sabre diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index f88aa1fa0..c23a03e6a 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -76,8 +76,8 @@ if (VIEWER) add_subdirectory(${LIBS_OPEN_PREFIX}llplugin) add_subdirectory(${LIBS_OPEN_PREFIX}llui) - # viewer media plugins - add_subdirectory(${LIBS_OPEN_PREFIX}media_plugins) + # viewer plugins directory + add_subdirectory(${LIBS_OPEN_PREFIX}plugins) # llplugin testbed code (is this the right way to include it?) #if (NOT LINUX) diff --git a/indra/cmake/BasicPluginBase.cmake b/indra/cmake/BasicPluginBase.cmake new file mode 100644 index 000000000..a4f339272 --- /dev/null +++ b/indra/cmake/BasicPluginBase.cmake @@ -0,0 +1,8 @@ +# -*- cmake -*- + + +set(BASIC_PLUGIN_BASE_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/plugins/base_basic/ + ) + +set(BASIC_PLUGIN_BASE_LIBRARIES basic_plugin_base) diff --git a/indra/cmake/MediaPluginBase.cmake b/indra/cmake/MediaPluginBase.cmake index 2be035b64..7e2d30dfa 100644 --- a/indra/cmake/MediaPluginBase.cmake +++ b/indra/cmake/MediaPluginBase.cmake @@ -1,8 +1,6 @@ # -*- cmake -*- +include(BasicPluginBase) -set(MEDIA_PLUGIN_BASE_INCLUDE_DIRS - ${LIBS_OPEN_DIR}/media_plugins/base/ - ) - -set(MEDIA_PLUGIN_BASE_LIBRARIES media_plugin_base) +set(MEDIA_PLUGIN_BASE_INCLUDE_DIRS ${LIBS_OPEN_DIR}/plugins/base_media ${BASIC_PLUGIN_BASE_INCLUDE_DIRS}) +set(MEDIA_PLUGIN_BASE_LIBRARIES media_plugin_base ${BASIC_PLUGIN_BASE_LIBRARIES}) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 628ee9ec4..3599a1d3d 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -442,6 +442,8 @@ set(viewer_SOURCE_FILES llviewerkeyboard.cpp llviewerlayer.cpp llviewermedia.cpp + llviewermediaeventemitter.cpp + llviewermediaobserver.cpp llviewermediafocus.cpp llviewermedia_streamingaudio.cpp llviewermenu.cpp @@ -456,6 +458,7 @@ set(viewer_SOURCE_FILES llviewerparceloverlay.cpp llviewerpartsim.cpp llviewerpartsource.cpp + llviewerpluginmanager.cpp llviewerregion.cpp llviewershadermgr.cpp llviewerstats.cpp @@ -918,6 +921,7 @@ set(viewer_HEADER_FILES llviewerkeyboard.h llviewerlayer.h llviewermedia.h + llviewermediaeventemitter.h llviewermediaobserver.h llviewermediafocus.h llviewermenu.h @@ -932,6 +936,7 @@ set(viewer_HEADER_FILES llviewerparceloverlay.h llviewerpartsim.h llviewerpartsource.h + llviewerpluginmanager.h llviewerprecompiledheaders.h llviewerregion.h llviewershadermgr.h @@ -1007,9 +1012,11 @@ source_group("CMake Rules" FILES ViewerInstall.cmake) set(statemachine_SOURCE_FILES statemachine/aistatemachine.cpp + statemachine/aifilepicker.cpp ) set(statemachine_HEADER_FILES statemachine/aistatemachine.h + statemachine/aifilepicker.h ) list(APPEND viewer_SOURCE_FILES ${statemachine_SOURCE_FILES}) list(APPEND viewer_HEADER_FILES ${statemachine_HEADER_FILES}) diff --git a/indra/newview/lldirpicker.cpp b/indra/newview/lldirpicker.cpp index 8b0ed39eb..3840b52bd 100644 --- a/indra/newview/lldirpicker.cpp +++ b/indra/newview/lldirpicker.cpp @@ -269,48 +269,36 @@ void LLDirPicker::reset() LLDirPicker::LLDirPicker() { - mFilePicker = new LLFilePicker(); reset(); } LLDirPicker::~LLDirPicker() { - delete mFilePicker; } void LLDirPicker::reset() { - if (mFilePicker) - mFilePicker->reset(); + LLFilePickerBase::reset(); } BOOL LLDirPicker::getDir(std::string* filename) { reset(); - if (mFilePicker) + GtkWindow* picker = buildFilePicker(false, true, "dirpicker"); + if (picker) { - GtkWindow* picker = mFilePicker->buildFilePicker(false, true, - "dirpicker"); - - if (picker) - { - gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("choose_the_directory").c_str()); - gtk_widget_show_all(GTK_WIDGET(picker)); - gtk_main(); - return (!mFilePicker->getFirstFile().empty()); - } + gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("choose_the_directory").c_str()); + gtk_widget_show_all(GTK_WIDGET(picker)); + gtk_main(); + return (!getFirstFile().empty()); } return FALSE; } std::string LLDirPicker::getDirName() { - if (mFilePicker) - { - return mFilePicker->getFirstFile(); - } - return ""; + return getFirstFile(); } #else // not implemented diff --git a/indra/newview/lldirpicker.h b/indra/newview/lldirpicker.h index 26f76915a..cdee1d094 100644 --- a/indra/newview/lldirpicker.h +++ b/indra/newview/lldirpicker.h @@ -57,9 +57,15 @@ #include #endif -class LLFilePicker; +#if LL_LINUX || LL_SOLARIS +#include "llfilepicker.h" +#endif class LLDirPicker +#if LL_LINUX || LL_SOLARIS + // On Linux we just implement LLDirPicker on top of LLFilePickerBase + : public LLFilePickerBase +#endif { public: // calling this before main() is undefined @@ -90,19 +96,13 @@ private: #endif -#if LL_LINUX || LL_SOLARIS - // On Linux we just implement LLDirPicker on top of LLFilePicker - LLFilePicker *mFilePicker; -#endif - std::string* mFileName; std::string mDir; BOOL mLocked; static LLDirPicker sInstance; -public: - // don't call these directly please. +private: LLDirPicker(); ~LLDirPicker(); }; diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index 5cea197ca..fb327e5a6 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -70,7 +70,7 @@ LLFilePicker LLFilePicker::sInstance; // // Implementation // -LLFilePicker::LLFilePicker() +LLFilePickerBase::LLFilePickerBase() : mCurrentFile(0), mLocked(FALSE) @@ -108,19 +108,13 @@ LLFilePicker::LLFilePicker() #endif } -LLFilePicker::~LLFilePicker() -{ - // nothing -} - - -const std::string LLFilePicker::getFirstFile() +const std::string LLFilePickerBase::getFirstFile() { mCurrentFile = 0; return getNextFile(); } -const std::string LLFilePicker::getNextFile() +const std::string LLFilePickerBase::getNextFile() { if (mCurrentFile >= getFileCount()) { @@ -133,7 +127,7 @@ const std::string LLFilePicker::getNextFile() } } -const std::string LLFilePicker::getCurFile() +const std::string LLFilePickerBase::getCurFile() { if (mCurrentFile >= getFileCount()) { @@ -146,7 +140,7 @@ const std::string LLFilePicker::getCurFile() } } -void LLFilePicker::reset() +void LLFilePickerBase::reset() { mLocked = FALSE; mFiles.clear(); @@ -155,7 +149,7 @@ void LLFilePicker::reset() #if LL_WINDOWS -BOOL LLFilePicker::setupFilter(ELoadFilter filter) +BOOL LLFilePickerBase::setupFilter(ELoadFilter filter) { BOOL res = TRUE; switch (filter) @@ -220,7 +214,7 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter) return res; } -BOOL LLFilePicker::getOpenFile(ELoadFilter filter) +BOOL LLFilePickerBase::getOpenFile(ELoadFilter filter) { if( mLocked ) { @@ -258,7 +252,7 @@ BOOL LLFilePicker::getOpenFile(ELoadFilter filter) return success; } -BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter) +BOOL LLFilePickerBase::getMultipleOpenFiles(ELoadFilter filter) { if( mLocked ) { @@ -321,7 +315,7 @@ BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter) return success; } -BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename) +BOOL LLFilePickerBase::getSaveFile(ESaveFilter filter, const std::string& filename) { if( mLocked ) { @@ -735,7 +729,7 @@ BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename) #elif LL_DARWIN -Boolean LLFilePicker::navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode) +Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode) { Boolean result = true; ELoadFilter filter = *((ELoadFilter*) callBackUD); @@ -832,7 +826,7 @@ Boolean LLFilePicker::navOpenFilterProc(AEDesc *theItem, void *info, void *callB return result; } -OSStatus LLFilePicker::doNavChooseDialog(ELoadFilter filter) +OSStatus LLFilePickerBase::doNavChooseDialog(ELoadFilter filter) { OSStatus error = noErr; NavDialogRef navRef = NULL; @@ -887,7 +881,7 @@ OSStatus LLFilePicker::doNavChooseDialog(ELoadFilter filter) return error; } -OSStatus LLFilePicker::doNavSaveDialog(ESaveFilter filter, const std::string& filename) +OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string& filename) { OSStatus error = noErr; NavDialogRef navRef = NULL; @@ -1059,7 +1053,7 @@ OSStatus LLFilePicker::doNavSaveDialog(ESaveFilter filter, const std::string& fi return error; } -BOOL LLFilePicker::getOpenFile(ELoadFilter filter) +BOOL LLFilePickerBase::getOpenFile(ELoadFilter filter) { if( mLocked ) return FALSE; @@ -1088,7 +1082,7 @@ BOOL LLFilePicker::getOpenFile(ELoadFilter filter) return success; } -BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter) +BOOL LLFilePickerBase::getMultipleOpenFiles(ELoadFilter filter) { if( mLocked ) return FALSE; @@ -1119,7 +1113,7 @@ BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter) return success; } -BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename) +BOOL LLFilePickerBase::getSaveFile(ESaveFilter filter, const std::string& filename) { if( mLocked ) return FALSE; @@ -1152,13 +1146,13 @@ BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename) # if LL_GTK // static -void LLFilePicker::add_to_selectedfiles(gpointer data, gpointer user_data) +void LLFilePickerBase::add_to_selectedfiles(gpointer data, gpointer user_data) { // We need to run g_filename_to_utf8 in the user's locale std::string saved_locale(setlocale(LC_ALL, NULL)); setlocale(LC_ALL, ""); - LLFilePicker* picker = (LLFilePicker*) user_data; + LLFilePickerBase* picker = (LLFilePickerBase*) user_data; GError *error = NULL; gchar* filename_utf8 = g_filename_to_utf8((gchar*)data, -1, NULL, NULL, &error); @@ -1189,7 +1183,7 @@ void LLFilePicker::add_to_selectedfiles(gpointer data, gpointer user_data) } // static -void LLFilePicker::chooser_responder(GtkWidget *widget, gint response, gpointer user_data) +void LLFilePickerBase::chooser_responder(GtkWidget *widget, gint response, gpointer user_data) { LLFilePicker* picker = (LLFilePicker*)user_data; @@ -1212,7 +1206,7 @@ void LLFilePicker::chooser_responder(GtkWidget *widget, gint response, gpointer } -GtkWindow* LLFilePicker::buildFilePicker(bool is_save, bool is_folder, std::string context) +GtkWindow* LLFilePickerBase::buildFilePicker(bool is_save, bool is_folder, std::string context) { if (LLWindowSDL::ll_try_gtk_init()) { @@ -1270,7 +1264,7 @@ GtkWindow* LLFilePicker::buildFilePicker(bool is_save, bool is_folder, std::stri g_signal_connect (GTK_FILE_CHOOSER(win), "response", - G_CALLBACK(LLFilePicker::chooser_responder), + G_CALLBACK(LLFilePickerBase::chooser_responder), this); gtk_window_set_modal(GTK_WINDOW(win), TRUE); @@ -1346,7 +1340,7 @@ static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker) } -BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename ) +BOOL LLFilePickerBase::getSaveFile( ESaveFilter filter, const std::string& filename ) { BOOL rtn = FALSE; @@ -1435,7 +1429,7 @@ BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename return rtn; } -BOOL LLFilePicker::getOpenFile( ELoadFilter filter ) +BOOL LLFilePickerBase::getOpenFile( ELoadFilter filter ) { BOOL rtn = FALSE; @@ -1479,7 +1473,7 @@ BOOL LLFilePicker::getOpenFile( ELoadFilter filter ) return rtn; } -BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter ) +BOOL LLFilePickerBase::getMultipleOpenFiles( ELoadFilter filter ) { BOOL rtn = FALSE; @@ -1511,7 +1505,7 @@ BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter ) // Hacky stubs designed to facilitate fake getSaveFile and getOpenFile with // static results, when we don't have a real filepicker. -BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename ) +BOOL LLFilePickerBase::getSaveFile( ESaveFilter filter, const std::string& filename ) { reset(); @@ -1525,7 +1519,7 @@ BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename return FALSE; } -BOOL LLFilePicker::getOpenFile( ELoadFilter filter ) +BOOL LLFilePickerBase::getOpenFile( ELoadFilter filter ) { reset(); @@ -1543,7 +1537,7 @@ BOOL LLFilePicker::getOpenFile( ELoadFilter filter ) return TRUE; } -BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter ) +BOOL LLFilePickerBase::getMultipleOpenFiles( ELoadFilter filter ) { reset(); return FALSE; @@ -1553,19 +1547,19 @@ BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter ) #else // not implemented -BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename ) +BOOL LLFilePickerBase::getSaveFile( ESaveFilter filter, const std::string& filename ) { reset(); return FALSE; } -BOOL LLFilePicker::getOpenFile( ELoadFilter filter ) +BOOL LLFilePickerBase::getOpenFile( ELoadFilter filter ) { reset(); return FALSE; } -BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter ) +BOOL LLFilePickerBase::getMultipleOpenFiles( ELoadFilter filter ) { reset(); return FALSE; diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index feba53423..8063a7068 100644 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -69,16 +69,11 @@ #include "SDL/SDL_syswm.h" #endif -class LLFilePicker +// This class is used as base class of a singleton and is therefore not +// allowed to have any static members or static local variables! +class LLFilePickerBase { -#ifdef LL_GTK - friend class LLDirPicker; - friend void chooser_responder(GtkWidget *, gint, gpointer); -#endif // LL_GTK public: - // calling this before main() is undefined - static LLFilePicker& instance( void ) { return sInstance; } - enum ELoadFilter { FFLOAD_ALL = 1, @@ -212,8 +207,6 @@ private: S32 mCurrentFile; BOOL mLocked; BOOL mMultiFile; - - static LLFilePicker sInstance; protected: #if LL_GTK @@ -221,10 +214,21 @@ protected: std::string context = "generic"); #endif +protected: + LLFilePickerBase(); +}; + +// True singleton, private constructors (and no friends). +class LLFilePicker : public LLFilePickerBase +{ public: - // don't call these directly please. - LLFilePicker(); - ~LLFilePicker(); + // calling this before main() is undefined + static LLFilePicker& instance( void ) { return sInstance; } + +private: + static LLFilePicker sInstance; + + LLFilePicker() { } }; #endif diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index 5af0de858..2d4ed9939 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -149,7 +149,7 @@ public: void updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail = FALSE, F32 delay = 0.f); LLFloaterPostcard* savePostcard(); void saveTexture(); - BOOL saveLocal(); + void saveLocal(); BOOL setThumbnailImageSize() ; void generateThumbnailImage(BOOL force_update = FALSE) ; @@ -1005,20 +1005,14 @@ void LLSnapshotLivePreview::saveTexture() mDataSize = 0; } -BOOL LLSnapshotLivePreview::saveLocal() +void LLSnapshotLivePreview::saveLocal() { - BOOL success = gViewerWindow->saveImageNumbered(mFormattedImage); + gViewerWindow->saveImageNumbered(mFormattedImage); // Relinquish image memory. Save button will be disabled as a side-effect. mFormattedImage = NULL; mDataSize = 0; updateSnapshot(FALSE, FALSE); - - if(success) - { - gViewerWindow->playSnapshotAnimAndSound(); - } - return success; } ///---------------------------------------------------------------------------- diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h index f185fb9b9..bbf2dfa01 100644 --- a/indra/newview/llmediactrl.h +++ b/indra/newview/llmediactrl.h @@ -34,7 +34,7 @@ #define LL_LLMediaCtrl_H #include "llviewermedia.h" - +#include "llviewermediaobserver.h" #include "lluictrl.h" #include "llframetimer.h" #include "lldynamictexture.h" diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 035bdebe1..ca21f6d0c 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -47,84 +47,12 @@ #include "lluuid.h" #include "llkeyboard.h" - // Merov: Temporary definitions while porting the new viewer media code to Snowglobe const int LEFT_BUTTON = 0; const int RIGHT_BUTTON = 1; -// Move this to its own file. - -LLViewerMediaEventEmitter::~LLViewerMediaEventEmitter() -{ - observerListType::iterator iter = mObservers.begin(); - - while( iter != mObservers.end() ) - { - LLViewerMediaObserver *self = *iter; - iter++; - remObserver(self); - } -} - /////////////////////////////////////////////////////////////////////////////// -// -bool LLViewerMediaEventEmitter::addObserver( LLViewerMediaObserver* observer ) -{ - if ( ! observer ) - return false; - - if ( std::find( mObservers.begin(), mObservers.end(), observer ) != mObservers.end() ) - return false; - - mObservers.push_back( observer ); - observer->mEmitters.push_back( this ); - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// -bool LLViewerMediaEventEmitter::remObserver( LLViewerMediaObserver* observer ) -{ - if ( ! observer ) - return false; - - mObservers.remove( observer ); - observer->mEmitters.remove(this); - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// -void LLViewerMediaEventEmitter::emitEvent( LLPluginClassMedia* media, LLPluginClassMediaOwner::EMediaEvent event ) -{ - observerListType::iterator iter = mObservers.begin(); - - while( iter != mObservers.end() ) - { - LLViewerMediaObserver *self = *iter; - ++iter; - self->handleMediaEvent( media, event ); - } -} - -// Move this to its own file. -LLViewerMediaObserver::~LLViewerMediaObserver() -{ - std::list::iterator iter = mEmitters.begin(); - - while( iter != mEmitters.end() ) - { - LLViewerMediaEventEmitter *self = *iter; - iter++; - self->remObserver( this ); - } -} - - -// Move this to its own file. -// helper class that tries to download a URL from a web site and calls a method +// Helper class that tries to download a URL from a web site and calls a method // on the Panel Land Media and to discover the MIME type class LLMimeDiscoveryResponder : public LLHTTPClient::Responder { @@ -279,9 +207,10 @@ void LLViewerMedia::updateBrowserUserAgent() for(; iter != end; iter++) { LLViewerMediaImpl* pimpl = *iter; - if(pimpl->mMediaSource && pimpl->mMediaSource->pluginSupportsMediaBrowser()) + LLPluginClassMedia* plugin = pimpl->getMediaPlugin(); + if(plugin && plugin->pluginSupportsMediaBrowser()) { - pimpl->mMediaSource->setBrowserUserAgent(user_agent); + plugin->setBrowserUserAgent(user_agent); } } @@ -366,7 +295,6 @@ LLViewerMediaImpl::LLViewerMediaImpl(const std::string& media_url, U8 media_loop, const std::string& mime_type) : - mMediaSource( NULL ), mMovieImageHasMips(false), mTextureId(texture_id), mMediaWidth(media_width), @@ -399,7 +327,7 @@ LLViewerMediaImpl::~LLViewerMediaImpl() ////////////////////////////////////////////////////////////////////////////////////////// bool LLViewerMediaImpl::initializeMedia(const std::string& mime_type) { - if((mMediaSource == NULL) || (mMimeType != mime_type)) + if((mPluginBase == NULL) || (mMimeType != mime_type)) { if(! initializePlugin(mime_type)) { @@ -413,7 +341,7 @@ bool LLViewerMediaImpl::initializeMedia(const std::string& mime_type) } // play(); - return (mMediaSource != NULL); + return (mPluginBase != NULL); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -434,14 +362,13 @@ void LLViewerMediaImpl::createMediaSource() void LLViewerMediaImpl::destroyMediaSource() { mNeedsNewTexture = true; - if(! mMediaSource) + if (!mPluginBase) { return; } // Restore the texture updateMovieImage(LLUUID::null, false); - delete mMediaSource; - mMediaSource = NULL; + destroyPlugin(); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -514,16 +441,17 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ LLNotifications::instance().add("NoPlugin", args); return NULL; -} +} ////////////////////////////////////////////////////////////////////////////////////////// bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) { - if(mMediaSource) + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) { // Save the previous media source's last set size before destroying it. - mMediaWidth = mMediaSource->getSetWidth(); - mMediaHeight = mMediaSource->getSetHeight(); + mMediaWidth = plugin->getSetWidth(); + mMediaHeight = plugin->getSetHeight(); } // Always delete the old media impl first. @@ -541,7 +469,7 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) media_source->setAutoScale(mMediaAutoScale); media_source->setBrowserUserAgent(LLViewerMedia::getCurrentUserAgent()); - mMediaSource = media_source; + mPluginBase = media_source; return true; } @@ -550,34 +478,38 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) void LLViewerMediaImpl::setSize(int width, int height) { + LLPluginClassMedia* plugin = getMediaPlugin(); mMediaWidth = width; mMediaHeight = height; - if(mMediaSource) + if (plugin) { - mMediaSource->setSize(width, height); + plugin->setSize(width, height); } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::play() { + LLPluginClassMedia* plugin = getMediaPlugin(); + // first stop any previously playing media // stop(); - // mMediaSource->addObserver( this ); - if(mMediaSource == NULL) + // plugin->addObserver( this ); + if (!plugin) { if(!initializePlugin(mMimeType)) { // Plugin failed initialization... should assert or something return; } + plugin = getMediaPlugin(); } // updateMovieImage(mTextureId, true); - mMediaSource->loadURI( mMediaURL ); - if(/*mMediaSource->pluginSupportsMediaTime()*/ true) + plugin->loadURI( mMediaURL ); + if(/*plugin->pluginSupportsMediaTime()*/ true) { start(); } @@ -586,9 +518,10 @@ void LLViewerMediaImpl::play() ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::stop() { - if(mMediaSource) + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) { - mMediaSource->stop(); + plugin->stop(); // destroyMediaSource(); } } @@ -596,52 +529,57 @@ void LLViewerMediaImpl::stop() ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::pause() { - if(mMediaSource) + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) { - mMediaSource->pause(); + plugin->pause(); } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::start() { - if(mMediaSource) + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) { - mMediaSource->start(); + plugin->start(); } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::seek(F32 time) { - if(mMediaSource) + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) { - mMediaSource->seek(time); + plugin->seek(time); } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::setVolume(F32 volume) { - if(mMediaSource) + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) { - mMediaSource->setVolume(volume); + plugin->setVolume(volume); } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::focus(bool focus) { - if (mMediaSource) + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) { // call focus just for the hell of it, even though this apopears to be a nop - mMediaSource->focus(focus); + plugin->focus(focus); if (focus) { // spoof a mouse click to *actually* pass focus // Don't do this anymore -- it actually clicks through now. -// mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_DOWN, 1, 1, 0); -// mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_UP, 1, 1, 0); +// plugin->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_DOWN, 1, 1, 0); +// plugin->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_UP, 1, 1, 0); } } } @@ -649,57 +587,62 @@ void LLViewerMediaImpl::focus(bool focus) ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::mouseDown(S32 x, S32 y) { + LLPluginClassMedia* plugin = getMediaPlugin(); scaleMouse(&x, &y); mLastMouseX = x; mLastMouseY = y; - if (mMediaSource) + if (plugin) { - mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_DOWN, LEFT_BUTTON, x, y, 0); + plugin->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_DOWN, LEFT_BUTTON, x, y, 0); } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::mouseUp(S32 x, S32 y) { + LLPluginClassMedia* plugin = getMediaPlugin(); scaleMouse(&x, &y); mLastMouseX = x; mLastMouseY = y; - if (mMediaSource) + if (plugin) { - mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_UP, LEFT_BUTTON, x, y, 0); + plugin->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_UP, LEFT_BUTTON, x, y, 0); } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::mouseMove(S32 x, S32 y) { + LLPluginClassMedia* plugin = getMediaPlugin(); scaleMouse(&x, &y); mLastMouseX = x; mLastMouseY = y; - if (mMediaSource) + if (plugin) { - mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_MOVE, LEFT_BUTTON, x, y, 0); + plugin->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_MOVE, LEFT_BUTTON, x, y, 0); } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::mouseLeftDoubleClick(S32 x, S32 y) { + LLPluginClassMedia* plugin = getMediaPlugin(); scaleMouse(&x, &y); mLastMouseX = x; mLastMouseY = y; - if (mMediaSource) + if (plugin) { - mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_DOUBLE_CLICK, LEFT_BUTTON, x, y, 0); + plugin->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_DOUBLE_CLICK, LEFT_BUTTON, x, y, 0); } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::onMouseCaptureLost() { - if (mMediaSource) + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) { - mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_UP, LEFT_BUTTON, mLastMouseX, mLastMouseY, 0); + plugin->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_UP, LEFT_BUTTON, mLastMouseX, mLastMouseY, 0); } } @@ -720,15 +663,17 @@ BOOL LLViewerMediaImpl::handleMouseUp(S32 x, S32 y, MASK mask) ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::navigateHome() { - if(mMediaSource) + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) { - mMediaSource->loadURI( mHomeURL ); + plugin->loadURI( mHomeURL ); } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type) { + LLPluginClassMedia* plugin = getMediaPlugin(); if(rediscover_type) { @@ -745,7 +690,7 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi // We use "data" internally for a text/html url for loading the login screen if(initializeMedia("text/html")) { - mMediaSource->loadURI( url ); + plugin->loadURI( url ); } } else @@ -753,17 +698,17 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi // This catches 'rtsp://' urls if(initializeMedia(scheme)) { - mMediaSource->loadURI( url ); + plugin->loadURI( url ); } } } - else if (mMediaSource) + else if (plugin) { - mMediaSource->loadURI( url ); + plugin->loadURI( url ); } - else if(initializeMedia(mime_type) && mMediaSource) + else if(initializeMedia(mime_type) && (plugin = getMediaPlugin())) { - mMediaSource->loadURI( url ); + plugin->loadURI( url ); } else { @@ -777,9 +722,10 @@ void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mi ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::navigateStop() { - if(mMediaSource) + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) { - mMediaSource->browse_stop(); + plugin->browse_stop(); } } @@ -788,8 +734,9 @@ void LLViewerMediaImpl::navigateStop() bool LLViewerMediaImpl::handleKeyHere(KEY key, MASK mask) { bool result = false; + LLPluginClassMedia* plugin = getMediaPlugin(); - if (mMediaSource) + if (plugin) { // FIXME: THIS IS SO WRONG. // Menu keys should be handled by the menu system and not passed to UI elements, but this is how LLTextEditor and LLLineEditor do it... @@ -797,19 +744,19 @@ bool LLViewerMediaImpl::handleKeyHere(KEY key, MASK mask) { if( 'C' == key ) { - mMediaSource->copy(); + plugin->copy(); result = true; } else if( 'V' == key ) { - mMediaSource->paste(); + plugin->paste(); result = true; } else if( 'X' == key ) { - mMediaSource->cut(); + plugin->cut(); result = true; } } @@ -819,9 +766,9 @@ bool LLViewerMediaImpl::handleKeyHere(KEY key, MASK mask) LLSD native_key_data = LLSD::emptyMap(); - result = mMediaSource->keyEvent(LLPluginClassMedia::KEY_EVENT_DOWN ,key, mask, native_key_data); + result = plugin->keyEvent(LLPluginClassMedia::KEY_EVENT_DOWN ,key, mask, native_key_data); // Since the viewer internal event dispatching doesn't give us key-up events, simulate one here. - (void)mMediaSource->keyEvent(LLPluginClassMedia::KEY_EVENT_UP ,key, mask, native_key_data); + (void)plugin->keyEvent(LLPluginClassMedia::KEY_EVENT_UP ,key, mask, native_key_data); } } @@ -832,8 +779,9 @@ bool LLViewerMediaImpl::handleKeyHere(KEY key, MASK mask) bool LLViewerMediaImpl::handleUnicodeCharHere(llwchar uni_char) { bool result = false; + LLPluginClassMedia* plugin = getMediaPlugin(); - if (mMediaSource) + if (plugin) { // only accept 'printable' characters, sigh... if (uni_char >= 32 // discard 'control' characters @@ -841,7 +789,7 @@ bool LLViewerMediaImpl::handleUnicodeCharHere(llwchar uni_char) { LLSD native_key_data = LLSD::emptyMap(); - mMediaSource->textInput(wstring_to_utf8str(LLWString(1, uni_char)), gKeyboard->currentMask(FALSE), native_key_data); + plugin->textInput(wstring_to_utf8str(LLWString(1, uni_char)), gKeyboard->currentMask(FALSE), native_key_data); } } @@ -851,10 +799,11 @@ bool LLViewerMediaImpl::handleUnicodeCharHere(llwchar uni_char) ////////////////////////////////////////////////////////////////////////////////////////// bool LLViewerMediaImpl::canNavigateForward() { - BOOL result = FALSE; - if (mMediaSource) + bool result = false; + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) { - result = mMediaSource->getHistoryForwardAvailable(); + result = plugin->getHistoryForwardAvailable(); } return result; } @@ -862,10 +811,11 @@ bool LLViewerMediaImpl::canNavigateForward() ////////////////////////////////////////////////////////////////////////////////////////// bool LLViewerMediaImpl::canNavigateBack() { - BOOL result = FALSE; - if (mMediaSource) + bool result = false; + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) { - result = mMediaSource->getHistoryBackAvailable(); + result = plugin->getHistoryBackAvailable(); } return result; } @@ -909,20 +859,21 @@ void LLViewerMediaImpl::updateMovieImage(const LLUUID& uuid, BOOL active) ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::update() { - if(mMediaSource == NULL) + LLPluginClassMedia* plugin = getMediaPlugin(); + if (!plugin) { return; } - mMediaSource->idle(); + plugin->idle(); - if(mMediaSource->isPluginExited()) + if (plugin->isPluginExited()) { destroyMediaSource(); return; } - if(!mMediaSource->textureValid()) + if (!plugin->textureValid()) { return; } @@ -937,7 +888,7 @@ void LLViewerMediaImpl::update() if(placeholder_image) { LLRect dirty_rect; - if(mMediaSource->getDirty(&dirty_rect)) + if (plugin->getDirty(&dirty_rect)) { // Constrain the dirty rect to be inside the texture S32 x_pos = llmax(dirty_rect.mLeft, 0); @@ -948,16 +899,16 @@ void LLViewerMediaImpl::update() if(width > 0 && height > 0) { - U8* data = mMediaSource->getBitsData(); + U8* data = plugin->getBitsData(); // Offset the pixels pointer to match x_pos and y_pos - data += ( x_pos * mMediaSource->getTextureDepth() * mMediaSource->getBitsWidth() ); - data += ( y_pos * mMediaSource->getTextureDepth() ); + data += ( x_pos * plugin->getTextureDepth() * plugin->getBitsWidth() ); + data += ( y_pos * plugin->getTextureDepth() ); placeholder_image->setSubImage( data, - mMediaSource->getBitsWidth(), - mMediaSource->getBitsHeight(), + plugin->getBitsWidth(), + plugin->getBitsHeight(), x_pos, y_pos, width, @@ -966,7 +917,7 @@ void LLViewerMediaImpl::update() } - mMediaSource->resetDirty(); + plugin->resetDirty(); } } } @@ -988,22 +939,23 @@ void LLViewerMediaImpl::updateImagesMediaStreams() } LLViewerMediaTexture* placeholder_image = (LLViewerMediaTexture*)LLViewerTextureManager::findTexture( mTextureId ); + LLPluginClassMedia* plugin = getMediaPlugin(); if (mNeedsNewTexture || placeholder_image->getUseMipMaps() || ! placeholder_image->mIsMediaTexture - || (placeholder_image->getWidth() != mMediaSource->getTextureWidth()) - || (placeholder_image->getHeight() != mMediaSource->getTextureHeight()) - || (mTextureUsedWidth != mMediaSource->getWidth()) - || (mTextureUsedHeight != mMediaSource->getHeight()) + || (placeholder_image->getWidth() != plugin->getTextureWidth()) + || (placeholder_image->getHeight() != plugin->getTextureHeight()) + || (mTextureUsedWidth != plugin->getWidth()) + || (mTextureUsedHeight != plugin->getHeight()) ) { llinfos << "initializing media placeholder" << llendl; llinfos << "movie image id " << mTextureId << llendl; - int texture_width = mMediaSource->getTextureWidth(); - int texture_height = mMediaSource->getTextureHeight(); - int texture_depth = mMediaSource->getTextureDepth(); + int texture_width = plugin->getTextureWidth(); + int texture_height = plugin->getTextureHeight(); + int texture_depth = plugin->getTextureDepth(); // MEDIAOPT: check to see if size actually changed before doing work placeholder_image->destroyGLTexture(); @@ -1017,10 +969,10 @@ void LLViewerMediaImpl::updateImagesMediaStreams() int discard_level = 0; // ask media source for correct GL image format constants - placeholder_image->setExplicitFormat(mMediaSource->getTextureFormatInternal(), - mMediaSource->getTextureFormatPrimary(), - mMediaSource->getTextureFormatType(), - mMediaSource->getTextureFormatSwapBytes()); + placeholder_image->setExplicitFormat(plugin->getTextureFormatInternal(), + plugin->getTextureFormatPrimary(), + plugin->getTextureFormatType(), + plugin->getTextureFormatSwapBytes()); placeholder_image->createGLTexture(discard_level, raw); @@ -1033,8 +985,8 @@ void LLViewerMediaImpl::updateImagesMediaStreams() // If the amount of the texture being drawn by the media goes down in either width or height, // recreate the texture to avoid leaving parts of the old image behind. - mTextureUsedWidth = mMediaSource->getWidth(); - mTextureUsedHeight = mMediaSource->getHeight(); + mTextureUsedWidth = plugin->getWidth(); + mTextureUsedHeight = plugin->getHeight(); } return placeholder_image; @@ -1050,24 +1002,25 @@ LLUUID LLViewerMediaImpl::getMediaTextureID() ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::setVisible(bool visible) { + LLPluginClassMedia* plugin = getMediaPlugin(); mVisible = visible; if(mVisible) { - if(mMediaSource && mMediaSource->isPluginExited()) + if(plugin && plugin->isPluginExited()) { destroyMediaSource(); } - if(!mMediaSource) + if(!plugin) { createMediaSource(); } } - if(mMediaSource) + if(plugin) { - mMediaSource->setPriority(mVisible?LLPluginClassMedia::PRIORITY_NORMAL:LLPluginClassMedia::PRIORITY_HIDDEN); + plugin->setPriority(mVisible?LLPluginClassBasic::PRIORITY_NORMAL:LLPluginClassBasic::PRIORITY_SLEEP); } } @@ -1080,10 +1033,11 @@ void LLViewerMediaImpl::mouseCapture() ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::getTextureSize(S32 *texture_width, S32 *texture_height) { - if(mMediaSource && mMediaSource->textureValid()) + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin && plugin->textureValid()) { - S32 real_texture_width = mMediaSource->getBitsWidth(); - S32 real_texture_height = mMediaSource->getBitsHeight(); + S32 real_texture_width = plugin->getBitsWidth(); + S32 real_texture_height = plugin->getBitsHeight(); { // The "texture width" coming back from the plugin may not be a power of two (thanks to webkit). @@ -1124,10 +1078,11 @@ void LLViewerMediaImpl::scaleMouse(S32 *mouse_x, S32 *mouse_y) bool LLViewerMediaImpl::isMediaPlaying() { bool result = false; + LLPluginClassMedia* plugin = getMediaPlugin(); - if(mMediaSource) + if(plugin) { - EMediaStatus status = mMediaSource->getStatus(); + EMediaStatus status = plugin->getStatus(); if(status == MEDIA_PLAYING || status == MEDIA_LOADING) result = true; } @@ -1138,10 +1093,11 @@ bool LLViewerMediaImpl::isMediaPlaying() bool LLViewerMediaImpl::isMediaPaused() { bool result = false; + LLPluginClassMedia* plugin = getMediaPlugin(); - if(mMediaSource) + if(plugin) { - if(mMediaSource->getStatus() == MEDIA_PAUSED) + if(plugin->getStatus() == MEDIA_PAUSED) result = true; } @@ -1152,7 +1108,7 @@ bool LLViewerMediaImpl::isMediaPaused() // bool LLViewerMediaImpl::hasMedia() { - return mMediaSource != NULL; + return mPluginBase != NULL; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1179,8 +1135,9 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* self, LLPluginClass void LLViewerMediaImpl::cut() { - if (mMediaSource) - mMediaSource->cut(); + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) + plugin->cut(); } //////////////////////////////////////////////////////////////////////////////// @@ -1188,8 +1145,9 @@ LLViewerMediaImpl::cut() BOOL LLViewerMediaImpl::canCut() const { - if (mMediaSource) - return mMediaSource->canCut(); + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) + return plugin->canCut(); else return FALSE; } @@ -1199,8 +1157,9 @@ LLViewerMediaImpl::canCut() const void LLViewerMediaImpl::copy() { - if (mMediaSource) - mMediaSource->copy(); + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) + plugin->copy(); } //////////////////////////////////////////////////////////////////////////////// @@ -1208,8 +1167,9 @@ LLViewerMediaImpl::copy() BOOL LLViewerMediaImpl::canCopy() const { - if (mMediaSource) - return mMediaSource->canCopy(); + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) + return plugin->canCopy(); else return FALSE; } @@ -1219,8 +1179,9 @@ LLViewerMediaImpl::canCopy() const void LLViewerMediaImpl::paste() { - if (mMediaSource) - mMediaSource->paste(); + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) + plugin->paste(); } //////////////////////////////////////////////////////////////////////////////// @@ -1228,8 +1189,9 @@ LLViewerMediaImpl::paste() BOOL LLViewerMediaImpl::canPaste() const { - if (mMediaSource) - return mMediaSource->canPaste(); + LLPluginClassMedia* plugin = getMediaPlugin(); + if (plugin) + return plugin->canPaste(); else return FALSE; } diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 1e9ef6c90..6b8bd2217 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -33,34 +33,17 @@ #ifndef LLVIEWERMEDIA_H #define LLVIEWERMEDIA_H +#include "llviewermediaeventemitter.h" +#include "llviewerpluginmanager.h" #include "llfocusmgr.h" - #include "llpanel.h" -#include "llpluginclassmediaowner.h" - -#include "llviewermediaobserver.h" class LLViewerMediaImpl; class LLUUID; -//class LLViewerMediaTexture; +class LLSD; class LLViewerTexture; typedef LLPointer viewer_media_t; -/////////////////////////////////////////////////////////////////////////////// -// -class LLViewerMediaEventEmitter -{ -public: - virtual ~LLViewerMediaEventEmitter(); - - bool addObserver( LLViewerMediaObserver* subject ); - bool remObserver( LLViewerMediaObserver* subject ); - void emitEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent event); - -private: - typedef std::list< LLViewerMediaObserver* > observerListType; - observerListType mObservers; -}; class LLViewerMedia { @@ -93,7 +76,7 @@ class LLViewerMedia // Implementation functions not exported into header file class LLViewerMediaImpl - : public LLMouseHandler, public LLRefCount, public LLPluginClassMediaOwner, public LLViewerMediaEventEmitter, public LLEditMenuHandler + : public LLViewerPluginManager, public LLMouseHandler, public LLPluginClassMediaOwner, public LLViewerMediaEventEmitter, public LLEditMenuHandler { LOG_CLASS(LLViewerMediaImpl); public: @@ -112,9 +95,12 @@ public: void setMediaType(const std::string& media_type); bool initializeMedia(const std::string& mime_type); bool initializePlugin(const std::string& media_type); - LLPluginClassMedia* getMediaPlugin() { return mMediaSource; } + LLPluginClassMedia* getMediaPlugin() const { return (LLPluginClassMedia*)mPluginBase; } void setSize(int width, int height); + // Inherited from LLViewerPluginManager. + /*virtual*/ void update(); + void play(); void stop(); void pause(); @@ -141,7 +127,6 @@ public: void getTextureSize(S32 *texture_width, S32 *texture_height); void scaleMouse(S32 *mouse_x, S32 *mouse_y); - void update(); void updateMovieImage(const LLUUID& image_id, BOOL active); void updateImagesMediaStreams(); LLUUID getMediaTextureID(); @@ -201,7 +186,6 @@ public: public: // a single media url with some data and an impl. - LLPluginClassMedia* mMediaSource; LLUUID mTextureId; bool mMovieImageHasMips; std::string mMediaURL; @@ -219,7 +203,6 @@ public: bool mSuspendUpdates; bool mVisible; - private: /*LLViewerMediaTexture*/LLViewerTexture *updatePlaceholderImage(); }; diff --git a/indra/newview/llviewermediaeventemitter.cpp b/indra/newview/llviewermediaeventemitter.cpp new file mode 100644 index 000000000..39e0b2b52 --- /dev/null +++ b/indra/newview/llviewermediaeventemitter.cpp @@ -0,0 +1,85 @@ +/** + * @file llviewermediaeventemitter.cpp + * @brief Implementation of LLViewerMediaEventEmitter + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llviewermediaeventemitter.h" +#include "llviewermediaobserver.h" +#include + +LLViewerMediaEventEmitter::~LLViewerMediaEventEmitter() +{ + observerListType::iterator iter = mObservers.begin(); + + while (iter != mObservers.end()) + { + LLViewerMediaObserver* self = *iter; + ++iter; + remObserver(self); + } +} + +bool LLViewerMediaEventEmitter::addObserver(LLViewerMediaObserver* observer) +{ + if (!observer) + return false; + + if (std::find(mObservers.begin(), mObservers.end(), observer) != mObservers.end()) + return false; + + mObservers.push_back(observer); + observer->mEmitters.push_back(this); + + return true; +} + +bool LLViewerMediaEventEmitter::remObserver(LLViewerMediaObserver* observer) +{ + if (!observer) + return false; + + mObservers.remove(observer); + observer->mEmitters.remove(this); + + return true; +} + +void LLViewerMediaEventEmitter::emitEvent(LLPluginClassMedia* media, LLPluginClassMediaOwner::EMediaEvent event) +{ + observerListType::iterator iter = mObservers.begin(); + + while (iter != mObservers.end()) + { + LLViewerMediaObserver* self = *iter; + ++iter; + self->handleMediaEvent(media, event); + } +} diff --git a/indra/newview/llviewermediaeventemitter.h b/indra/newview/llviewermediaeventemitter.h new file mode 100644 index 000000000..17453a9e1 --- /dev/null +++ b/indra/newview/llviewermediaeventemitter.h @@ -0,0 +1,56 @@ +/** + * @file llviewermediaeventemitter.h + * @brief Helper class + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LLVIEWERMEDIAEVENTEMITTER_H +#define LLVIEWERMEDIAEVENTEMITTER_H + +#include "llpluginclassmediaowner.h" +#include + +class LLViewerMediaObserver; +class LLPluginClassMedia; + +class LLViewerMediaEventEmitter +{ +public: + virtual ~LLViewerMediaEventEmitter(); + + bool addObserver(LLViewerMediaObserver* subject); + bool remObserver(LLViewerMediaObserver* subject); + void emitEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent event); + +private: + typedef std::list< LLViewerMediaObserver*> observerListType; + observerListType mObservers; +}; + +#endif // LLVIEWERMEDIAEVENTEMITTER_H diff --git a/indra/newview/llviewermediaobserver.cpp b/indra/newview/llviewermediaobserver.cpp new file mode 100644 index 000000000..6bed14311 --- /dev/null +++ b/indra/newview/llviewermediaobserver.cpp @@ -0,0 +1,47 @@ +/** + * @file llviewermediaobserver.cpp + * @brief Implementation of class LLViewerMediaObserver. + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llviewermediaeventemitter.h" +#include "llviewermediaobserver.h" + +LLViewerMediaObserver::~LLViewerMediaObserver() +{ + std::list::iterator iter = mEmitters.begin(); + + while (iter != mEmitters.end()) + { + LLViewerMediaEventEmitter* self = *iter; + ++iter; + self->remObserver(this); + } +} diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index b4d01d476..7e276e51f 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -552,8 +552,6 @@ class LLFileTakeSnapshotToDisk : public view_listener_t 6144, supersample)) { - gViewerWindow->playSnapshotAnimAndSound(); - LLPointer formatted; switch(LLFloaterSnapshot::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat"))) { diff --git a/indra/newview/llviewerparcelmedia.h b/indra/newview/llviewerparcelmedia.h index 7531a0f59..50e7aaa2c 100644 --- a/indra/newview/llviewerparcelmedia.h +++ b/indra/newview/llviewerparcelmedia.h @@ -34,6 +34,7 @@ #define LLVIEWERPARCELMEDIA_H #include "llviewermedia.h" +#include "llviewermediaobserver.h" // For use by other patches so they know that media filtering is implemented. #define MEDIA_FILTERING 1 @@ -42,7 +43,6 @@ class LLMessageSystem; class LLParcel; class LLViewerParcelMediaNavigationObserver; - // This class understands land parcels, network traffic, LSL media // transport commands, and talks to the LLViewerMedia class to actually // do playback. It allows us to remove code from LLViewerParcelMgr. diff --git a/indra/newview/llviewerpluginmanager.cpp b/indra/newview/llviewerpluginmanager.cpp new file mode 100644 index 000000000..58ee33585 --- /dev/null +++ b/indra/newview/llviewerpluginmanager.cpp @@ -0,0 +1,56 @@ +/** + * @file llviewerpluginmanager.cpp + * @brief Client interface to the plugin engine + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llviewerpluginmanager.h" + +void LLViewerPluginManager::destroyPlugin() +{ + delete mPluginBase; + mPluginBase = NULL; +} + +void LLViewerPluginManager::update() +{ + if (!mPluginBase) + { + return; + } + + mPluginBase->idle(); + + if (mPluginBase->isPluginExited()) + { + destroyPlugin(); + return; + } +} diff --git a/indra/newview/llviewerpluginmanager.h b/indra/newview/llviewerpluginmanager.h new file mode 100644 index 000000000..b56d11eee --- /dev/null +++ b/indra/newview/llviewerpluginmanager.h @@ -0,0 +1,115 @@ +/** + * @file llviewerpluginmanager.h + * @brief Client interface to the plugin engine + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LLVIEWERPLUGINMANAGER_H +#define LLVIEWERPLUGINMANAGER_H + +#include +#include "llmemory.h" +#include "llerror.h" +#include "lldir.h" +#include "llfile.h" +#include "llviewercontrol.h" +#include "llnotifications.h" +#include "llpluginclassbasic.h" + +class LLViewerPluginManager : public LLRefCount +{ + LOG_CLASS(LLViewerPluginManager); + +public: + // Construct an uninitialized LLViewerPluginManager object. + LLViewerPluginManager(void) : mPluginBase(NULL) { } + + // Create a PLUGIN_TYPE (must be derived from LLPluginClassBasic). + // This uses PLUGIN_TYPE::launcher_name() and PLUGIN_TYPE::plugin_basename(). + // If successful, returns the created LLPluginClassBasic, NULL otherwise. + template + LLPluginClassBasic* createPlugin(T* user_data); + + // Delete the plugin. + void destroyPlugin(); + + // Handle plugin messages. + virtual void update(); + + // Return pointer to plugin. + LLPluginClassBasic* getPlugin(void) const { return mPluginBase; } + +protected: + LLPluginClassBasic* mPluginBase; //!< Pointer to the base class of the underlaying plugin. +}; + +template +LLPluginClassBasic* LLViewerPluginManager::createPlugin(T* user_data) +{ + // Always delete the old plugin first. + destroyPlugin(); + + std::string plugin_name = gDirUtilp->getLLPluginFilename(PLUGIN_TYPE::plugin_basename()); + + // See if the plugin executable exists. + llstat s; + if (LLFile::stat(PLUGIN_TYPE::launcher_name(), &s)) + { + LL_WARNS("Plugin") << "Couldn't find launcher at " << PLUGIN_TYPE::launcher_name() << LL_ENDL; + } + else if (LLFile::stat(plugin_name, &s)) + { + LL_WARNS("Plugin") << "Couldn't find plugin at " << plugin_name << LL_ENDL; + } + else + { + LLPluginClassBasic* plugin = new PLUGIN_TYPE(user_data); + if (plugin->init(PLUGIN_TYPE::launcher_name(), plugin_name, gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins"))) + { + mPluginBase = plugin; + } + else + { + LL_WARNS("Plugin") << "Failed to init plugin. Destroying." << LL_ENDL; + delete plugin; + } + } + + if (mPluginBase) + return mPluginBase; + + LL_WARNS("Plugin") << "plugin intialization failed for plugin: " << PLUGIN_TYPE::plugin_basename() << LL_ENDL; + LLSD args; + args["MIME_TYPE"] = PLUGIN_TYPE::plugin_basename(); // FIXME: Use different notification. + LLNotifications::instance().add("NoPlugin", args); + + return NULL; +} + +#endif // LLVIEWERPLUGINMANAGER_H diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index a82fdb3d5..2139e75d8 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -89,7 +89,7 @@ #include "llmaniptranslate.h" #include "llface.h" #include "llfeaturemanager.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "llfloater.h" #include "llfloateractivespeakers.h" #include "llfloaterbuildoptions.h" @@ -3988,27 +3988,27 @@ BOOL LLViewerWindow::mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d } // Saves an image to the harddrive as "SnapshotX" where X >= 1. -BOOL LLViewerWindow::saveImageNumbered(LLImageFormatted *image) +void LLViewerWindow::saveImageNumbered(LLPointer image) { if (!image) { - return FALSE; + return; } - LLFilePicker::ESaveFilter pick_type; + ESaveFilter pick_type; std::string extension("." + image->getExtension()); if (extension == ".j2c") - pick_type = LLFilePicker::FFSAVE_J2C; + pick_type = FFSAVE_J2C; else if (extension == ".bmp") - pick_type = LLFilePicker::FFSAVE_BMP; + pick_type = FFSAVE_BMP; else if (extension == ".jpg") - pick_type = LLFilePicker::FFSAVE_JPEG; + pick_type = FFSAVE_JPEG; else if (extension == ".png") - pick_type = LLFilePicker::FFSAVE_PNG; + pick_type = FFSAVE_PNG; else if (extension == ".tga") - pick_type = LLFilePicker::FFSAVE_TGA; + pick_type = FFSAVE_TGA; else - pick_type = LLFilePicker::FFSAVE_ALL; // ??? + pick_type = FFSAVE_ALL; // ??? // Get a base file location if needed. if ( ! isSnapshotLocSet()) @@ -4018,20 +4018,34 @@ BOOL LLViewerWindow::saveImageNumbered(LLImageFormatted *image) // getSaveFile will append an appropriate extension to the proposed name, based on the ESaveFilter constant passed in. // pick a directory in which to save - LLFilePicker& picker = LLFilePicker::instance(); - if (!picker.getSaveFile(pick_type, proposed_name)) - { - // Clicked cancel - return FALSE; - } + AIFilePicker* filepicker = new AIFilePicker; // Deleted in LLViewerWindow::saveImageNumbered_filepicker_callback + filepicker->open(proposed_name, pick_type, "", "snapshot"); + filepicker->run(boost::bind(&LLViewerWindow::saveImageNumbered_filepicker_callback, this, image, extension, filepicker, _1)); + return; + } + // LLViewerWindow::sSnapshotBaseName and LLViewerWindow::sSnapshotDir already known. Go straight to saveImageNumbered_continued. + saveImageNumbered_continued(image, extension); +} + +void LLViewerWindow::saveImageNumbered_filepicker_callback(LLPointer image, std::string const& extension, AIFilePicker* filepicker, bool success) +{ + llassert((bool)*filepicker == success); + if (success) + { // Copy the directory + file name - std::string filepath = picker.getFirstFile(); + std::string filepath = filepicker->getFilename(); LLViewerWindow::sSnapshotBaseName = gDirUtilp->getBaseFileName(filepath, true); LLViewerWindow::sSnapshotDir = gDirUtilp->getDirName(filepath); - } + saveImageNumbered_continued(image, extension); + } + delete filepicker; +} + +void LLViewerWindow::saveImageNumbered_continued(LLPointer image, std::string const& extension) +{ // Look for an unused file name std::string filepath; S32 i = 1; @@ -4051,7 +4065,10 @@ BOOL LLViewerWindow::saveImageNumbered(LLImageFormatted *image) } while( -1 != err ); // search until the file is not found (i.e., stat() gives an error). - return image->save(filepath); + if (image->save(filepath)) + { + playSnapshotAnimAndSound(); + } } void LLViewerWindow::resetSnapshotLoc() diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 21867f7eb..cbf3f0187 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -60,6 +60,7 @@ class LLTextBox; class LLImageRaw; class LLHUDIcon; class LLMouseHandler; +class AIFilePicker; #define PICK_HALF_WIDTH 5 #define PICK_DIAMETER (2 * PICK_HALF_WIDTH + 1) @@ -282,19 +283,21 @@ public: // snapshot functionality. // perhaps some of this should move to llfloatershapshot? -MG - typedef enum e_snapshot_type + enum ESnapshotType { SNAPSHOT_TYPE_COLOR, SNAPSHOT_TYPE_DEPTH, SNAPSHOT_TYPE_OBJECT_ID - } ESnapshotType; + }; BOOL saveSnapshot(const std::string& filename, S32 image_width, S32 image_height, BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR); BOOL rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height, BOOL keep_window_aspect = TRUE, BOOL is_texture = FALSE, BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR, S32 max_size = MAX_SNAPSHOT_IMAGE_SIZE, F32 supersample = 1.f ); BOOL thumbnailSnapshot(LLImageRaw *raw, S32 preview_width, S32 preview_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type) ; BOOL isSnapshotLocSet() const { return ! sSnapshotDir.empty(); } void resetSnapshotLoc() const { sSnapshotDir.clear(); } - BOOL saveImageNumbered(LLImageFormatted *image); + void saveImageNumbered(LLPointer image); + void saveImageNumbered_filepicker_callback(LLPointer image, std::string const& extension, AIFilePicker* filepicker, bool success); + void saveImageNumbered_continued(LLPointer image, std::string const& extension); // Reset the directory where snapshots are saved. // Client will open directory picker on next snapshot save. diff --git a/indra/newview/statemachine/aifilepicker.cpp b/indra/newview/statemachine/aifilepicker.cpp new file mode 100644 index 000000000..cd6856100 --- /dev/null +++ b/indra/newview/statemachine/aifilepicker.cpp @@ -0,0 +1,369 @@ +/** + * @file aifilepicker.cpp + * @brief Implementation of AIFilePicker + * + * Copyright (c) 2010, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * 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. + * + * CHANGELOG + * and additional copyright holders. + * + * 04/12/2010 + * Initial version, written by Aleric Inglewood @ SL + */ + +#include "../llviewerprecompiledheaders.h" +#include "../llviewermedia.h" +#include "../lltrans.h" +#include "llpluginclassmedia.h" +#include "llpluginmessageclasses.h" +#include "aifilepicker.h" +#if LL_WINDOWS +#include "../llviewerwindow.h" +#endif +#if LL_GTK && LL_X11 +#include "llwindowsdl.h" +#endif + +enum filepicker_state_type { + AIFilePicker_initialize_plugin = AIStateMachine::max_state, + AIFilePicker_plugin_running, + AIFilePicker_canceled, + AIFilePicker_done +}; + +char const* AIFilePicker::state_str_impl(state_type run_state) const +{ + switch(run_state) + { + AI_CASE_RETURN(AIFilePicker_initialize_plugin); + AI_CASE_RETURN(AIFilePicker_plugin_running); + AI_CASE_RETURN(AIFilePicker_canceled); + AI_CASE_RETURN(AIFilePicker_done); + } + return "UNKNOWN STATE"; +} + +AIFilePicker::AIFilePicker(void) : mPluginManager(NULL), mCanceled(false) +{ +} + +void AIFilePicker::store_folder(std::string const& context, std::string const& folder) +{ + if (!folder.empty()) + { + mContextMap[context] = folder; + } +} + +std::string AIFilePicker::get_folder(std::string const& default_path, std::string const& context) +{ + context_map_type::iterator iter = mContextMap.find(context); + if (iter != mContextMap.end()) + { + return iter->second; + } + else if (!default_path.empty()) + { + return default_path; + } + else if ((iter = mContextMap.find("savefile")) != mContextMap.end()) + { + return iter->second; + } + else + { + // This is the last resort when all else failed. Open the file chooser in directory 'home'. + char const* home = NULL; +#if LL_WINDOWS + #warning "Attention WINDOWS DEVELOPER: Set 'home' to a sensible default directory (users Desktop?)" +#else + home = getenv("HOME"); +#endif + return home ? home : ""; + } +} + +void AIFilePicker::open(ELoadFilter filter, std::string const& default_path, std::string const& context, bool multiple) +{ + mContext = context; + mFolder = get_folder(default_path, context); + mOpenType = multiple ? load_multiple : load; + switch(filter) + { + case FFLOAD_ALL: + mFilter = "all"; + break; + case FFLOAD_WAV: + mFilter = "wav"; + break; + case FFLOAD_IMAGE: + mFilter = "image"; + break; + case FFLOAD_ANIM: + mFilter = "anim"; + break; +#ifdef _CORY_TESTING + case FFLOAD_GEOMETRY: + mFilter = "geometry"; + break; +#endif + case FFLOAD_XML: + mFilter = "xml"; + break; + case FFLOAD_SLOBJECT: + mFilter = "slobject"; + break; + case FFLOAD_RAW: + mFilter = "raw"; + break; + case FFLOAD_TEXT: + mFilter = "text"; + break; + } +} + +void AIFilePicker::open(std::string const& filename, ESaveFilter filter, std::string const& default_path, std::string const& context) +{ + mFilename = filename; + mContext = context; + mFolder = get_folder(default_path, context); + mOpenType = save; + switch(filter) + { + case FFSAVE_ALL: + mFilter = "all"; + break; + case FFSAVE_WAV: + mFilter = "wav"; + break; + case FFSAVE_TGA: + mFilter = "tga"; + break; + case FFSAVE_BMP: + mFilter = "bmp"; + break; + case FFSAVE_AVI: + mFilter = "avi"; + break; + case FFSAVE_ANIM: + mFilter = "anim"; + break; +#ifdef _CORY_TESTING + case FFSAVE_GEOMETRY: + mFilter = "geometry"; + break; +#endif + case FFSAVE_XML: + mFilter = "xml"; + break; + case FFSAVE_COLLADA: + mFilter = "collada"; + break; + case FFSAVE_RAW: + mFilter = "raw"; + break; + case FFSAVE_J2C: + mFilter = "j2c"; + break; + case FFSAVE_PNG: + mFilter = "png"; + break; + case FFSAVE_JPEG: + mFilter = "jpeg"; + break; + case FFSAVE_HPA: + mFilter = "hpa"; + break; + case FFSAVE_TEXT: + mFilter = "text"; + break; + case FFSAVE_LSL: + mFilter = "lsl"; + break; + } +} + +void AIFilePicker::initialize_impl(void) +{ + mCanceled = false; + if (mFilter.empty()) + { + llwarns << "Calling AIFilePicker::initialize_impl() with empty mFilter. Call open before calling run!" << llendl; + abort(); + return; + } + mPluginManager = new LLViewerPluginManager; + if (!mPluginManager) + { + abort(); + return; + } + LLPluginClassBasic* plugin = mPluginManager->createPlugin(this); + if (!plugin) + { + abort(); + return; + } + set_state(AIFilePicker_initialize_plugin); +} + +void AIFilePicker::multiplex_impl(void) +{ + mPluginManager->update(); // Give the plugin some CPU for it's messages. + LLPluginClassBasic* plugin = mPluginManager->getPlugin(); + if (!plugin || plugin->isPluginExited()) + { + // This happens when there was a problem with the plugin (ie, it crashed). + abort(); + return; + } + switch (mRunState) + { + case AIFilePicker_initialize_plugin: + { + if (!plugin->isPluginRunning()) + { + break; // Still initializing. + } + + // Send initialization message. + LLPluginMessage initialization_message(LLPLUGIN_MESSAGE_CLASS_BASIC, "initialization"); + static char const* key_str[] = { + "all_files", "sound_files", "animation_files", "image_files", "save_file_verb", + "targa_image_files", "bitmap_image_files", "avi_movie_file", "xaf_animation_file", + "xml_file", "raw_file", "compressed_image_files", "load_file_verb", "load_files" + }; + LLSD dictionary; + for (int key = 0; key < sizeof(key_str) / sizeof(key_str[0]); ++key) + { + dictionary[key_str[key]] = LLTrans::getString(key_str[key]); + } + initialization_message.setValueLLSD("dictionary", dictionary); +#if LL_WINDOWS || (LL_GTK && LL_X11) + std::ostringstream window_id_str; +#if LL_WINDOWS + unsigned long window_id = gViewerWindow->getPlatformWindow(); +#else + unsigned long window_id = LLWindowSDL::get_SDL_XWindowID(); +#endif + if (window_id != None) + { + window_id_str << std::hex << "0x" << window_id; + initialization_message.setValue("window_id", window_id_str.str()); + } + else + { + LL_WARNS("Plugin") << "Couldn't get xwid to use for transient." << LL_ENDL; + } +#endif // LL_WINDOWS || (LL_GTK && LL_X11) + plugin->sendMessage(initialization_message); + + // Send open message. + LLPluginMessage open_message(LLPLUGIN_MESSAGE_CLASS_BASIC, "open"); + open_message.setValue("type", (mOpenType == save) ? "save" : (mOpenType == load) ? "load" : "load_multiple"); + open_message.setValue("filter", mFilter); + if (mOpenType == save) open_message.setValue("default", mFilename); + open_message.setValue("folder", mFolder); + open_message.setValue("gorgon", "block"); // Don't expect HeartBeat messages after an "open". + plugin->sendMessage(open_message); + + set_state(AIFilePicker_plugin_running); + break; + } + case AIFilePicker_plugin_running: + { + // Users are slow, no need to look for new messages from the plugin all too often. + yield_ms(250); + break; + } + case AIFilePicker_canceled: + { + mCanceled = true; + finish(); + break; + } + case AIFilePicker_done: + { + // Store folder of first filename as context. + store_folder(mContext, getFolder()); + finish(); + } + } +} + +void AIFilePicker::abort_impl(void) +{ +} + +void AIFilePicker::finish_impl(void) +{ + mPluginManager = NULL; // This deletes the plugin, since mPluginManager is a LLPointer. + mFilter.clear(); // Check that open is called before calling run (again). +} + +// This function is called when a new message is received from the plugin. +// We get here when calling mPluginManager->update() in the first line of +// AIFilePicker::multiplex_impl. +// +// Note that we can't call finish() or abort() directly in this function, +// as that deletes mPluginManager and we're using the plugin manager +// right now (to receive this message)! +void AIFilePicker::receivePluginMessage(const LLPluginMessage &message) +{ + std::string message_class = message.getClass(); + + if (message_class == LLPLUGIN_MESSAGE_CLASS_BASIC) + { + std::string message_name = message.getName(); + if (message_name == "canceled") + { + set_state(AIFilePicker_canceled); + } + else if (message_name == "done") + { + LLSD filenames = message.getValueLLSD("filenames"); + mFilenames.clear(); + for(LLSD::array_iterator filename = filenames.beginArray(); filename != filenames.endArray(); ++filename) + { + mFilenames.push_back(*filename); + } + set_state(AIFilePicker_done); + } + else + { + LL_WARNS("Plugin") << "Unknown " << message_class << " class message: " << message_name << LL_ENDL; + } + } +} + +std::string const& AIFilePicker::getFilename(void) const +{ + // Only call this function after the AIFilePicker finished successfully without being canceled. + llassert_always(!mFilenames.empty()); + // This function returns the first filename in the case that more than one was selected. + return mFilenames[0]; +} + +std::string AIFilePicker::getFolder(void) const +{ + // Return the folder of the first filename. + return gDirUtilp->getDirName(getFilename()); +} + diff --git a/indra/newview/statemachine/aifilepicker.h b/indra/newview/statemachine/aifilepicker.h new file mode 100644 index 000000000..008afb1d7 --- /dev/null +++ b/indra/newview/statemachine/aifilepicker.h @@ -0,0 +1,160 @@ +/** + * @file aifilepicker.h + * @brief File picker State machine + * + * Copyright (c) 2010, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * 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. + * + * CHANGELOG + * and additional copyright holders. + * + * 02/12/2010 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AIFILEPICKER_H +#define AIFILEPICKER_H + +#include "aistatemachine.h" +#include "llpluginclassmedia.h" +#include "../llviewermedia.h" +#include + +enum ELoadFilter +{ + FFLOAD_ALL, + FFLOAD_WAV, + FFLOAD_IMAGE, + FFLOAD_ANIM, + FFLOAD_XML, + FFLOAD_SLOBJECT, + FFLOAD_RAW, + FFLOAD_TEXT +}; + +enum ESaveFilter +{ + FFSAVE_ALL, + FFSAVE_WAV, + FFSAVE_TGA, + FFSAVE_BMP, + FFSAVE_AVI, + FFSAVE_ANIM, + FFSAVE_XML, + FFSAVE_COLLADA, + FFSAVE_RAW, + FFSAVE_J2C, + FFSAVE_PNG, + FFSAVE_JPEG, + FFSAVE_HPA, + FFSAVE_TEXT, + FFSAVE_LSL +}; + +// A file picker state machine. +// +// Before calling run(), call open() to pass needed parameters. +// +// When the state machine finishes, call isCanceled to check +// whether or not getFilename and getFolder will be valid. +// +// Objects of this type can be reused multiple times, see +// also the documentation of AIStateMachine. +class AIFilePicker : public AIStateMachine { +public: + AIFilePicker(void); + + // The starting directory that the user will be in when the file picker opens + // will be the same as the directory used the last time the file picker was + // opened with the same context. If the file picker was never opened before + // with the given context, the starting directory will be set to default_path + // unless that is the empty string, in which case it will be equal to the + // directory used the last time the filepicker was opened with context "savefile". + void open(std::string const& filename, ESaveFilter filter = FFSAVE_ALL, std::string const& default_path = "", std::string const& context = "savefile"); + void open(ELoadFilter filter = FFLOAD_ALL, std::string const& default_path = "", std::string const& context = "openfile", bool multiple = false); + + bool isCanceled(void) const { return mCanceled; } + std::string const& getFilename(void) const; + std::string getFolder(void) const; + std::vector const& getFilenames(void) const { return mFilenames; } + +private: + friend class AIPluginFilePicker; + + // This is called from AIPluginFilePicker::receivePluginMessage, see below. + void receivePluginMessage(LLPluginMessage const& message); + +public: + enum open_type { save, load, load_multiple }; + +private: + LLPointer mPluginManager; //!< Pointer to the plugin manager. + // FIXME: this should be a separate, thread-safe singleton. + typedef std::map context_map_type; //!< Type of mContextMap. + context_map_type mContextMap; //!< Map context (ie, "snapshot" or "image") to last used folder. + std::string mContext; //!< Some key to indicate the context (remembers the folder per key). + + // Input variables (cache variable between call to open and run). + open_type mOpenType; //!< Set to whether opening a filepicker to select for saving one file, for loading one file, or loading multiple files. + std::string mFilter; //!< A keyword indicating which file types (extensions) we want to see. + std::string mFilename; //!< When saving: proposed filename. + std::string mFolder; //!< Initial folder to start in. + // Output variables: + bool mCanceled; //!< True if the file picker was canceled or closed. + std::vector mFilenames; //!< Filesnames. + + // Store a folder for the given context. + void store_folder(std::string const& context, std::string const& folder); + // Return the last folder stored for 'context', or default_path if none, or context "savefile" if empty, or $HOME if none. + std::string get_folder(std::string const& default_path, std::string const& context); + +protected: + // Handle initializing the object. + /*virtual*/ void initialize_impl(void); + + // Handle mRunState. + /*virtual*/ void multiplex_impl(void); + + // Handle aborting from current bs_run state. + /*virtual*/ void abort_impl(void); + + // Handle cleaning up from initialization (or post abort) state. + /*virtual*/ void finish_impl(void); + + // Implemenation of state_str for run states. + /*virtual*/ char const* state_str_impl(state_type run_state) const; +}; + +// Viewer-side helper class for objects with a lifetime equal to the +// plugin child process (SLPlugin). Don't use directly. +class AIPluginFilePicker : public LLPluginClassBasic { + LOG_CLASS(AIPluginFilePicker); +public: + AIPluginFilePicker(AIFilePicker* state_machine) : mStateMachine(state_machine) { } + + static std::string launcher_name(void) { return gDirUtilp->getLLPluginLauncher(); } + static char const* plugin_basename(void) { return "basic_plugin_filepicker"; } + + /*virtual*/ void receivePluginMessage(LLPluginMessage const& message) { mStateMachine->receivePluginMessage(message); } + +private: + AIFilePicker* mStateMachine; +}; + +#endif diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index a663389ea..42859efbe 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -220,13 +220,18 @@ class WindowsManifest(ViewerManifest): # self.path("openjpeg.dll") # self.end_prefix() + # Plugins - FilePicker + if self.prefix(src='../plugins/filepicker/%s' % self.args['configuration'], dst="llplugin"): + self.path("basic_plugin_filepicker.dll") + self.end_prefix() + # Media plugins - QuickTime - if self.prefix(src='../media_plugins/quicktime/%s' % self.args['configuration'], dst="llplugin"): + if self.prefix(src='../plugins/quicktime/%s' % self.args['configuration'], dst="llplugin"): self.path("media_plugin_quicktime.dll") self.end_prefix() # Media plugins - WebKit/Qt - if self.prefix(src='../media_plugins/webkit/%s' % self.args['configuration'], dst="llplugin"): + if self.prefix(src='../plugins/webkit/%s' % self.args['configuration'], dst="llplugin"): self.path("media_plugin_webkit.dll") self.end_prefix() @@ -533,8 +538,9 @@ class DarwinManifest(ViewerManifest): # plugins if self.prefix(src="", dst="llplugin"): - self.path("../media_plugins/quicktime/" + self.args['configuration'] + "/media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib") - self.path("../media_plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib") + self.path("../plugins/filepicker/" + self.args['configuration'] + "/basic_plugin_filepicker.dylib", "basic_plugin_filepicker.dylib") + self.path("../plugins/quicktime/" + self.args['configuration'] + "/media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib") + self.path("../plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib") self.path("../../libraries/universal-darwin/lib_release/libllqtwebkit.dylib", "libllqtwebkit.dylib") self.end_prefix("llplugin") @@ -689,8 +695,9 @@ class LinuxManifest(ViewerManifest): # plugins if self.prefix(src="", dst="bin/llplugin"): - self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so") - self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") + self.path("../plugins/filepicker/libbasic_plugin_filepicker.so", "libbasic_plugin_filepicker.so") + self.path("../plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so") + self.path("../plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") self.end_prefix("bin/llplugin") # Per platform MIME config on the cheap. See SNOW-307 / DEV-41388 diff --git a/indra/plugins/filepicker/basic_plugin_filepicker.cpp b/indra/plugins/filepicker/basic_plugin_filepicker.cpp new file mode 100644 index 000000000..bd415c64a --- /dev/null +++ b/indra/plugins/filepicker/basic_plugin_filepicker.cpp @@ -0,0 +1,243 @@ +/** + * @file plugin_filepicker.cpp + * @brief Plugin that runs a file picker. + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 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://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +#include "linden_common.h" +#include "basic_plugin_base.h" +#include "llfilepicker.h" + +class FilepickerPlugin : public BasicPluginBase +{ + public: + FilepickerPlugin(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); + ~FilepickerPlugin(); + + /*virtual*/ void receiveMessage(char const* message_string); + + private: + bool init(); +}; + +FilepickerPlugin::FilepickerPlugin(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) : + BasicPluginBase(send_message_function, plugin_instance) +{ +} + +FilepickerPlugin::~FilepickerPlugin() +{ +} + +static LLFilePicker::ESaveFilter str2savefilter(std::string const& filter) +{ + // Complement of AIFilePicker::open(std::string const& filename, ESaveFilter filter, std::string const& folder) + if (filter == "wav") + return LLFilePicker::FFSAVE_WAV; + else if (filter == "tga") + return LLFilePicker::FFSAVE_TGA; + else if (filter == "bmp") + return LLFilePicker::FFSAVE_BMP; + else if (filter == "avi") + return LLFilePicker::FFSAVE_AVI; + else if (filter == "anim") + return LLFilePicker::FFSAVE_ANIM; +#ifdef _CORY_TESTING + else if (filter == "geometry") + return LLFilePicker::FFSAVE_GEOMETRY; +#endif + else if (filter == "xml") + return LLFilePicker::FFSAVE_XML; + else if (filter == "collada") + return LLFilePicker::FFSAVE_COLLADA; + else if (filter == "raw") + return LLFilePicker::FFSAVE_RAW; + else if (filter == "j2c") + return LLFilePicker::FFSAVE_J2C; + else if (filter == "png") + return LLFilePicker::FFSAVE_PNG; + else if (filter == "jpeg") + return LLFilePicker::FFSAVE_JPEG; + else if (filter == "hpa") + return LLFilePicker::FFSAVE_HPA; + else if (filter == "text") + return LLFilePicker::FFSAVE_TEXT; + else if (filter == "lsl") + return LLFilePicker::FFSAVE_LSL; + else + return LLFilePicker::FFSAVE_ALL; +} + +static LLFilePicker::ELoadFilter str2loadfilter(std::string const& filter) +{ + // Complement of AIFilePicker::open(ELoadFilter filter, std::string const& folder) + if (filter == "wav") + return LLFilePicker::FFLOAD_WAV; + else if (filter == "image") + return LLFilePicker::FFLOAD_IMAGE; + else if (filter == "anim") + return LLFilePicker::FFLOAD_ANIM; +#ifdef _CORY_TESTING + else if (filter == "geometry") + return LLFilePicker::FFLOAD_GEOMETRY; +#endif + else if (filter == "xml") + return LLFilePicker::FFLOAD_XML; + else if (filter == "slobject") + return LLFilePicker::FFLOAD_SLOBJECT; + else if (filter == "raw") + return LLFilePicker::FFLOAD_RAW; + else if (filter == "text") + return LLFilePicker::FFLOAD_TEXT; + else + return LLFilePicker::FFLOAD_ALL; +} + +// This is the SLPlugin process. +// This is part of the loaded DSO. +// +// This function is called from LLPluginInstance::sendMessage +// for messages received from the viewer (that are not 'internal'). +void FilepickerPlugin::receiveMessage(char const* message_string) +{ + LLPluginMessage message_in; + + if (message_in.parse(message_string) >= 0) + { + std::string message_class = message_in.getClass(); + std::string message_name = message_in.getName(); + + if (message_class == LLPLUGIN_MESSAGE_CLASS_BASE) + { + if (message_name == "init") + { + LLPluginMessage message("base", "init_response"); + LLSD versions = LLSD::emptyMap(); + versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_BASIC] = LLPLUGIN_MESSAGE_CLASS_BASIC_VERSION; + message.setValueLLSD("versions", versions); + + std::string plugin_version = "Filepicker Plugin, version 1.0.0.0"; + message.setValue("plugin_version", plugin_version); + sendMessage(message); + } + else if (message_name == "idle") + { + // This whole message should not have existed imho -- Aleric + } + else + { + std::cerr << "FilepickerPlugin::receiveMessage: unknown base message: " << message_name << std::endl; + } + } + else if (message_class == LLPLUGIN_MESSAGE_CLASS_BASIC) + { + // This message should be sent at most once per SLPlugin invokation. + if (message_name == "initialization") + { + LLSD dictionary = message_in.getValueLLSD("dictionary"); + for (LLSD::map_iterator iter = dictionary.beginMap(); iter != dictionary.endMap(); ++iter) + { + translation::add(iter->first, iter->second.asString()); + } + if (message_in.hasValue("window_id")) + { + unsigned long window_id = strtoul(message_in.getValue("window_id").c_str(), NULL, 16); + LLFilePicker::instance().setWindowID(window_id); + } + } + // This message may theoretically be repeated (though currently the plugin is terminated after returning). + else if (message_name == "open") + { + std::string type = message_in.getValue("type"); + std::string filter = message_in.getValue("filter"); + std::string folder = message_in.getValue("folder"); + + bool canceled; + if (type == "save") + { + canceled = !LLFilePicker::instance().getSaveFile(str2savefilter(filter), message_in.getValue("default"), folder); + } + else if (type == "load") + { + canceled = !LLFilePicker::instance().getLoadFile(str2loadfilter(filter), folder); + } + else // type == "load_multiple" + { + canceled = !LLFilePicker::instance().getMultipleLoadFiles(str2loadfilter(filter), folder); + } + if (canceled) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_BASIC, "canceled"); + message.setValue("perseus", "unblock"); + sendMessage(message); + } + else + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_BASIC, "done"); + message.setValue("perseus", "unblock"); + LLSD filenames; + for (std::string filename = LLFilePicker::instance().getFirstFile(); !filename.empty(); filename = LLFilePicker::instance().getNextFile()) + { + filenames.append(filename); + } + message.setValueLLSD("filenames", filenames); + sendMessage(message); + } + } + else + { + std::cerr << "FilepickerPlugin::receiveMessage: unknown basic message: " << message_name << std::endl; + } + } + else + { + std::cerr << "FilepickerPlugin::receiveMessage: unknown message class: " << message_class << std::endl; + } + } +} + +bool FilepickerPlugin::init(void) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_BASIC, "name_text"); + message.setValue("name", "Filepicker Plugin"); + sendMessage(message); + + return true; +} + +int create_plugin(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance, BasicPluginBase** plugin_object) +{ + *plugin_object = new FilepickerPlugin(send_message_function, plugin_instance); + return 0; +} + From 784fdd4f379dc340a94c4ab88e6b15a9ce3495f7 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 6 May 2011 15:00:50 +0200 Subject: [PATCH 06/21] Split plugin classes and derive AIFilePicker from BasicPluginBase (part 2). This commit contains all changes to indra/llplugin. Since there were no differences at all between Singularity and imprudence before this patch, it's a perfect port. --- indra/llplugin/CMakeLists.txt | 2 + indra/llplugin/llpluginclassbasic.cpp | 189 +++++++++++++++++++++++ indra/llplugin/llpluginclassbasic.h | 126 +++++++++++++++ indra/llplugin/llpluginclassmedia.cpp | 163 +++---------------- indra/llplugin/llpluginclassmedia.h | 83 ++-------- indra/llplugin/llplugininstance.cpp | 33 ++-- indra/llplugin/llplugininstance.h | 14 +- indra/llplugin/llpluginmessage.h | 6 +- indra/llplugin/llpluginmessageclasses.h | 3 + indra/llplugin/llpluginprocesschild.cpp | 28 +++- indra/llplugin/llpluginprocessparent.cpp | 35 +++++ indra/llplugin/llpluginprocessparent.h | 1 + 12 files changed, 457 insertions(+), 226 deletions(-) create mode 100644 indra/llplugin/llpluginclassbasic.cpp create mode 100644 indra/llplugin/llpluginclassbasic.h diff --git a/indra/llplugin/CMakeLists.txt b/indra/llplugin/CMakeLists.txt index 7a7f4e583..270a95d54 100644 --- a/indra/llplugin/CMakeLists.txt +++ b/indra/llplugin/CMakeLists.txt @@ -24,6 +24,7 @@ include_directories( ) set(llplugin_SOURCE_FILES + llpluginclassbasic.cpp llpluginclassmedia.cpp llplugincookiestore.cpp llplugininstance.cpp @@ -37,6 +38,7 @@ set(llplugin_SOURCE_FILES set(llplugin_HEADER_FILES CMakeLists.txt + llpluginclassbasic.h llpluginclassmedia.h llpluginclassmediaowner.h llplugincookiestore.h diff --git a/indra/llplugin/llpluginclassbasic.cpp b/indra/llplugin/llpluginclassbasic.cpp new file mode 100644 index 000000000..0567b157f --- /dev/null +++ b/indra/llplugin/llpluginclassbasic.cpp @@ -0,0 +1,189 @@ +/** + * @file llpluginclassbasic.cpp + * @brief LLPluginClassBasic handles a plugin which knows about the "basic" message class. + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 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://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +#include "linden_common.h" +#include "indra_constants.h" + +#include "llpluginclassbasic.h" +#include "llpluginmessageclasses.h" + +LLPluginClassBasic::LLPluginClassBasic(void) : mPlugin(NULL), mDeleteOK(true) +{ + // Note that this only initializes the base class, the derived class doesn't exist yet! + // Derived classes must therefore call their own reset_impl() from their constructor. + reset(); +} + +LLPluginClassBasic::~LLPluginClassBasic() +{ + llassert_always(mDeleteOK); + delete mPlugin; +} + +bool LLPluginClassBasic::init(std::string const& launcher_filename, std::string const& plugin_filename, bool debug) +{ + LL_DEBUGS("Plugin") << "launcher: " << launcher_filename << LL_ENDL; + LL_DEBUGS("Plugin") << "plugin: " << plugin_filename << LL_ENDL; + + mPlugin = new LLPluginProcessParent(this); + mPlugin->setSleepTime(mSleepTime); + + mPlugin->init(launcher_filename, plugin_filename, debug); + + return init_impl(); +} + +void LLPluginClassBasic::reset() +{ + if (mPlugin != NULL) + { + delete mPlugin; + mPlugin = NULL; + } + mSleepTime = 1.0f / 50.0f; + mPriority = PRIORITY_NORMAL; + reset_impl(); +} + +void LLPluginClassBasic::idle(void) +{ + if(mPlugin) + { + mPlugin->idle(); + } + + idle_impl(); + + if(mPlugin && mPlugin->isRunning()) + { + // Send queued messages + while(!mSendQueue.empty()) + { + LLPluginMessage message = mSendQueue.front(); + mSendQueue.pop(); + mPlugin->sendMessage(message); + } + } +} + +char const* LLPluginClassBasic::priorityToString(EPriority priority) +{ + const char* result = "UNKNOWN"; + switch(priority) + { + case PRIORITY_SLEEP: result = "sleep"; break; + case PRIORITY_LOW: result = "low"; break; + case PRIORITY_NORMAL: result = "normal"; break; + case PRIORITY_HIGH: result = "high"; break; + } + + return result; +} + +void LLPluginClassBasic::setPriority(EPriority priority) +{ + if (mPriority != priority) + { + mPriority = priority; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_BASIC, "set_priority"); + + std::string priority_string = priorityToString(priority); + switch(priority) + { + case PRIORITY_SLEEP: + mSleepTime = 1.0f; + break; + case PRIORITY_LOW: + mSleepTime = 1.0f / 25.0f; + break; + case PRIORITY_NORMAL: + mSleepTime = 1.0f / 50.0f; + break; + case PRIORITY_HIGH: + mSleepTime = 1.0f / 100.0f; + break; + } + + message.setValue("priority", priority_string); + sendMessage(message); + + if(mPlugin) + { + mPlugin->setSleepTime(mSleepTime); + } + + LL_DEBUGS("PluginPriority") << this << ": setting priority to " << priority_string << LL_ENDL; + + priorityChanged(mPriority); + } +} + +/* virtual */ +void LLPluginClassBasic::receivePluginMessage(const LLPluginMessage &message) +{ + std::string message_class = message.getClass(); + + if (message_class == LLPLUGIN_MESSAGE_CLASS_BASIC) + { + std::string message_name = message.getName(); + + // This class hasn't defined any incoming messages yet. +// if (message_name == "message_name") +// { +// } +// else + { + LL_WARNS("Plugin") << "Unknown " << message_class << " class message: " << message_name << LL_ENDL; + } + } +} + +// This is the viewer process (the parent process) +// +// Call this function to send a message to a plugin. +// It calls LLPluginProcessParent::sendMessage. +void LLPluginClassBasic::sendMessage(LLPluginMessage const& message) +{ + if(mPlugin && mPlugin->isRunning()) + { + mPlugin->sendMessage(message); + } + else + { + // The plugin isn't set up yet -- queue this message to be sent after initialization. + mSendQueue.push(message); + } +} diff --git a/indra/llplugin/llpluginclassbasic.h b/indra/llplugin/llpluginclassbasic.h new file mode 100644 index 000000000..a9e4dba46 --- /dev/null +++ b/indra/llplugin/llpluginclassbasic.h @@ -0,0 +1,126 @@ +/** + * @file llpluginclassbasic.h + * @brief LLPluginClassBasic handles interaction with a plugin which knows about the "basic" message class. + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 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://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +#ifndef LL_LLPLUGINCLASSBASIC_H +#define LL_LLPLUGINCLASSBASIC_H + +#include "llerror.h" // Needed for LOG_CLASS +#include "stdtypes.h" // Needed for F64 +#include "llpluginprocessparent.h" +#include "llpluginclassmediaowner.h" +#include "llpluginmessage.h" +#include +#include + +class LLPluginClassBasic : public LLPluginProcessParentOwner +{ + LOG_CLASS(LLPluginClassBasic); + +public: + LLPluginClassBasic(void); + virtual ~LLPluginClassBasic(); + + // Local initialization, called when creating a plugin process. Return true if successful. + bool init(std::string const& launcher_filename, std::string const& plugin_filename, bool debug); + + // Undoes everything init did. Called when destroying a plugin process. + void reset(void); + + void idle(void); + + // Send message to the plugin, either queueing or sending directly. + void sendMessage(LLPluginMessage const& message); + + // "Loading" means uninitialized or any state prior to fully running (processing commands). + bool isPluginLoading(void) const { return mPlugin ? mPlugin->isLoading() : false; } + + // "Running" means the steady state -- i.e. processing messages. + bool isPluginRunning(void) const { return mPlugin ? mPlugin->isRunning() : false; } + + // "Exited" means any regular or error state after "Running" (plugin may have crashed or exited normally). + bool isPluginExited(void) const { return mPlugin ? mPlugin->isDone() : false; } + + std::string getPluginVersion() const { return mPlugin ? mPlugin->getPluginVersion() : std::string(""); } + + bool getDisableTimeout() const { return mPlugin ? mPlugin->getDisableTimeout() : false; } + + void setDisableTimeout(bool disable) { if (mPlugin) mPlugin->setDisableTimeout(disable); } + + enum EPriority + { + PRIORITY_SLEEP, // Sleep 1 second every message. + PRIORITY_LOW, // Sleep 1/25 second. + PRIORITY_NORMAL, // Sleep 1/50 second. + PRIORITY_HIGH // Sleep 1/100 second. + }; + + static char const* priorityToString(EPriority priority); + void setPriority(EPriority priority); + +protected: + EPriority mPriority; + LLPluginProcessParent* mPlugin; + +private: + F64 mSleepTime; + std::queue mSendQueue; // Used to queue messages while the plugin initializes. + +protected: + // Called as last function when calling 'init()'. + virtual bool init_impl(void) { return true; } + + // Called as last function when calling 'reset()'. + virtual void reset_impl(void) { } + + // Called from idle() before flushing messages to the plugin. + virtual void idle_impl(void) { } + + // Called from setPriority. + virtual void priorityChanged(EPriority priority) { } + + // Inherited from LLPluginProcessParentOwner. + /*virtual*/ void receivePluginMessage(LLPluginMessage const&); + + //-------------------------------------- + // Debug use only + // +private: + bool mDeleteOK; + +public: + void setDeleteOK(bool flag) { mDeleteOK = flag; } +}; + +#endif // LL_LLPLUGINCLASSBASIC_H diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 85241bec1..2bbad6011 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -54,49 +54,24 @@ static int nextPowerOf2( int value ) return next_power_of_2; } -LLPluginClassMedia::LLPluginClassMedia(LLPluginClassMediaOwner *owner) +LLPluginClassMedia::LLPluginClassMedia(LLPluginClassMediaOwner *owner): mOwner(owner) { - mOwner = owner; - mPlugin = NULL; - reset(); - - //debug use - mDeleteOK = true ; + // Most initialization is done with reset_impl(), which we call here + // in order to avoid code duplication. + LLPluginClassMedia::reset_impl(); } - -LLPluginClassMedia::~LLPluginClassMedia() +bool LLPluginClassMedia::init_impl(void) { - llassert_always(mDeleteOK) ; - reset(); -} - -bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug) -{ - LL_DEBUGS("Plugin") << "launcher: " << launcher_filename << LL_ENDL; - LL_DEBUGS("Plugin") << "plugin: " << plugin_filename << LL_ENDL; - - mPlugin = new LLPluginProcessParent(this); - mPlugin->setSleepTime(mSleepTime); - // Queue up the media init message -- it will be sent after all the currently queued messages. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "init"); sendMessage(message); - - mPlugin->init(launcher_filename, plugin_filename, debug); return true; } - -void LLPluginClassMedia::reset() +void LLPluginClassMedia::reset_impl(void) { - if(mPlugin != NULL) - { - delete mPlugin; - mPlugin = NULL; - } - mTextureParamsReceived = false; mRequestedTextureDepth = 0; mRequestedTextureInternalFormat = 0; @@ -125,14 +100,12 @@ void LLPluginClassMedia::reset() mDirtyRect = LLRect::null; mAutoScaleMedia = false; mRequestedVolume = 1.0f; - mPriority = PRIORITY_NORMAL; mLowPrioritySizeLimit = LOW_PRIORITY_TEXTURE_SIZE_DEFAULT; mAllowDownsample = false; mPadding = 0; mLastMouseX = 0; mLastMouseY = 0; mStatus = LLPluginClassMediaOwner::MEDIA_NONE; - mSleepTime = 1.0f / 100.0f; mCanCut = false; mCanCopy = false; mCanPaste = false; @@ -158,13 +131,8 @@ void LLPluginClassMedia::reset() mLoadedDuration = 0.0f; } -void LLPluginClassMedia::idle(void) +void LLPluginClassMedia::idle_impl(void) { - if(mPlugin) - { - mPlugin->idle(); - } - if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL) || (mPlugin->isBlocked())) { // Can't process a size change at this time @@ -260,17 +228,6 @@ void LLPluginClassMedia::idle(void) LL_DEBUGS("Plugin") << "Sending size_change" << LL_ENDL; } } - - if(mPlugin && mPlugin->isRunning()) - { - // Send queued messages - while(!mSendQueue.empty()) - { - LLPluginMessage message = mSendQueue.front(); - mSendQueue.pop(); - mPlugin->sendMessage(message); - } - } } int LLPluginClassMedia::getTextureWidth() const @@ -295,18 +252,16 @@ unsigned char* LLPluginClassMedia::getBitsData() void LLPluginClassMedia::setSize(int width, int height) { - if((width > 0) && (height > 0)) + if (width <= 0 || height <= 0) + { + width = height = -1; + } + if (mSetMediaWidth != width || mSetMediaHeight != height) { mSetMediaWidth = width; mSetMediaHeight = height; + setSizeInternal(); } - else - { - mSetMediaWidth = -1; - mSetMediaHeight = -1; - } - - setSizeInternal(); } void LLPluginClassMedia::setSizeInternal(void) @@ -335,7 +290,7 @@ void LLPluginClassMedia::setSizeInternal(void) { switch(mPriority) { - case PRIORITY_SLIDESHOW: + case PRIORITY_SLEEP: case PRIORITY_LOW: // Reduce maximum texture dimension to (or below) mLowPrioritySizeLimit while((mRequestedMediaWidth > mLowPrioritySizeLimit) || (mRequestedMediaHeight > mLowPrioritySizeLimit)) @@ -584,71 +539,10 @@ void LLPluginClassMedia::loadURI(const std::string &uri) sendMessage(message); } -const char* LLPluginClassMedia::priorityToString(EPriority priority) +void LLPluginClassMedia::priorityChanged(EPriority priority) { - const char* result = "UNKNOWN"; - switch(priority) - { - case PRIORITY_UNLOADED: result = "unloaded"; break; - case PRIORITY_STOPPED: result = "stopped"; break; - case PRIORITY_HIDDEN: result = "hidden"; break; - case PRIORITY_SLIDESHOW: result = "slideshow"; break; - case PRIORITY_LOW: result = "low"; break; - case PRIORITY_NORMAL: result = "normal"; break; - case PRIORITY_HIGH: result = "high"; break; - } - - return result; -} - -void LLPluginClassMedia::setPriority(EPriority priority) -{ - if(mPriority != priority) - { - mPriority = priority; - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "set_priority"); - - std::string priority_string = priorityToString(priority); - switch(priority) - { - case PRIORITY_UNLOADED: - mSleepTime = 1.0f; - break; - case PRIORITY_STOPPED: - mSleepTime = 1.0f; - break; - case PRIORITY_HIDDEN: - mSleepTime = 1.0f; - break; - case PRIORITY_SLIDESHOW: - mSleepTime = 1.0f; - break; - case PRIORITY_LOW: - mSleepTime = 1.0f / 25.0f; - break; - case PRIORITY_NORMAL: - mSleepTime = 1.0f / 50.0f; - break; - case PRIORITY_HIGH: - mSleepTime = 1.0f / 100.0f; - break; - } - - message.setValue("priority", priority_string); - - sendMessage(message); - - if(mPlugin) - { - mPlugin->setSleepTime(mSleepTime); - } - - LL_DEBUGS("PluginPriority") << this << ": setting priority to " << priority_string << LL_ENDL; - - // This may affect the calculated size, so recalculate it here. - setSizeInternal(); - } + // This may affect the calculated size, so recalculate it here. + setSizeInternal(); } void LLPluginClassMedia::setLowPrioritySizeLimit(int size) @@ -657,7 +551,6 @@ void LLPluginClassMedia::setLowPrioritySizeLimit(int size) if(mLowPrioritySizeLimit != power) { mLowPrioritySizeLimit = power; - // This may affect the calculated size, so recalculate it here. setSizeInternal(); } @@ -726,7 +619,11 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) { std::string message_class = message.getClass(); - if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) + if (message_class == LLPLUGIN_MESSAGE_CLASS_BASIC) + { + LLPluginClassBasic::receivePluginMessage(message); + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) { std::string message_name = message.getName(); if(message_name == "texture_params") @@ -1003,10 +900,9 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) // } // else { - LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL; + LL_WARNS("Plugin") << "Unknown " << message_class << " class message: " << message_name << LL_ENDL; } } - } /* virtual */ @@ -1029,19 +925,6 @@ void LLPluginClassMedia::mediaEvent(LLPluginClassMediaOwner::EMediaEvent event) } } -void LLPluginClassMedia::sendMessage(const LLPluginMessage &message) -{ - if(mPlugin && mPlugin->isRunning()) - { - mPlugin->sendMessage(message); - } - else - { - // The plugin isn't set up yet -- queue this message to be sent after initialization. - mSendQueue.push(message); - } -} - //////////////////////////////////////////////////////////// // MARK: media_browser class functions bool LLPluginClassMedia::pluginSupportsMediaBrowser(void) diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index 0004971c6..1996c6588 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -36,29 +36,17 @@ #ifndef LL_LLPLUGINCLASSMEDIA_H #define LL_LLPLUGINCLASSMEDIA_H +#include "llpluginclassbasic.h" #include "llgltypes.h" -#include "llpluginprocessparent.h" #include "llrect.h" -#include "llpluginclassmediaowner.h" -#include #include "v4color.h" -class LLPluginClassMedia : public LLPluginProcessParentOwner +class LLPluginClassMedia : public LLPluginClassBasic { LOG_CLASS(LLPluginClassMedia); + public: LLPluginClassMedia(LLPluginClassMediaOwner *owner); - virtual ~LLPluginClassMedia(); - - // local initialization, called by the media manager when creating a source - virtual bool init(const std::string &launcher_filename, - const std::string &plugin_filename, - bool debug); - - // undoes everything init() didm called by the media manager when destroying a source - virtual void reset(); - - void idle(void); // All of these may return 0 or an actual valid value. // Callers need to check the return for 0, and not use the values in that case. @@ -101,22 +89,22 @@ public: bool getDirty(LLRect *dirty_rect = NULL); void resetDirty(void); - typedef enum + enum EMouseEventType { MOUSE_EVENT_DOWN, MOUSE_EVENT_UP, MOUSE_EVENT_MOVE, MOUSE_EVENT_DOUBLE_CLICK - }EMouseEventType; + }; void mouseEvent(EMouseEventType type, int button, int x, int y, MASK modifiers); - typedef enum + enum EKeyEventType { KEY_EVENT_DOWN, KEY_EVENT_UP, KEY_EVENT_REPEAT - }EKeyEventType; + }; bool keyEvent(EKeyEventType type, int key_code, MASK modifiers, LLSD native_key_data); @@ -127,39 +115,13 @@ public: void loadURI(const std::string &uri); - // "Loading" means uninitialized or any state prior to fully running (processing commands) - bool isPluginLoading(void) { return mPlugin?mPlugin->isLoading():false; }; - - // "Running" means the steady state -- i.e. processing messages - bool isPluginRunning(void) { return mPlugin?mPlugin->isRunning():false; }; - - // "Exited" means any regular or error state after "Running" (plugin may have crashed or exited normally) - bool isPluginExited(void) { return mPlugin?mPlugin->isDone():false; }; - - std::string getPluginVersion() { return mPlugin?mPlugin->getPluginVersion():std::string(""); }; - - bool getDisableTimeout() { return mPlugin?mPlugin->getDisableTimeout():false; }; - void setDisableTimeout(bool disable) { if(mPlugin) mPlugin->setDisableTimeout(disable); }; - // Inherited from LLPluginProcessParentOwner /* virtual */ void receivePluginMessage(const LLPluginMessage &message); /* virtual */ void pluginLaunchFailed(); /* virtual */ void pluginDied(); + // Inherited from LLPluginClassBasic + /* virtual */ void priorityChanged(EPriority priority); - - typedef enum - { - PRIORITY_UNLOADED, // media plugin isn't even loaded. - PRIORITY_STOPPED, // media is not playing, shouldn't need to update at all. - PRIORITY_HIDDEN, // media is not being displayed or is out of view, don't need to do graphic updates, but may still update audio, playhead, etc. - PRIORITY_SLIDESHOW, // media is in the far distance, updates very infrequently - PRIORITY_LOW, // media is in the distance, may be rendered at reduced size - PRIORITY_NORMAL, // normal (default) priority - PRIORITY_HIGH // media has user focus and/or is taking up most of the screen - }EPriority; - - static const char* priorityToString(EPriority priority); - void setPriority(EPriority priority); void setLowPrioritySizeLimit(int size); F64 getCPUUsage(); @@ -256,17 +218,18 @@ public: void initializeUrlHistory(const LLSD& url_history); protected: - - LLPluginClassMediaOwner *mOwner; + virtual bool init_impl(void); + virtual void reset_impl(void); + virtual void idle_impl(void); // Notify this object's owner that an event has occurred. void mediaEvent(LLPluginClassMediaOwner::EMediaEvent event); - void sendMessage(const LLPluginMessage &message); // Send message internally, either queueing or sending directly. - std::queue mSendQueue; // Used to queue messages while the plugin initializes. - void setSizeInternal(void); +protected: + LLPluginClassMediaOwner *mOwner; + bool mTextureParamsReceived; // the mRequestedTexture* fields are only valid when this is true S32 mRequestedTextureDepth; LLGLenum mRequestedTextureInternalFormat; @@ -313,16 +276,11 @@ protected: float mRequestedVolume; - // Priority of this media stream - EPriority mPriority; int mLowPrioritySizeLimit; bool mAllowDownsample; int mPadding; - - LLPluginProcessParent *mPlugin; - LLRect mDirtyRect; std::string translateModifiers(MASK modifiers); @@ -332,8 +290,6 @@ protected: int mLastMouseY; LLPluginClassMediaOwner::EMediaStatus mStatus; - - F64 mSleepTime; bool mCanCut; bool mCanCopy; @@ -363,15 +319,6 @@ protected: F64 mDuration; F64 mCurrentRate; F64 mLoadedDuration; - -//-------------------------------------- - //debug use only - // -private: - bool mDeleteOK ; -public: - void setDeleteOK(bool flag) { mDeleteOK = flag ;} -//-------------------------------------- }; #endif // LL_LLPLUGINCLASSMEDIA_H diff --git a/indra/llplugin/llplugininstance.cpp b/indra/llplugin/llplugininstance.cpp index 3a1395cd2..914eadcce 100644 --- a/indra/llplugin/llplugininstance.cpp +++ b/indra/llplugin/llplugininstance.cpp @@ -55,8 +55,8 @@ const char *LLPluginInstance::PLUGIN_INIT_FUNCTION_NAME = "LLPluginInitEntryPoin */ LLPluginInstance::LLPluginInstance(LLPluginInstanceMessageListener *owner) : mDSOHandle(NULL), - mPluginUserData(NULL), - mPluginSendMessageFunction(NULL) + mPluginObject(NULL), + mReceiveMessageFunction(NULL) { mOwner = owner; } @@ -109,9 +109,9 @@ int LLPluginInstance::load(std::string &plugin_file) if(result == APR_SUCCESS) { - result = init_function(staticReceiveMessage, (void*)this, &mPluginSendMessageFunction, &mPluginUserData); + result = init_function(&LLPluginInstance::staticReceiveMessage, this, &mReceiveMessageFunction, &mPluginObject); - if(result != APR_SUCCESS) + if(result != 0) { LL_WARNS("Plugin") << "call to init function failed with error " << result << LL_ENDL; } @@ -120,6 +120,14 @@ int LLPluginInstance::load(std::string &plugin_file) return (int)result; } +// This is the SLPlugin process (the child process). +// This is not part of a DSO. +// +// This function is called from LLPluginProcessChild::receiveMessageRaw +// for messages received from the viewer that are not internal. +// +// It sends the message to the DSO by calling the registered 'received' +// function (for example, FilepickerPlugin::receiveMessage). /** * Sends a message to the plugin. * @@ -127,10 +135,10 @@ int LLPluginInstance::load(std::string &plugin_file) */ void LLPluginInstance::sendMessage(const std::string &message) { - if(mPluginSendMessageFunction) + if(mReceiveMessageFunction) { LL_DEBUGS("Plugin") << "sending message to plugin: \"" << message << "\"" << LL_ENDL; - mPluginSendMessageFunction(message.c_str(), &mPluginUserData); + mReceiveMessageFunction(message.c_str(), &mPluginObject); } else { @@ -147,14 +155,19 @@ void LLPluginInstance::idle(void) } // static -void LLPluginInstance::staticReceiveMessage(const char *message_string, void **user_data) +void LLPluginInstance::staticReceiveMessage(char const* message_string, LLPluginInstance** self_ptr) { - // TODO: validate that the user_data argument is still a valid LLPluginInstance pointer + // TODO: validate that the self argument is still a valid LLPluginInstance pointer // we could also use a key that's looked up in a map (instead of a direct pointer) for safety, but that's probably overkill - LLPluginInstance *self = (LLPluginInstance*)*user_data; - self->receiveMessage(message_string); + (*self_ptr)->receiveMessage(message_string); } +// This is the SLPlugin process. +// This is not part of a DSO. +// +// This function is called by a loaded DSO (through a function pointer, it +// is called from BasicPluginBase::sendMessage) for messages it wants to +// send to the viewer. It calls LLPluginProcessChild::receivePluginMessage. /** * Plugin receives message from plugin loader shell. * diff --git a/indra/llplugin/llplugininstance.h b/indra/llplugin/llplugininstance.h index 9cf6075ff..faa1cbb7f 100644 --- a/indra/llplugin/llplugininstance.h +++ b/indra/llplugin/llplugininstance.h @@ -51,6 +51,8 @@ public: virtual void receivePluginMessage(const std::string &message) = 0; }; +class BasicPluginBase; + /** * @brief LLPluginInstance handles loading the dynamic library of a plugin and setting up its entry points for message passing. */ @@ -79,26 +81,28 @@ public: * @param[in] message_string Null-terminated C string * @param[in] user_data The opaque reference that the callee supplied during setup. */ - typedef void (*sendMessageFunction) (const char *message_string, void **user_data); + typedef void (*receiveMessageFunction)(char const* message_string, BasicPluginBase** plugin_object); + + typedef void (*sendMessageFunction)(char const* message_string, LLPluginInstance** plugin_instance); /** The signature of the plugin init function. TODO:DOC check direction (pluging loader shell to plugin?) * * @param[in] host_user_data Data from plugin loader shell. * @param[in] plugin_send_function Function for sending from the plugin loader shell to plugin. */ - typedef int (*pluginInitFunction) (sendMessageFunction host_send_func, void *host_user_data, sendMessageFunction *plugin_send_func, void **plugin_user_data); + typedef int (*pluginInitFunction)(sendMessageFunction send_message_function, LLPluginInstance* plugin_instance, receiveMessageFunction* receive_message_function, BasicPluginBase** plugin_object); /** Name of plugin init function */ static const char *PLUGIN_INIT_FUNCTION_NAME; private: - static void staticReceiveMessage(const char *message_string, void **user_data); + static void staticReceiveMessage(char const* message_string, LLPluginInstance** plugin_instance); void receiveMessage(const char *message_string); apr_dso_handle_t *mDSOHandle; - void *mPluginUserData; - sendMessageFunction mPluginSendMessageFunction; + BasicPluginBase* mPluginObject; + receiveMessageFunction mReceiveMessageFunction; LLPluginInstanceMessageListener *mOwner; }; diff --git a/indra/llplugin/llpluginmessage.h b/indra/llplugin/llpluginmessage.h index fe504c8b9..3c7b91750 100644 --- a/indra/llplugin/llpluginmessage.h +++ b/indra/llplugin/llpluginmessage.h @@ -99,8 +99,10 @@ public: // (this clears out all existing state before starting the parse) // Returns -1 on failure, otherwise returns the number of key/value pairs in the message. int parse(const std::string &message); - - + + // For debugging purposes. + friend std::ostream& operator<<(std::ostream& os, LLPluginMessage const& message) { return os << message.mMessage; } + private: LLSD mMessage; diff --git a/indra/llplugin/llpluginmessageclasses.h b/indra/llplugin/llpluginmessageclasses.h index 8812a1676..b61720ab4 100644 --- a/indra/llplugin/llpluginmessageclasses.h +++ b/indra/llplugin/llpluginmessageclasses.h @@ -48,6 +48,9 @@ #define LLPLUGIN_MESSAGE_CLASS_BASE "base" #define LLPLUGIN_MESSAGE_CLASS_BASE_VERSION "1.0" +#define LLPLUGIN_MESSAGE_CLASS_BASIC "basic" +#define LLPLUGIN_MESSAGE_CLASS_BASIC_VERSION "1.0" + #define LLPLUGIN_MESSAGE_CLASS_MEDIA "media" #define LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION "1.0" diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp index d2238236f..2c47087ab 100644 --- a/indra/llplugin/llpluginprocesschild.cpp +++ b/indra/llplugin/llpluginprocesschild.cpp @@ -286,6 +286,11 @@ bool LLPluginProcessChild::isDone(void) return result; } +// This is the SLPlugin process. +// This is not part of a DSO. +// +// This function is called by SLPlugin to send a message (originating from +// SLPlugin itself) to the loaded DSO. It calls LLPluginInstance::sendMessage. void LLPluginProcessChild::sendMessageToPlugin(const LLPluginMessage &message) { if (mInstance) @@ -305,15 +310,26 @@ void LLPluginProcessChild::sendMessageToPlugin(const LLPluginMessage &message) } } +// This is the SLPlugin process (the child process). +// This is not part of a DSO. +// +// This function is called by SLPlugin to send 'message' to the viewer (the parent process). void LLPluginProcessChild::sendMessageToParent(const LLPluginMessage &message) { std::string buffer = message.generate(); LL_DEBUGS("Plugin") << "Sending to parent: " << buffer << LL_ENDL; + // Write the serialized message to the pipe. writeMessageRaw(buffer); } +// This is the SLPlugin process (the child process). +// This is not part of a DSO. +// +// This function is called when the serialized message 'message' was received from the viewer. +// It parses the message and handles LLPLUGIN_MESSAGE_CLASS_INTERNAL. +// Other message classes are passed on to LLPluginInstance::sendMessage. void LLPluginProcessChild::receiveMessageRaw(const std::string &message) { // Incoming message from the TCP Socket @@ -449,7 +465,17 @@ void LLPluginProcessChild::receiveMessageRaw(const std::string &message) } } -/* virtual */ +// This is the SLPlugin process. +// This is not part of a DSO. +// +// This function is called from LLPluginInstance::receiveMessage +// for messages from a loaded DSO that have to be passed to the +// viewer. +// +// It handles the base messages that are responses to messages sent by this +// class, and passes the rest on to LLPluginMessagePipeOwner::writeMessageRaw +// to be written to the pipe. +/* virtual */ void LLPluginProcessChild::receivePluginMessage(const std::string &message) { LL_DEBUGS("Plugin") << "Received from plugin: " << message << LL_ENDL; diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index 2be917be6..35931426d 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -579,6 +579,9 @@ void LLPluginProcessParent::setSleepTime(F64 sleep_time, bool force_send) } } +// This is the viewer process (the parent process) +// +// This function is called to send a message to the plugin. void LLPluginProcessParent::sendMessage(const LLPluginMessage &message) { if(message.hasValue("blocking_response")) @@ -588,6 +591,11 @@ void LLPluginProcessParent::sendMessage(const LLPluginMessage &message) // reset the heartbeat timer, since there will have been no heartbeats while the plugin was blocked. mHeartbeat.setTimerExpirySec(mPluginLockupTimeout); } + if (message.hasValue("gorgon")) + { + // After this message it is expected that the plugin will not send any more messages for a long time. + mBlocked = true; + } std::string buffer = message.generate(); LL_DEBUGS("Plugin") << "Sending: " << buffer << LL_ENDL; @@ -636,6 +644,16 @@ void LLPluginProcessParent::setMessagePipe(LLPluginMessagePipe *message_pipe) } } +apr_status_t LLPluginProcessParent::socketError(apr_status_t error) +{ + mSocketError = error; + if (APR_STATUS_IS_EPIPE(error)) + { + errorState(); + } + return error; +}; + //static void LLPluginProcessParent::dirtyPollSet() { @@ -852,6 +870,10 @@ void LLPluginProcessParent::servicePoll() } } +// This the viewer process (the parent process). +// +// This function is called when a message is received from a plugin. +// It parses the message and passes it on to LLPluginProcessParent::receiveMessage. void LLPluginProcessParent::receiveMessageRaw(const std::string &message) { LL_DEBUGS("Plugin") << "Received: " << message << LL_ENDL; @@ -863,6 +885,13 @@ void LLPluginProcessParent::receiveMessageRaw(const std::string &message) { mBlocked = true; } + if(parsed.hasValue("perseus")) + { + mBlocked = false; + + // reset the heartbeat timer, since there will have been no heartbeats while the plugin was blocked. + mHeartbeat.setTimerExpirySec(mPluginLockupTimeout); + } if(mPolledInput) { @@ -905,6 +934,12 @@ void LLPluginProcessParent::receiveMessageEarly(const LLPluginMessage &message) } } +// This is the viewer process (the parent process). +// +// This function is called for messages that have to +// be written to the plugin. +// Note that LLPLUGIN_MESSAGE_CLASS_INTERNAL messages +// are not sent to the plugin, but are handled here. void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) { std::string message_class = message.getClass(); diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h index bba3643d6..a82407d05 100644 --- a/indra/llplugin/llpluginprocessparent.h +++ b/indra/llplugin/llpluginprocessparent.h @@ -99,6 +99,7 @@ public: /*virtual*/ void receiveMessageRaw(const std::string &message); /*virtual*/ void receiveMessageEarly(const LLPluginMessage &message); /*virtual*/ void setMessagePipe(LLPluginMessagePipe *message_pipe) ; + /*virtual*/ apr_status_t socketError(apr_status_t error); // This adds a memory segment shared with the client, generating a name for the segment. The name generated is guaranteed to be unique on the host. // The caller must call removeSharedMemory first (and wait until getSharedMemorySize returns 0 for the indicated name) before re-adding a segment with the same name. From 16cd4c5c4ba19a43cbc2757768ba4a5bb2e3647b Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 6 May 2011 16:08:24 +0200 Subject: [PATCH 07/21] Split plugin classes and derive AIFilePicker from BasicPluginBase (part 3). This commit deletes all indra/media_plugins and copied one-on-one the indra/plugins directory from my (imprudence) statemachine branch (which contains the AIFilePicker patch). git shows this as a lot of 'renamed' files because originally it was a rename. However, there are a lot of changes as well: it's both an upgrade to a newer plugin system (changes by LL) as well as an upgrade to my refactored plugin system with a file picker as plugin. Since this commit is a one-on-one copy, it disregards any changes that were in Singularity and not in imprudence in indra/media_plugins however. I will add those back in the next commit. --- indra/media_plugins/CMakeLists.txt | 15 - .../example/media_plugin_example.cpp | 412 ----- .../gstreamer010/llmediaimplgstreamer.h | 53 - .../llmediaimplgstreamer_syms.cpp | 167 -- .../gstreamer010/llmediaimplgstreamer_syms.h | 74 - .../llmediaimplgstreamer_syms_raw.inc | 51 - .../llmediaimplgstreamer_syms_rawv.inc | 5 - .../llmediaimplgstreamertriviallogging.h | 55 - .../webkit/dummy_volume_catcher.cpp | 58 - indra/media_plugins/webkit/volume_catcher.h | 54 - indra/plugins/CMakeLists.txt | 17 + indra/plugins/base_basic/CMakeLists.txt | 39 + .../plugins/base_basic/basic_plugin_base.cpp | 136 ++ .../base_basic/basic_plugin_base.exp} | 0 indra/plugins/base_basic/basic_plugin_base.h | 88 + .../base_media}/CMakeLists.txt | 11 +- .../base_media}/media_plugin_base.cpp | 70 +- .../plugins/base_media/media_plugin_base.exp | 2 + .../base_media}/media_plugin_base.h | 48 +- indra/plugins/example_basic/CMakeLists.txt | 68 + .../example_basic/basic_plugin_example.cpp | 123 ++ .../example_media}/CMakeLists.txt | 19 +- .../example_media/media_plugin_example.cpp | 490 ++++++ indra/plugins/filepicker/CMakeLists.txt | 81 + indra/plugins/filepicker/llfilepicker.cpp | 1414 +++++++++++++++++ indra/plugins/filepicker/llfilepicker.h | 231 +++ .../gstreamer010/CMakeLists.txt | 11 - .../gstreamer010/llmediaimplgstreamer.h | 60 + .../llmediaimplgstreamertriviallogging.h | 65 + .../llmediaimplgstreamervidplug.cpp | 201 ++- .../llmediaimplgstreamervidplug.h | 38 +- .../media_plugin_gstreamer010.cpp | 602 ++++--- .../quicktime/CMakeLists.txt | 3 +- .../quicktime/media_plugin_quicktime.cpp | 29 +- .../webkit/CMakeLists.txt | 20 +- indra/plugins/webkit/dummy_volume_catcher.cpp | 65 + .../webkit/linux_volume_catcher.cpp | 40 +- indra/plugins/webkit/linux_volume_catcher.h | 56 + .../webkit/linux_volume_catcher_pa_syms.inc | 0 .../linux_volume_catcher_paglib_syms.inc | 0 .../webkit/mac_volume_catcher.cpp | 37 +- .../webkit/media_plugin_webkit.cpp | 135 +- indra/plugins/webkit/volume_catcher.h | 61 + .../webkit/windows_volume_catcher.cpp | 39 +- 44 files changed, 3666 insertions(+), 1577 deletions(-) delete mode 100644 indra/media_plugins/CMakeLists.txt delete mode 100644 indra/media_plugins/example/media_plugin_example.cpp delete mode 100644 indra/media_plugins/gstreamer010/llmediaimplgstreamer.h delete mode 100644 indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp delete mode 100644 indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h delete mode 100644 indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc delete mode 100644 indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc delete mode 100644 indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h delete mode 100644 indra/media_plugins/webkit/dummy_volume_catcher.cpp delete mode 100644 indra/media_plugins/webkit/volume_catcher.h create mode 100644 indra/plugins/CMakeLists.txt create mode 100644 indra/plugins/base_basic/CMakeLists.txt create mode 100755 indra/plugins/base_basic/basic_plugin_base.cpp rename indra/{media_plugins/base/media_plugin_base.exp => plugins/base_basic/basic_plugin_base.exp} (100%) mode change 100755 => 100644 create mode 100755 indra/plugins/base_basic/basic_plugin_base.h rename indra/{media_plugins/base => plugins/base_media}/CMakeLists.txt (81%) rename indra/{media_plugins/base => plugins/base_media}/media_plugin_base.cpp (66%) mode change 100644 => 100755 create mode 100755 indra/plugins/base_media/media_plugin_base.exp rename indra/{media_plugins/base => plugins/base_media}/media_plugin_base.h (67%) mode change 100644 => 100755 create mode 100644 indra/plugins/example_basic/CMakeLists.txt create mode 100644 indra/plugins/example_basic/basic_plugin_example.cpp rename indra/{media_plugins/example => plugins/example_media}/CMakeLists.txt (83%) create mode 100755 indra/plugins/example_media/media_plugin_example.cpp create mode 100644 indra/plugins/filepicker/CMakeLists.txt create mode 100644 indra/plugins/filepicker/llfilepicker.cpp create mode 100644 indra/plugins/filepicker/llfilepicker.h rename indra/{media_plugins => plugins}/gstreamer010/CMakeLists.txt (82%) create mode 100755 indra/plugins/gstreamer010/llmediaimplgstreamer.h create mode 100755 indra/plugins/gstreamer010/llmediaimplgstreamertriviallogging.h rename indra/{media_plugins => plugins}/gstreamer010/llmediaimplgstreamervidplug.cpp (70%) mode change 100644 => 100755 rename indra/{media_plugins => plugins}/gstreamer010/llmediaimplgstreamervidplug.h (65%) mode change 100644 => 100755 rename indra/{media_plugins => plugins}/gstreamer010/media_plugin_gstreamer010.cpp (70%) mode change 100644 => 100755 rename indra/{media_plugins => plugins}/quicktime/CMakeLists.txt (97%) mode change 100644 => 100755 rename indra/{media_plugins => plugins}/quicktime/media_plugin_quicktime.cpp (95%) mode change 100644 => 100755 rename indra/{media_plugins => plugins}/webkit/CMakeLists.txt (88%) create mode 100644 indra/plugins/webkit/dummy_volume_catcher.cpp rename indra/{media_plugins => plugins}/webkit/linux_volume_catcher.cpp (90%) create mode 100755 indra/plugins/webkit/linux_volume_catcher.h rename indra/{media_plugins => plugins}/webkit/linux_volume_catcher_pa_syms.inc (100%) rename indra/{media_plugins => plugins}/webkit/linux_volume_catcher_paglib_syms.inc (100%) rename indra/{media_plugins => plugins}/webkit/mac_volume_catcher.cpp (85%) rename indra/{media_plugins => plugins}/webkit/media_plugin_webkit.cpp (93%) mode change 100644 => 100755 create mode 100644 indra/plugins/webkit/volume_catcher.h rename indra/{media_plugins => plugins}/webkit/windows_volume_catcher.cpp (61%) diff --git a/indra/media_plugins/CMakeLists.txt b/indra/media_plugins/CMakeLists.txt deleted file mode 100644 index 9858d093e..000000000 --- a/indra/media_plugins/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# -*- cmake -*- - -add_subdirectory(base) - -add_subdirectory(webkit) - -if (LINUX) - add_subdirectory(gstreamer010) -endif (LINUX) - -if (DARWIN OR WINDOWS) - add_subdirectory(quicktime) -endif (DARWIN OR WINDOWS) - -add_subdirectory(example) diff --git a/indra/media_plugins/example/media_plugin_example.cpp b/indra/media_plugins/example/media_plugin_example.cpp deleted file mode 100644 index da7de0179..000000000 --- a/indra/media_plugins/example/media_plugin_example.cpp +++ /dev/null @@ -1,412 +0,0 @@ -/** - * @file media_plugin_example.cpp - * @brief Example plugin for LLMedia API plugin system - * - * @cond - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - * @endcond - */ - -#include "linden_common.h" - -#include "llgl.h" -#include "llplugininstance.h" -#include "llpluginmessage.h" -#include "llpluginmessageclasses.h" -#include "media_plugin_base.h" - -#include - -//////////////////////////////////////////////////////////////////////////////// -// -class MediaPluginExample : - public MediaPluginBase -{ - public: - MediaPluginExample( LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data ); - ~MediaPluginExample(); - - /*virtual*/ void receiveMessage( const char* message_string ); - - private: - bool init(); - void update( F64 milliseconds ); - void write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b ); - bool mFirstTime; - - time_t mLastUpdateTime; - enum Constants { ENumObjects = 10 }; - unsigned char* mBackgroundPixels; - int mColorR[ ENumObjects ]; - int mColorG[ ENumObjects ]; - int mColorB[ ENumObjects ]; - int mXpos[ ENumObjects ]; - int mYpos[ ENumObjects ]; - int mXInc[ ENumObjects ]; - int mYInc[ ENumObjects ]; - int mBlockSize[ ENumObjects ]; - bool mMouseButtonDown; - bool mStopAction; -}; - -//////////////////////////////////////////////////////////////////////////////// -// -MediaPluginExample::MediaPluginExample( LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data ) : - MediaPluginBase( host_send_func, host_user_data ) -{ - mFirstTime = true; - mWidth = 0; - mHeight = 0; - mDepth = 4; - mPixels = 0; - mMouseButtonDown = false; - mStopAction = false; - mLastUpdateTime = 0; -} - -//////////////////////////////////////////////////////////////////////////////// -// -MediaPluginExample::~MediaPluginExample() -{ -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginExample::receiveMessage( const char* message_string ) -{ -// std::cerr << "MediaPluginWebKit::receiveMessage: received message: \"" << message_string << "\"" << std::endl; - LLPluginMessage message_in; - - if(message_in.parse(message_string) >= 0) - { - std::string message_class = message_in.getClass(); - std::string message_name = message_in.getName(); - if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE) - { - if(message_name == "init") - { - LLPluginMessage message("base", "init_response"); - LLSD versions = LLSD::emptyMap(); - versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; - versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; - versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION; - message.setValueLLSD("versions", versions); - - std::string plugin_version = "Example plugin 1.0..0"; - message.setValue("plugin_version", plugin_version); - sendMessage(message); - } - else if(message_name == "idle") - { - // no response is necessary here. - F64 time = message_in.getValueReal("time"); - - // Convert time to milliseconds for update() - update((int)(time * 1000.0f)); - } - else if(message_name == "cleanup") - { - } - else if(message_name == "shm_added") - { - SharedSegmentInfo info; - info.mAddress = message_in.getValuePointer("address"); - info.mSize = (size_t)message_in.getValueS32("size"); - std::string name = message_in.getValue("name"); - - mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); - - } - else if(message_name == "shm_remove") - { - std::string name = message_in.getValue("name"); - - SharedSegmentMap::iterator iter = mSharedSegments.find(name); - if(iter != mSharedSegments.end()) - { - if(mPixels == iter->second.mAddress) - { - // This is the currently active pixel buffer. Make sure we stop drawing to it. - mPixels = NULL; - mTextureSegmentName.clear(); - } - mSharedSegments.erase(iter); - } - else - { -// std::cerr << "MediaPluginWebKit::receiveMessage: unknown shared memory region!" << std::endl; - } - - // Send the response so it can be cleaned up. - LLPluginMessage message("base", "shm_remove_response"); - message.setValue("name", name); - sendMessage(message); - } - else - { -// std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl; - } - } - else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) - { - if(message_name == "init") - { - // Plugin gets to decide the texture parameters to use. - mDepth = 4; - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); - message.setValueS32("default_width", 1024); - message.setValueS32("default_height", 1024); - message.setValueS32("depth", mDepth); - message.setValueU32("internalformat", GL_RGBA); - message.setValueU32("format", GL_RGBA); - message.setValueU32("type", GL_UNSIGNED_BYTE); - message.setValueBoolean("coords_opengl", true); - sendMessage(message); - } - else if(message_name == "size_change") - { - std::string name = message_in.getValue("name"); - S32 width = message_in.getValueS32("width"); - S32 height = message_in.getValueS32("height"); - S32 texture_width = message_in.getValueS32("texture_width"); - S32 texture_height = message_in.getValueS32("texture_height"); - - if(!name.empty()) - { - // Find the shared memory region with this name - SharedSegmentMap::iterator iter = mSharedSegments.find(name); - if(iter != mSharedSegments.end()) - { - mPixels = (unsigned char*)iter->second.mAddress; - mWidth = width; - mHeight = height; - - mTextureWidth = texture_width; - mTextureHeight = texture_height; - }; - }; - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); - message.setValue("name", name); - message.setValueS32("width", width); - message.setValueS32("height", height); - message.setValueS32("texture_width", texture_width); - message.setValueS32("texture_height", texture_height); - sendMessage(message); - - } - else if(message_name == "load_uri") - { - } - else if(message_name == "mouse_event") - { - std::string event = message_in.getValue("event"); - if(event == "down") - { - - } - else if(event == "up") - { - } - else if(event == "double_click") - { - } - } - } - else - { -// std::cerr << "MediaPluginWebKit::receiveMessage: unknown message class: " << message_class << std::endl; - }; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginExample::write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b ) -{ - // make sure we don't write outside the buffer - if ( ( x < 0 ) || ( x >= mWidth ) || ( y < 0 ) || ( y >= mHeight ) ) - return; - - if ( mBackgroundPixels != NULL ) - { - unsigned char *pixel = mBackgroundPixels; - pixel += y * mWidth * mDepth; - pixel += ( x * mDepth ); - pixel[ 0 ] = b; - pixel[ 1 ] = g; - pixel[ 2 ] = r; - - setDirty( x, y, x + 1, y + 1 ); - }; -} - -//////////////////////////////////////////////////////////////////////////////// -// -void MediaPluginExample::update( F64 milliseconds ) -{ - if ( mWidth < 1 || mWidth > 2048 || mHeight < 1 || mHeight > 2048 ) - return; - - if ( mPixels == 0 ) - return; - - if ( mFirstTime ) - { - for( int n = 0; n < ENumObjects; ++n ) - { - mXpos[ n ] = ( mWidth / 2 ) + rand() % ( mWidth / 16 ) - ( mWidth / 32 ); - mYpos[ n ] = ( mHeight / 2 ) + rand() % ( mHeight / 16 ) - ( mHeight / 32 ); - - mColorR[ n ] = rand() % 0x60 + 0x60; - mColorG[ n ] = rand() % 0x60 + 0x60; - mColorB[ n ] = rand() % 0x60 + 0x60; - - mXInc[ n ] = 0; - while ( mXInc[ n ] == 0 ) - mXInc[ n ] = rand() % 7 - 3; - - mYInc[ n ] = 0; - while ( mYInc[ n ] == 0 ) - mYInc[ n ] = rand() % 9 - 4; - - mBlockSize[ n ] = rand() % 0x30 + 0x10; - }; - - delete [] mBackgroundPixels; - - mBackgroundPixels = new unsigned char[ mWidth * mHeight * mDepth ]; - - mFirstTime = false; - }; - - if ( mStopAction ) - return; - - if ( time( NULL ) > mLastUpdateTime + 3 ) - { - const int num_squares = rand() % 20 + 4; - int sqr1_r = rand() % 0x80 + 0x20; - int sqr1_g = rand() % 0x80 + 0x20; - int sqr1_b = rand() % 0x80 + 0x20; - int sqr2_r = rand() % 0x80 + 0x20; - int sqr2_g = rand() % 0x80 + 0x20; - int sqr2_b = rand() % 0x80 + 0x20; - - for ( int y1 = 0; y1 < num_squares; ++y1 ) - { - for ( int x1 = 0; x1 < num_squares; ++x1 ) - { - int px_start = mWidth * x1 / num_squares; - int px_end = ( mWidth * ( x1 + 1 ) ) / num_squares; - int py_start = mHeight * y1 / num_squares; - int py_end = ( mHeight * ( y1 + 1 ) ) / num_squares; - - for( int y2 = py_start; y2 < py_end; ++y2 ) - { - for( int x2 = px_start; x2 < px_end; ++x2 ) - { - int rowspan = mWidth * mDepth; - - if ( ( y1 % 2 ) ^ ( x1 % 2 ) ) - { - mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr1_r; - mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr1_g; - mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr1_b; - } - else - { - mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr2_r; - mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr2_g; - mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr2_b; - }; - }; - }; - }; - }; - - time( &mLastUpdateTime ); - }; - - memcpy( mPixels, mBackgroundPixels, mWidth * mHeight * mDepth ); - - for( int n = 0; n < ENumObjects; ++n ) - { - if ( rand() % 50 == 0 ) - { - mXInc[ n ] = 0; - while ( mXInc[ n ] == 0 ) - mXInc[ n ] = rand() % 7 - 3; - - mYInc[ n ] = 0; - while ( mYInc[ n ] == 0 ) - mYInc[ n ] = rand() % 9 - 4; - }; - - if ( mXpos[ n ] + mXInc[ n ] < 0 || mXpos[ n ] + mXInc[ n ] >= mWidth - mBlockSize[ n ] ) - mXInc[ n ] =- mXInc[ n ]; - - if ( mYpos[ n ] + mYInc[ n ] < 0 || mYpos[ n ] + mYInc[ n ] >= mHeight - mBlockSize[ n ] ) - mYInc[ n ] =- mYInc[ n ]; - - mXpos[ n ] += mXInc[ n ]; - mYpos[ n ] += mYInc[ n ]; - - for( int y = 0; y < mBlockSize[ n ]; ++y ) - { - for( int x = 0; x < mBlockSize[ n ]; ++x ) - { - mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 0 ] = mColorR[ n ]; - mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 1 ] = mColorG[ n ]; - mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 2 ] = mColorB[ n ]; - }; - }; - }; - - setDirty( 0, 0, mWidth, mHeight ); -}; - -//////////////////////////////////////////////////////////////////////////////// -// -bool MediaPluginExample::init() -{ - LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text" ); - message.setValue( "name", "Example Plugin" ); - sendMessage( message ); - - return true; -}; - -//////////////////////////////////////////////////////////////////////////////// -// -int init_media_plugin( LLPluginInstance::sendMessageFunction host_send_func, - void* host_user_data, - LLPluginInstance::sendMessageFunction *plugin_send_func, - void **plugin_user_data ) -{ - MediaPluginExample* self = new MediaPluginExample( host_send_func, host_user_data ); - *plugin_send_func = MediaPluginExample::staticReceiveMessage; - *plugin_user_data = ( void* )self; - - return 0; -} - diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h deleted file mode 100644 index 6bc272c00..000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file llmediaimplgstreamer.h - * @author Tofu Linden - * @brief implementation that supports media playback via GStreamer. - * - * @cond - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - * @endcond - */ - -// header guard -#ifndef llmediaimplgstreamer_h -#define llmediaimplgstreamer_h - -#if LL_GSTREAMER010_ENABLED - -extern "C" { -#include -#include - -#include "apr_pools.h" -#include "apr_dso.h" -} - - -extern "C" { -gboolean llmediaimplgstreamer_bus_callback (GstBus *bus, - GstMessage *message, - gpointer data); -} - -#endif // LL_GSTREAMER010_ENABLED - -#endif // llmediaimplgstreamer_h diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp deleted file mode 100644 index 2e4baaa9e..000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/** - * @file llmediaimplgstreamer_syms.cpp - * @brief dynamic GStreamer symbol-grabbing code - * - * @cond - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - * @endcond - */ - -#if LL_GSTREAMER010_ENABLED - -#include - -extern "C" { -#include - -#include "apr_pools.h" -#include "apr_dso.h" -} - -#include "llmediaimplgstreamertriviallogging.h" - -#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) RTN (*ll##GSTSYM)(__VA_ARGS__) = NULL -#include "llmediaimplgstreamer_syms_raw.inc" -#include "llmediaimplgstreamer_syms_rawv.inc" -#undef LL_GST_SYM - -// a couple of stubs for disgusting reasons -GstDebugCategory* -ll_gst_debug_category_new(gchar *name, guint color, gchar *description) -{ - static GstDebugCategory dummy; - return &dummy; -} -void ll_gst_debug_register_funcptr(GstDebugFuncPtr func, gchar* ptrname) -{ -} - -static bool sSymsGrabbed = false; -static apr_pool_t *sSymGSTDSOMemoryPool = NULL; -static apr_dso_handle_t *sSymGSTDSOHandleG = NULL; -static apr_dso_handle_t *sSymGSTDSOHandleV = NULL; - - -bool grab_gst_syms(std::string gst_dso_name, - std::string gst_dso_name_vid) -{ - if (sSymsGrabbed) - { - // already have grabbed good syms - return TRUE; - } - - bool sym_error = false; - bool rtn = false; - apr_status_t rv; - apr_dso_handle_t *sSymGSTDSOHandle = NULL; - -#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##GSTSYM, sSymGSTDSOHandle, #GSTSYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #GSTSYM); if (REQ) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #GSTSYM, (void*)ll##GSTSYM);}while(0) - - //attempt to load the shared libraries - apr_pool_create(&sSymGSTDSOMemoryPool, NULL); - - if ( APR_SUCCESS == (rv = apr_dso_load(&sSymGSTDSOHandle, - gst_dso_name.c_str(), - sSymGSTDSOMemoryPool) )) - { - INFOMSG("Found DSO: %s", gst_dso_name.c_str()); -#include "llmediaimplgstreamer_syms_raw.inc" - - if ( sSymGSTDSOHandle ) - { - sSymGSTDSOHandleG = sSymGSTDSOHandle; - sSymGSTDSOHandle = NULL; - } - - if ( APR_SUCCESS == - (rv = apr_dso_load(&sSymGSTDSOHandle, - gst_dso_name_vid.c_str(), - sSymGSTDSOMemoryPool) )) - { - INFOMSG("Found DSO: %s", gst_dso_name_vid.c_str()); -#include "llmediaimplgstreamer_syms_rawv.inc" - rtn = !sym_error; - } - else - { - INFOMSG("Couldn't load DSO: %s", gst_dso_name_vid.c_str()); - rtn = false; // failure - } - } - else - { - INFOMSG("Couldn't load DSO: %s", gst_dso_name.c_str()); - rtn = false; // failure - } - - if (sym_error) - { - WARNMSG("Failed to find necessary symbols in GStreamer libraries."); - } - - if ( sSymGSTDSOHandle ) - { - sSymGSTDSOHandleV = sSymGSTDSOHandle; - sSymGSTDSOHandle = NULL; - } -#undef LL_GST_SYM - - sSymsGrabbed = !!rtn; - return rtn; -} - - -void ungrab_gst_syms() -{ - // should be safe to call regardless of whether we've - // actually grabbed syms. - - if ( sSymGSTDSOHandleG ) - { - apr_dso_unload(sSymGSTDSOHandleG); - sSymGSTDSOHandleG = NULL; - } - - if ( sSymGSTDSOHandleV ) - { - apr_dso_unload(sSymGSTDSOHandleV); - sSymGSTDSOHandleV = NULL; - } - - if ( sSymGSTDSOMemoryPool ) - { - apr_pool_destroy(sSymGSTDSOMemoryPool); - sSymGSTDSOMemoryPool = NULL; - } - - // NULL-out all of the symbols we'd grabbed -#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) do{ll##GSTSYM = NULL;}while(0) -#include "llmediaimplgstreamer_syms_raw.inc" -#include "llmediaimplgstreamer_syms_rawv.inc" -#undef LL_GST_SYM - - sSymsGrabbed = false; -} - - -#endif // LL_GSTREAMER010_ENABLED diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h deleted file mode 100644 index d1559089c..000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h +++ /dev/null @@ -1,74 +0,0 @@ -/** - * @file llmediaimplgstreamer_syms.h - * @brief dynamic GStreamer symbol-grabbing code - * - * @cond - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - * @endcond - */ - -#include "linden_common.h" - -#if LL_GSTREAMER010_ENABLED - -extern "C" { -#include -} - -bool grab_gst_syms(std::string gst_dso_name, - std::string gst_dso_name_vid); -void ungrab_gst_syms(); - -#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) extern RTN (*ll##GSTSYM)(__VA_ARGS__) -#include "llmediaimplgstreamer_syms_raw.inc" -#include "llmediaimplgstreamer_syms_rawv.inc" -#undef LL_GST_SYM - -// regrettable hacks to give us better runtime compatibility with older systems -#define llg_return_if_fail(COND) do{if (!(COND)) return;}while(0) -#define llg_return_val_if_fail(COND,V) do{if (!(COND)) return V;}while(0) - -// regrettable hacks because GStreamer was not designed for runtime loading -#undef GST_TYPE_MESSAGE -#define GST_TYPE_MESSAGE (llgst_message_get_type()) -#undef GST_TYPE_OBJECT -#define GST_TYPE_OBJECT (llgst_object_get_type()) -#undef GST_TYPE_PIPELINE -#define GST_TYPE_PIPELINE (llgst_pipeline_get_type()) -#undef GST_TYPE_ELEMENT -#define GST_TYPE_ELEMENT (llgst_element_get_type()) -#undef GST_TYPE_VIDEO_SINK -#define GST_TYPE_VIDEO_SINK (llgst_video_sink_get_type()) -// more regrettable hacks to stub-out these .h-exposed GStreamer internals -void ll_gst_debug_register_funcptr(GstDebugFuncPtr func, gchar* ptrname); -#undef _gst_debug_register_funcptr -#define _gst_debug_register_funcptr ll_gst_debug_register_funcptr -GstDebugCategory* ll_gst_debug_category_new(gchar *name, guint color, gchar *description); -#undef _gst_debug_category_new -#define _gst_debug_category_new ll_gst_debug_category_new -#undef __gst_debug_enabled -#define __gst_debug_enabled (0) - -// more hacks -#define LLGST_MESSAGE_TYPE_NAME(M) (llgst_message_type_get_name(GST_MESSAGE_TYPE(M))) - -#endif // LL_GSTREAMER010_ENABLED diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc deleted file mode 100644 index b33e59363..000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc +++ /dev/null @@ -1,51 +0,0 @@ - -// required symbols to grab -LL_GST_SYM(true, gst_pad_peer_accept_caps, gboolean, GstPad *pad, GstCaps *caps); -LL_GST_SYM(true, gst_buffer_new, GstBuffer*, void); -LL_GST_SYM(true, gst_buffer_set_caps, void, GstBuffer*, GstCaps *); -LL_GST_SYM(true, gst_structure_set_value, void, GstStructure *, const gchar *, const GValue*); -LL_GST_SYM(true, gst_init_check, gboolean, int *argc, char **argv[], GError ** err); -LL_GST_SYM(true, gst_message_get_type, GType, void); -LL_GST_SYM(true, gst_message_type_get_name, const gchar*, GstMessageType type); -LL_GST_SYM(true, gst_message_parse_error, void, GstMessage *message, GError **gerror, gchar **debug); -LL_GST_SYM(true, gst_message_parse_warning, void, GstMessage *message, GError **gerror, gchar **debug); -LL_GST_SYM(true, gst_message_parse_state_changed, void, GstMessage *message, GstState *oldstate, GstState *newstate, GstState *pending); -LL_GST_SYM(true, gst_element_set_state, GstStateChangeReturn, GstElement *element, GstState state); -LL_GST_SYM(true, gst_object_unref, void, gpointer object); -LL_GST_SYM(true, gst_object_get_type, GType, void); -LL_GST_SYM(true, gst_pipeline_get_type, GType, void); -LL_GST_SYM(true, gst_pipeline_get_bus, GstBus*, GstPipeline *pipeline); -LL_GST_SYM(true, gst_bus_add_watch, guint, GstBus * bus, GstBusFunc func, gpointer user_data); -LL_GST_SYM(true, gst_element_factory_make, GstElement*, const gchar *factoryname, const gchar *name); -LL_GST_SYM(true, gst_element_get_type, GType, void); -LL_GST_SYM(true, gst_static_pad_template_get, GstPadTemplate*, GstStaticPadTemplate *pad_template); -LL_GST_SYM(true, gst_element_class_add_pad_template, void, GstElementClass *klass, GstPadTemplate *temp); -LL_GST_SYM(true, gst_element_class_set_details, void, GstElementClass *klass, const GstElementDetails *details); -LL_GST_SYM(true, gst_caps_unref, void, GstCaps* caps); -LL_GST_SYM(true, gst_caps_ref, GstCaps *, GstCaps* caps); -//LL_GST_SYM(true, gst_caps_is_empty, gboolean, const GstCaps *caps); -LL_GST_SYM(true, gst_caps_from_string, GstCaps *, const gchar *string); -LL_GST_SYM(true, gst_caps_replace, void, GstCaps **caps, GstCaps *newcaps); -LL_GST_SYM(true, gst_caps_get_structure, GstStructure *, const GstCaps *caps, guint index); -LL_GST_SYM(true, gst_caps_copy, GstCaps *, const GstCaps * caps); -//LL_GST_SYM(true, gst_caps_intersect, GstCaps *, const GstCaps *caps1, const GstCaps *caps2); -LL_GST_SYM(true, gst_element_register, gboolean, GstPlugin *plugin, const gchar *name, guint rank, GType type); -LL_GST_SYM(true, _gst_plugin_register_static, void, GstPluginDesc *desc); -LL_GST_SYM(true, gst_structure_get_int, gboolean, const GstStructure *structure, const gchar *fieldname, gint *value); -LL_GST_SYM(true, gst_structure_get_value, G_CONST_RETURN GValue *, const GstStructure *structure, const gchar *fieldname); -LL_GST_SYM(true, gst_value_get_fraction_numerator, gint, const GValue *value); -LL_GST_SYM(true, gst_value_get_fraction_denominator, gint, const GValue *value); -LL_GST_SYM(true, gst_structure_get_name, G_CONST_RETURN gchar *, const GstStructure *structure); -LL_GST_SYM(true, gst_element_seek, bool, GstElement *, gdouble, GstFormat, GstSeekFlags, GstSeekType, gint64, GstSeekType, gint64); - -// optional symbols to grab -LL_GST_SYM(false, gst_registry_fork_set_enabled, void, gboolean enabled); -LL_GST_SYM(false, gst_segtrap_set_enabled, void, gboolean enabled); -LL_GST_SYM(false, gst_message_parse_buffering, void, GstMessage *message, gint *percent); -LL_GST_SYM(false, gst_message_parse_info, void, GstMessage *message, GError **gerror, gchar **debug); -LL_GST_SYM(false, gst_element_query_position, gboolean, GstElement *element, GstFormat *format, gint64 *cur); -LL_GST_SYM(false, gst_version, void, guint *major, guint *minor, guint *micro, guint *nano); - -// GStreamer 'internal' symbols which may not be visible in some runtimes but are still used in expanded GStreamer header macros - yuck! We'll substitute our own stubs for these. -//LL_GST_SYM(true, _gst_debug_register_funcptr, void, GstDebugFuncPtr func, gchar* ptrname); -//LL_GST_SYM(true, _gst_debug_category_new, GstDebugCategory *, gchar *name, guint color, gchar *description); diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc deleted file mode 100644 index 14fbcb48b..000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc +++ /dev/null @@ -1,5 +0,0 @@ - -// required symbols to grab -LL_GST_SYM(true, gst_video_sink_get_type, GType, void); - -// optional symbols to grab diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h deleted file mode 100644 index e7b31bec9..000000000 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @file llmediaimplgstreamertriviallogging.h - * @brief minimal logging utilities. - * - * @cond - * $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$ - * @endcond - */ - -#ifndef __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ -#define __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ - -#include - -extern "C" { -#include -#include -} - -///////////////////////////////////////////////////////////////////////// -// Debug/Info/Warning macros. -#define MSGMODULEFOO "(media plugin)" -#define STDERRMSG(...) do{\ - fprintf(stderr, " pid:%d: ", (int)getpid());\ - fprintf(stderr, MSGMODULEFOO " %s:%d: ", __FUNCTION__, __LINE__);\ - fprintf(stderr, __VA_ARGS__);\ - fputc('\n',stderr);\ - }while(0) -#define NULLMSG(...) do{}while(0) - -#define DEBUGMSG NULLMSG -#define INFOMSG STDERRMSG -#define WARNMSG STDERRMSG -///////////////////////////////////////////////////////////////////////// - -#endif /* __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ */ diff --git a/indra/media_plugins/webkit/dummy_volume_catcher.cpp b/indra/media_plugins/webkit/dummy_volume_catcher.cpp deleted file mode 100644 index d54b31b2a..000000000 --- a/indra/media_plugins/webkit/dummy_volume_catcher.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @file dummy_volume_catcher.cpp - * @brief A null implementation of the "VolumeCatcher" class for platforms where it's not implemented yet. - * - * @cond - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - * @endcond - */ - -#include "volume_catcher.h" - - -class VolumeCatcherImpl -{ -}; - -///////////////////////////////////////////////////// - -VolumeCatcher::VolumeCatcher() -{ - pimpl = NULL; -} - -VolumeCatcher::~VolumeCatcher() -{ -} - -void VolumeCatcher::setVolume(F32 volume) -{ -} - -void VolumeCatcher::setPan(F32 pan) -{ -} - -void VolumeCatcher::pump() -{ -} - diff --git a/indra/media_plugins/webkit/volume_catcher.h b/indra/media_plugins/webkit/volume_catcher.h deleted file mode 100644 index 337f2913d..000000000 --- a/indra/media_plugins/webkit/volume_catcher.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file volume_catcher.h - * @brief Interface to a class with platform-specific implementations that allows control of the audio volume of all sources in the current process. - * - * @cond - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - * @endcond - */ - -#ifndef VOLUME_CATCHER_H -#define VOLUME_CATCHER_H - -#include "linden_common.h" - -class VolumeCatcherImpl; - -class VolumeCatcher -{ - public: - VolumeCatcher(); - ~VolumeCatcher(); - - void setVolume(F32 volume); // 0.0 - 1.0 - - // Set the left-right pan of audio sources - // where -1.0 = left, 0 = center, and 1.0 = right - void setPan(F32 pan); - - void pump(); // call this at least a few times a second if you can - it affects how quickly we can 'catch' a new audio source and adjust its volume - - private: - VolumeCatcherImpl *pimpl; -}; - -#endif // VOLUME_CATCHER_H diff --git a/indra/plugins/CMakeLists.txt b/indra/plugins/CMakeLists.txt new file mode 100644 index 000000000..de57249ba --- /dev/null +++ b/indra/plugins/CMakeLists.txt @@ -0,0 +1,17 @@ +# -*- cmake -*- + +add_subdirectory(base_basic) +add_subdirectory(base_media) +add_subdirectory(filepicker) +add_subdirectory(webkit) + +if (LINUX) + add_subdirectory(gstreamer010) +endif (LINUX) + +if (WINDOWS OR DARWIN) + add_subdirectory(quicktime) +endif (WINDOWS OR DARWIN) + +add_subdirectory(example_basic) +add_subdirectory(example_media) diff --git a/indra/plugins/base_basic/CMakeLists.txt b/indra/plugins/base_basic/CMakeLists.txt new file mode 100644 index 000000000..1ff52fda5 --- /dev/null +++ b/indra/plugins/base_basic/CMakeLists.txt @@ -0,0 +1,39 @@ +# -*- cmake -*- + +project(basic_plugin_base) + +include(00-Common) +include(LLCommon) +include(LLPlugin) +include(Linking) +include(PluginAPI) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} +) + +### basic_plugin_base + +if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) + if(WINDOWS) + add_definitions(/FIXED:NO) + else(WINDOWS) # not windows therefore gcc LINUX and DARWIN + add_definitions(-fPIC) + endif(WINDOWS) +endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4) + +set(basic_plugin_base_SOURCE_FILES + basic_plugin_base.cpp +) + +set(basic_plugin_base_HEADER_FILES + CMakeLists.txt + + basic_plugin_base.h +) + +add_library(basic_plugin_base + ${basic_plugin_base_SOURCE_FILES} +) + diff --git a/indra/plugins/base_basic/basic_plugin_base.cpp b/indra/plugins/base_basic/basic_plugin_base.cpp new file mode 100755 index 000000000..90b5964ef --- /dev/null +++ b/indra/plugins/base_basic/basic_plugin_base.cpp @@ -0,0 +1,136 @@ +/** + * @file basic_plugin_base.cpp + * @brief Basic plugin base class for Basic API plugin system + * + * All plugins should be a subclass of BasicPluginBase. + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 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://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +#include "linden_common.h" +#include "basic_plugin_base.h" + + +// TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint + +//////////////////////////////////////////////////////////////////////////////// +/// Basic plugin constructor. +/// +/// @param[in] send_message_function Function for sending messages from plugin to plugin loader shell +/// @param[in] plugin_instance Message data for messages from plugin to plugin loader shell +BasicPluginBase::BasicPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) +{ + mSendMessageFunction = send_message_function; + mPluginInstance = plugin_instance; +} + +/** + * Receive message from plugin loader shell. + * + * @param[in] message_string Message string + * @param[in] user_data Message data + * + */ +void BasicPluginBase::staticReceiveMessage(char const* message_string, BasicPluginBase** self_ptr) +{ + BasicPluginBase* self = *self_ptr; + if(self != NULL) + { + self->receiveMessage(message_string); + + // If the plugin has processed the delete message, delete it. + if(self->mDeleteMe) + { + delete self; + *self_ptr = NULL; + } + } +} + +// This is the SLPlugin process. +// This is the loaded DSO. +// +// Call this function to send 'message' to the viewer. +/** + * Send message to plugin loader shell. + * + * @param[in] message Message data being sent to plugin loader shell + * + */ +void BasicPluginBase::sendMessage(const LLPluginMessage &message) +{ + std::string output = message.generate(); + mSendMessageFunction(output.c_str(), &mPluginInstance); +} + +#if LL_WINDOWS +# define LLSYMEXPORT __declspec(dllexport) +#elif LL_LINUX +# define LLSYMEXPORT __attribute__ ((visibility("default"))) +#else +# define LLSYMEXPORT /**/ +#endif + +extern "C" +{ + LLSYMEXPORT int LLPluginInitEntryPoint(LLPluginInstance::sendMessageFunction send_message_function, + LLPluginInstance* plugin_instance, + LLPluginInstance::receiveMessageFunction* receive_message_function, + BasicPluginBase** plugin_object); +} + +/** + * Plugin initialization and entry point. Establishes communication channel for messages between plugin and plugin loader shell. TODO:DOC - Please check! + * + * @param[in] send_message_function Function for sending messages from plugin to viewer + * @param[in] plugin_instance Message data for messages from plugin to plugin loader shell + * @param[out] receive_message_function Function for receiving message from viewer to plugin + * @param[out] plugin_object Pointer to plugin instance + * + * @return int, where 0=success + * + */ +LLSYMEXPORT int +LLPluginInitEntryPoint(LLPluginInstance::sendMessageFunction send_message_function, + LLPluginInstance* plugin_instance, + LLPluginInstance::receiveMessageFunction* receive_message_function, + BasicPluginBase** plugin_object) +{ + *receive_message_function = BasicPluginBase::staticReceiveMessage; + return create_plugin(send_message_function, plugin_instance, plugin_object); +} + +#ifdef WIN32 +int WINAPI DllEntryPoint( HINSTANCE hInstance, unsigned long reason, void* params ) +{ + return 1; +} +#endif diff --git a/indra/media_plugins/base/media_plugin_base.exp b/indra/plugins/base_basic/basic_plugin_base.exp old mode 100755 new mode 100644 similarity index 100% rename from indra/media_plugins/base/media_plugin_base.exp rename to indra/plugins/base_basic/basic_plugin_base.exp diff --git a/indra/plugins/base_basic/basic_plugin_base.h b/indra/plugins/base_basic/basic_plugin_base.h new file mode 100755 index 000000000..bbabb18b7 --- /dev/null +++ b/indra/plugins/base_basic/basic_plugin_base.h @@ -0,0 +1,88 @@ +/** + * @file basic_plugin_base.h + * @brief Basic plugin base class for Basic API plugin system + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 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://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +#ifndef BASIC_PLUGIN_BASE_H +#define BASIC_PLUGIN_BASE_H + +#include + +#include "linden_common.h" + +#include "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" + +class BasicPluginBase +{ +public: + BasicPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); + //! Basic plugin destructor. + virtual ~BasicPluginBase() {} + + //! Handle received message from plugin loader shell. + virtual void receiveMessage(char const* message_string) = 0; + + // This function is actually called and then calls the member function above. + static void staticReceiveMessage(char const* message_string, BasicPluginBase** self_ptr); + +protected: + void sendMessage(LLPluginMessage const& message); + + //! Message data being sent to plugin loader shell by mSendMessageFunction. + LLPluginInstance* mPluginInstance; + + //! Function to send message from plugin to plugin loader shell. + LLPluginInstance::sendMessageFunction mSendMessageFunction; + + //! Flag to delete plugin instance (self). + bool mDeleteMe; +}; + +/** The plugin must define this function to create its instance. + * It should look something like this: + * @code + * { + * *plugin_object = new FooPluginBar(send_message_function, plugin_instance); + * return 0; + * } + * @endcode + */ +int create_plugin( + LLPluginInstance::sendMessageFunction send_message_function, + LLPluginInstance* plugin_instance, + BasicPluginBase** plugin_object); + +#endif // BASIC_PLUGIN_BASE + diff --git a/indra/media_plugins/base/CMakeLists.txt b/indra/plugins/base_media/CMakeLists.txt similarity index 81% rename from indra/media_plugins/base/CMakeLists.txt rename to indra/plugins/base_media/CMakeLists.txt index 8d620433a..f2c8d7225 100644 --- a/indra/media_plugins/base/CMakeLists.txt +++ b/indra/plugins/base_media/CMakeLists.txt @@ -12,6 +12,7 @@ include(LLWindow) include(Linking) include(PluginAPI) include(FindOpenGL) +include(BasicPluginBase) include_directories( ${LLPLUGIN_INCLUDE_DIRS} @@ -20,9 +21,9 @@ include_directories( ${LLIMAGE_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} + ${BASIC_PLUGIN_BASE_INCLUDE_DIRS} ) - ### media_plugin_base if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) @@ -47,3 +48,11 @@ add_library(media_plugin_base ${media_plugin_base_SOURCE_FILES} ) +target_link_libraries(media_plugin_base + ${BASIC_PLUGIN_BASE_LIBRARIES} +) + +add_dependencies(media_plugin_base + ${BASIC_PLUGIN_BASE_LIBRARIES} +) + diff --git a/indra/media_plugins/base/media_plugin_base.cpp b/indra/plugins/base_media/media_plugin_base.cpp old mode 100644 new mode 100755 similarity index 66% rename from indra/media_plugins/base/media_plugin_base.cpp rename to indra/plugins/base_media/media_plugin_base.cpp index 47acdfd28..102d18996 --- a/indra/media_plugins/base/media_plugin_base.cpp +++ b/indra/plugins/base_media/media_plugin_base.cpp @@ -40,18 +40,15 @@ // TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint + //////////////////////////////////////////////////////////////////////////////// /// Media plugin constructor. /// -/// @param[in] host_send_func Function for sending messages from plugin to plugin loader shell +/// @param[in] send_message_function Function for sending messages from plugin to plugin loader shell /// @param[in] host_user_data Message data for messages from plugin to plugin loader shell - -MediaPluginBase::MediaPluginBase( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data ) +MediaPluginBase::MediaPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) + : BasicPluginBase(send_message_function, plugin_instance) { - mHostSendFunction = host_send_func; - mHostUserData = host_user_data; mDeleteMe = false; mPixels = 0; mWidth = 0; @@ -103,43 +100,6 @@ void MediaPluginBase::setStatus(EStatus status) } } - -/** - * Receive message from plugin loader shell. - * - * @param[in] message_string Message string - * @param[in] user_data Message data - * - */ -void MediaPluginBase::staticReceiveMessage(const char *message_string, void **user_data) -{ - MediaPluginBase *self = (MediaPluginBase*)*user_data; - - if(self != NULL) - { - self->receiveMessage(message_string); - - // If the plugin has processed the delete message, delete it. - if(self->mDeleteMe) - { - delete self; - *user_data = NULL; - } - } -} - -/** - * Send message to plugin loader shell. - * - * @param[in] message Message data being sent to plugin loader shell - * - */ -void MediaPluginBase::sendMessage(const LLPluginMessage &message) -{ - std::string output = message.generate(); - mHostSendFunction(output.c_str(), &mHostUserData); -} - /** * Notifies plugin loader shell that part of display area needs to be redrawn. * @@ -183,28 +143,6 @@ void MediaPluginBase::sendStatus() # define LLSYMEXPORT /**/ #endif -extern "C" -{ - LLSYMEXPORT int LLPluginInitEntryPoint(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data); -} - -/** - * Plugin initialization and entry point. Establishes communication channel for messages between plugin and plugin loader shell. TODO:DOC - Please check! - * - * @param[in] host_send_func Function for sending messages from plugin to plugin loader shell - * @param[in] host_user_data Message data for messages from plugin to plugin loader shell - * @param[out] plugin_send_func Function for plugin to receive messages from plugin loader shell - * @param[out] plugin_user_data Pointer to plugin instance - * - * @return int, where 0=success - * - */ -LLSYMEXPORT int -LLPluginInitEntryPoint(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) -{ - return init_media_plugin(host_send_func, host_user_data, plugin_send_func, plugin_user_data); -} - #ifdef WIN32 int WINAPI DllEntryPoint( HINSTANCE hInstance, unsigned long reason, void* params ) { diff --git a/indra/plugins/base_media/media_plugin_base.exp b/indra/plugins/base_media/media_plugin_base.exp new file mode 100755 index 000000000..d8c7bb712 --- /dev/null +++ b/indra/plugins/base_media/media_plugin_base.exp @@ -0,0 +1,2 @@ +_LLPluginInitEntryPoint + diff --git a/indra/media_plugins/base/media_plugin_base.h b/indra/plugins/base_media/media_plugin_base.h old mode 100644 new mode 100755 similarity index 67% rename from indra/media_plugins/base/media_plugin_base.h rename to indra/plugins/base_media/media_plugin_base.h index efb0629a5..5aebbe1a0 --- a/indra/media_plugins/base/media_plugin_base.h +++ b/indra/plugins/base_media/media_plugin_base.h @@ -33,27 +33,17 @@ * @endcond */ -#include "linden_common.h" +#ifndef MEDIA_PLUGIN_BASE_H +#define MEDIA_PLUGIN_BASE_H -#include "llplugininstance.h" -#include "llpluginmessage.h" -#include "llpluginmessageclasses.h" +#include "basic_plugin_base.h" - -class MediaPluginBase +class MediaPluginBase : public BasicPluginBase { public: - MediaPluginBase(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); - /** Media plugin destructor. */ - virtual ~MediaPluginBase() {} - - /** Handle received message from plugin loader shell. */ - virtual void receiveMessage(const char *message_string) = 0; - - static void staticReceiveMessage(const char *message_string, void **user_data); + MediaPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); protected: - /** Plugin status. */ typedef enum { @@ -76,7 +66,6 @@ protected: size_t mSize; }; - void sendMessage(const LLPluginMessage &message); void sendStatus(); std::string statusString(); void setStatus(EStatus status); @@ -87,13 +76,6 @@ protected: /** Map of shared memory names to shared memory. */ typedef std::map SharedSegmentMap; - - /** Function to send message from plugin to plugin loader shell. */ - LLPluginInstance::sendMessageFunction mHostSendFunction; - /** Message data being sent to plugin loader shell by mHostSendFunction. */ - void *mHostUserData; - /** Flag to delete plugin instance (self). */ - bool mDeleteMe; /** Pixel array to display. TODO:DOC are pixels always 24-bit RGB format, aligned on 32-bit boundary? Also: calling this a pixel array may be misleading since 1 pixel > 1 char. */ unsigned char* mPixels; /** TODO:DOC what's this for -- does a texture have its own piece of shared memory? updated on size_change_request, cleared on shm_remove */ @@ -115,22 +97,4 @@ protected: }; -/** The plugin must define this function to create its instance. - * It should look something like this: - * @code - * { - * MediaPluginFoo *self = new MediaPluginFoo(host_send_func, host_user_data); - * *plugin_send_func = MediaPluginFoo::staticReceiveMessage; - * *plugin_user_data = (void*)self; - * - * return 0; - * } - * @endcode - */ -int init_media_plugin( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data, - LLPluginInstance::sendMessageFunction *plugin_send_func, - void **plugin_user_data); - - +#endif // MEDIA_PLUGIN_BASE_H diff --git a/indra/plugins/example_basic/CMakeLists.txt b/indra/plugins/example_basic/CMakeLists.txt new file mode 100644 index 000000000..fdc71a60c --- /dev/null +++ b/indra/plugins/example_basic/CMakeLists.txt @@ -0,0 +1,68 @@ +# -*- cmake -*- + +project(basic_plugin_example) + +include(00-Common) +include(LLCommon) +include(LLPlugin) +include(Linking) +include(PluginAPI) +include(BasicPluginBase) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${BASIC_PLUGIN_BASE_INCLUDE_DIRS} +) + +### basic_plugin_example + +if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) + if(WINDOWS) + add_definitions(/FIXED:NO) + else(WINDOWS) # not windows therefore gcc LINUX and DARWIN + add_definitions(-fPIC) + endif(WINDOWS) +endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4) + +set(basic_plugin_example_SOURCE_FILES + basic_plugin_example.cpp + ) + +add_library(basic_plugin_example + SHARED + ${basic_plugin_example_SOURCE_FILES} +) + +target_link_libraries(basic_plugin_example + ${LLPLUGIN_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${BASIC_PLUGIN_BASE_LIBRARIES} +) + +add_dependencies(basic_plugin_example + ${LLPLUGIN_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${BASIC_PLUGIN_BASE_LIBRARIES} +) + +if (WINDOWS) + set_target_properties( + basic_plugin_example + PROPERTIES + LINK_FLAGS "/MANIFEST:NO" + ) +endif (WINDOWS) + +if (DARWIN) + # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name + set_target_properties( + basic_plugin_example + PROPERTIES + PREFIX "" + BUILD_WITH_INSTALL_RPATH 1 + INSTALL_NAME_DIR "@executable_path" + LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_basic/basic_plugin_base.exp" + ) + +endif (DARWIN) diff --git a/indra/plugins/example_basic/basic_plugin_example.cpp b/indra/plugins/example_basic/basic_plugin_example.cpp new file mode 100644 index 000000000..a6a255276 --- /dev/null +++ b/indra/plugins/example_basic/basic_plugin_example.cpp @@ -0,0 +1,123 @@ +/** + * @file basic_plugin_example.cpp + * @brief Example Plugin for a basic plugin. + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 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://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +#include "linden_common.h" +#include "basic_plugin_base.h" + +class BasicPluginExample : public BasicPluginBase +{ + public: + BasicPluginExample(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); + ~BasicPluginExample(); + + /*virtual*/ void receiveMessage(char const* message_string); + + private: + bool init(); +}; + +BasicPluginExample::BasicPluginExample(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) : + BasicPluginBase(send_message_function, plugin_instance) +{ +} + +BasicPluginExample::~BasicPluginExample() +{ +} + +void BasicPluginExample::receiveMessage(char const* message_string) +{ + LLPluginMessage message_in; + + if (message_in.parse(message_string) >= 0) + { + std::string message_class = message_in.getClass(); + std::string message_name = message_in.getName(); + + if (message_class == LLPLUGIN_MESSAGE_CLASS_BASE) + { + if (message_name == "init") + { + LLPluginMessage message("base", "init_response"); + LLSD versions = LLSD::emptyMap(); + versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_BASIC] = LLPLUGIN_MESSAGE_CLASS_BASIC_VERSION; + message.setValueLLSD("versions", versions); + + std::string plugin_version = "Basic Plugin Example, version 1.0.0.0"; + message.setValue("plugin_version", plugin_version); + sendMessage(message); + } + else if (message_name == "idle") + { + // This whole message should not have existed imho -- Aleric + } + else + { + std::cerr << "BasicPluginExample::receiveMessage: unknown base message: " << message_name << std::endl; + } + } + else if (message_class == LLPLUGIN_MESSAGE_CLASS_BASIC) + { + if (message_name == "poke") + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_BASIC, "pokeback"); + sendMessage(message); + } + } + else + { + std::cerr << "BasicPluginExample::receiveMessage: unknown message class: " << message_class << std::endl; + } + } +} + +bool BasicPluginExample::init(void) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_BASIC, "name_text"); + message.setValue("name", "Basic Plugin Example"); + sendMessage(message); + + return true; +} + +int create_plugin(LLPluginInstance::sendMessageFunction send_message_function, + LLPluginInstance* plugin_instance, + BasicPluginBase** plugin_object) +{ + *plugin_object = new BasicPluginExample(send_message_function, plugin_instance); + return 0; +} + diff --git a/indra/media_plugins/example/CMakeLists.txt b/indra/plugins/example_media/CMakeLists.txt similarity index 83% rename from indra/media_plugins/example/CMakeLists.txt rename to indra/plugins/example_media/CMakeLists.txt index 54dc5de1e..99859ae8a 100644 --- a/indra/media_plugins/example/CMakeLists.txt +++ b/indra/plugins/example_media/CMakeLists.txt @@ -14,7 +14,7 @@ include(PluginAPI) include(MediaPluginBase) include(FindOpenGL) -include(ExamplePlugin) +#include(ExamplePlugin) include_directories( ${LLPLUGIN_INCLUDE_DIRS} @@ -26,16 +26,15 @@ include_directories( ${LLWINDOW_INCLUDE_DIRS} ) - ### media_plugin_example -if(NOT WORD_SIZE EQUAL 32) - if(WINDOWS) - add_definitions(/FIXED:NO) - else(WINDOWS) # not windows therefore gcc LINUX and DARWIN - add_definitions(-fPIC) - endif(WINDOWS) -endif(NOT WORD_SIZE EQUAL 32) +if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) + if(WINDOWS) + add_definitions(/FIXED:NO) + else(WINDOWS) # not windows therefore gcc LINUX and DARWIN + add_definitions(-fPIC) + endif(WINDOWS) +endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4) set(media_plugin_example_SOURCE_FILES media_plugin_example.cpp @@ -76,7 +75,7 @@ if (DARWIN) PREFIX "" BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "@executable_path" - LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp" + LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp" ) endif (DARWIN) diff --git a/indra/plugins/example_media/media_plugin_example.cpp b/indra/plugins/example_media/media_plugin_example.cpp new file mode 100755 index 000000000..bdb77fb6e --- /dev/null +++ b/indra/plugins/example_media/media_plugin_example.cpp @@ -0,0 +1,490 @@ +/** + * @file media_plugin_example.cpp + * @brief Example plugin for LLMedia API plugin system + * + * @cond + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008-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://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +#include "linden_common.h" + +#include "llgl.h" +#include "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" +#include "media_plugin_base.h" + +#include + +//////////////////////////////////////////////////////////////////////////////// +// +class MediaPluginExample : + public MediaPluginBase +{ + public: + MediaPluginExample(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); + ~MediaPluginExample(); + + /*virtual*/ void receiveMessage( const char* message_string ); + + private: + bool init(); + void update( F64 milliseconds ); + void write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b ); + bool mFirstTime; + + time_t mLastUpdateTime; + enum Constants { ENumObjects = 10 }; + unsigned char* mBackgroundPixels; + int mColorR[ ENumObjects ]; + int mColorG[ ENumObjects ]; + int mColorB[ ENumObjects ]; + int mXpos[ ENumObjects ]; + int mYpos[ ENumObjects ]; + int mXInc[ ENumObjects ]; + int mYInc[ ENumObjects ]; + int mBlockSize[ ENumObjects ]; + bool mMouseButtonDown; + bool mStopAction; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +MediaPluginExample::MediaPluginExample(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) : + MediaPluginBase(send_message_function, plugin_instance) +{ + mFirstTime = true; + mWidth = 0; + mHeight = 0; + mDepth = 4; + mPixels = 0; + mMouseButtonDown = false; + mStopAction = false; + mLastUpdateTime = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +MediaPluginExample::~MediaPluginExample() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginExample::receiveMessage( const char* message_string ) +{ + LLPluginMessage message_in; + + if ( message_in.parse( message_string ) >= 0 ) + { + std::string message_class = message_in.getClass(); + std::string message_name = message_in.getName(); + + if ( message_class == LLPLUGIN_MESSAGE_CLASS_BASE ) + { + if ( message_name == "init" ) + { + LLPluginMessage message( "base", "init_response" ); + LLSD versions = LLSD::emptyMap(); + versions[ LLPLUGIN_MESSAGE_CLASS_BASE ] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; + versions[ LLPLUGIN_MESSAGE_CLASS_MEDIA ] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; + versions[ LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER ] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION; + message.setValueLLSD( "versions", versions ); + + std::string plugin_version = "Example media plugin, Example Version 1.0.0.0"; + message.setValue( "plugin_version", plugin_version ); + sendMessage( message ); + } + else + if ( message_name == "idle" ) + { + // no response is necessary here. + F64 time = message_in.getValueReal( "time" ); + + // Convert time to milliseconds for update() + update( time ); + } + else + if ( message_name == "cleanup" ) + { + // clean up here + } + else + if ( message_name == "shm_added" ) + { + SharedSegmentInfo info; + info.mAddress = message_in.getValuePointer( "address" ); + info.mSize = ( size_t )message_in.getValueS32( "size" ); + std::string name = message_in.getValue( "name" ); + + mSharedSegments.insert( SharedSegmentMap::value_type( name, info ) ); + + } + else + if ( message_name == "shm_remove" ) + { + std::string name = message_in.getValue( "name" ); + + SharedSegmentMap::iterator iter = mSharedSegments.find( name ); + if( iter != mSharedSegments.end() ) + { + if ( mPixels == iter->second.mAddress ) + { + // This is the currently active pixel buffer. + // Make sure we stop drawing to it. + mPixels = NULL; + mTextureSegmentName.clear(); + }; + mSharedSegments.erase( iter ); + } + else + { + //std::cerr << "MediaPluginExample::receiveMessage: unknown shared memory region!" << std::endl; + }; + + // Send the response so it can be cleaned up. + LLPluginMessage message( "base", "shm_remove_response" ); + message.setValue( "name", name ); + sendMessage( message ); + } + else + { + //std::cerr << "MediaPluginExample::receiveMessage: unknown base message: " << message_name << std::endl; + }; + } + else + if ( message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA ) + { + if ( message_name == "init" ) + { + // Plugin gets to decide the texture parameters to use. + LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params" ); + message.setValueS32( "default_width", mWidth ); + message.setValueS32( "default_height", mHeight ); + message.setValueS32( "depth", mDepth ); + message.setValueU32( "internalformat", GL_RGBA ); + message.setValueU32( "format", GL_RGBA ); + message.setValueU32( "type", GL_UNSIGNED_BYTE ); + message.setValueBoolean( "coords_opengl", false ); + sendMessage( message ); + } + else if ( message_name == "size_change" ) + { + std::string name = message_in.getValue( "name" ); + S32 width = message_in.getValueS32( "width" ); + S32 height = message_in.getValueS32( "height" ); + S32 texture_width = message_in.getValueS32( "texture_width" ); + S32 texture_height = message_in.getValueS32( "texture_height" ); + + if ( ! name.empty() ) + { + // Find the shared memory region with this name + SharedSegmentMap::iterator iter = mSharedSegments.find( name ); + if ( iter != mSharedSegments.end() ) + { + mPixels = ( unsigned char* )iter->second.mAddress; + mWidth = width; + mHeight = height; + + mTextureWidth = texture_width; + mTextureHeight = texture_height; + + init(); + }; + }; + + LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response" ); + message.setValue( "name", name ); + message.setValueS32( "width", width ); + message.setValueS32( "height", height ); + message.setValueS32( "texture_width", texture_width ); + message.setValueS32( "texture_height", texture_height ); + sendMessage( message ); + } + else + if ( message_name == "load_uri" ) + { + std::string uri = message_in.getValue( "uri" ); + if ( ! uri.empty() ) + { + }; + } + else + if ( message_name == "mouse_event" ) + { + std::string event = message_in.getValue( "event" ); + S32 button = message_in.getValueS32( "button" ); + + // left mouse button + if ( button == 0 ) + { + int mouse_x = message_in.getValueS32( "x" ); + int mouse_y = message_in.getValueS32( "y" ); + std::string modifiers = message_in.getValue( "modifiers" ); + + if ( event == "move" ) + { + if ( mMouseButtonDown ) + write_pixel( mouse_x, mouse_y, rand() % 0x80 + 0x80, rand() % 0x80 + 0x80, rand() % 0x80 + 0x80 ); + } + else + if ( event == "down" ) + { + mMouseButtonDown = true; + } + else + if ( event == "up" ) + { + mMouseButtonDown = false; + } + else + if ( event == "double_click" ) + { + }; + }; + } + else + if ( message_name == "key_event" ) + { + std::string event = message_in.getValue( "event" ); + S32 key = message_in.getValueS32( "key" ); + std::string modifiers = message_in.getValue( "modifiers" ); + + if ( event == "down" ) + { + if ( key == ' ') + { + mLastUpdateTime = 0; + update( 0.0f ); + }; + }; + } + else + { + //std::cerr << "MediaPluginExample::receiveMessage: unknown media message: " << message_string << std::endl; + }; + } + else + if ( message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER ) + { + if ( message_name == "browse_reload" ) + { + mLastUpdateTime = 0; + mFirstTime = true; + mStopAction = false; + update( 0.0f ); + } + else + if ( message_name == "browse_stop" ) + { + for( int n = 0; n < ENumObjects; ++n ) + mXInc[ n ] = mYInc[ n ] = 0; + + mStopAction = true; + update( 0.0f ); + } + else + { + //std::cerr << "MediaPluginExample::receiveMessage: unknown media_browser message: " << message_string << std::endl; + }; + } + else + { + //std::cerr << "MediaPluginExample::receiveMessage: unknown message class: " << message_class << std::endl; + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginExample::write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b ) +{ + // make sure we don't write outside the buffer + if ( ( x < 0 ) || ( x >= mWidth ) || ( y < 0 ) || ( y >= mHeight ) ) + return; + + if ( mBackgroundPixels != NULL ) + { + unsigned char *pixel = mBackgroundPixels; + pixel += y * mWidth * mDepth; + pixel += ( x * mDepth ); + pixel[ 0 ] = b; + pixel[ 1 ] = g; + pixel[ 2 ] = r; + + setDirty( x, y, x + 1, y + 1 ); + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void MediaPluginExample::update( F64 milliseconds ) +{ + if ( mWidth < 1 || mWidth > 2048 || mHeight < 1 || mHeight > 2048 ) + return; + + if ( mPixels == 0 ) + return; + + if ( mFirstTime ) + { + for( int n = 0; n < ENumObjects; ++n ) + { + mXpos[ n ] = ( mWidth / 2 ) + rand() % ( mWidth / 16 ) - ( mWidth / 32 ); + mYpos[ n ] = ( mHeight / 2 ) + rand() % ( mHeight / 16 ) - ( mHeight / 32 ); + + mColorR[ n ] = rand() % 0x60 + 0x60; + mColorG[ n ] = rand() % 0x60 + 0x60; + mColorB[ n ] = rand() % 0x60 + 0x60; + + mXInc[ n ] = 0; + while ( mXInc[ n ] == 0 ) + mXInc[ n ] = rand() % 7 - 3; + + mYInc[ n ] = 0; + while ( mYInc[ n ] == 0 ) + mYInc[ n ] = rand() % 9 - 4; + + mBlockSize[ n ] = rand() % 0x30 + 0x10; + }; + + delete [] mBackgroundPixels; + + mBackgroundPixels = new unsigned char[ mWidth * mHeight * mDepth ]; + + mFirstTime = false; + }; + + if ( mStopAction ) + return; + + if ( time( NULL ) > mLastUpdateTime + 3 ) + { + const int num_squares = rand() % 20 + 4; + int sqr1_r = rand() % 0x80 + 0x20; + int sqr1_g = rand() % 0x80 + 0x20; + int sqr1_b = rand() % 0x80 + 0x20; + int sqr2_r = rand() % 0x80 + 0x20; + int sqr2_g = rand() % 0x80 + 0x20; + int sqr2_b = rand() % 0x80 + 0x20; + + for ( int y1 = 0; y1 < num_squares; ++y1 ) + { + for ( int x1 = 0; x1 < num_squares; ++x1 ) + { + int px_start = mWidth * x1 / num_squares; + int px_end = ( mWidth * ( x1 + 1 ) ) / num_squares; + int py_start = mHeight * y1 / num_squares; + int py_end = ( mHeight * ( y1 + 1 ) ) / num_squares; + + for( int y2 = py_start; y2 < py_end; ++y2 ) + { + for( int x2 = px_start; x2 < px_end; ++x2 ) + { + int rowspan = mWidth * mDepth; + + if ( ( y1 % 2 ) ^ ( x1 % 2 ) ) + { + mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr1_r; + mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr1_g; + mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr1_b; + } + else + { + mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr2_r; + mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr2_g; + mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr2_b; + }; + }; + }; + }; + }; + + time( &mLastUpdateTime ); + }; + + memcpy( mPixels, mBackgroundPixels, mWidth * mHeight * mDepth ); + + for( int n = 0; n < ENumObjects; ++n ) + { + if ( rand() % 50 == 0 ) + { + mXInc[ n ] = 0; + while ( mXInc[ n ] == 0 ) + mXInc[ n ] = rand() % 7 - 3; + + mYInc[ n ] = 0; + while ( mYInc[ n ] == 0 ) + mYInc[ n ] = rand() % 9 - 4; + }; + + if ( mXpos[ n ] + mXInc[ n ] < 0 || mXpos[ n ] + mXInc[ n ] >= mWidth - mBlockSize[ n ] ) + mXInc[ n ] =- mXInc[ n ]; + + if ( mYpos[ n ] + mYInc[ n ] < 0 || mYpos[ n ] + mYInc[ n ] >= mHeight - mBlockSize[ n ] ) + mYInc[ n ] =- mYInc[ n ]; + + mXpos[ n ] += mXInc[ n ]; + mYpos[ n ] += mYInc[ n ]; + + for( int y = 0; y < mBlockSize[ n ]; ++y ) + { + for( int x = 0; x < mBlockSize[ n ]; ++x ) + { + mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 0 ] = mColorR[ n ]; + mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 1 ] = mColorG[ n ]; + mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 2 ] = mColorB[ n ]; + }; + }; + }; + + setDirty( 0, 0, mWidth, mHeight ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +bool MediaPluginExample::init() +{ + LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text" ); + message.setValue( "name", "Example Plugin" ); + sendMessage( message ); + + return true; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +int create_plugin(LLPluginInstance::sendMessageFunction send_message_function, + LLPluginInstance* plugin_instance, + BasicPluginBase** plugin_object) +{ + *plugin_object = new MediaPluginExample(send_message_function, plugin_instance); + return 0; +} diff --git a/indra/plugins/filepicker/CMakeLists.txt b/indra/plugins/filepicker/CMakeLists.txt new file mode 100644 index 000000000..cea18360a --- /dev/null +++ b/indra/plugins/filepicker/CMakeLists.txt @@ -0,0 +1,81 @@ +# -*- cmake -*- + +project(basic_plugin_filepicker) + +include(00-Common) +include(LLCommon) +include(LLPlugin) +include(Linking) +include(PluginAPI) +include(BasicPluginBase) +include(UI) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${BASIC_PLUGIN_BASE_INCLUDE_DIRS} + ${LLUI_INCLUDE_DIRS} +) + +### basic_plugin_filepicker + +if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) + if(WINDOWS) + add_definitions(/FIXED:NO) + else(WINDOWS) # not windows therefore gcc LINUX and DARWIN + add_definitions(-fPIC) + endif(WINDOWS) +endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4) + +set(basic_plugin_filepicker_SOURCE_FILES + basic_plugin_filepicker.cpp + llfilepicker.cpp + ) + +set(basic_plugin_filepicker_HEADER_FILES + llfilepicker.h + ) + +set_source_files_properties(${basic_plugin_filepicker_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND basic_plugin_filepicker_SOURCE_FILES ${basic_plugin_filepicker_HEADER_FILES}) + +add_library(basic_plugin_filepicker + SHARED + ${basic_plugin_filepicker_SOURCE_FILES} +) + +target_link_libraries(basic_plugin_filepicker + ${LLPLUGIN_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${BASIC_PLUGIN_BASE_LIBRARIES} + ${UI_LIBRARIES} +) + +add_dependencies(basic_plugin_filepicker + ${LLPLUGIN_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${BASIC_PLUGIN_BASE_LIBRARIES} +) + +if (WINDOWS) + set_target_properties( + basic_plugin_filepicker + PROPERTIES + LINK_FLAGS "/MANIFEST:NO" + ) +endif (WINDOWS) + +if (DARWIN) + # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name + set_target_properties( + basic_plugin_filepicker + PROPERTIES + PREFIX "" + BUILD_WITH_INSTALL_RPATH 1 + INSTALL_NAME_DIR "@executable_path" + LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_basic/basic_plugin_base.exp" + ) + +endif (DARWIN) diff --git a/indra/plugins/filepicker/llfilepicker.cpp b/indra/plugins/filepicker/llfilepicker.cpp new file mode 100644 index 000000000..f9a3ab8c1 --- /dev/null +++ b/indra/plugins/filepicker/llfilepicker.cpp @@ -0,0 +1,1414 @@ +/** + * @file llfilepicker.cpp + * @brief OS-specific file picker + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llfilepicker.h" +#include "llpreprocessor.h" +#include "llerror.h" +#include "basic_plugin_base.h" // For PLS_INFOS etc. + +#if LL_SDL +#include "llwindowsdl.h" // for some X/GTK utils to help with filepickers +#endif // LL_SDL + +// Translation map. +typedef std::map translation_map_type; +translation_map_type translation_map; + +// A temporary hack to minimize the number of changes from the original llfilepicker.cpp. +#define LLTrans translation +namespace translation +{ + std::string getString(char const* key) + { + translation_map_type::iterator iter = translation_map.find(key); + return (iter != translation_map.end()) ? iter->second : key; + } + + void add(std::string const& key, std::string const& translation) + { + PLS_INFOS << "Adding translation \"" << key << "\" --> \"" << translation << "\"" << PLS_ENDL; + translation_map[key] = translation; + } +} + +#if LL_GTK +namespace LLWindowSDL { + bool ll_try_gtk_init(void) + { + static BOOL done_gtk_diag = FALSE; + static BOOL gtk_is_good = FALSE; + static BOOL done_setlocale = FALSE; + static BOOL tried_gtk_init = FALSE; + + if (!done_setlocale) + { + PLS_INFOS << "Starting GTK Initialization." << PLS_ENDL; + //maybe_lock_display(); + gtk_disable_setlocale(); + //maybe_unlock_display(); + done_setlocale = TRUE; + } + + if (!tried_gtk_init) + { + tried_gtk_init = TRUE; + if (!g_thread_supported ()) g_thread_init (NULL); + //maybe_lock_display(); + gtk_is_good = gtk_init_check(NULL, NULL); + //maybe_unlock_display(); + if (!gtk_is_good) + PLS_WARNS << "GTK Initialization failed." << PLS_ENDL; + } + if (gtk_is_good && !done_gtk_diag) + { + PLS_INFOS << "GTK Initialized." << PLS_ENDL; + PLS_INFOS << "- Compiled against GTK version " + << GTK_MAJOR_VERSION << "." + << GTK_MINOR_VERSION << "." + << GTK_MICRO_VERSION << PLS_ENDL; + PLS_INFOS << "- Running against GTK version " + << gtk_major_version << "." + << gtk_minor_version << "." + << gtk_micro_version << PLS_ENDL; + //maybe_lock_display(); + const gchar* gtk_warning = gtk_check_version( + GTK_MAJOR_VERSION, + GTK_MINOR_VERSION, + GTK_MICRO_VERSION); + //maybe_unlock_display(); + if (gtk_warning) + { + PLS_WARNS << "- GTK COMPATIBILITY WARNING: " << gtk_warning << PLS_ENDL; + gtk_is_good = FALSE; + } + else + { + PLS_INFOS << "- GTK version is good." << PLS_ENDL; + } + done_gtk_diag = TRUE; + } + return gtk_is_good; + } +} +#endif + +// +// Globals +// + +LLFilePicker LLFilePicker::sInstance; + +#if LL_WINDOWS +#define SOUND_FILTER L"Sounds (*.wav)\0*.wav\0" +#define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.tga;*.bmp;*.jpg;*.jpeg;*.png\0" +#define ANIM_FILTER L"Animations (*.bvh)\0*.bvh\0" +#ifdef _CORY_TESTING +#define GEOMETRY_FILTER L"SL Geometry (*.slg)\0*.slg\0" +#endif +#define XML_FILTER L"XML files (*.xml)\0*.xml\0" +#define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0" +#define RAW_FILTER L"RAW files (*.raw)\0*.raw\0" +#endif + +// +// Implementation +// +LLFilePickerBase::LLFilePickerBase() + : mCurrentFile(0), + mLocked(FALSE) + +{ + reset(); + +#if LL_WINDOWS + mOFN.lStructSize = sizeof(OPENFILENAMEW); + mOFN.hwndOwner = NULL; // Set later with setWindowID + mOFN.hInstance = NULL; + mOFN.lpstrCustomFilter = NULL; + mOFN.nMaxCustFilter = 0; + mOFN.lpstrFile = NULL; // set in open and close + mOFN.nMaxFile = LL_MAX_PATH; + mOFN.lpstrFileTitle = NULL; + mOFN.nMaxFileTitle = 0; + mOFN.lpstrInitialDir = NULL; + mOFN.lpstrTitle = NULL; + mOFN.Flags = 0; // set in open and close + mOFN.nFileOffset = 0; + mOFN.nFileExtension = 0; + mOFN.lpstrDefExt = NULL; + mOFN.lCustData = 0L; + mOFN.lpfnHook = NULL; + mOFN.lpTemplateName = NULL; +#endif + +#if LL_DARWIN + memset(&mNavOptions, 0, sizeof(mNavOptions)); + OSStatus error = NavGetDefaultDialogCreationOptions(&mNavOptions); + if (error == noErr) + { + mNavOptions.modality = kWindowModalityAppModal; + } +#endif +} + +const std::string LLFilePickerBase::getFirstFile() +{ + mCurrentFile = 0; + return getNextFile(); +} + +const std::string LLFilePickerBase::getNextFile() +{ + if (mCurrentFile >= getFileCount()) + { + mLocked = FALSE; + return std::string(); + } + else + { + return mFiles[mCurrentFile++]; + } +} + +const std::string LLFilePickerBase::getCurFile() +{ + if (mCurrentFile >= getFileCount()) + { + mLocked = FALSE; + return std::string(); + } + else + { + return mFiles[mCurrentFile]; + } +} + +void LLFilePickerBase::reset() +{ + mLocked = FALSE; + mFiles.clear(); + mCurrentFile = 0; +} + +#if LL_WINDOWS + +bool LLFilePickerBase::setupFilter(ELoadFilter filter) +{ + bool res = TRUE; + switch (filter) + { + case FFLOAD_ALL: + mOFN.lpstrFilter = L"All Files (*.*)\0*.*\0" \ + SOUND_FILTER \ + IMAGE_FILTER \ + ANIM_FILTER \ + L"\0"; + break; + case FFLOAD_WAV: + mOFN.lpstrFilter = SOUND_FILTER \ + L"\0"; + break; + case FFLOAD_IMAGE: + mOFN.lpstrFilter = IMAGE_FILTER \ + L"\0"; + break; + case FFLOAD_ANIM: + mOFN.lpstrFilter = ANIM_FILTER \ + L"\0"; + break; +#ifdef _CORY_TESTING + case FFLOAD_GEOMETRY: + mOFN.lpstrFilter = GEOMETRY_FILTER \ + L"\0"; + break; +#endif + case FFLOAD_XML: + mOFN.lpstrFilter = XML_FILTER \ + L"\0"; + break; + case FFLOAD_SLOBJECT: + mOFN.lpstrFilter = SLOBJECT_FILTER \ + L"\0"; + break; + case FFLOAD_RAW: + mOFN.lpstrFilter = RAW_FILTER \ + L"\0"; + break; + default: + res = FALSE; + break; + } + return res; +} + +// FIXME: Use folder +bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) +{ + if( mLocked ) + { + return FALSE; + } + bool success = FALSE; + + // don't provide default file selection + mFilesW[0] = '\0'; + + mOFN.lpstrFile = mFilesW; + mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE; + mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR ; + mOFN.nFilterIndex = 1; + + setupFilter(filter); + + // Modal, so pause agent + send_agent_pause(); + + reset(); + + // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!! + success = GetOpenFileName(&mOFN); + if (success) + { + std::string filename = utf16str_to_utf8str(llutf16string(mFilesW)); + mFiles.push_back(filename); + } + send_agent_resume(); + + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + return success; +} + +// FIXME: Use folder +bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) +{ + if( mLocked ) + { + return FALSE; + } + bool success = FALSE; + + // don't provide default file selection + mFilesW[0] = '\0'; + + mOFN.lpstrFile = mFilesW; + mOFN.nFilterIndex = 1; + mOFN.nMaxFile = FILENAME_BUFFER_SIZE; + mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | + OFN_EXPLORER | OFN_ALLOWMULTISELECT; + + setupFilter(filter); + + reset(); + + // Modal, so pause agent + send_agent_pause(); + // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!! + success = GetOpenFileName(&mOFN); // pauses until ok or cancel. + if( success ) + { + // The getopenfilename api doesn't tell us if we got more than + // one file, so we have to test manually by checking string + // lengths. + if( wcslen(mOFN.lpstrFile) > mOFN.nFileOffset ) /*Flawfinder: ignore*/ + { + std::string filename = utf16str_to_utf8str(llutf16string(mFilesW)); + mFiles.push_back(filename); + } + else + { + mLocked = TRUE; + WCHAR* tptrw = mFilesW; + std::string dirname; + while(1) + { + if (*tptrw == 0 && *(tptrw+1) == 0) // double '\0' + break; + if (*tptrw == 0) + tptrw++; // shouldn't happen? + std::string filename = utf16str_to_utf8str(llutf16string(tptrw)); + if (dirname.empty()) + dirname = filename + "\\"; + else + mFiles.push_back(dirname + filename); + tptrw += filename.size(); + } + } + } + send_agent_resume(); + + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + return success; +} + +// FIXME: Use folder +bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) +{ + if( mLocked ) + { + return FALSE; + } + bool success = FALSE; + + mOFN.lpstrFile = mFilesW; + if (!filename.empty()) + { + llutf16string tstring = utf8str_to_utf16str(filename); + wcsncpy(mFilesW, tstring.c_str(), FILENAME_BUFFER_SIZE); } /*Flawfinder: ignore*/ + else + { + mFilesW[0] = '\0'; + } + + switch( filter ) + { + case FFSAVE_ALL: + mOFN.lpstrDefExt = NULL; + mOFN.lpstrFilter = + L"All Files (*.*)\0*.*\0" \ + L"WAV Sounds (*.wav)\0*.wav\0" \ + L"Targa, Bitmap Images (*.tga; *.bmp)\0*.tga;*.bmp\0" \ + L"\0"; + break; + case FFSAVE_LSL: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.lsl", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + mOFN.lpstrDefExt = L"lsl"; + mOFN.lpstrFilter = + L"LSL Files (*.lsl)\0*.lsl\0" + L"Text files (*.txt)\0*.txt\0" + L"\0"; + break; + case FFSAVE_TEXT: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.txt", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + mOFN.lpstrDefExt = L"txt"; + mOFN.lpstrFilter = + L"Text files (*.txt)\0*.txt\0" + L"RTF Files (*.rtf)\0*.rtf\0" + L"\0"; + break; + case FFSAVE_WAV: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.wav", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + mOFN.lpstrDefExt = L"wav"; + mOFN.lpstrFilter = + L"WAV Sounds (*.wav)\0*.wav\0" \ + L"\0"; + break; + case FFSAVE_TGA: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.tga", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + mOFN.lpstrDefExt = L"tga"; + mOFN.lpstrFilter = + L"Targa Images (*.tga)\0*.tga\0" \ + L"\0"; + break; + case FFSAVE_BMP: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.bmp", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + mOFN.lpstrDefExt = L"bmp"; + mOFN.lpstrFilter = + L"Bitmap Images (*.bmp)\0*.bmp\0" \ + L"\0"; + break; + case FFSAVE_PNG: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.png", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + mOFN.lpstrDefExt = L"png"; + mOFN.lpstrFilter = + L"PNG Images (*.png)\0*.png\0" \ + L"\0"; + break; + case FFSAVE_JPEG: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.jpeg", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + mOFN.lpstrDefExt = L"jpeg"; + mOFN.lpstrFilter = + L"JPEG Images (*.jpeg)\0*.jpeg\0" \ + L"\0"; + break; + case FFSAVE_AVI: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.avi", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + mOFN.lpstrDefExt = L"avi"; + mOFN.lpstrFilter = + L"AVI Movie File (*.avi)\0*.avi\0" \ + L"\0"; + break; + case FFSAVE_ANIM: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.xaf", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + mOFN.lpstrDefExt = L"xaf"; + mOFN.lpstrFilter = + L"XAF Anim File (*.xaf)\0*.xaf\0" \ + L"\0"; + break; +#ifdef _CORY_TESTING + case FFSAVE_GEOMETRY: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.slg", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + mOFN.lpstrDefExt = L"slg"; + mOFN.lpstrFilter = + L"SLG SL Geometry File (*.slg)\0*.slg\0" \ + L"\0"; + break; +#endif + case FFSAVE_XML: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.xml", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + + mOFN.lpstrDefExt = L"xml"; + mOFN.lpstrFilter = + L"XML File (*.xml)\0*.xml\0" \ + L"\0"; + break; + case FFSAVE_COLLADA: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.collada", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + mOFN.lpstrDefExt = L"collada"; + mOFN.lpstrFilter = + L"COLLADA File (*.collada)\0*.collada\0" \ + L"\0"; + break; + case FFSAVE_RAW: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.raw", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + } + mOFN.lpstrDefExt = L"raw"; + mOFN.lpstrFilter = RAW_FILTER \ + L"\0"; + break; + case FFSAVE_J2C: + if (filename.empty()) + { + wcsncpy( mFilesW,L"untitled.j2c", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"j2c"; + mOFN.lpstrFilter = + L"Compressed Images (*.j2c)\0*.j2c\0" \ + L"\0"; + break; + default: + return FALSE; + } + + + mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE; + mOFN.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST; + + reset(); + + // Modal, so pause agent + send_agent_pause(); + { + // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!! + success = GetSaveFileName(&mOFN); + if (success) + { + std::string filename = utf16str_to_utf8str(llutf16string(mFilesW)); + mFiles.push_back(filename); + } + gKeyboard->resetKeys(); + } + send_agent_resume(); + + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + return success; +} + +#elif LL_DARWIN + +Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode) +{ + Boolean result = true; + ELoadFilter filter = *((ELoadFilter*) callBackUD); + OSStatus error = noErr; + + if (filterMode == kNavFilteringBrowserList && filter != FFLOAD_ALL && (theItem->descriptorType == typeFSRef || theItem->descriptorType == typeFSS)) + { + // navInfo is only valid for typeFSRef and typeFSS + NavFileOrFolderInfo *navInfo = (NavFileOrFolderInfo*) info; + if (!navInfo->isFolder) + { + AEDesc desc; + error = AECoerceDesc(theItem, typeFSRef, &desc); + if (error == noErr) + { + FSRef fileRef; + error = AEGetDescData(&desc, &fileRef, sizeof(fileRef)); + if (error == noErr) + { + LSItemInfoRecord fileInfo; + error = LSCopyItemInfoForRef(&fileRef, kLSRequestExtension | kLSRequestTypeCreator, &fileInfo); + if (error == noErr) + { + if (filter == FFLOAD_IMAGE) + { + if (fileInfo.filetype != 'JPEG' && fileInfo.filetype != 'JPG ' && + fileInfo.filetype != 'BMP ' && fileInfo.filetype != 'TGA ' && + fileInfo.filetype != 'TIFF' && fileInfo.filetype != 'PSD ' && + fileInfo.filetype != 'BMPf' && fileInfo.filetype != 'TPIC' && + fileInfo.filetype != 'PNG ' && + (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("jpeg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && + CFStringCompare(fileInfo.extension, CFSTR("jpg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && + CFStringCompare(fileInfo.extension, CFSTR("bmp"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && + CFStringCompare(fileInfo.extension, CFSTR("tga"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && + CFStringCompare(fileInfo.extension, CFSTR("psd"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && + CFStringCompare(fileInfo.extension, CFSTR("tiff"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && + CFStringCompare(fileInfo.extension, CFSTR("tif"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && + CFStringCompare(fileInfo.extension, CFSTR("png"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) + ) + { + result = false; + } + } + else if (filter == FFLOAD_WAV) + { + if (fileInfo.filetype != 'WAVE' && fileInfo.filetype != 'WAV ' && + (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("wave"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && + CFStringCompare(fileInfo.extension, CFSTR("wav"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) + ) + { + result = false; + } + } + else if (filter == FFLOAD_ANIM) + { + if (fileInfo.filetype != 'BVH ' && + (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("bvh"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) + ) + { + result = false; + } + } +#ifdef _CORY_TESTING + else if (filter == FFLOAD_GEOMETRY) + { + if (fileInfo.filetype != 'SLG ' && + (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("slg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) + ) + { + result = false; + } + } +#endif + else if (filter == FFLOAD_SLOBJECT) + { + PLS_WARNS << "*** navOpenFilterProc: FFLOAD_SLOBJECT NOT IMPLEMENTED ***" << PLS_ENDL; + } + else if (filter == FFLOAD_RAW) + { + if (fileInfo.filetype != '\?\?\?\?' && + (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("raw"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) + ) + { + result = false; + } + } + + if (fileInfo.extension) + { + CFRelease(fileInfo.extension); + } + } + } + AEDisposeDesc(&desc); + } + } + } + return result; +} + +OSStatus LLFilePickerBase::doNavChooseDialog(ELoadFilter filter) +{ + OSStatus error = noErr; + NavDialogRef navRef = NULL; + NavReplyRecord navReply; + + memset(&navReply, 0, sizeof(navReply)); + + // NOTE: we are passing the address of a local variable here. + // This is fine, because the object this call creates will exist for less than the lifetime of this function. + // (It is destroyed by NavDialogDispose() below.) + error = NavCreateChooseFileDialog(&mNavOptions, NULL, NULL, NULL, navOpenFilterProc, (void*)(&filter), &navRef); + + //gViewerWindow->mWindow->beforeDialog(); + + if (error == noErr) + error = NavDialogRun(navRef); + + //gViewerWindow->mWindow->afterDialog(); + + if (error == noErr) + error = NavDialogGetReply(navRef, &navReply); + + if (navRef) + NavDialogDispose(navRef); + + if (error == noErr && navReply.validRecord) + { + SInt32 count = 0; + SInt32 index; + + // AE indexes are 1 based... + error = AECountItems(&navReply.selection, &count); + for (index = 1; index <= count; index++) + { + FSRef fsRef; + AEKeyword theAEKeyword; + DescType typeCode; + Size actualSize = 0; + char path[MAX_PATH]; /*Flawfinder: ignore*/ + + memset(&fsRef, 0, sizeof(fsRef)); + error = AEGetNthPtr(&navReply.selection, index, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize); + + if (error == noErr) + error = FSRefMakePath(&fsRef, (UInt8*) path, sizeof(path)); + + if (error == noErr) + mFiles.push_back(std::string(path)); + } + } + + return error; +} + +OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string& filename) +{ + OSStatus error = noErr; + NavDialogRef navRef = NULL; + NavReplyRecord navReply; + + memset(&navReply, 0, sizeof(navReply)); + + // Setup the type, creator, and extension + OSType type, creator; + CFStringRef extension = NULL; + switch (filter) + { + case FFSAVE_WAV: + type = 'WAVE'; + creator = 'TVOD'; + extension = CFSTR(".wav"); + break; + + case FFSAVE_TGA: + type = 'TPIC'; + creator = 'prvw'; + extension = CFSTR(".tga"); + break; + + case FFSAVE_BMP: + type = 'BMPf'; + creator = 'prvw'; + extension = CFSTR(".bmp"); + break; + case FFSAVE_JPEG: + type = 'JPEG'; + creator = 'prvw'; + extension = CFSTR(".jpeg"); + break; + case FFSAVE_PNG: + type = 'PNG '; + creator = 'prvw'; + extension = CFSTR(".png"); + break; + case FFSAVE_AVI: + type = '\?\?\?\?'; + creator = '\?\?\?\?'; + extension = CFSTR(".mov"); + break; + + case FFSAVE_ANIM: + type = '\?\?\?\?'; + creator = '\?\?\?\?'; + extension = CFSTR(".xaf"); + break; + +#ifdef _CORY_TESTING + case FFSAVE_GEOMETRY: + type = '\?\?\?\?'; + creator = '\?\?\?\?'; + extension = CFSTR(".slg"); + break; +#endif + case FFSAVE_RAW: + type = '\?\?\?\?'; + creator = '\?\?\?\?'; + extension = CFSTR(".raw"); + break; + + case FFSAVE_J2C: + type = '\?\?\?\?'; + creator = 'prvw'; + extension = CFSTR(".j2c"); + break; + + case FFSAVE_ALL: + default: + type = '\?\?\?\?'; + creator = '\?\?\?\?'; + extension = CFSTR(""); + break; + } + + // Create the dialog + error = NavCreatePutFileDialog(&mNavOptions, type, creator, NULL, NULL, &navRef); + if (error == noErr) + { + CFStringRef nameString = NULL; + bool hasExtension = true; + + // Create a CFString of the initial file name + if (!filename.empty()) + nameString = CFStringCreateWithCString(NULL, filename.c_str(), kCFStringEncodingUTF8); + else + nameString = CFSTR("Untitled"); + + // Add the extension if one was not provided + if (nameString && !CFStringHasSuffix(nameString, extension)) + { + CFStringRef tempString = nameString; + hasExtension = false; + nameString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), tempString, extension); + CFRelease(tempString); + } + + // Set the name in the dialog + if (nameString) + { + error = NavDialogSetSaveFileName(navRef, nameString); + CFRelease(nameString); + } + else + { + error = paramErr; + } + } + + //gViewerWindow->mWindow->beforeDialog(); + + // Run the dialog + if (error == noErr) + error = NavDialogRun(navRef); + + //gViewerWindow->mWindow->afterDialog(); + + if (error == noErr) + error = NavDialogGetReply(navRef, &navReply); + + if (navRef) + NavDialogDispose(navRef); + + if (error == noErr && navReply.validRecord) + { + SInt32 count = 0; + + // AE indexes are 1 based... + error = AECountItems(&navReply.selection, &count); + if (count > 0) + { + // Get the FSRef to the containing folder + FSRef fsRef; + AEKeyword theAEKeyword; + DescType typeCode; + Size actualSize = 0; + + memset(&fsRef, 0, sizeof(fsRef)); + error = AEGetNthPtr(&navReply.selection, 1, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize); + + if (error == noErr) + { + char path[PATH_MAX]; /*Flawfinder: ignore*/ + char newFileName[SINGLE_FILENAME_BUFFER_SIZE]; /*Flawfinder: ignore*/ + + error = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX); + if (error == noErr) + { + if (CFStringGetCString(navReply.saveFileName, newFileName, sizeof(newFileName), kCFStringEncodingUTF8)) + { + mFiles.push_back(std::string(path) + "/" + std::string(newFileName)); + } + else + { + error = paramErr; + } + } + else + { + error = paramErr; + } + } + } + } + + return error; +} + +// FIXME: Use folder +bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) +{ + if( mLocked ) + return FALSE; + + bool success = FALSE; + + OSStatus error = noErr; + + reset(); + + mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; + // Modal, so pause agent + send_agent_pause(); + { + error = doNavChooseDialog(filter); + } + send_agent_resume(); + if (error == noErr) + { + if (getFileCount()) + success = true; + } + + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + return success; +} + +// FIXME: Use folder +bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) +{ + if( mLocked ) + return FALSE; + + bool success = FALSE; + + OSStatus error = noErr; + + reset(); + + mNavOptions.optionFlags |= kNavAllowMultipleFiles; + // Modal, so pause agent + send_agent_pause(); + { + error = doNavChooseDialog(filter); + } + send_agent_resume(); + if (error == noErr) + { + if (getFileCount()) + success = true; + if (getFileCount() > 1) + mLocked = TRUE; + } + + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + return success; +} + +// FIXME: Use folder +bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) +{ + if( mLocked ) + return FALSE; + bool success = FALSE; + OSStatus error = noErr; + + reset(); + + mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; + + // Modal, so pause agent + send_agent_pause(); + { + error = doNavSaveDialog(filter, filename); + } + send_agent_resume(); + if (error == noErr) + { + if (getFileCount()) + success = true; + } + + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + return success; +} + +#elif LL_LINUX || LL_SOLARIS + +# if LL_GTK + +// static +void LLFilePickerBase::add_to_selectedfiles(gpointer data, gpointer user_data) +{ + // We need to run g_filename_to_utf8 in the user's locale + std::string saved_locale(setlocale(LC_ALL, NULL)); + setlocale(LC_ALL, ""); + + LLFilePickerBase* picker = (LLFilePickerBase*) user_data; + GError *error = NULL; +// gchar* filename_utf8 = g_filename_to_utf8((gchar*)data, +// -1, NULL, NULL, &error); + gchar* filename_utf8 = g_filename_display_name ((gchar*) data); + if (error) + { + // *FIXME. + // This condition should really be notified to the user, e.g. + // through a message box. Just logging it is inappropriate. + + // Ghhhh. g_filename_display_name is new to glib 2.6, and it + // is too new for SL! (Note that the latest glib as of this + // writing is 2.22. *sigh*) LL supplied *makeASCII family are + // also unsuitable since they allow control characters... + + // muhahaha ... Imprudence can ! + + + PLS_WARNS << "g_filename_display_name failed on" << filename_utf8 << ": "<< error->message << PLS_ENDL; + } + + if (filename_utf8) + { + picker->mFiles.push_back(std::string(filename_utf8)); + PLS_DEBUGS << "ADDED FILE " << filename_utf8 << PLS_ENDL; + g_free(filename_utf8); + } + + setlocale(LC_ALL, saved_locale.c_str()); +} + +// static +void LLFilePickerBase::chooser_responder(GtkWidget *widget, gint response, gpointer user_data) +{ + LLFilePicker* picker = (LLFilePicker*)user_data; + + PLS_DEBUGS << "GTK DIALOG RESPONSE " << response << PLS_ENDL; + + if (response == GTK_RESPONSE_ACCEPT) + { + GSList *file_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget)); + g_slist_foreach(file_list, (GFunc)add_to_selectedfiles, user_data); + g_slist_foreach(file_list, (GFunc)g_free, NULL); + g_slist_free (file_list); + } + + gtk_widget_destroy(widget); + gtk_main_quit(); +} + + +GtkWindow* LLFilePickerBase::buildFilePicker(bool is_save, bool is_folder, std::string const& folder) +{ + if (LLWindowSDL::ll_try_gtk_init()) + { + GtkWidget *win = NULL; + GtkFileChooserAction pickertype = + is_save? + (is_folder? + GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER : + GTK_FILE_CHOOSER_ACTION_SAVE) : + (is_folder? + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER : + GTK_FILE_CHOOSER_ACTION_OPEN); + + win = gtk_file_chooser_dialog_new(NULL, NULL, + pickertype, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + is_folder ? + GTK_STOCK_APPLY : + (is_save ? + GTK_STOCK_SAVE : + GTK_STOCK_OPEN), + GTK_RESPONSE_ACCEPT, + (gchar *)NULL); + + if (!folder.empty()) + { + gtk_file_chooser_set_current_folder + (GTK_FILE_CHOOSER(win), + folder.c_str()); + } + +# if LL_X11 + // Make GTK tell the window manager to associate this + // dialog with our non-GTK raw X11 window, which should try + // to keep it on top etc. + if (mX11WindowID != None) + { + gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin + GdkWindow *gdkwin = gdk_window_foreign_new(mX11WindowID); + gdk_window_set_transient_for(GTK_WIDGET(win)->window, gdkwin); + } +# endif //LL_X11 + + g_signal_connect (GTK_FILE_CHOOSER(win), + "response", + G_CALLBACK(LLFilePickerBase::chooser_responder), + this); + + gtk_window_set_modal(GTK_WINDOW(win), TRUE); + + /* GTK 2.6: if (is_folder) + gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(win), + TRUE); */ + + return GTK_WINDOW(win); + } + else + { + return NULL; + } +} + +static void add_common_filters_to_gtkchooser(GtkFileFilter *gfilter, + GtkWindow *picker, + std::string filtername) +{ + gtk_file_filter_set_name(gfilter, filtername.c_str()); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), + gfilter); + GtkFileFilter *allfilter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(allfilter, "*"); + gtk_file_filter_set_name(allfilter, LLTrans::getString("all_files").c_str()); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), allfilter); + gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(picker), gfilter); +} + +static std::string add_simple_pattern_filter_to_gtkchooser(GtkWindow *picker, + std::string pattern, + std::string filtername) +{ + GtkFileFilter *gfilter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(gfilter, pattern.c_str()); + add_common_filters_to_gtkchooser(gfilter, picker, filtername); + return filtername; +} + +static std::string add_simple_mime_filter_to_gtkchooser(GtkWindow *picker, + std::string mime, + std::string filtername) +{ + GtkFileFilter *gfilter = gtk_file_filter_new(); + gtk_file_filter_add_mime_type(gfilter, mime.c_str()); + add_common_filters_to_gtkchooser(gfilter, picker, filtername); + return filtername; +} + +static std::string add_wav_filter_to_gtkchooser(GtkWindow *picker) +{ + GtkFileFilter *gfilter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(gfilter, "*.wav"); + gtk_file_filter_add_mime_type(gfilter,"audio/x-wav");//not working + + std::string filtername = LLTrans::getString("sound_files") + " (*.wav)"; + add_common_filters_to_gtkchooser(gfilter, picker, filtername); + return filtername; +} + +static std::string add_bvh_filter_to_gtkchooser(GtkWindow *picker) +{ + return add_simple_pattern_filter_to_gtkchooser(picker, "*.bvh", + LLTrans::getString("animation_files") + " (*.bvh)"); +} + +static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker) +{ + GtkFileFilter *gfilter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(gfilter, "*.tga"); + gtk_file_filter_add_mime_type(gfilter, "image/jpeg"); + gtk_file_filter_add_mime_type(gfilter, "image/png"); + gtk_file_filter_add_mime_type(gfilter, "image/bmp"); + std::string filtername = LLTrans::getString("image_files") + " (*.tga; *.bmp; *.jpg; *.png)"; + add_common_filters_to_gtkchooser(gfilter, picker, filtername); + return filtername; +} + + +bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) +{ + bool rtn = FALSE; + + //gViewerWindow->mWindow->beforeDialog(); + + reset(); + + GtkWindow* picker = buildFilePicker(true, false, folder); + + if (picker) + { + std::string suggest_name = "untitled"; + std::string suggest_ext = ""; + std::string caption = LLTrans::getString("save_file_verb") + " "; + switch (filter) + { + case FFSAVE_WAV: + caption += add_wav_filter_to_gtkchooser(picker); + suggest_ext = ".wav"; + break; + case FFSAVE_TGA: + caption += add_simple_pattern_filter_to_gtkchooser + (picker, "*.tga", LLTrans::getString("targa_image_files") + " (*.tga)"); + suggest_ext = ".tga"; + break; + case FFSAVE_BMP: + caption += add_simple_mime_filter_to_gtkchooser + (picker, "image/bmp", LLTrans::getString("bitmap_image_files") + " (*.bmp)"); + suggest_ext = ".bmp"; + break; + case FFSAVE_AVI: + caption += add_simple_mime_filter_to_gtkchooser + (picker, "video/x-msvideo", + LLTrans::getString("avi_movie_file") + " (*.avi)"); + suggest_ext = ".avi"; + break; + case FFSAVE_ANIM: + caption += add_simple_pattern_filter_to_gtkchooser + (picker, "*.xaf", LLTrans::getString("xaf_animation_file") + " (*.xaf)"); + suggest_ext = ".xaf"; + break; + case FFSAVE_XML: + caption += add_simple_pattern_filter_to_gtkchooser + (picker, "*.xml", LLTrans::getString("xml_file") + " (*.xml)"); + suggest_ext = ".xml"; + break; + case FFSAVE_RAW: + caption += add_simple_pattern_filter_to_gtkchooser + (picker, "*.raw", LLTrans::getString("raw_file") + " (*.raw)"); + suggest_ext = ".raw"; + break; + case FFSAVE_J2C: + caption += add_simple_mime_filter_to_gtkchooser + (picker, "images/jp2", + LLTrans::getString("compressed_image_files") + " (*.j2c)"); + suggest_ext = ".j2c"; + break; + default:; + break; + } + + gtk_window_set_title(GTK_WINDOW(picker), caption.c_str()); + + if (filename.empty()) + { + suggest_name += suggest_ext; + + gtk_file_chooser_set_current_name + (GTK_FILE_CHOOSER(picker), + suggest_name.c_str()); + } + else + { + gtk_file_chooser_set_current_name + (GTK_FILE_CHOOSER(picker), filename.c_str()); + } + + gtk_widget_show_all(GTK_WIDGET(picker)); + gtk_main(); + + rtn = (getFileCount() == 1); + } + + //gViewerWindow->mWindow->afterDialog(); + + return rtn; +} + +bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) +{ + bool rtn = FALSE; + + //gViewerWindow->mWindow->beforeDialog(); + + reset(); + + GtkWindow* picker = buildFilePicker(false, false, folder); + + if (picker) + { + std::string caption = LLTrans::getString("load_file_verb") + " "; + std::string filtername = ""; + switch (filter) + { + case FFLOAD_WAV: + filtername = add_wav_filter_to_gtkchooser(picker); + break; + case FFLOAD_ANIM: + filtername = add_bvh_filter_to_gtkchooser(picker); + break; + case FFLOAD_IMAGE: + filtername = add_imageload_filter_to_gtkchooser(picker); + break; + default:; + break; + } + + caption += filtername; + + gtk_window_set_title(GTK_WINDOW(picker), caption.c_str()); + + gtk_widget_show_all(GTK_WIDGET(picker)); + gtk_main(); + + rtn = (getFileCount() == 1); + } + + //gViewerWindow->mWindow->afterDialog(); + + return rtn; +} + +bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) +{ + bool rtn = FALSE; + + //gViewerWindow->mWindow->beforeDialog(); + + reset(); + + GtkWindow* picker = buildFilePicker(false, false, folder); + + if (picker) + { + gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(picker), + TRUE); + + gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("load_files").c_str()); + + gtk_widget_show_all(GTK_WIDGET(picker)); + gtk_main(); + rtn = !mFiles.empty(); + } + + //gViewerWindow->mWindow->afterDialog(); + + return rtn; +} + +# else // LL_GTK + +// Hacky stubs designed to facilitate fake getSaveFile and getLoadFile with +// static results, when we don't have a real filepicker. + +bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) +{ + reset(); + + PLS_INFOS << "getSaveFile suggested filename is [" << filename << "]" << PLS_ENDL; + if (!filename.empty()) + { + mFiles.push_back(gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + filename); + return TRUE; + } + return FALSE; +} + +// FIXME: Use folder +bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) +{ + reset(); + + // HACK: Static filenames for 'open' until we implement filepicker + std::string filename = gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + "upload"; + switch (filter) + { + case FFLOAD_WAV: filename += ".wav"; break; + case FFLOAD_IMAGE: filename += ".tga"; break; + case FFLOAD_ANIM: filename += ".bvh"; break; + default: break; + } + mFiles.push_back(filename); + PLS_INFOS << "getLoadFile: Will try to open file: " << hackyfilename << PLS_ENDL; + return TRUE; +} + +bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) +{ + reset(); + return FALSE; +} + +#endif // LL_GTK + +#else // not implemented + +bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) +{ + reset(); + return FALSE; +} + +bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) +{ + reset(); + return FALSE; +} + +bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) +{ + reset(); + return FALSE; +} + +#endif diff --git a/indra/plugins/filepicker/llfilepicker.h b/indra/plugins/filepicker/llfilepicker.h new file mode 100644 index 000000000..d14612112 --- /dev/null +++ b/indra/plugins/filepicker/llfilepicker.h @@ -0,0 +1,231 @@ +/** + * @file llfilepicker.h + * @brief OS-specific file picker + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +// OS specific file selection dialog. This is implemented as a +// singleton class, so call the instance() method to get the working +// instance. When you call getMultipleLoadFile(), it locks the picker +// until you iterate to the end of the list of selected files with +// getNextFile() or call reset(). + +#ifndef LL_LLFILEPICKER_H +#define LL_LLFILEPICKER_H + +#include "stdtypes.h" +#include +#include +#include + +#if LL_DARWIN +#include + +// AssertMacros.h does bad things. +#undef verify +#undef check +#undef require + +#include "llstring.h" + +#endif + +// Need commdlg.h for OPENFILENAMEA +#ifdef LL_WINDOWS +#include +#endif + +// mostly for Linux, possible on others +#if LL_GTK +# include "gtk/gtk.h" +#endif // LL_GTK + +// also mostly for Linux, for some X11-specific filepicker usability tweaks +#if LL_X11 +#include "SDL/SDL_syswm.h" +#endif + +// This class is used as base class of a singleton and is therefore not +// allowed to have any static members or static local variables! +class LLFilePickerBase +{ +public: + enum ELoadFilter + { + FFLOAD_ALL = 1, + FFLOAD_WAV = 2, + FFLOAD_IMAGE = 3, + FFLOAD_ANIM = 4, +#ifdef _CORY_TESTING + FFLOAD_GEOMETRY = 5, +#endif + FFLOAD_XML = 6, + FFLOAD_SLOBJECT = 7, + FFLOAD_RAW = 8, + FFLOAD_TEXT = 9, + }; + + enum ESaveFilter + { + FFSAVE_ALL = 1, + FFSAVE_WAV = 3, + FFSAVE_TGA = 4, + FFSAVE_BMP = 5, + FFSAVE_AVI = 6, + FFSAVE_ANIM = 7, +#ifdef _CORY_TESTING + FFSAVE_GEOMETRY = 8, +#endif + FFSAVE_XML = 9, + FFSAVE_COLLADA = 10, + FFSAVE_RAW = 11, + FFSAVE_J2C = 12, + FFSAVE_PNG = 13, + FFSAVE_JPEG = 14, + FFSAVE_HPA = 15, + FFSAVE_TEXT = 16, + FFSAVE_LSL = 17 + }; + + // open the dialog. This is a modal operation + bool getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder); + bool getLoadFile(ELoadFilter filter, std::string const& folder); + bool getMultipleLoadFiles(ELoadFilter filter, std::string const& folder); + + // Get the filename(s) found. getFirstFile() sets the pointer to + // the start of the structure and allows the start of iteration. + const std::string getFirstFile(); + + // getNextFile() increments the internal representation and + // returns the next file specified by the user. Returns NULL when + // no more files are left. Further calls to getNextFile() are + // undefined. + const std::string getNextFile(); + + // This utility function extracts the current file name without + // doing any incrementing. + const std::string getCurFile(); + + // Returns the index of the current file. + S32 getCurFileNum() const { return mCurrentFile; } + + S32 getFileCount() const { return (S32)mFiles.size(); } + + // See llvfs/lldir.h : getBaseFileName and getDirName to extract base or directory names + + // clear any lists of buffers or whatever, and make sure the file + // picker isn't locked. + void reset(); + +private: + enum + { + SINGLE_FILENAME_BUFFER_SIZE = 1024, + //FILENAME_BUFFER_SIZE = 65536 + FILENAME_BUFFER_SIZE = 65000 + }; + +#if LL_WINDOWS + OPENFILENAMEW mOFN; // for open and save dialogs + WCHAR mFilesW[FILENAME_BUFFER_SIZE]; + +public: + void setWindowID(unsigned long window_id) { mOFN.hwndOwner = (HWND)window_id; } + +private: + bool setupFilter(ELoadFilter filter); +#endif // LL_WINDOWS + +#if LL_DARWIN + NavDialogCreationOptions mNavOptions; + std::vector mFileVector; + UInt32 mFileIndex; + + OSStatus doNavChooseDialog(ELoadFilter filter); + OSStatus doNavSaveDialog(ESaveFilter filter, const std::string& filename); + void getFilePath(SInt32 index); + void getFileName(SInt32 index); + static Boolean navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode); +#endif // LL_DARWIN + +#if LL_GTK + static void add_to_selectedfiles(gpointer data, gpointer user_data); + static void chooser_responder(GtkWidget *widget, gint response, gpointer user_data); + // we remember the last path that was accessed for a particular usage + std::map mContextToPathMap; + std::string mCurContextName; +#if LL_X11 + Window mX11WindowID; +#endif + +public: + std::map & get_ContextToPathMap(void) { return mContextToPathMap; } +#if LL_X11 + void setWindowID(unsigned long window_id) { mX11WindowID = (Window)window_id; } +#endif + +#endif // LL_GTK +#if !LL_WINDOWS && !(LL_GTK && LL_X11) + void setWindowID(unsigned long window_id) { PLS_WARNS << "Calling unimplemented LLFilePickerBase::setWindowID" << PLS_ENDL; } +#endif + +private: + std::vector mFiles; + S32 mCurrentFile; + bool mLocked; + bool mMultiFile; + +protected: +#if LL_GTK + GtkWindow* buildFilePicker(bool is_save, bool is_folder, std::string const& folder); +#endif + +protected: + LLFilePickerBase(); +}; + +// True singleton, private constructors (and no friends). +class LLFilePicker : public LLFilePickerBase +{ +public: + // calling this before main() is undefined + static LLFilePicker& instance( void ) { return sInstance; } + +private: + static LLFilePicker sInstance; + + LLFilePicker() { } +}; + +namespace translation +{ + void add(std::string const& key, std::string const& translation); +} + +#endif diff --git a/indra/media_plugins/gstreamer010/CMakeLists.txt b/indra/plugins/gstreamer010/CMakeLists.txt similarity index 82% rename from indra/media_plugins/gstreamer010/CMakeLists.txt rename to indra/plugins/gstreamer010/CMakeLists.txt index 5786bd1e2..f8a2a510e 100644 --- a/indra/media_plugins/gstreamer010/CMakeLists.txt +++ b/indra/plugins/gstreamer010/CMakeLists.txt @@ -30,23 +30,13 @@ include_directories( ### media_plugin_gstreamer010 -if(NOT WORD_SIZE EQUAL 32) - if(WINDOWS) - add_definitions(/FIXED:NO) - else(WINDOWS) # not windows therefore gcc LINUX and DARWIN - add_definitions(-fPIC) - endif(WINDOWS) -endif(NOT WORD_SIZE EQUAL 32) - set(media_plugin_gstreamer010_SOURCE_FILES media_plugin_gstreamer010.cpp - llmediaimplgstreamer_syms.cpp llmediaimplgstreamervidplug.cpp ) set(media_plugin_gstreamer010_HEADER_FILES llmediaimplgstreamervidplug.h - llmediaimplgstreamer_syms.h llmediaimplgstreamertriviallogging.h ) @@ -69,4 +59,3 @@ add_dependencies(media_plugin_gstreamer010 ${LLCOMMON_LIBRARIES} ) - diff --git a/indra/plugins/gstreamer010/llmediaimplgstreamer.h b/indra/plugins/gstreamer010/llmediaimplgstreamer.h new file mode 100755 index 000000000..bfc443b9e --- /dev/null +++ b/indra/plugins/gstreamer010/llmediaimplgstreamer.h @@ -0,0 +1,60 @@ +/** + * @file llmediaimplgstreamer.h + * @author Tofu Linden + * @brief implementation that supports media playback via GStreamer. + * + * @cond + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +// header guard +#ifndef llmediaimplgstreamer_h +#define llmediaimplgstreamer_h + +#if LL_GSTREAMER010_ENABLED + +extern "C" { +#include +#include + +#include "apr_pools.h" +#include "apr_dso.h" +} + + +extern "C" { +gboolean llmediaimplgstreamer_bus_callback (GstBus *bus, + GstMessage *message, + gpointer data); +} + +#endif // LL_GSTREAMER010_ENABLED + +#endif // llmediaimplgstreamer_h diff --git a/indra/plugins/gstreamer010/llmediaimplgstreamertriviallogging.h b/indra/plugins/gstreamer010/llmediaimplgstreamertriviallogging.h new file mode 100755 index 000000000..bb90aa12f --- /dev/null +++ b/indra/plugins/gstreamer010/llmediaimplgstreamertriviallogging.h @@ -0,0 +1,65 @@ +/** + * @file llmediaimplgstreamertriviallogging.h + * @brief minimal logging utilities. + * + * @cond + * $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://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +#ifndef __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ +#define __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ + +#include + +///////////////////////////////////////////////////////////////////////// +// Debug/Info/Warning macros. +#if LL_WINDOWS +#include +#define LL_GETPID GetCurrentProcessId +#else +#include +#include +#define LL_GETPID getpid +#endif +#define MSGMODULEFOO "(media plugin)" +#define STDERRMSG(...) do{\ + fprintf(stderr, " pid:%d: ", (int)LL_GETPID());\ + fprintf(stderr, MSGMODULEFOO " %s:%d: ", __FUNCTION__, __LINE__);\ + fprintf(stderr, __VA_ARGS__);\ + fputc('\n',stderr);\ + }while(0) +#define NULLMSG(...) do{}while(0) + +#define DEBUGMSG NULLMSG +#define INFOMSG STDERRMSG +#define WARNMSG STDERRMSG +///////////////////////////////////////////////////////////////////////// + +#endif /* __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ */ diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp b/indra/plugins/gstreamer010/llmediaimplgstreamervidplug.cpp old mode 100644 new mode 100755 similarity index 70% rename from indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp rename to indra/plugins/gstreamer010/llmediaimplgstreamervidplug.cpp index cdb7f4fae..5c8a99f0c --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp +++ b/indra/plugins/gstreamer010/llmediaimplgstreamervidplug.cpp @@ -1,28 +1,35 @@ /** - * @file llmediaimplgstreamervidplug.h + * @file llmediaimplgstreamervidplug.cpp * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl * * @cond - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 * - * 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://secondlife.com/developers/opensource/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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 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. * $/LicenseInfo$ + * * @endcond */ @@ -34,15 +41,29 @@ #include #include -#include "llmediaimplgstreamer_syms.h" #include "llmediaimplgstreamertriviallogging.h" +// #include "llthread.h" #include "llmediaimplgstreamervidplug.h" - GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug); #define GST_CAT_DEFAULT gst_slvideo_debug +/* Filter signals and args *//* +enum +{ + *//* FILL ME *//* + LAST_SIGNAL +}; + +enum +{ + ARG_0 +}; + +#define SLV_SIZECAPS ", width=(int){1,2,4,8,16,32,64,128,256,512,1024}, height=(int){1,2,4,8,16,32,64,128,256,512,1024} " +#define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS ";" GST_VIDEO_CAPS_BGRx SLV_SIZECAPS +*/ #define SLV_SIZECAPS ", width=(int)[1,2048], height=(int)[1,2048] " #define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS @@ -74,9 +95,9 @@ gst_slvideo_base_init (gpointer gclass) }; GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - llgst_element_class_add_pad_template (element_class, - llgst_static_pad_template_get (&sink_factory)); - llgst_element_class_set_details (element_class, &element_details); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + gst_element_class_set_details (element_class, &element_details); } @@ -87,7 +108,7 @@ gst_slvideo_finalize (GObject * object) slvideo = GST_SLVIDEO (object); if (slvideo->caps) { - llgst_caps_unref(slvideo->caps); + gst_caps_unref(slvideo->caps); } G_OBJECT_CLASS(parent_class)->finalize (object); @@ -98,7 +119,7 @@ static GstFlowReturn gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf) { GstSLVideo *slvideo; - llg_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); + g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); slvideo = GST_SLVIDEO(bsink); @@ -190,7 +211,7 @@ gst_slvideo_get_caps (GstBaseSink * bsink) GstSLVideo *slvideo; slvideo = GST_SLVIDEO(bsink); - return llgst_caps_ref (slvideo->caps); + return gst_caps_ref (slvideo->caps); } @@ -200,21 +221,32 @@ gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps) { GstSLVideo *filter; GstStructure *structure; +// GstCaps *intersection; GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); filter = GST_SLVIDEO(bsink); - int width, height; +/* + intersection = gst_caps_intersect (filter->caps, caps); + if (gst_caps_is_empty (intersection)) + { + // no overlap between our caps and requested caps + return FALSE; + } + gst_caps_unref(intersection); +*/ + int width = 0; + int height = 0; gboolean ret; const GValue *fps; const GValue *par; - structure = llgst_caps_get_structure (caps, 0); - ret = llgst_structure_get_int (structure, "width", &width); - ret = ret && llgst_structure_get_int (structure, "height", &height); - fps = llgst_structure_get_value (structure, "framerate"); + structure = gst_caps_get_structure (caps, 0); + ret = gst_structure_get_int (structure, "width", &width); + ret = ret && gst_structure_get_int (structure, "height", &height); + fps = gst_structure_get_value (structure, "framerate"); ret = ret && (fps != NULL); - par = llgst_structure_get_value (structure, "pixel-aspect-ratio"); + par = gst_structure_get_value (structure, "pixel-aspect-ratio"); if (!ret) return FALSE; @@ -224,34 +256,35 @@ gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps) filter->width = width; filter->height = height; - - filter->fps_n = llgst_value_get_fraction_numerator(fps); - filter->fps_d = llgst_value_get_fraction_denominator(fps); + filter->fps_n = gst_value_get_fraction_numerator(fps); + filter->fps_d = gst_value_get_fraction_denominator(fps); if (par) { - filter->par_n = llgst_value_get_fraction_numerator(par); - filter->par_d = llgst_value_get_fraction_denominator(par); + filter->par_n = gst_value_get_fraction_numerator(par); + filter->par_d = gst_value_get_fraction_denominator(par); } else { filter->par_n = 1; filter->par_d = 1; } + GST_VIDEO_SINK_WIDTH(filter) = width; GST_VIDEO_SINK_HEIGHT(filter) = height; - + // crufty lump - we *always* accept *only* RGBX now. /* + filter->format = SLV_PF_UNKNOWN; - if (0 == strcmp(llgst_structure_get_name(structure), + if (0 == strcmp(gst_structure_get_name(structure), "video/x-raw-rgb")) { int red_mask; int green_mask; int blue_mask; - llgst_structure_get_int(structure, "red_mask", &red_mask); - llgst_structure_get_int(structure, "green_mask", &green_mask); - llgst_structure_get_int(structure, "blue_mask", &blue_mask); + gst_structure_get_int(structure, "red_mask", &red_mask); + gst_structure_get_int(structure, "green_mask", &green_mask); + gst_structure_get_int(structure, "blue_mask", &blue_mask); if ((unsigned int)red_mask == 0xFF000000 && (unsigned int)green_mask == 0x00FF0000 && (unsigned int)blue_mask == 0x0000FF00) @@ -265,12 +298,13 @@ gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps) filter->format = SLV_PF_BGRX; //fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n"); } - }*/ - + + }*/ + filter->format = SLV_PF_RGBX; GST_OBJECT_UNLOCK(filter); - + return TRUE; } @@ -317,15 +351,15 @@ gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, // we can ignore these and reverse-negotiate our preferred dimensions with // the peer if we like - we need to do this to obey dynamic resize requests // flowing in from the app. - structure = llgst_caps_get_structure (caps, 0); - if (!llgst_structure_get_int(structure, "width", &width) || - !llgst_structure_get_int(structure, "height", &height)) + structure = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_int(structure, "width", &width) || + !gst_structure_get_int(structure, "height", &height)) { GST_WARNING_OBJECT (slvideo, "no width/height in caps %" GST_PTR_FORMAT, caps); return GST_FLOW_NOT_NEGOTIATED; } - GstBuffer *newbuf = llgst_buffer_new(); + GstBuffer *newbuf = gst_buffer_new(); bool made_bufferdata_ptr = false; #define MAXDEPTHHACK 4 @@ -345,19 +379,19 @@ gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, GstCaps *desired_caps; GstStructure *desired_struct; - desired_caps = llgst_caps_copy (caps); - desired_struct = llgst_caps_get_structure (desired_caps, 0); + desired_caps = gst_caps_copy (caps); + desired_struct = gst_caps_get_structure (desired_caps, 0); GValue value = {0}; g_value_init(&value, G_TYPE_INT); g_value_set_int(&value, slwantwidth); - llgst_structure_set_value (desired_struct, "width", &value); + gst_structure_set_value (desired_struct, "width", &value); g_value_unset(&value); g_value_init(&value, G_TYPE_INT); g_value_set_int(&value, slwantheight); - llgst_structure_set_value (desired_struct, "height", &value); + gst_structure_set_value (desired_struct, "height", &value); - if (llgst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo), + if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo), desired_caps)) { // todo: re-use buffers from a pool? @@ -368,13 +402,13 @@ gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, GST_BUFFER_SIZE(newbuf) = slwantwidth * slwantheight * MAXDEPTHHACK; GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf)); GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf); - llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps); + gst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps); made_bufferdata_ptr = true; } else { // peer hates our cap suggestion INFOMSG("peer hates us :("); - llgst_caps_unref(desired_caps); + gst_caps_unref(desired_caps); } } } @@ -386,7 +420,7 @@ gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, GST_BUFFER_SIZE(newbuf) = width * height * MAXDEPTHHACK; GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf)); GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf); - llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps); + gst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps); } *buf = GST_BUFFER_CAST(newbuf); @@ -428,6 +462,20 @@ gst_slvideo_class_init (GstSLVideoClass * klass) #undef LLGST_DEBUG_FUNCPTR } +/* +static void +gst_slvideo_update_caps (GstSLVideo * slvideo) +{ + GstCaps *caps; + + // GStreamer will automatically convert colourspace if necessary. + // GStreamer will automatically resize media to one of these enumerated + // powers-of-two that we ask for (yay GStreamer!) + caps = gst_caps_from_string (SLV_ALLCAPS); + + gst_caps_replace (&slvideo->caps, caps); +} +*/ /* initialize the new element * instantiate pads and add them to element @@ -450,24 +498,24 @@ gst_slvideo_init (GstSLVideo * filter, filter->retained_frame_width = filter->width; filter->retained_frame_height = filter->height; filter->retained_frame_format = SLV_PF_UNKNOWN; - GstCaps *caps = llgst_caps_from_string (SLV_ALLCAPS); - llgst_caps_replace (&filter->caps, caps); + GstCaps *caps = gst_caps_from_string (SLV_ALLCAPS); + gst_caps_replace (&filter->caps, caps); filter->resize_forced_always = false; filter->resize_try_width = -1; filter->resize_try_height = -1; GST_OBJECT_UNLOCK(filter); + + //gst_slvideo_update_caps(filter); } static void gst_slvideo_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { - llg_return_if_fail (GST_IS_SLVIDEO (object)); + g_return_if_fail (GST_IS_SLVIDEO (object)); - switch (prop_id) { - default: + if (prop_id) { G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; } } @@ -475,12 +523,10 @@ static void gst_slvideo_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { - llg_return_if_fail (GST_IS_SLVIDEO (object)); + g_return_if_fail (GST_IS_SLVIDEO (object)); - switch (prop_id) { - default: + if (prop_id) { G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; } } @@ -498,7 +544,7 @@ plugin_init (GstPlugin * plugin) GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin", 0, (gchar*)"Second Life Video Sink"); - return llgst_element_register (plugin, "private-slvideo", + return gst_element_register (plugin, "private-slvideo", GST_RANK_NONE, GST_TYPE_SLVIDEO); } @@ -508,20 +554,19 @@ plugin_init (GstPlugin * plugin) some g++ versions buggily avoid __attribute__((constructor)) functions - so we provide an explicit plugin init function. */ -#define PACKAGE (gchar*)"packagehack" -// this macro quietly refers to PACKAGE internally -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - (gchar*)"private-slvideoplugin", - (gchar*)"SL Video sink plugin", - plugin_init, (gchar*)"1.0", (gchar*)"LGPL", - (gchar*)"Second Life", - (gchar*)"http://www.secondlife.com/"); -#undef PACKAGE + void gst_slvideo_init_class (void) { - ll_gst_plugin_register_static (&gst_plugin_desc); - DEBUGMSG("CLASS INIT"); + gst_plugin_register_static( GST_VERSION_MAJOR, + GST_VERSION_MINOR, + (const gchar *)"private-slvideoplugin", + (gchar *)"SL Video sink plugin", + plugin_init, + (const gchar *)"0.1", + GST_LICENSE_UNKNOWN, + (const gchar *)"Second Life", + (const gchar *)"Second Life", + (const gchar *)"http://www.secondlife.com/" ); } #endif // LL_GSTREAMER010_ENABLED diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h b/indra/plugins/gstreamer010/llmediaimplgstreamervidplug.h old mode 100644 new mode 100755 similarity index 65% rename from indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h rename to indra/plugins/gstreamer010/llmediaimplgstreamervidplug.h index 29d65fa4e..5e8c72c98 --- a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h +++ b/indra/plugins/gstreamer010/llmediaimplgstreamervidplug.h @@ -3,26 +3,33 @@ * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl * * @cond - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 * - * 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://secondlife.com/developers/opensource/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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 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. * $/LicenseInfo$ + * * @endcond */ @@ -35,6 +42,7 @@ extern "C" { #include #include #include +// #include } G_BEGIN_DECLS diff --git a/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp b/indra/plugins/gstreamer010/media_plugin_gstreamer010.cpp old mode 100644 new mode 100755 similarity index 70% rename from indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp rename to indra/plugins/gstreamer010/media_plugin_gstreamer010.cpp index 352b63583..8d0dc31e2 --- a/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp +++ b/indra/plugins/gstreamer010/media_plugin_gstreamer010.cpp @@ -3,31 +3,49 @@ * @brief GStreamer-0.10 plugin for LLMedia API plugin system * * @cond - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 * - * 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://secondlife.com/developers/opensource/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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 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. * $/LicenseInfo$ + * * @endcond */ #include "linden_common.h" +// Needed for _getcwd() RC +#ifdef LL_WINDOWS +#include +#include +#include +#endif + +#ifdef LL_DARWIN +#include +#endif + #include "llgl.h" #include "llplugininstance.h" @@ -39,6 +57,7 @@ extern "C" { #include +#include } #include "llmediaimplgstreamer.h" @@ -46,14 +65,12 @@ extern "C" { #include "llmediaimplgstreamervidplug.h" -#include "llmediaimplgstreamer_syms.h" - ////////////////////////////////////////////////////////////////////////////// // class MediaPluginGStreamer010 : public MediaPluginBase { public: - MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); ~MediaPluginGStreamer010(); /* virtual */ void receiveMessage(const char *message_string); @@ -61,6 +78,8 @@ public: static bool startup(); static bool closedown(); + static void set_gst_plugin_path(); + gboolean processGSTEvents(GstBus *bus, GstMessage *message); @@ -76,7 +95,7 @@ private: bool play(double rate); bool getTimePos(double &sec_out); - static const double MIN_LOOP_SEC = 1.0F; + #define MIN_LOOP_SEC 1.0F bool mIsLooping; @@ -132,11 +151,12 @@ private: bool mSeekWanted; double mSeekDestination; + + std::string mLastTitle; // Very GStreamer-specific GMainLoop *mPump; // event pump for this media GstElement *mPlaybin; - GstElement *mVisualizer; GstSLVideo *mVideoSink; }; @@ -144,9 +164,9 @@ private: bool MediaPluginGStreamer010::mDoneInit = false; MediaPluginGStreamer010::MediaPluginGStreamer010( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data ) : - MediaPluginBase(host_send_func, host_user_data), + LLPluginInstance::sendMessageFunction send_message_function, + LLPluginInstance* plugin_instance) : + MediaPluginBase(send_message_function, plugin_instance), mBusWatchID ( 0 ), mCurrentRowbytes ( 4 ), mTextureFormatPrimary ( GL_RGBA ), @@ -155,12 +175,11 @@ MediaPluginGStreamer010::MediaPluginGStreamer010( mSeekDestination(0.0), mPump ( NULL ), mPlaybin ( NULL ), - mVisualizer ( NULL ), mVideoSink ( NULL ), mCommand ( COMMAND_NONE ) { std::ostringstream str; - INFOMSG("MediaPluginGStreamer010 constructor - my PID=%u", U32(getpid())); + INFOMSG("MediaPluginGStreamer010 constructor - my PID=%u", U32(LL_GETPID())); } /////////////////////////////////////////////////////////////////////////////// @@ -191,149 +210,179 @@ MediaPluginGStreamer010::processGSTEvents(GstBus *bus, GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING) { DEBUGMSG("Got GST message type: %s", - LLGST_MESSAGE_TYPE_NAME (message)); + GST_MESSAGE_TYPE_NAME (message)); } else { // TODO: grok 'duration' message type DEBUGMSG("Got GST message type: %s", - LLGST_MESSAGE_TYPE_NAME (message)); + GST_MESSAGE_TYPE_NAME (message)); } - switch (GST_MESSAGE_TYPE (message)) { - case GST_MESSAGE_BUFFERING: { - // NEEDS GST 0.10.11+ - if (llgst_message_parse_buffering) + switch (GST_MESSAGE_TYPE (message)) + { + case GST_MESSAGE_BUFFERING: { + // NEEDS GST 0.10.11+ and America discovered by C.Columbus gint percent = 0; - llgst_message_parse_buffering(message, &percent); + gst_message_parse_buffering(message, &percent); DEBUGMSG("GST buffering: %d%%", percent); - } - break; - } - case GST_MESSAGE_STATE_CHANGED: { - GstState old_state; - GstState new_state; - GstState pending_state; - llgst_message_parse_state_changed(message, - &old_state, - &new_state, - &pending_state); -#ifdef LL_GST_REPORT_STATE_CHANGES - // not generally very useful, and rather spammy. - DEBUGMSG("state change (old,,pending): %s,<%s>,%s", - get_gst_state_name(old_state), - get_gst_state_name(new_state), - get_gst_state_name(pending_state)); -#endif // LL_GST_REPORT_STATE_CHANGES - switch (new_state) { - case GST_STATE_VOID_PENDING: - break; - case GST_STATE_NULL: - break; - case GST_STATE_READY: - setStatus(STATUS_LOADED); - break; - case GST_STATE_PAUSED: - setStatus(STATUS_PAUSED); - break; - case GST_STATE_PLAYING: - setStatus(STATUS_PLAYING); break; } - break; - } - case GST_MESSAGE_ERROR: { - GError *err = NULL; - gchar *debug = NULL; + case GST_MESSAGE_STATE_CHANGED: { + GstState old_state; + GstState new_state; + GstState pending_state; + gst_message_parse_state_changed(message, + &old_state, + &new_state, + &pending_state); + #ifdef LL_GST_REPORT_STATE_CHANGES + // not generally very useful, and rather spammy. + DEBUGMSG("state change (old,,pending): %s,<%s>,%s", + get_gst_state_name(old_state), + get_gst_state_name(new_state), + get_gst_state_name(pending_state)); + #endif // LL_GST_REPORT_STATE_CHANGES - llgst_message_parse_error (message, &err, &debug); - WARNMSG("GST error: %s", err?err->message:"(unknown)"); - if (err) - g_error_free (err); - g_free (debug); - - mCommand = COMMAND_STOP; - - setStatus(STATUS_ERROR); - - break; - } - case GST_MESSAGE_INFO: { - if (llgst_message_parse_info) + switch (new_state) + { + case GST_STATE_VOID_PENDING: + break; + case GST_STATE_NULL: + break; + case GST_STATE_READY: + setStatus(STATUS_LOADED); + break; + case GST_STATE_PAUSED: + setStatus(STATUS_PAUSED); + break; + case GST_STATE_PLAYING: + setStatus(STATUS_PLAYING); + break; + } + break; + } + case GST_MESSAGE_ERROR: + { + GError *err = NULL; + gchar *debug = NULL; + + gst_message_parse_error (message, &err, &debug); + WARNMSG("GST error: %s", err?err->message:"(unknown)"); + if (err) + g_error_free (err); + g_free (debug); + + mCommand = COMMAND_STOP; + + setStatus(STATUS_ERROR); + + break; + } + case GST_MESSAGE_INFO: { GError *err = NULL; gchar *debug = NULL; - llgst_message_parse_info (message, &err, &debug); + gst_message_parse_info (message, &err, &debug); INFOMSG("GST info: %s", err?err->message:"(unknown)"); if (err) g_error_free (err); g_free (debug); + + break; } - break; - } - case GST_MESSAGE_WARNING: { - GError *err = NULL; - gchar *debug = NULL; - - llgst_message_parse_warning (message, &err, &debug); - WARNMSG("GST warning: %s", err?err->message:"(unknown)"); - if (err) - g_error_free (err); - g_free (debug); - - break; - } - case GST_MESSAGE_EOS: - /* end-of-stream */ - DEBUGMSG("GST end-of-stream."); - if (mIsLooping) + case GST_MESSAGE_WARNING: { - DEBUGMSG("looping media..."); - double eos_pos_sec = 0.0F; - bool got_eos_position = getTimePos(eos_pos_sec); + GError *err = NULL; + gchar *debug = NULL; + + gst_message_parse_warning (message, &err, &debug); + WARNMSG("GST warning: %s", err?err->message:"(unknown)"); + if (err) + g_error_free (err); + g_free (debug); + + break; + } + case GST_MESSAGE_TAG: + { + GstTagList *new_tags; - if (got_eos_position && eos_pos_sec < MIN_LOOP_SEC) + gst_message_parse_tag( message, &new_tags ); + + gchar *title = NULL; + + if ( gst_tag_list_get_string(new_tags, GST_TAG_TITLE, &title) ) { - // if we know that the movie is really short, don't - // loop it else it can easily become a time-hog - // because of GStreamer spin-up overhead - DEBUGMSG("really short movie (%0.3fsec) - not gonna loop this, pausing instead.", eos_pos_sec); - // inject a COMMAND_PAUSE - mCommand = COMMAND_PAUSE; - } - else - { -#undef LLGST_LOOP_BY_SEEKING -// loop with a stop-start instead of a seek, because it actually seems rather -// faster than seeking on remote streams. -#ifdef LLGST_LOOP_BY_SEEKING - // first, try looping by an explicit rewind - bool seeksuccess = seek(0.0); - if (seeksuccess) + //WARMING("Title: %s", title); + std::string newtitle(title); + gst_tag_list_free(new_tags); + + if ( newtitle != mLastTitle && !newtitle.empty() ) { - play(1.0); + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text"); + message.setValue("name", newtitle ); + sendMessage( message ); + mLastTitle = newtitle; + } + g_free(title); + } + + break; + } + case GST_MESSAGE_EOS: + { + /* end-of-stream */ + DEBUGMSG("GST end-of-stream."); + if (mIsLooping) + { + DEBUGMSG("looping media..."); + double eos_pos_sec = 0.0F; + bool got_eos_position = getTimePos(eos_pos_sec); + + if (got_eos_position && eos_pos_sec < MIN_LOOP_SEC) + { + // if we know that the movie is really short, don't + // loop it else it can easily become a time-hog + // because of GStreamer spin-up overhead + DEBUGMSG("really short movie (%0.3fsec) - not gonna loop this, pausing instead.", eos_pos_sec); + // inject a COMMAND_PAUSE + mCommand = COMMAND_PAUSE; } else -#endif // LLGST_LOOP_BY_SEEKING - { // use clumsy stop-start to loop - DEBUGMSG("didn't loop by rewinding - stopping and starting instead..."); - stop(); - play(1.0); + { + #undef LLGST_LOOP_BY_SEEKING + // loop with a stop-start instead of a seek, because it actually seems rather + // faster than seeking on remote streams. + #ifdef LLGST_LOOP_BY_SEEKING + // first, try looping by an explicit rewind + bool seeksuccess = seek(0.0); + if (seeksuccess) + { + play(1.0); + } + else + #endif // LLGST_LOOP_BY_SEEKING + { // use clumsy stop-start to loop + DEBUGMSG("didn't loop by rewinding - stopping and starting instead..."); + stop(); + play(1.0); + } } } - } - else // not a looping media - { - // inject a COMMAND_STOP - mCommand = COMMAND_STOP; - } - break; - default: - /* unhandled message */ - break; + else // not a looping media + { + // inject a COMMAND_STOP + mCommand = COMMAND_STOP; + } + } break; + + default: + /* unhandled message */ + break; } /* we want to be notified again the next time there is a message @@ -539,12 +588,8 @@ MediaPluginGStreamer010::pause() { DEBUGMSG("pausing media..."); // todo: error-check this? - if (mDoneInit && mPlaybin) - { - llgst_element_set_state(mPlaybin, GST_STATE_PAUSED); - return true; - } - return false; + gst_element_set_state(mPlaybin, GST_STATE_PAUSED); + return true; } bool @@ -552,12 +597,8 @@ MediaPluginGStreamer010::stop() { DEBUGMSG("stopping media..."); // todo: error-check this? - if (mDoneInit && mPlaybin) - { - llgst_element_set_state(mPlaybin, GST_STATE_READY); - return true; - } - return false; + gst_element_set_state(mPlaybin, GST_STATE_READY); + return true; } bool @@ -567,12 +608,8 @@ MediaPluginGStreamer010::play(double rate) DEBUGMSG("playing media... rate=%f", rate); // todo: error-check this? - if (mDoneInit && mPlaybin) - { - llgst_element_set_state(mPlaybin, GST_STATE_PLAYING); - return true; - } - return false; + gst_element_set_state(mPlaybin, GST_STATE_PLAYING); + return true; } bool @@ -600,7 +637,7 @@ MediaPluginGStreamer010::seek(double time_sec) bool success = false; if (mDoneInit && mPlaybin) { - success = llgst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME, + success = gst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME, GstSeekFlags(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), GST_SEEK_TYPE_SET, gint64(time_sec*GST_SECOND), @@ -615,15 +652,13 @@ bool MediaPluginGStreamer010::getTimePos(double &sec_out) { bool got_position = false; - if (mDoneInit && mPlaybin) + if (mPlaybin) { gint64 pos; GstFormat timefmt = GST_FORMAT_TIME; - got_position = - llgst_element_query_position && - llgst_element_query_position(mPlaybin, - &timefmt, - &pos); + got_position = gst_element_query_position(mPlaybin, + &timefmt, + &pos); got_position = got_position && (timefmt == GST_FORMAT_TIME); // GStreamer may have other ideas, but we consider the current position @@ -665,7 +700,7 @@ MediaPluginGStreamer010::load() DEBUGMSG("setting up media..."); mIsLooping = false; - mVolume = 0.1234567; // minor hack to force an initial volume update + mVolume = (float) 0.1234567; // minor hack to force an initial volume update // Create a pumpable main-loop for this media mPump = g_main_loop_new (NULL, FALSE); @@ -676,7 +711,7 @@ MediaPluginGStreamer010::load() } // instantiate a playbin element to do the hard work - mPlaybin = llgst_element_factory_make ("playbin", "play"); + mPlaybin = gst_element_factory_make ("playbin", "play"); if (!mPlaybin) { setStatus(STATUS_ERROR); @@ -684,50 +719,21 @@ MediaPluginGStreamer010::load() } // get playbin's bus - GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin)); + GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (mPlaybin)); if (!bus) { setStatus(STATUS_ERROR); return false; // error } - mBusWatchID = llgst_bus_add_watch (bus, + mBusWatchID = gst_bus_add_watch (bus, llmediaimplgstreamer_bus_callback, this); - llgst_object_unref (bus); - -#if 0 // not quite stable/correct yet - // get a visualizer element (bonus feature!) - char* vis_name = getenv("LL_GST_VIS_NAME"); - if (!vis_name || - (vis_name && std::string(vis_name)!="none")) - { - if (vis_name) - { - mVisualizer = llgst_element_factory_make (vis_name, "vis"); - } - if (!mVisualizer) - { - mVisualizer = llgst_element_factory_make ("libvisual_jess", "vis"); - if (!mVisualizer) - { - mVisualizer = llgst_element_factory_make ("goom", "vis"); - if (!mVisualizer) - { - mVisualizer = llgst_element_factory_make ("libvisual_lv_scope", "vis"); - if (!mVisualizer) - { - // That's okay, we don't NEED this. - } - } - } - } - } -#endif + gst_object_unref (bus); if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) { // instantiate a custom video sink mVideoSink = - GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo")); + GST_SLVIDEO(gst_element_factory_make ("private-slvideo", "slvideo")); if (!mVideoSink) { WARNMSG("Could not instantiate private-slvideo element."); @@ -740,11 +746,6 @@ MediaPluginGStreamer010::load() g_object_set(mPlaybin, "video-sink", mVideoSink, NULL); } - if (mVisualizer) - { - g_object_set(mPlaybin, "vis-plugin", mVisualizer, NULL); - } - return true; } @@ -762,17 +763,11 @@ MediaPluginGStreamer010::unload () if (mPlaybin) { - llgst_element_set_state (mPlaybin, GST_STATE_NULL); - llgst_object_unref (GST_OBJECT (mPlaybin)); + gst_element_set_state (mPlaybin, GST_STATE_NULL); + gst_object_unref (GST_OBJECT (mPlaybin)); mPlaybin = NULL; } - if (mVisualizer) - { - llgst_object_unref (GST_OBJECT (mVisualizer)); - mVisualizer = NULL; - } - if (mPump) { g_main_loop_quit(mPump); @@ -802,7 +797,10 @@ MediaPluginGStreamer010::startup() // Init the glib type system - we need it. g_type_init(); + set_gst_plugin_path(); + +/* // Get symbols! #if LL_DARWIN if (! grab_gst_syms("libgstreamer-0.10.dylib", @@ -818,24 +816,24 @@ MediaPluginGStreamer010::startup() WARNMSG("Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled."); return false; } - - if (llgst_segtrap_set_enabled) - { - llgst_segtrap_set_enabled(FALSE); - } - else - { - WARNMSG("gst_segtrap_set_enabled() is not available; plugin crashes won't be caught."); - } - +*/ +// if (gst_segtrap_set_enabled) +// { + gst_segtrap_set_enabled(FALSE); +// } +// else +// { +// WARNMSG("gst_segtrap_set_enabled() is not available; plugin crashes won't be caught."); +// } +/* #if LL_LINUX // Gstreamer tries a fork during init, waitpid-ing on it, // which conflicts with any installed SIGCHLD handler... struct sigaction tmpact, oldact; - if (llgst_registry_fork_set_enabled) { + if (gst_registry_fork_set_enabled) { // if we can disable SIGCHLD-using forking behaviour, // do it. - llgst_registry_fork_set_enabled(false); + gst_registry_fork_set_enabled(false); } else { // else temporarily install default SIGCHLD handler @@ -846,24 +844,24 @@ MediaPluginGStreamer010::startup() sigaction(SIGCHLD, &tmpact, &oldact); } #endif // LL_LINUX - +*/ // Protect against GStreamer resetting the locale, yuck. static std::string saved_locale; saved_locale = setlocale(LC_ALL, NULL); // finally, try to initialize GStreamer! GError *err = NULL; - gboolean init_gst_success = llgst_init_check(NULL, NULL, &err); + gboolean init_gst_success = gst_init_check(NULL, NULL, &err); // restore old locale setlocale(LC_ALL, saved_locale.c_str() ); - +/* #if LL_LINUX // restore old SIGCHLD handler - if (!llgst_registry_fork_set_enabled) + if (!gst_registry_fork_set_enabled) sigaction(SIGCHLD, &oldact, NULL); #endif // LL_LINUX - +*/ if (!init_gst_success) // fail { if (err) @@ -877,16 +875,139 @@ MediaPluginGStreamer010::startup() } return false; } - + + // Set up logging facilities + gst_debug_remove_log_function( gst_debug_log_default ); +// gst_debug_add_log_function( gstreamer_log, NULL ); + // Init our custom plugins - only really need do this once. gst_slvideo_init_class(); - +/* + // List the plugins GStreamer can find + LL_DEBUGS("MediaImpl") << "Found GStreamer plugins:" << LL_ENDL; + GList *list; + GstRegistry *registry = gst_registry_get_default(); + std::string loaded = ""; + for (list = gst_registry_get_plugin_list(registry); + list != NULL; + list = g_list_next(list)) + { + GstPlugin *list_plugin = (GstPlugin *)list->data; + (bool)gst_plugin_is_loaded(list_plugin) ? loaded = "Yes" : loaded = "No"; + LL_DEBUGS("MediaImpl") << gst_plugin_get_name(list_plugin) << ", loaded? " << loaded << LL_ENDL; + } + gst_plugin_list_free(list); +*/ mDoneInit = true; } return true; } +void MediaPluginGStreamer010::set_gst_plugin_path() +{ + // Linux sets GST_PLUGIN_PATH in wrapper.sh, not here. +#if LL_WINDOWS || LL_DARWIN + + std::string imp_dir = ""; + + // Get the current working directory: +#if LL_WINDOWS + char* raw_dir; + raw_dir = _getcwd(NULL,0); + if( raw_dir != NULL ) + { + imp_dir = std::string( raw_dir ); + } +#elif LL_DARWIN + CFBundleRef main_bundle = CFBundleGetMainBundle(); + if( main_bundle != NULL ) + { + CFURLRef bundle_url = CFBundleCopyBundleURL( main_bundle ); + if( bundle_url != NULL ) + { + #ifndef MAXPATHLEN + #define MAXPATHLEN 1024 + #endif + char raw_dir[MAXPATHLEN]; + if( CFURLGetFileSystemRepresentation( bundle_url, true, (UInt8 *)raw_dir, MAXPATHLEN) ) + { + imp_dir = std::string( raw_dir ) + "/Contents/MacOS/"; + } + CFRelease(bundle_url); + } + } +#endif + + if( imp_dir == "" ) + { + WARNMSG("Could not get application directory, not setting GST_PLUGIN_PATH."); + return; + } + + DEBUGMSG("Imprudence is installed at %s", imp_dir); + + // ":" on Mac and 'Nix, ";" on Windows + std::string separator = G_SEARCHPATH_SEPARATOR_S; + + // Grab the current path, if it's set. + std::string old_plugin_path = ""; + char *old_path = getenv("GST_PLUGIN_PATH"); + if(old_path == NULL) + { + DEBUGMSG("Did not find user-set GST_PLUGIN_PATH."); + } + else + { + old_plugin_path = separator + std::string( old_path ); + } + + + // Search both Imprudence and Imprudence\lib\gstreamer-plugins. + // But we also want to search the path the user has set, if any. + std::string plugin_path = + "GST_PLUGIN_PATH=" + +#if LL_WINDOWS + imp_dir + "\\lib\\gstreamer-plugins" + +#elif LL_DARWIN + imp_dir + separator + + imp_dir + "/../Resources/lib/gstreamer-plugins" + +#endif + old_plugin_path; + + int put_result; + + // Place GST_PLUGIN_PATH in the environment settings +#if LL_WINDOWS + put_result = _putenv( (char*)plugin_path.c_str() ); +#elif LL_DARWIN + put_result = putenv( (char*)plugin_path.c_str() ); +#endif + + if( put_result == -1 ) + { + WARNMSG("Setting GST_PLUGIN_PATH failed!"); + } + else + { + DEBUGMSG("GST_PLUGIN_PATH set to %s", getenv("GST_PLUGIN_PATH")); + } + + // Don't load system plugins. We only want to use ours, to avoid conflicts. +#if LL_WINDOWS + put_result = _putenv( "GST_PLUGIN_SYSTEM_PATH=\"\"" ); +#elif LL_DARWIN + put_result = putenv( "GST_PLUGIN_SYSTEM_PATH=\"\"" ); +#endif + + if( put_result == -1 ) + { + WARNMSG("Setting GST_PLUGIN_SYSTEM_PATH=\"\" failed!"); + } + +#endif // LL_WINDOWS || LL_DARWIN +} + void MediaPluginGStreamer010::sizeChanged() @@ -928,7 +1049,7 @@ MediaPluginGStreamer010::closedown() if (!mDoneInit) return false; // error - ungrab_gst_syms(); +// ungrab_gst_syms(); mDoneInit = false; @@ -949,11 +1070,10 @@ std::string MediaPluginGStreamer010::getVersion() { std::string plugin_version = "GStreamer010 media plugin, GStreamer version "; - if (mDoneInit && - llgst_version) + if (mDoneInit) // && gst_version) { guint major, minor, micro, nano; - llgst_version(&major, &minor, µ, &nano); + gst_version(&major, &minor, µ, &nano); plugin_version += llformat("%u.%u.%u.%u (runtime), %u.%u.%u.%u (headers)", (unsigned int)major, (unsigned int)minor, (unsigned int)micro, (unsigned int)nano, (unsigned int)GST_VERSION_MAJOR, (unsigned int)GST_VERSION_MINOR, (unsigned int)GST_VERSION_MICRO, (unsigned int)GST_VERSION_NANO); } else @@ -1211,14 +1331,11 @@ void MediaPluginGStreamer010::receiveMessage(const char *message_string) } } -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +int create_plugin(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance, BasicPluginBase** plugin_object) { if (MediaPluginGStreamer010::startup()) { - MediaPluginGStreamer010 *self = new MediaPluginGStreamer010(host_send_func, host_user_data); - *plugin_send_func = MediaPluginGStreamer010::staticReceiveMessage; - *plugin_user_data = (void*)self; - + *plugin_object = new MediaPluginGStreamer010(send_message_function, plugin_instance); return 0; // okay } else @@ -1234,15 +1351,14 @@ int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void class MediaPluginGStreamer010 : public MediaPluginBase { public: - MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); ~MediaPluginGStreamer010(); /* virtual */ void receiveMessage(const char *message_string); }; MediaPluginGStreamer010::MediaPluginGStreamer010( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data ) : - MediaPluginBase(host_send_func, host_user_data) + LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) : + MediaPluginBase(send_message_function, plugin_instance) { // no-op } @@ -1258,7 +1374,7 @@ void MediaPluginGStreamer010::receiveMessage(const char *message_string) } // We're building without GStreamer enabled. Just refuse to initialize. -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +int create_plugin(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance, BasicPluginBase** plugin_object) { return -1; } diff --git a/indra/media_plugins/quicktime/CMakeLists.txt b/indra/plugins/quicktime/CMakeLists.txt old mode 100644 new mode 100755 similarity index 97% rename from indra/media_plugins/quicktime/CMakeLists.txt rename to indra/plugins/quicktime/CMakeLists.txt index df191f543..c827404c2 --- a/indra/media_plugins/quicktime/CMakeLists.txt +++ b/indra/plugins/quicktime/CMakeLists.txt @@ -30,7 +30,6 @@ if (DARWIN) find_library(CARBON_LIBRARY Carbon) endif (DARWIN) - ### media_plugin_quicktime set(media_plugin_quicktime_SOURCE_FILES @@ -77,7 +76,7 @@ if (QUICKTIME) PREFIX "" BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "@executable_path" - LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp" + LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp" ) # We use a bunch of deprecated system APIs. diff --git a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp b/indra/plugins/quicktime/media_plugin_quicktime.cpp old mode 100644 new mode 100755 similarity index 95% rename from indra/media_plugins/quicktime/media_plugin_quicktime.cpp rename to indra/plugins/quicktime/media_plugin_quicktime.cpp index 1f516c0c5..a03cadf19 --- a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp +++ b/indra/plugins/quicktime/media_plugin_quicktime.cpp @@ -40,10 +40,6 @@ #if defined(LL_DARWIN) #include #elif defined(LL_WINDOWS) - #undef __STDC_CONSTANT_MACROS //Needed, as boost/unordered_map.hpp already defines INT32_C, etc. - #if defined(_MSC_VER) && _MSC_VER >= 1600 - #define _STDINT_H //Visual Studio 2010 includes its own stdint header already - #endif #include "MacTypes.h" #include "QTML.h" #include "Movies.h" @@ -58,7 +54,7 @@ class MediaPluginQuickTime : public MediaPluginBase { public: - MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + MediaPluginQuickTime(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); ~MediaPluginQuickTime(); /* virtual */ void receiveMessage(const char *message_string); @@ -768,9 +764,9 @@ private: }; MediaPluginQuickTime::MediaPluginQuickTime( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data ) : - MediaPluginBase(host_send_func, host_user_data), + LLPluginInstance::sendMessageFunction send_message_function, + LLPluginInstance* plugin_instance) : + MediaPluginBase(send_message_function, plugin_instance), mMinWidth( 0 ), mMaxWidth( 2048 ), mMinHeight( 0 ), @@ -1067,12 +1063,9 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string) }; } -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +int create_plugin(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance, BasicPluginBase** plugin_object) { - MediaPluginQuickTime *self = new MediaPluginQuickTime(host_send_func, host_user_data); - *plugin_send_func = MediaPluginQuickTime::staticReceiveMessage; - *plugin_user_data = (void*)self; - + *plugin_object = new MediaPluginQuickTime(send_message_function, plugin_instance); return 0; } @@ -1083,15 +1076,15 @@ int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void class MediaPluginQuickTime : public MediaPluginBase { public: - MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + MediaPluginQuickTime(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); ~MediaPluginQuickTime(); /* virtual */ void receiveMessage(const char *message_string); }; MediaPluginQuickTime::MediaPluginQuickTime( - LLPluginInstance::sendMessageFunction host_send_func, - void *host_user_data ) : - MediaPluginBase(host_send_func, host_user_data) + LLPluginInstance::sendMessageFunction send_message_function, + LLPluginInstance* plugin_instance) : + MediaPluginBase(send_message_function, plugin_instance) { // no-op } @@ -1107,7 +1100,7 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string) } // We're building without quicktime enabled. Just refuse to initialize. -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +int create_plugin(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance, BasicPluginBase** plugin_object) { return -1; } diff --git a/indra/media_plugins/webkit/CMakeLists.txt b/indra/plugins/webkit/CMakeLists.txt similarity index 88% rename from indra/media_plugins/webkit/CMakeLists.txt rename to indra/plugins/webkit/CMakeLists.txt index 3b1f67954..1ffa2608a 100644 --- a/indra/media_plugins/webkit/CMakeLists.txt +++ b/indra/plugins/webkit/CMakeLists.txt @@ -30,17 +30,8 @@ include_directories( ${LLQTWEBKIT_INCLUDE_DIR} ) - ### media_plugin_webkit -if(NOT WORD_SIZE EQUAL 32) - if(WINDOWS) - add_definitions(/FIXED:NO) - else(WINDOWS) # not windows therefore gcc LINUX and DARWIN - add_definitions(-fPIC) - endif(WINDOWS) -endif(NOT WORD_SIZE EQUAL 32) - set(media_plugin_webkit_SOURCE_FILES media_plugin_webkit.cpp ) @@ -60,11 +51,9 @@ set(media_plugin_webkit_LINK_LIBRARIES # Select which VolumeCatcher implementation to use if (LINUX) - if (PULSEAUDIO_FOUND) + if (PULSEAUDIO) list(APPEND media_plugin_webkit_SOURCE_FILES linux_volume_catcher.cpp) - else (PULSEAUDIO_FOUND) - list(APPEND media_plugin_webkit_SOURCE_FILES dummy_volume_catcher.cpp) - endif (PULSEAUDIO_FOUND) + endif (PULSEAUDIO) list(APPEND media_plugin_webkit_LINK_LIBRARIES ${UI_LIBRARIES} # for glib/GTK ) @@ -78,6 +67,9 @@ elseif (DARWIN) ) elseif (WINDOWS) list(APPEND media_plugin_webkit_SOURCE_FILES windows_volume_catcher.cpp) +else (LINUX) + # All other platforms use the dummy volume catcher for now. + list(APPEND media_plugin_webkit_SOURCE_FILES dummy_volume_catcher.cpp) endif (LINUX) set_source_files_properties(${media_plugin_webkit_HEADER_FILES} @@ -114,7 +106,7 @@ if (DARWIN) PREFIX "" BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "@executable_path" - LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp" + LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp" ) # copy the webkit dylib to the build directory diff --git a/indra/plugins/webkit/dummy_volume_catcher.cpp b/indra/plugins/webkit/dummy_volume_catcher.cpp new file mode 100644 index 000000000..4df988789 --- /dev/null +++ b/indra/plugins/webkit/dummy_volume_catcher.cpp @@ -0,0 +1,65 @@ +/** + * @file dummy_volume_catcher.cpp + * @brief A null implementation of the "VolumeCatcher" class for platforms where it's not implemented yet. + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 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://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +#include "volume_catcher.h" + + +class VolumeCatcherImpl +{ +}; + +///////////////////////////////////////////////////// + +VolumeCatcher::VolumeCatcher() +{ + pimpl = NULL; +} + +VolumeCatcher::~VolumeCatcher() +{ +} + +void VolumeCatcher::setVolume(F32 volume) +{ +} + +void VolumeCatcher::setPan(F32 pan) +{ +} + +void VolumeCatcher::pump() +{ +} + diff --git a/indra/media_plugins/webkit/linux_volume_catcher.cpp b/indra/plugins/webkit/linux_volume_catcher.cpp similarity index 90% rename from indra/media_plugins/webkit/linux_volume_catcher.cpp rename to indra/plugins/webkit/linux_volume_catcher.cpp index 321d1f91f..cc3836ea5 100644 --- a/indra/media_plugins/webkit/linux_volume_catcher.cpp +++ b/indra/plugins/webkit/linux_volume_catcher.cpp @@ -3,26 +3,33 @@ * @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources * * @cond - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 * - * 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://secondlife.com/developers/opensource/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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 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. * $/LicenseInfo$ + * * @endcond */ @@ -35,8 +42,9 @@ 5) Keep a list of all living audio players that we care about, adjust the volumes of all of them when we get a new setVolume() call */ +# include //imprudence + #include "linden_common.h" -#include #include "volume_catcher.h" diff --git a/indra/plugins/webkit/linux_volume_catcher.h b/indra/plugins/webkit/linux_volume_catcher.h new file mode 100755 index 000000000..d4a1b38f9 --- /dev/null +++ b/indra/plugins/webkit/linux_volume_catcher.h @@ -0,0 +1,56 @@ +/** + * @file linux_volume_catcher.h + * @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 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://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +#ifndef LINUX_VOLUME_CATCHER_H +#define LINUX_VOLUME_CATCHER_H + +#include "linden_common.h" + +class LinuxVolumeCatcherImpl; + +class LinuxVolumeCatcher +{ + public: + LinuxVolumeCatcher(); + ~LinuxVolumeCatcher(); + + void setVolume(F32 volume); // 0.0 - 1.0 + void pump(); // call this at least a few times a second if you can - it affects how quickly we can 'catch' a new audio source and adjust its volume + + private: + LinuxVolumeCatcherImpl *pimpl; +}; + +#endif // LINUX_VOLUME_CATCHER_H diff --git a/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc b/indra/plugins/webkit/linux_volume_catcher_pa_syms.inc similarity index 100% rename from indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc rename to indra/plugins/webkit/linux_volume_catcher_pa_syms.inc diff --git a/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc b/indra/plugins/webkit/linux_volume_catcher_paglib_syms.inc similarity index 100% rename from indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc rename to indra/plugins/webkit/linux_volume_catcher_paglib_syms.inc diff --git a/indra/media_plugins/webkit/mac_volume_catcher.cpp b/indra/plugins/webkit/mac_volume_catcher.cpp similarity index 85% rename from indra/media_plugins/webkit/mac_volume_catcher.cpp rename to indra/plugins/webkit/mac_volume_catcher.cpp index 8a06bb848..190823f73 100644 --- a/indra/media_plugins/webkit/mac_volume_catcher.cpp +++ b/indra/plugins/webkit/mac_volume_catcher.cpp @@ -3,26 +3,33 @@ * @brief A Mac OS X specific hack to control the volume level of all audio channels opened by a process. * * @cond - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 * - * 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://secondlife.com/developers/opensource/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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 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. * $/LicenseInfo$ + * * @endcond */ diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/plugins/webkit/media_plugin_webkit.cpp old mode 100644 new mode 100755 similarity index 93% rename from indra/media_plugins/webkit/media_plugin_webkit.cpp rename to indra/plugins/webkit/media_plugin_webkit.cpp index 4ae84e952..97920cf9f --- a/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/indra/plugins/webkit/media_plugin_webkit.cpp @@ -33,9 +33,10 @@ * @endcond */ +#include "linden_common.h" + #include "llqtwebkit.h" -#include "linden_common.h" #include "indra_constants.h" // for indra keyboard codes #include "llgl.h" @@ -44,6 +45,7 @@ #include "llpluginmessage.h" #include "llpluginmessageclasses.h" #include "media_plugin_base.h" +#include // set to 1 if you're using the version of llqtwebkit that's QPixmap-ified #if LL_LINUX @@ -64,7 +66,6 @@ extern "C" { # include # include #endif -#include #if LL_WINDOWS // *NOTE:Mani - This captures the module handle for the dll. This is used below @@ -78,6 +79,20 @@ extern "C" { } #endif +#ifdef LL_STANDALONE +#include +#elif defined(LL_LINUX) +// We don't provide Qt headers for non-standalone, therefore define this here. +// Our prebuilt is built with QT_NAMESPACE undefined. +#define QT_MANGLE_NAMESPACE(name) name +#define Q_INIT_RESOURCE(name) \ + do { extern int QT_MANGLE_NAMESPACE(qInitResources_ ## name) (); \ + QT_MANGLE_NAMESPACE(qInitResources_ ## name) (); } while (0) +#else +// Apparently this symbol doesn't exist in the windows and Mac tar balls provided by LL. +#define Q_INIT_RESOURCE(name) /*nothing*/ +#endif + //////////////////////////////////////////////////////////////////////////////// // class MediaPluginWebKit : @@ -85,7 +100,7 @@ class MediaPluginWebKit : public LLEmbeddedBrowserWindowObserver { public: - MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + MediaPluginWebKit(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); ~MediaPluginWebKit(); /*virtual*/ void receiveMessage(const char *message_string); @@ -123,7 +138,6 @@ private: F32 mBackgroundR; F32 mBackgroundG; F32 mBackgroundB; - std::string mTarget; VolumeCatcher mVolumeCatcher; @@ -312,7 +326,11 @@ private: LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled ); // create single browser window - mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight, mTarget); +#if LLQTWEBKIT_API_VERSION >= 2 + mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow(mWidth, mHeight /*, mTarget*/ ); // We don't have mTarget yet. +#else + mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight ); +#endif // tell LLQtWebKit about the size of the browser window LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); @@ -322,6 +340,12 @@ private: // append details to agent string LLQtWebKit::getInstance()->setBrowserAgentId( mUserAgent ); + +// Viewer 2+ -- MC +#if LL_WINDOWS + // Set up window open behavior + LLQtWebKit::getInstance()->setWindowOpenBehavior(mBrowserWindowId, LLQtWebKit::WOB_SIMULATE_BLANK_HREF_CLICK); +#endif #if !LL_QTWEBKIT_USES_PIXMAPS // don't flip bitmap @@ -513,9 +537,16 @@ private: void onClickLinkHref(const EventType& event) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_href"); +#if LLQTWEBKIT_API_VERSION >= 2 message.setValue("uri", event.getEventUri()); message.setValue("target", event.getStringValue()); message.setValue("uuid", event.getStringValue2()); +#else + // This will work as long as we don't need "uuid", which will be needed for MoaP. + message.setValue("uri", event.getStringValue()); + message.setValue("target", event.getStringValue2()); + message.setValueU32("target_type", event.getLinkType()); +#endif sendMessage(message); } @@ -524,10 +555,13 @@ private: void onClickLinkNoFollow(const EventType& event) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow"); +#if LLQTWEBKIT_API_VERSION >= 2 message.setValue("uri", event.getEventUri()); +#else + message.setValue("uri", event.getStringValue()); +#endif sendMessage(message); } - //////////////////////////////////////////////////////////////////////////////// // virtual @@ -540,42 +574,6 @@ private: // message.setValueBoolean("dead", (event.getIntValue() != 0)) sendMessage(message); } - - //////////////////////////////////////////////////////////////////////////////// - // virtual - void onWindowCloseRequested(const EventType& event) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "close_request"); - message.setValue("uuid", event.getStringValue()); - sendMessage(message); - } - - //////////////////////////////////////////////////////////////////////////////// - // virtual - void onWindowGeometryChangeRequested(const EventType& event) - { - int x, y, width, height; - event.getRectValue(x, y, width, height); - - // This sometimes gets called with a zero-size request. Don't pass these along. - if(width > 0 && height > 0) - { - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "geometry_change"); - message.setValue("uuid", event.getStringValue()); - message.setValueS32("x", x); - message.setValueS32("y", y); - message.setValueS32("width", width); - message.setValueS32("height", height); - sendMessage(message); - } - } - - //////////////////////////////////////////////////////////////////////////////// - // virtual - std::string onRequestFilePicker( const EventType& eventIn ) - { - return blockingPickFile(); - } LLQtWebKit::EKeyboardModifier decodeModifiers(std::string &modifiers) { @@ -721,31 +719,11 @@ private: } } - - std::string mPickedFile; - - std::string blockingPickFile(void) - { - mPickedFile.clear(); - - LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "pick_file"); - message.setValueBoolean("blocking_request", true); - - // The "blocking_request" key in the message means this sendMessage call will block until a response is received. - sendMessage(message); - - return mPickedFile; - } - - void onPickFileResponse(const std::string &file) - { - mPickedFile = file; - } }; -MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) : - MediaPluginBase(host_send_func, host_user_data) +MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) : + MediaPluginBase(send_message_function, plugin_instance) { // std::cerr << "MediaPluginWebKit constructor" << std::endl; @@ -766,6 +744,9 @@ MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_ mJavascriptEnabled = true; // default to on mPluginsEnabled = true; // default to on mUserAgent = "LLPluginMedia Web Browser"; + + // Initialize WebCore resource. + Q_INIT_RESOURCE(WebCore); } MediaPluginWebKit::~MediaPluginWebKit() @@ -879,8 +860,6 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) { if(message_name == "init") { - mTarget = message_in.getValue("target"); - // This is the media init message -- all necessary data for initialization should have been received. if(initBrowser()) { @@ -1100,14 +1079,10 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE ); checkEditState(); } - if(message_name == "pick_file_response") - { - onPickFileResponse(message_in.getValue("file")); - } else { // std::cerr << "MediaPluginWebKit::receiveMessage: unknown media message: " << message_string << std::endl; - } + }; } else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER) { @@ -1207,17 +1182,6 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) } } } - else if(message_name == "proxy_window_opened") - { - std::string target = message_in.getValue("target"); - std::string uuid = message_in.getValue("uuid"); - LLQtWebKit::getInstance()->proxyWindowOpened(mBrowserWindowId, target, uuid); - } - else if(message_name == "proxy_window_closed") - { - std::string uuid = message_in.getValue("uuid"); - LLQtWebKit::getInstance()->proxyWindowClosed(mBrowserWindowId, uuid); - } else { // std::cerr << "MediaPluginWebKit::receiveMessage: unknown media_browser message: " << message_string << std::endl; @@ -1235,12 +1199,9 @@ void MediaPluginWebKit::setVolume(F32 volume) mVolumeCatcher.setVolume(volume); } -int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +int create_plugin(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance, BasicPluginBase** plugin_object) { - MediaPluginWebKit *self = new MediaPluginWebKit(host_send_func, host_user_data); - *plugin_send_func = MediaPluginWebKit::staticReceiveMessage; - *plugin_user_data = (void*)self; - + *plugin_object = new MediaPluginWebKit(send_message_function, plugin_instance); return 0; } diff --git a/indra/plugins/webkit/volume_catcher.h b/indra/plugins/webkit/volume_catcher.h new file mode 100644 index 000000000..855e99fc0 --- /dev/null +++ b/indra/plugins/webkit/volume_catcher.h @@ -0,0 +1,61 @@ +/** + * @file volume_catcher.h + * @brief Interface to a class with platform-specific implementations that allows control of the audio volume of all sources in the current process. + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 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://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +#ifndef VOLUME_CATCHER_H +#define VOLUME_CATCHER_H + +#include "linden_common.h" + +class VolumeCatcherImpl; + +class VolumeCatcher +{ + public: + VolumeCatcher(); + ~VolumeCatcher(); + + void setVolume(F32 volume); // 0.0 - 1.0 + + // Set the left-right pan of audio sources + // where -1.0 = left, 0 = center, and 1.0 = right + void setPan(F32 pan); + + void pump(); // call this at least a few times a second if you can - it affects how quickly we can 'catch' a new audio source and adjust its volume + + private: + VolumeCatcherImpl *pimpl; +}; + +#endif // VOLUME_CATCHER_H diff --git a/indra/media_plugins/webkit/windows_volume_catcher.cpp b/indra/plugins/webkit/windows_volume_catcher.cpp similarity index 61% rename from indra/media_plugins/webkit/windows_volume_catcher.cpp rename to indra/plugins/webkit/windows_volume_catcher.cpp index 46a7a3db6..64f70c419 100644 --- a/indra/media_plugins/webkit/windows_volume_catcher.cpp +++ b/indra/plugins/webkit/windows_volume_catcher.cpp @@ -3,30 +3,39 @@ * @brief A Windows implementation of volume level control of all audio channels opened by a process. * * @cond - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 * - * 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://secondlife.com/developers/opensource/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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 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. * $/LicenseInfo$ + * * @endcond */ #include "volume_catcher.h" +# define WIN32_LEAN_AND_MEAN +# include #include #include "llmemory.h" class VolumeCatcherImpl : public LLSingleton From c46c86ca4bbad98c32a6350a34309389026ac551 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 6 May 2011 16:29:43 +0200 Subject: [PATCH 08/21] Split plugin classes and derive AIFilePicker from BasicPluginBase (part 4). Add back fixes that were in Singularity (in indra/media_plugins) but not in imprudence. Also: Add "shutdown" plugin message and terminate file picker plugins cleanly. The DSO (libbasic_plugin_filepicker.so) now tells the child process / plugin loader (SLPlugin) to terminate (using the 'shutdown' message), and AIFilePicker::finish_impl destroys the plugin object on the viewer side when it's ... finished thus. Also added a large comment that gives an overview of all classes involved on the viewer side. Additional fixes for filepicker. Plugin refactor bug fix: mDeleteMe was uninitialized in AIPluginFilePicker. --- indra/llplugin/llpluginclassbasic.h | 3 ++ indra/llplugin/llpluginprocesschild.cpp | 9 ++++ indra/llplugin/llpluginprocesschild.h | 2 +- indra/llplugin/llpluginprocessparent.cpp | 5 ++ indra/llplugin/llpluginprocessparent.h | 4 ++ indra/newview/llviewerwindow.cpp | 4 +- indra/newview/statemachine/aifilepicker.cpp | 6 ++- indra/newview/statemachine/aifilepicker.h | 53 ++++++++++++++++++- .../plugins/base_basic/basic_plugin_base.cpp | 22 ++++++-- indra/plugins/base_basic/basic_plugin_base.h | 3 ++ .../plugins/base_media/media_plugin_base.cpp | 1 - indra/plugins/example_basic/CMakeLists.txt | 4 +- indra/plugins/example_media/CMakeLists.txt | 4 +- indra/plugins/filepicker/CMakeLists.txt | 4 +- .../filepicker/basic_plugin_filepicker.cpp | 6 +++ indra/plugins/filepicker/llfilepicker.cpp | 2 +- .../media_plugin_gstreamer010.cpp | 2 +- .../quicktime/media_plugin_quicktime.cpp | 4 ++ indra/plugins/webkit/CMakeLists.txt | 4 +- indra/plugins/webkit/linux_volume_catcher.cpp | 3 +- indra/plugins/webkit/media_plugin_webkit.cpp | 39 ++------------ 21 files changed, 129 insertions(+), 55 deletions(-) diff --git a/indra/llplugin/llpluginclassbasic.h b/indra/llplugin/llpluginclassbasic.h index a9e4dba46..6c0602608 100644 --- a/indra/llplugin/llpluginclassbasic.h +++ b/indra/llplugin/llpluginclassbasic.h @@ -113,6 +113,9 @@ protected: // Inherited from LLPluginProcessParentOwner. /*virtual*/ void receivePluginMessage(LLPluginMessage const&); + // Inherited from LLPluginProcessParentOwner. + /*virtual*/ void receivedShutdown() { mPlugin->exitState(); } + //-------------------------------------- // Debug use only // diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp index 2c47087ab..f844c5a2b 100644 --- a/indra/llplugin/llpluginprocesschild.cpp +++ b/indra/llplugin/llpluginprocesschild.cpp @@ -553,6 +553,15 @@ void LLPluginProcessChild::receivePluginMessage(const std::string &message) } } } + else if (message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL) + { + std::string message_name = parsed.getName(); + if(message_name == "shutdown") + { + // The plugin is finished. + setState(STATE_UNLOADING); + } + } } if(passMessage) diff --git a/indra/llplugin/llpluginprocesschild.h b/indra/llplugin/llpluginprocesschild.h index 5d643d792..66082db7c 100644 --- a/indra/llplugin/llpluginprocesschild.h +++ b/indra/llplugin/llpluginprocesschild.h @@ -88,7 +88,7 @@ private: STATE_PLUGIN_LOADED, // plugin library has been loaded STATE_PLUGIN_INITIALIZING, // plugin is processing init message STATE_RUNNING, // steady state (processing messages) - STATE_UNLOADING, // plugin has sent shutdown_response and needs to be unloaded + STATE_UNLOADING, // plugin has sent shutdown and needs to be unloaded STATE_UNLOADED, // plugin has been unloaded STATE_ERROR, // generic bailout state STATE_DONE // state machine will sit in this state after either error or normal termination. diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index 35931426d..df2f0b512 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -1001,6 +1001,11 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) LL_DEBUGS("Plugin") << "cpu usage reported as " << mCPUUsage << LL_ENDL; } + else if(message_name == "shutdown") + { + LL_INFOS("Plugin") << "received shutdown message" << LL_ENDL; + mOwner->receivedShutdown(); + } else if(message_name == "shm_add_response") { // Nothing to do here. diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h index a82407d05..1ec5ef4c3 100644 --- a/indra/llplugin/llpluginprocessparent.h +++ b/indra/llplugin/llpluginprocessparent.h @@ -53,6 +53,7 @@ public: virtual ~LLPluginProcessParentOwner(); virtual void receivePluginMessage(const LLPluginMessage &message) = 0; virtual bool receivePluginMessageEarly(const LLPluginMessage &message) {return false;}; + virtual void receivedShutdown() = 0; // This will only be called when the plugin has died unexpectedly virtual void pluginLaunchFailed() {}; virtual void pluginDied() {}; @@ -88,6 +89,9 @@ public: // Go to the proper error state void errorState(void); + // Go to exit state. + void exitState(void) { setState(STATE_EXITING); } + void setSleepTime(F64 sleep_time, bool force_send = false); F64 getSleepTime(void) const { return mSleepTime; }; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 2139e75d8..2e684633c 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -4031,7 +4031,7 @@ void LLViewerWindow::saveImageNumbered(LLPointer image) void LLViewerWindow::saveImageNumbered_filepicker_callback(LLPointer image, std::string const& extension, AIFilePicker* filepicker, bool success) { llassert((bool)*filepicker == success); - if (success) + if (success && !filepicker->isCanceled()) { // Copy the directory + file name std::string filepath = filepicker->getFilename(); @@ -4041,7 +4041,7 @@ void LLViewerWindow::saveImageNumbered_filepicker_callback(LLPointerdeleteMe(); } void LLViewerWindow::saveImageNumbered_continued(LLPointer image, std::string const& extension) diff --git a/indra/newview/statemachine/aifilepicker.cpp b/indra/newview/statemachine/aifilepicker.cpp index cd6856100..35d875dfd 100644 --- a/indra/newview/statemachine/aifilepicker.cpp +++ b/indra/newview/statemachine/aifilepicker.cpp @@ -314,7 +314,11 @@ void AIFilePicker::abort_impl(void) void AIFilePicker::finish_impl(void) { - mPluginManager = NULL; // This deletes the plugin, since mPluginManager is a LLPointer. + if (mPluginManager) + { + mPluginManager->destroyPlugin(); + mPluginManager = NULL; + } mFilter.clear(); // Check that open is called before calling run (again). } diff --git a/indra/newview/statemachine/aifilepicker.h b/indra/newview/statemachine/aifilepicker.h index 008afb1d7..fd5bd0cc2 100644 --- a/indra/newview/statemachine/aifilepicker.h +++ b/indra/newview/statemachine/aifilepicker.h @@ -67,6 +67,56 @@ enum ESaveFilter FFSAVE_LSL }; +/* + + AIFilePicker is an AIStateMachine, that has a LLViewerPluginManager (mPluginManager) + to manage a plugin that runs the actual filepicker in a separate process. + + The relationship with the plugin is as follows: + +new AIFilePicker + + AIFilePicker::mPluginManager = new LLViewerPluginManager + + LLPluginProcessParentOwner LLPluginMessagePipeOwner + | | LLPluginMessagePipeOwner::mOwner = LLPluginProcessParentOwner + | | LLPluginMessagePipeOwner::mMessagePipe = LLPluginMessagePipe + | | LLPluginMessagePipeOwner::receiveMessageRaw calls + v | LLPluginProcessParent::receiveMessageRaw + LLPluginClassBasic v + | LLPluginClassBasic::mPlugin = new LLPluginProcessParent ---> new LLPluginMessagePipe + | LLPluginProcessParent::mOwner = (LLPluginProcessParentOwner*)LLPluginClassBasic + | LLPluginProcessParent::sendMessage calls + | LLPluginMessagePipeOwner::writeMessageRaw calls + | mMessagePipe->LLPluginMessagePipe::addMessage + | LLPluginProcessParent::receiveMessageRaw calls + | LLPluginProcessParent::receiveMessage calls + | LLPluginProcessParentOwner::receivePluginMessage == AIPluginFilePicker::receivePluginMessage + | + | LLPluginClassBasic::sendMessage calls mPlugin->LLPluginProcessParent::sendMessage + | + v + LLViewerPluginManager::mPluginBase = new AIPluginFilePicker + AIPluginFilePicker::receivePluginMessage calls + AIFilePicker::receivePluginMessage + + AIPluginFilePicker::mStateMachine = AIFilePicker + + Where the entry point to send messages to the plugin is LLPluginClassBasic::sendMessage, + and the end point for messages received from the plugin is AIFilePicker::receivePluginMessage. + + Termination happens by receiving the "canceled" or "done" message, + which sets the state to AIFilePicker_canceled or AIFilePicker_done + respectively, causing a call to AIStateMachine::finish(), which calls + AIFilePicker::finish_impl which destroys the plugin (mPluginBase) + and the plugin manager (mPluginManager). + + AIStateMachine::finish() also calls the registered callback function, + which should call AIStateMachine::deleteMe(), causing the AIFilePicker + to be deleted. + +*/ + // A file picker state machine. // // Before calling run(), call open() to pass needed parameters. @@ -151,7 +201,8 @@ public: static std::string launcher_name(void) { return gDirUtilp->getLLPluginLauncher(); } static char const* plugin_basename(void) { return "basic_plugin_filepicker"; } - /*virtual*/ void receivePluginMessage(LLPluginMessage const& message) { mStateMachine->receivePluginMessage(message); } + /*virtual*/ void receivePluginMessage(LLPluginMessage const& message) { mStateMachine->receivePluginMessage(message); } + /*virtual*/ void receivedShutdown(void) { /* Nothing -- we terminate on deletion (from AIStateMachine::mainloop) */ } private: AIFilePicker* mStateMachine; diff --git a/indra/plugins/base_basic/basic_plugin_base.cpp b/indra/plugins/base_basic/basic_plugin_base.cpp index 90b5964ef..d7d486502 100755 --- a/indra/plugins/base_basic/basic_plugin_base.cpp +++ b/indra/plugins/base_basic/basic_plugin_base.cpp @@ -46,10 +46,9 @@ /// /// @param[in] send_message_function Function for sending messages from plugin to plugin loader shell /// @param[in] plugin_instance Message data for messages from plugin to plugin loader shell -BasicPluginBase::BasicPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) +BasicPluginBase::BasicPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) : + mPluginInstance(plugin_instance), mSendMessageFunction(send_message_function), mDeleteMe(false) { - mSendMessageFunction = send_message_function; - mPluginInstance = plugin_instance; } /** @@ -79,6 +78,12 @@ void BasicPluginBase::staticReceiveMessage(char const* message_string, BasicPlug // This is the loaded DSO. // // Call this function to send 'message' to the viewer. +// Note: mSendMessageFunction points to LLPluginInstance::staticReceiveMessage, so indirectly this +// just calls LLPluginInstance::receiveMessage (mPluginInstance->receiveMessage) where +// mPluginInstance is the LLPluginInstance created in LLPluginProcessChild::idle during +// state STATE_PLUGIN_LOADING. That function then immediately calls mOwner->receivePluginMessage +// which is implemented as LLPluginProcessChild::receivePluginMessage, the same +// LLPluginProcessChild object that created the LLPluginInstance. /** * Send message to plugin loader shell. * @@ -91,6 +96,17 @@ void BasicPluginBase::sendMessage(const LLPluginMessage &message) mSendMessageFunction(output.c_str(), &mPluginInstance); } +/** + * Send shutdown message to the plugin loader shell. + * + * This will cause the SLPlugin process that loaded this DSO to be terminated. + */ +void BasicPluginBase::sendShutdownMessage(void) +{ + LLPluginMessage shutdownmessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shutdown"); + sendMessage(shutdownmessage); +} + #if LL_WINDOWS # define LLSYMEXPORT __declspec(dllexport) #elif LL_LINUX diff --git a/indra/plugins/base_basic/basic_plugin_base.h b/indra/plugins/base_basic/basic_plugin_base.h index bbabb18b7..f615fce87 100755 --- a/indra/plugins/base_basic/basic_plugin_base.h +++ b/indra/plugins/base_basic/basic_plugin_base.h @@ -57,6 +57,9 @@ public: // This function is actually called and then calls the member function above. static void staticReceiveMessage(char const* message_string, BasicPluginBase** self_ptr); + // Shoot down the whole process. + void sendShutdownMessage(void); + protected: void sendMessage(LLPluginMessage const& message); diff --git a/indra/plugins/base_media/media_plugin_base.cpp b/indra/plugins/base_media/media_plugin_base.cpp index 102d18996..20769f2f7 100755 --- a/indra/plugins/base_media/media_plugin_base.cpp +++ b/indra/plugins/base_media/media_plugin_base.cpp @@ -49,7 +49,6 @@ MediaPluginBase::MediaPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) : BasicPluginBase(send_message_function, plugin_instance) { - mDeleteMe = false; mPixels = 0; mWidth = 0; mHeight = 0; diff --git a/indra/plugins/example_basic/CMakeLists.txt b/indra/plugins/example_basic/CMakeLists.txt index fdc71a60c..98f5d1657 100644 --- a/indra/plugins/example_basic/CMakeLists.txt +++ b/indra/plugins/example_basic/CMakeLists.txt @@ -17,13 +17,13 @@ include_directories( ### basic_plugin_example -if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) +if(NOT WORD_SIZE EQUAL 32) if(WINDOWS) add_definitions(/FIXED:NO) else(WINDOWS) # not windows therefore gcc LINUX and DARWIN add_definitions(-fPIC) endif(WINDOWS) -endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4) +endif (NOT WORD_SIZE EQUAL 32) set(basic_plugin_example_SOURCE_FILES basic_plugin_example.cpp diff --git a/indra/plugins/example_media/CMakeLists.txt b/indra/plugins/example_media/CMakeLists.txt index 99859ae8a..0a8214eef 100644 --- a/indra/plugins/example_media/CMakeLists.txt +++ b/indra/plugins/example_media/CMakeLists.txt @@ -28,13 +28,13 @@ include_directories( ### media_plugin_example -if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) +if(NOT WORD_SIZE EQUAL 32) if(WINDOWS) add_definitions(/FIXED:NO) else(WINDOWS) # not windows therefore gcc LINUX and DARWIN add_definitions(-fPIC) endif(WINDOWS) -endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4) +endif (NOT WORD_SIZE EQUAL 32) set(media_plugin_example_SOURCE_FILES media_plugin_example.cpp diff --git a/indra/plugins/filepicker/CMakeLists.txt b/indra/plugins/filepicker/CMakeLists.txt index cea18360a..d3b233e01 100644 --- a/indra/plugins/filepicker/CMakeLists.txt +++ b/indra/plugins/filepicker/CMakeLists.txt @@ -19,13 +19,13 @@ include_directories( ### basic_plugin_filepicker -if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) +if(NOT WORD_SIZE EQUAL 32) if(WINDOWS) add_definitions(/FIXED:NO) else(WINDOWS) # not windows therefore gcc LINUX and DARWIN add_definitions(-fPIC) endif(WINDOWS) -endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4) + endif (NOT WORD_SIZE EQUAL 32) set(basic_plugin_filepicker_SOURCE_FILES basic_plugin_filepicker.cpp diff --git a/indra/plugins/filepicker/basic_plugin_filepicker.cpp b/indra/plugins/filepicker/basic_plugin_filepicker.cpp index bd415c64a..498dff3f1 100644 --- a/indra/plugins/filepicker/basic_plugin_filepicker.cpp +++ b/indra/plugins/filepicker/basic_plugin_filepicker.cpp @@ -150,6 +150,10 @@ void FilepickerPlugin::receiveMessage(char const* message_string) message.setValue("plugin_version", plugin_version); sendMessage(message); } + else if (message_name == "cleanup") + { + // We have no resources that need care. Just do nothing. + } else if (message_name == "idle") { // This whole message should not have existed imho -- Aleric @@ -213,6 +217,8 @@ void FilepickerPlugin::receiveMessage(char const* message_string) message.setValueLLSD("filenames", filenames); sendMessage(message); } + // We're done. Exit the whole application. + sendShutdownMessage(); } else { diff --git a/indra/plugins/filepicker/llfilepicker.cpp b/indra/plugins/filepicker/llfilepicker.cpp index f9a3ab8c1..1b54265ff 100644 --- a/indra/plugins/filepicker/llfilepicker.cpp +++ b/indra/plugins/filepicker/llfilepicker.cpp @@ -1048,7 +1048,7 @@ void LLFilePickerBase::chooser_responder(GtkWidget *widget, gint response, gpoin if (response == GTK_RESPONSE_ACCEPT) { GSList *file_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget)); - g_slist_foreach(file_list, (GFunc)add_to_selectedfiles, user_data); + g_slist_foreach(file_list, (GFunc)add_to_selectedfiles, picker); g_slist_foreach(file_list, (GFunc)g_free, NULL); g_slist_free (file_list); } diff --git a/indra/plugins/gstreamer010/media_plugin_gstreamer010.cpp b/indra/plugins/gstreamer010/media_plugin_gstreamer010.cpp index 8d0dc31e2..38a6805fe 100755 --- a/indra/plugins/gstreamer010/media_plugin_gstreamer010.cpp +++ b/indra/plugins/gstreamer010/media_plugin_gstreamer010.cpp @@ -652,7 +652,7 @@ bool MediaPluginGStreamer010::getTimePos(double &sec_out) { bool got_position = false; - if (mPlaybin) + if (mDoneInit && mPlaybin) { gint64 pos; GstFormat timefmt = GST_FORMAT_TIME; diff --git a/indra/plugins/quicktime/media_plugin_quicktime.cpp b/indra/plugins/quicktime/media_plugin_quicktime.cpp index a03cadf19..1c15b7483 100755 --- a/indra/plugins/quicktime/media_plugin_quicktime.cpp +++ b/indra/plugins/quicktime/media_plugin_quicktime.cpp @@ -40,6 +40,10 @@ #if defined(LL_DARWIN) #include #elif defined(LL_WINDOWS) + #undef __STDC_CONSTANT_MACROS //Needed, as boost/unordered_map.hpp already defines INT32_C, etc. + #if defined(_MSC_VER) && _MSC_VER >= 1600 + #define _STDINT_H //Visual Studio 2010 includes its own stdint header already + #endif #include "MacTypes.h" #include "QTML.h" #include "Movies.h" diff --git a/indra/plugins/webkit/CMakeLists.txt b/indra/plugins/webkit/CMakeLists.txt index 1ffa2608a..49ea3e7f6 100644 --- a/indra/plugins/webkit/CMakeLists.txt +++ b/indra/plugins/webkit/CMakeLists.txt @@ -51,9 +51,9 @@ set(media_plugin_webkit_LINK_LIBRARIES # Select which VolumeCatcher implementation to use if (LINUX) - if (PULSEAUDIO) + if (PULSEAUDIO_FOUND) list(APPEND media_plugin_webkit_SOURCE_FILES linux_volume_catcher.cpp) - endif (PULSEAUDIO) + endif (PULSEAUDIO_FOUND) list(APPEND media_plugin_webkit_LINK_LIBRARIES ${UI_LIBRARIES} # for glib/GTK ) diff --git a/indra/plugins/webkit/linux_volume_catcher.cpp b/indra/plugins/webkit/linux_volume_catcher.cpp index cc3836ea5..ca1b1cfa2 100644 --- a/indra/plugins/webkit/linux_volume_catcher.cpp +++ b/indra/plugins/webkit/linux_volume_catcher.cpp @@ -42,9 +42,8 @@ 5) Keep a list of all living audio players that we care about, adjust the volumes of all of them when we get a new setVolume() call */ -# include //imprudence - #include "linden_common.h" +# include #include "volume_catcher.h" diff --git a/indra/plugins/webkit/media_plugin_webkit.cpp b/indra/plugins/webkit/media_plugin_webkit.cpp index 97920cf9f..8067e25bc 100755 --- a/indra/plugins/webkit/media_plugin_webkit.cpp +++ b/indra/plugins/webkit/media_plugin_webkit.cpp @@ -79,20 +79,6 @@ extern "C" { } #endif -#ifdef LL_STANDALONE -#include -#elif defined(LL_LINUX) -// We don't provide Qt headers for non-standalone, therefore define this here. -// Our prebuilt is built with QT_NAMESPACE undefined. -#define QT_MANGLE_NAMESPACE(name) name -#define Q_INIT_RESOURCE(name) \ - do { extern int QT_MANGLE_NAMESPACE(qInitResources_ ## name) (); \ - QT_MANGLE_NAMESPACE(qInitResources_ ## name) (); } while (0) -#else -// Apparently this symbol doesn't exist in the windows and Mac tar balls provided by LL. -#define Q_INIT_RESOURCE(name) /*nothing*/ -#endif - //////////////////////////////////////////////////////////////////////////////// // class MediaPluginWebKit : @@ -138,6 +124,7 @@ private: F32 mBackgroundR; F32 mBackgroundG; F32 mBackgroundB; + std::string mTarget; VolumeCatcher mVolumeCatcher; @@ -326,11 +313,7 @@ private: LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled ); // create single browser window -#if LLQTWEBKIT_API_VERSION >= 2 - mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow(mWidth, mHeight /*, mTarget*/ ); // We don't have mTarget yet. -#else - mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight ); -#endif + mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow(mWidth, mHeight, mTarget); // tell LLQtWebKit about the size of the browser window LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); @@ -537,16 +520,9 @@ private: void onClickLinkHref(const EventType& event) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_href"); -#if LLQTWEBKIT_API_VERSION >= 2 message.setValue("uri", event.getEventUri()); message.setValue("target", event.getStringValue()); message.setValue("uuid", event.getStringValue2()); -#else - // This will work as long as we don't need "uuid", which will be needed for MoaP. - message.setValue("uri", event.getStringValue()); - message.setValue("target", event.getStringValue2()); - message.setValueU32("target_type", event.getLinkType()); -#endif sendMessage(message); } @@ -555,11 +531,7 @@ private: void onClickLinkNoFollow(const EventType& event) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow"); -#if LLQTWEBKIT_API_VERSION >= 2 message.setValue("uri", event.getEventUri()); -#else - message.setValue("uri", event.getStringValue()); -#endif sendMessage(message); } @@ -744,9 +716,6 @@ MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction send_ mJavascriptEnabled = true; // default to on mPluginsEnabled = true; // default to on mUserAgent = "LLPluginMedia Web Browser"; - - // Initialize WebCore resource. - Q_INIT_RESOURCE(WebCore); } MediaPluginWebKit::~MediaPluginWebKit() @@ -773,6 +742,8 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) { if(message_name == "init") { + mTarget = message_in.getValue("target"); + LLPluginMessage message("base", "init_response"); LLSD versions = LLSD::emptyMap(); versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; @@ -1082,7 +1053,7 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) else { // std::cerr << "MediaPluginWebKit::receiveMessage: unknown media message: " << message_string << std::endl; - }; + } } else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER) { From 80bbf5d0832ad9d59a9e437371e0bfe7de29d66b Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 8 May 2011 15:14:29 +0200 Subject: [PATCH 09/21] Fix underlinkage of libmedia_plugin_gstreamer.so Needed for the newer plugin code. This is basically just a copy of indra/cmake/GStreamer010Plugin.cmake from Imprudence. --- indra/cmake/GStreamer010Plugin.cmake | 56 ++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/indra/cmake/GStreamer010Plugin.cmake b/indra/cmake/GStreamer010Plugin.cmake index 2b45a1abc..90ed35c81 100644 --- a/indra/cmake/GStreamer010Plugin.cmake +++ b/indra/cmake/GStreamer010Plugin.cmake @@ -6,11 +6,13 @@ if (STANDALONE) pkg_check_modules(GSTREAMER010 REQUIRED gstreamer-0.10) pkg_check_modules(GSTREAMER010_PLUGINS_BASE REQUIRED gstreamer-plugins-base-0.10) -elseif (LINUX) + +else (STANDALONE) + + # Possibly libxml and glib should have their own .cmake file instead... use_prebuilt_binary(glib) # gstreamer needs glib - use_prebuilt_binary(gstreamer) - # possible libxml should have its own .cmake file instead use_prebuilt_binary(libxml) + use_prebuilt_binary(gstreamer) set(GSTREAMER010_FOUND ON FORCE BOOL) set(GSTREAMER010_PLUGINS_BASE_FOUND ON FORCE BOOL) set(GSTREAMER010_INCLUDE_DIRS @@ -18,23 +20,47 @@ elseif (LINUX) ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/glib-2.0 ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/libxml2 ) + +endif (STANDALONE) + +if (WINDOWS) # We don't need to explicitly link against gstreamer itself, because # LLMediaImplGStreamer probes for the system's copy at runtime. - set(GSTREAMER010_LIBRARIES - gobject-2.0 - gmodule-2.0 - dl - gthread-2.0 - rt - glib-2.0 - ) -endif (STANDALONE) + set(GSTREAMER010_LIBRARIES + libgstvideo + libgstaudio + libgstbase-0.10 + libgstreamer-0.10 + gobject-2.0 + gmodule-2.0 + gthread-2.0 + glib-2.0 + ) +else (WINDOWS) + # We don't need to explicitly link against gstreamer itself, because + # LLMediaImplGStreamer probes for the system's copy at runtime. + set(GSTREAMER010_LIBRARIES + gstvideo-0.10 + gstaudio-0.10 + gstbase-0.10 + gstreamer-0.10 + gobject-2.0 + gmodule-2.0 + dl + gthread-2.0 + rt + glib-2.0 + ) + + +endif (WINDOWS) + if (GSTREAMER010_FOUND AND GSTREAMER010_PLUGINS_BASE_FOUND) set(GSTREAMER010 ON CACHE BOOL "Build with GStreamer-0.10 streaming media support.") + add_definitions(-DLL_GSTREAMER010_ENABLED=1) endif (GSTREAMER010_FOUND AND GSTREAMER010_PLUGINS_BASE_FOUND) -if (GSTREAMER010) - add_definitions(-DLL_GSTREAMER010_ENABLED=1) -endif (GSTREAMER010) + + From 75ff0fc04d91771f3af035a0484e1b8f4bd0791b Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 8 May 2011 15:31:51 +0200 Subject: [PATCH 10/21] Add more support for debugging plugins. Added support for plugin debug messages and better error reporting when something goes wrong during start up of SLPlugin. Also added more debug output regarding general plugin messages as well as debug output related to AIFilePicker. --- indra/llcommon/llerror.cpp | 54 ++++-- indra/llcommon/llprocesslauncher.cpp | 179 ++++++++++++++++-- indra/llplugin/llpluginclassmedia.cpp | 2 +- indra/llplugin/llpluginmessage.h | 7 + indra/llplugin/llpluginprocesschild.cpp | 1 + indra/llplugin/llpluginprocessparent.cpp | 40 +++- indra/newview/app_settings/logcontrol.xml | 9 + indra/newview/llviewermedia.cpp | 2 + indra/newview/statemachine/aifilepicker.cpp | 2 + indra/newview/statemachine/aifilepicker.h | 4 + .../plugins/base_basic/basic_plugin_base.cpp | 21 ++ indra/plugins/base_basic/basic_plugin_base.h | 43 ++++- .../filepicker/basic_plugin_filepicker.cpp | 2 +- indra/plugins/filepicker/llfilepicker.cpp | 2 +- 14 files changed, 330 insertions(+), 38 deletions(-) diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index a9c4380a1..73ec354bc 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -46,6 +46,7 @@ # include #endif // !LL_WINDOWS #include +#include #include "llapp.h" #include "llapr.h" @@ -1076,28 +1077,49 @@ namespace LLError switch (site.mLevel) { - case LEVEL_DEBUG: prefix << "DEBUG: "; break; - case LEVEL_INFO: prefix << "INFO: "; break; - case LEVEL_WARN: prefix << "WARNING: "; break; - case LEVEL_ERROR: prefix << "ERROR: "; break; - default: prefix << "XXX: "; break; + case LEVEL_DEBUG: prefix << "DEBUG"; break; + case LEVEL_INFO: prefix << "INFO"; break; + case LEVEL_WARN: prefix << "WARNING"; break; + case LEVEL_ERROR: prefix << "ERROR"; break; + default: prefix << "XXX"; break; }; - - if (settings_w->printLocation) + + bool need_function = true; + if (site.mBroadTag && *site.mBroadTag != '\0') { - prefix << abbreviateFile(site.mFile) - << "(" << site.mLine << ") : "; + prefix << "(\"" << site.mBroadTag << "\")"; +#if LL_DEBUG + // Suppress printing mFunction if mBroadTag is set, starts with + // "Plugin " and ends with "child": a debug message from a plugin. + size_t taglen = strlen(site.mBroadTag); + if (taglen >= 12 && strncmp(site.mBroadTag, "Plugin ", 7) == 0 && + strcmp(site.mBroadTag + taglen - 5, "child") == 0) + { + need_function = false; + } +#endif } + + prefix << ": "; - #if LL_WINDOWS - // DevStudio: __FUNCTION__ already includes the full class name - #else - if (site.mClassInfo != typeid(NoClassInfo)) + if (need_function) { - prefix << className(site.mClassInfo) << "::"; + if (settings_w->printLocation) + { + prefix << abbreviateFile(site.mFile) + << "(" << site.mLine << ") : "; + } + +#if LL_WINDOWS + // DevStudio: __FUNCTION__ already includes the full class name +#else + if (need_function && site.mClassInfo != typeid(NoClassInfo)) + { + prefix << className(site.mClassInfo) << "::"; + } +#endif + prefix << site.mFunction << ": "; } - #endif - prefix << site.mFunction << ": "; if (site.mPrintOnce) { diff --git a/indra/llcommon/llprocesslauncher.cpp b/indra/llcommon/llprocesslauncher.cpp index e27aaa3c5..ee120fc94 100644 --- a/indra/llcommon/llprocesslauncher.cpp +++ b/indra/llcommon/llprocesslauncher.cpp @@ -32,7 +32,11 @@ #include "linden_common.h" +#include +#include +#include #include "llprocesslauncher.h" +#include "aiaprpool.h" #include #if LL_DARWIN || LL_LINUX @@ -142,6 +146,7 @@ bool LLProcessLauncher::isRunning(void) return (mProcessHandle != 0); } + bool LLProcessLauncher::kill(void) { bool result = true; @@ -201,6 +206,79 @@ static bool reap_pid(pid_t pid) return result; } +#if LL_DEBUG +// Define this to create a temporary pipe(2) between parent and child process, so +// that the child process can report error messages that it encounters when +// trying to execve(2). Most notably failing to start due to missing libraries +// or undefined symbols. +#define DEBUG_PIPE_CHILD_ERROR_REPORTING 1 +#endif + +#ifdef DEBUG_PIPE_CHILD_ERROR_REPORTING + +// Called by child process. +static void write_pipe(apr_file_t* out, char const* message) +{ + apr_size_t const bytes_to_write = strlen(message) + 1; // +1 for the length byte. + assert(bytes_to_write < 256); + char buf[256]; + strncpy(buf + 1, message, sizeof(buf) - 1); + *reinterpret_cast(buf) = bytes_to_write - 1; + + apr_size_t bytes_written; + apr_status_t status = apr_file_write_full(out, buf, bytes_to_write, &bytes_written); + if (status != APR_SUCCESS) + { + std::cerr << "apr_file_write_full: " << apr_strerror(status, buf, sizeof(buf)) << std::endl; + } + else if (bytes_written != bytes_to_write) + { + std::cerr << "apr_file_write_full: bytes_written (" << bytes_written << ") != bytes_to_write (" << bytes_to_write << ")!" << std::endl; + } + status = apr_file_flush(out); + if (status != APR_SUCCESS) + { + std::cerr << "apr_file_flush: " << apr_strerror(status, buf, sizeof(buf)) << std::endl; + } + +#ifdef _DEBUG + std::cerr << "apr_file_write_full: Wrote " << bytes_written << " bytes to the pipe." << std::endl; +#endif +} + +// Called by parent process. +static std::string read_pipe(apr_file_t* in, bool timeout_ok = false) +{ + char buf[256]; + unsigned char bytes_to_read; + apr_size_t bytes_read; + apr_status_t status = apr_file_read_full(in, &bytes_to_read, 1, &bytes_read); + if (status != APR_SUCCESS) + { + if (APR_STATUS_IS_TIMEUP(status) && timeout_ok) + { + return "TIMEOUT"; + } + llwarns << "apr_file_read_full: " << apr_strerror(status, buf, sizeof(buf)) << llendl; + assert(APR_STATUS_IS_EOF(status)); + return "END OF FILE"; + } + assert(bytes_read == 1); + status = apr_file_read_full(in, buf, bytes_to_read, &bytes_read); + if (status != APR_SUCCESS) + { + llwarns << "apr_file_read_full: " << apr_strerror(status, buf, sizeof(buf)) << llendl; + assert(status == APR_SUCCESS); // Fail + } + assert(bytes_read == bytes_to_read); + + std::string received(buf, bytes_read); + llinfos << "Received: \"" << received << "\" (bytes read: " << bytes_read << ")" << llendl; + return received; +} + +#endif // DEBUG_PIPE_CHILD_ERROR_REPORTING + int LLProcessLauncher::launch(void) { // If there was already a process associated with this object, kill it. @@ -237,23 +315,96 @@ int LLProcessLauncher::launch(void) } } - // flush all buffers before the child inherits them - ::fflush(NULL); - - pid_t id = vfork(); - if(id == 0) + pid_t id; { - // child process - - ::execv(mExecutable.c_str(), (char * const *)fake_argv); - - // If we reach this point, the exec failed. - // Use _exit() instead of exit() per the vfork man page. - _exit(0); +#ifdef DEBUG_PIPE_CHILD_ERROR_REPORTING + // Set up a pipe to the child process for error reporting. + apr_file_t* in; + apr_file_t* out; + AIAPRPool pool; + pool.create(); + apr_status_t status = apr_file_pipe_create_ex(&in, &out, APR_FULL_BLOCK, pool()); + assert(status == APR_SUCCESS); + bool success = (status == APR_SUCCESS); + if (success) + { + apr_interval_time_t const timeout = 10000000; // 10 seconds. + status = apr_file_pipe_timeout_set(in, timeout); + assert(status == APR_SUCCESS); + success = (status == APR_SUCCESS); + } +#endif // DEBUG_PIPE_CHILD_ERROR_REPORTING + + // flush all buffers before the child inherits them + ::fflush(NULL); + + id = vfork(); + if (id == 0) + { + // child process + +#ifdef DEBUG_PIPE_CHILD_ERROR_REPORTING + // Tell parent process we're about to call execv. + write_pipe(out, "CALLING EXECV"); +#ifdef _DEBUG + char const* display = getenv("DISPLAY"); + std::cerr << "Calling ::execv(\"" << mExecutable << '"'; + for(int j = 0; j < i; ++j) + std::cerr << ", \"" << fake_argv[j] << '"'; + std::cerr << ") with DISPLAY=\"" << (display ? display : "NULL") << '"' << std::endl; +#endif +#endif // DEBUG_PIPE_CHILD_ERROR_REPORTING + + ::execv(mExecutable.c_str(), (char * const *)fake_argv); + +#ifdef DEBUG_PIPE_CHILD_ERROR_REPORTING + status = APR_FROM_OS_ERROR(apr_get_os_error()); + char message[256]; + char errbuf[128]; + apr_strerror(status, errbuf, sizeof(errbuf)); + snprintf(message, sizeof(message), "Child process: execv: %s: %s", mExecutable.c_str(), errbuf); + write_pipe(out, message); +#ifdef _DEBUG + std::cerr << "::execv() failed." << std::endl; +#endif +#endif // DEBUG_PIPE_CHILD_ERROR_REPORTING + + // If we reach this point, the exec failed. + // Use _exit() instead of exit() per the vfork man page. + _exit(0); + } + + // parent process + +#ifdef DEBUG_PIPE_CHILD_ERROR_REPORTING + // Close unused pipe end. + apr_file_close(out); + + if (success) + { + // Attempt to do error reporting. + std::string message = read_pipe(in); + success = (message == "CALLING EXECV"); + assert(success); + if (success) + { + status = apr_file_pipe_timeout_set(in, 2000000); // Only wait 2 seconds. + message = read_pipe(in, true); + if (message != "TIMEOUT" && message != "END OF FILE") + { + // Most likely execv failed. + llwarns << message << llendl; + assert(false); // Fail in debug mode. + } + } + } + + // Clean up. + apr_file_close(in); +#endif // DEBUG_PIPE_CHILD_ERROR_REPORTING + } - // parent process - if(current_wd >= 0) { // restore the previous working directory diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 2bbad6011..c8b903e58 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -674,7 +674,7 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) mDirtyRect.unionWith(newDirtyRect); } - LL_DEBUGS("Plugin") << "adjusted incoming rect is: (" + LL_DEBUGS("PluginUpdated") << "adjusted incoming rect is: (" << newDirtyRect.mLeft << ", " << newDirtyRect.mTop << ", " << newDirtyRect.mRight << ", " diff --git a/indra/llplugin/llpluginmessage.h b/indra/llplugin/llpluginmessage.h index 3c7b91750..7bc4ea12d 100644 --- a/indra/llplugin/llpluginmessage.h +++ b/indra/llplugin/llpluginmessage.h @@ -100,6 +100,13 @@ public: // Returns -1 on failure, otherwise returns the number of key/value pairs in the message. int parse(const std::string &message); + enum LLPLUGIN_LOG_LEVEL { + LOG_LEVEL_DEBUG, + LOG_LEVEL_INFO, + LOG_LEVEL_WARN, + LOG_LEVEL_ERR, + }; + // For debugging purposes. friend std::ostream& operator<<(std::ostream& os, LLPluginMessage const& message) { return os << message.mMessage; } diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp index f844c5a2b..3ba765bf6 100644 --- a/indra/llplugin/llpluginprocesschild.cpp +++ b/indra/llplugin/llpluginprocesschild.cpp @@ -227,6 +227,7 @@ void LLPluginProcessChild::idle(void) case STATE_DONE: // just sit here. + LL_WARNS("Plugin") << "Calling LLPluginProcessChild::idle while in STATE_DONE!" << LL_ENDL; break; } diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index df2f0b512..fc94ee849 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -598,7 +598,16 @@ void LLPluginProcessParent::sendMessage(const LLPluginMessage &message) } std::string buffer = message.generate(); - LL_DEBUGS("Plugin") << "Sending: " << buffer << LL_ENDL; +#if LL_DEBUG + if (message.getName() == "mouse_event") + { + LL_DEBUGS("PluginMouseEvent") << "Sending: " << buffer << LL_ENDL; + } + else + { + LL_DEBUGS("Plugin") << "Sending: " << buffer << LL_ENDL; + } +#endif writeMessageRaw(buffer); // Try to send message immediately. @@ -876,7 +885,7 @@ void LLPluginProcessParent::servicePoll() // It parses the message and passes it on to LLPluginProcessParent::receiveMessage. void LLPluginProcessParent::receiveMessageRaw(const std::string &message) { - LL_DEBUGS("Plugin") << "Received: " << message << LL_ENDL; + LL_DEBUGS("PluginRaw") << "Received: " << message << LL_ENDL; LLPluginMessage parsed; if(parsed.parse(message) != -1) @@ -998,8 +1007,7 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) mCPUUsage = message.getValueReal("cpu_usage"); - LL_DEBUGS("Plugin") << "cpu usage reported as " << mCPUUsage << LL_ENDL; - + LL_DEBUGS("PluginHeartbeat") << "cpu usage reported as " << mCPUUsage << LL_ENDL; } else if(message_name == "shutdown") { @@ -1024,6 +1032,30 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) mSharedMemoryRegions.erase(iter); } } + else if(message_name == "log_message") + { + std::string msg=message.getValue("message"); + S32 level=message.getValueS32("log_level"); + + switch(level) + { + case LLPluginMessage::LOG_LEVEL_DEBUG: + LL_DEBUGS("Plugin child")< ShaderLoading Openjpeg + + Plugin + Plugin child + + PluginRaw child + + + + diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index ca21f6d0c..ced145d34 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -399,10 +399,12 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ if(LLFile::stat(launcher_name, &s)) { LL_WARNS("Media") << "Couldn't find launcher at " << launcher_name << LL_ENDL; + llassert(false); // Fail in debugging mode. } else if(LLFile::stat(plugin_name, &s)) { LL_WARNS("Media") << "Couldn't find plugin at " << plugin_name << LL_ENDL; + llassert(false); // Fail in debugging mode. } else { diff --git a/indra/newview/statemachine/aifilepicker.cpp b/indra/newview/statemachine/aifilepicker.cpp index 35d875dfd..33ef0c16c 100644 --- a/indra/newview/statemachine/aifilepicker.cpp +++ b/indra/newview/statemachine/aifilepicker.cpp @@ -338,10 +338,12 @@ void AIFilePicker::receivePluginMessage(const LLPluginMessage &message) std::string message_name = message.getName(); if (message_name == "canceled") { + LL_DEBUGS("Plugin") << "received message \"canceled\"" << LL_ENDL; set_state(AIFilePicker_canceled); } else if (message_name == "done") { + LL_DEBUGS("Plugin") << "received message \"done\"" << LL_ENDL; LLSD filenames = message.getValueLLSD("filenames"); mFilenames.clear(); for(LLSD::array_iterator filename = filenames.beginArray(); filename != filenames.endArray(); ++filename) diff --git a/indra/newview/statemachine/aifilepicker.h b/indra/newview/statemachine/aifilepicker.h index fd5bd0cc2..b1caf4701 100644 --- a/indra/newview/statemachine/aifilepicker.h +++ b/indra/newview/statemachine/aifilepicker.h @@ -175,6 +175,9 @@ private: std::string get_folder(std::string const& default_path, std::string const& context); protected: + // Call deleteMe(), not delete. + /*virtual*/ ~AIFilePicker() { LL_DEBUGS("Plugin") << "Calling AIFilePicker::~AIFilePicker()" << LL_ENDL; } + // Handle initializing the object. /*virtual*/ void initialize_impl(void); @@ -197,6 +200,7 @@ class AIPluginFilePicker : public LLPluginClassBasic { LOG_CLASS(AIPluginFilePicker); public: AIPluginFilePicker(AIFilePicker* state_machine) : mStateMachine(state_machine) { } + /*virtual*/ ~AIPluginFilePicker() { LL_DEBUGS("Plugin") << "Calling AIPluginFilePicker::~AIPluginFilePicker()" << LL_ENDL; } static std::string launcher_name(void) { return gDirUtilp->getLLPluginLauncher(); } static char const* plugin_basename(void) { return "basic_plugin_filepicker"; } diff --git a/indra/plugins/base_basic/basic_plugin_base.cpp b/indra/plugins/base_basic/basic_plugin_base.cpp index d7d486502..f791b5887 100755 --- a/indra/plugins/base_basic/basic_plugin_base.cpp +++ b/indra/plugins/base_basic/basic_plugin_base.cpp @@ -41,6 +41,9 @@ // TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint +// Used for logging. +BasicPluginBase* BasicPluginBase::sPluginBase; + //////////////////////////////////////////////////////////////////////////////// /// Basic plugin constructor. /// @@ -49,6 +52,8 @@ BasicPluginBase::BasicPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) : mPluginInstance(plugin_instance), mSendMessageFunction(send_message_function), mDeleteMe(false) { + llassert(!sPluginBase); + sPluginBase = this; } /** @@ -93,9 +98,25 @@ void BasicPluginBase::staticReceiveMessage(char const* message_string, BasicPlug void BasicPluginBase::sendMessage(const LLPluginMessage &message) { std::string output = message.generate(); + PLS_DEBUGS << "BasicPluginBase::sendMessage: Sending: " << output << PLS_ENDL; mSendMessageFunction(output.c_str(), &mPluginInstance); } +/** + * Send debug log message to plugin loader shell. + * + * @param[in] message Log message being sent to plugin loader shell + * @param[in] level Log message level, enum of LLPluginMessage::LLPLUGIN_LOG_LEVEL + * + */ +void BasicPluginBase::sendLogMessage(std::string const& message, LLPluginMessage::LLPLUGIN_LOG_LEVEL level) +{ + LLPluginMessage logmessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "log_message"); + logmessage.setValue("message", message); + logmessage.setValueS32("log_level",level); + mSendMessageFunction(logmessage.generate().c_str(), &mPluginInstance); +} + /** * Send shutdown message to the plugin loader shell. * diff --git a/indra/plugins/base_basic/basic_plugin_base.h b/indra/plugins/base_basic/basic_plugin_base.h index f615fce87..492b408e7 100755 --- a/indra/plugins/base_basic/basic_plugin_base.h +++ b/indra/plugins/base_basic/basic_plugin_base.h @@ -38,6 +38,8 @@ #include +#include + #include "linden_common.h" #include "llplugininstance.h" @@ -49,7 +51,7 @@ class BasicPluginBase public: BasicPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance); //! Basic plugin destructor. - virtual ~BasicPluginBase() {} + virtual ~BasicPluginBase() { sPluginBase = NULL; } //! Handle received message from plugin loader shell. virtual void receiveMessage(char const* message_string) = 0; @@ -57,6 +59,12 @@ public: // This function is actually called and then calls the member function above. static void staticReceiveMessage(char const* message_string, BasicPluginBase** self_ptr); + // Pointer to self used for logging (this object should be a singleton). + static BasicPluginBase* sPluginBase; + + // Used for log messages. Use macros below. + void sendLogMessage(std::string const& message, LLPluginMessage::LLPLUGIN_LOG_LEVEL level); + // Shoot down the whole process. void sendShutdownMessage(void); @@ -87,5 +95,38 @@ int create_plugin( LLPluginInstance* plugin_instance, BasicPluginBase** plugin_object); +#if LL_DEBUG +#define LL_DEBUG_PLUGIN_MESSAGES 1 +#else +#define LL_DEBUG_PLUGIN_MESSAGES 0 +#endif + +/** Convenience macros for calling BasicPluginBase::sendLogMessage. + * To log a message, use one of: + * @code + * PLS_DEBUGS << "Hello debug!" << PLS_ENDL; + * PLS_INFOS << "Hello info!" << PLS_ENDL; + * PLS_WARNS << "Hello warning!" << PLS_ENDL; + * PLS_ERRS << "Hello error!" << PLS_ENDL; + * @endcode + */ +#define PLS_LOG_MESSAGE(level, generate_code) \ + do { \ + if (generate_code && BasicPluginBase::sPluginBase) \ + { \ + LLPluginMessage::LLPLUGIN_LOG_LEVEL _pls_log_msg_level = level;\ + std::ostringstream _pls_log_msg_stream; \ + _pls_log_msg_stream +#define PLS_ENDL \ + LLError::End(); \ + BasicPluginBase::sPluginBase->sendLogMessage(_pls_log_msg_stream.str(), _pls_log_msg_level); \ + } \ + } while(0) +// Only send plugin log messages of level info and lower when compiled with debugging. +#define PLS_DEBUGS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_DEBUG, LL_DEBUG_PLUGIN_MESSAGES) +#define PLS_INFOS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_INFO, LL_DEBUG_PLUGIN_MESSAGES) +#define PLS_WARNS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_WARN, 1) +#define PLS_ERRS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_ERR, 1) + #endif // BASIC_PLUGIN_BASE diff --git a/indra/plugins/filepicker/basic_plugin_filepicker.cpp b/indra/plugins/filepicker/basic_plugin_filepicker.cpp index 498dff3f1..b30131479 100644 --- a/indra/plugins/filepicker/basic_plugin_filepicker.cpp +++ b/indra/plugins/filepicker/basic_plugin_filepicker.cpp @@ -140,7 +140,7 @@ void FilepickerPlugin::receiveMessage(char const* message_string) { if (message_name == "init") { - LLPluginMessage message("base", "init_response"); + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_BASE, "init_response"); LLSD versions = LLSD::emptyMap(); versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; versions[LLPLUGIN_MESSAGE_CLASS_BASIC] = LLPLUGIN_MESSAGE_CLASS_BASIC_VERSION; diff --git a/indra/plugins/filepicker/llfilepicker.cpp b/indra/plugins/filepicker/llfilepicker.cpp index 1b54265ff..b9c616195 100644 --- a/indra/plugins/filepicker/llfilepicker.cpp +++ b/indra/plugins/filepicker/llfilepicker.cpp @@ -56,7 +56,7 @@ namespace translation void add(std::string const& key, std::string const& translation) { - PLS_INFOS << "Adding translation \"" << key << "\" --> \"" << translation << "\"" << PLS_ENDL; + PLS_DEBUGS << "Adding translation \"" << key << "\" --> \"" << translation << "\"" << PLS_ENDL; translation_map[key] = translation; } } From 81550aa6de858c94b2ff3e914d83e187831e24fb Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 9 May 2011 20:22:04 +0200 Subject: [PATCH 11/21] Workaround for gcc 4.2.x. g++ 4.2 (and possibly earlier) apparently call a copy constructor when passing a temporary to a function that takes a const reference. Added code to allow copy-constructing the AI*Access classes for this compiler. g++-4.2.x also bails out when it encounters files that do not end on a newline. So, also added those where they were missing. --- indra/llaudio/llaudiodecodemgr.cpp | 2 +- indra/llcommon/aithreadsafe.h | 41 +++++++++++++++++++- indra/newview/ascentfloatercontactgroups.cpp | 2 +- indra/newview/ascentfloatercontactgroups.h | 2 +- indra/newview/ascentprefssys.h | 2 +- indra/newview/dsaparam.cpp | 2 +- indra/newview/emerald.cpp | 2 +- indra/newview/emeraldboobutils.cpp | 2 +- indra/newview/llpanelobject.cpp | 2 +- indra/newview/llphysicsmotion.cpp | 2 +- indra/newview/llpreviewgesture.cpp | 2 +- indra/newview/llpreviewscript.cpp | 2 +- indra/newview/llsavedsettingsglue.cpp | 2 +- indra/newview/qtoolalign.cpp | 2 +- indra/newview/qtoolalign.h | 2 +- indra/newview/scriptcounter.h | 2 +- indra/newview/wlfPanel_AdvSettings.cpp | 2 +- 17 files changed, 55 insertions(+), 18 deletions(-) diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index 908eca5a6..41930f539 100644 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -723,4 +723,4 @@ BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid) } return FALSE; -} \ No newline at end of file +} diff --git a/indra/llcommon/aithreadsafe.h b/indra/llcommon/aithreadsafe.h index 70cd2a3db..0163c01b2 100644 --- a/indra/llcommon/aithreadsafe.h +++ b/indra/llcommon/aithreadsafe.h @@ -36,6 +36,11 @@ #include "llthread.h" #include "llerror.h" +// g++ 4.2.x (and before?) have the bug that when you try to pass a temporary +// to a function taking a const reference, it still calls the copy constructor. +// Define this to hack around that. +#define AI_NEED_ACCESS_CC (defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ < 3)) || (__GNUC__ < 4))) + template struct AIReadAccessConst; template struct AIReadAccess; template struct AIWriteAccess; @@ -66,6 +71,10 @@ protected: // Accessors. T const* ptr() const { return reinterpret_cast(mMemory); } T* ptr() { return reinterpret_cast(mMemory); } + +#if AI_NEED_ACCESS_CC + int mAccessCopyCount; +#endif }; /** @@ -239,12 +248,18 @@ struct AIReadAccessConst mState(readlocked) { mWrapper.mRWLock.rdlock(); +#if AI_NEED_ACCESS_CC + mWrapper.mAccessCopyCount = 1; +#endif } //! Destruct the AI*Access object. // These should never be dynamically allocated, so there is no need to make this virtual. ~AIReadAccessConst() { +#if AI_NEED_ACCESS_CC + if (--(this->mWrapper.mAccessCopyCount) > 0) return; +#endif if (mState == readlocked) mWrapper.mRWLock.rdunlock(); else if (mState == writelocked) @@ -267,9 +282,14 @@ protected: AIThreadSafe& mWrapper; //!< Reference to the object that we provide access to. state_type const mState; //!< The lock state that mWrapper is in. +#if !AI_NEED_ACCESS_CC private: // Disallow copy constructing directly. AIReadAccessConst(AIReadAccessConst const&); +#else +public: + AIReadAccessConst(AIReadAccessConst const& orig) : mWrapper(orig.mWrapper), mState(orig.mState) { mWrapper.mAccessCopyCount++; } +#endif }; /** @@ -461,7 +481,13 @@ template struct AIAccess { //! Construct a AIAccess from a non-constant AIThreadSafeSimple. - AIAccess(AIThreadSafeSimple& wrapper) : mWrapper(wrapper) { this->mWrapper.mMutex.lock(); } + AIAccess(AIThreadSafeSimple& wrapper) : mWrapper(wrapper) + { + this->mWrapper.mMutex.lock(); +#if AI_NEED_ACCESS_CC + this->mWrapper.mAccessCopyCount = 1; +#endif + } //! Access the underlaying object for (read and) write access. T* operator->() const { return this->mWrapper.ptr(); } @@ -469,14 +495,25 @@ struct AIAccess //! Access the underlaying object for (read and) write access. T& operator*() const { return *this->mWrapper.ptr(); } - ~AIAccess() { this->mWrapper.mMutex.unlock(); } + ~AIAccess() + { +#if AI_NEED_ACCESS_CC + if (--(this->mWrapper.mAccessCopyCount) > 0) return; +#endif + this->mWrapper.mMutex.unlock(); + } protected: AIThreadSafeSimple& mWrapper; //!< Reference to the object that we provide access to. +#if !AI_NEED_ACCESS_CC private: // Disallow copy constructing directly. AIAccess(AIAccess const&); +#else +public: + AIAccess(AIAccess const& orig) : mWrapper(orig.mWrapper) { this->mWrapper.mAccessCopyCount++; } +#endif }; #endif diff --git a/indra/newview/ascentfloatercontactgroups.cpp b/indra/newview/ascentfloatercontactgroups.cpp index 80f8a2204..d4097493d 100644 --- a/indra/newview/ascentfloatercontactgroups.cpp +++ b/indra/newview/ascentfloatercontactgroups.cpp @@ -238,4 +238,4 @@ void ASFloaterContactGroups::populateGroupList() } } } -} \ No newline at end of file +} diff --git a/indra/newview/ascentfloatercontactgroups.h b/indra/newview/ascentfloatercontactgroups.h index ed9fa44f1..809dce781 100644 --- a/indra/newview/ascentfloatercontactgroups.h +++ b/indra/newview/ascentfloatercontactgroups.h @@ -92,4 +92,4 @@ email for me right now, also in all cap. my father not very understand of free softwares and he make a fun of RMS. -*/ \ No newline at end of file +*/ diff --git a/indra/newview/ascentprefssys.h b/indra/newview/ascentprefssys.h index f9e507d9f..d62c9bdf1 100644 --- a/indra/newview/ascentprefssys.h +++ b/indra/newview/ascentprefssys.h @@ -100,4 +100,4 @@ protected: BOOL mSpellDisplay; }; -#endif \ No newline at end of file +#endif diff --git a/indra/newview/dsaparam.cpp b/indra/newview/dsaparam.cpp index e4c89d40e..c7720e61e 100644 --- a/indra/newview/dsaparam.cpp +++ b/indra/newview/dsaparam.cpp @@ -66,4 +66,4 @@ DSA *get_dsa2048() if ((dsa->p == NULL) || (dsa->q == NULL) || (dsa->g == NULL)) { DSA_free(dsa); return(NULL); } return(dsa); - } \ No newline at end of file + } diff --git a/indra/newview/emerald.cpp b/indra/newview/emerald.cpp index 1d89381cf..c2a31a65f 100644 --- a/indra/newview/emerald.cpp +++ b/indra/newview/emerald.cpp @@ -590,4 +590,4 @@ EDSA::~EDSA() { delete mDSAImpl; mDSAImpl = NULL; -} \ No newline at end of file +} diff --git a/indra/newview/emeraldboobutils.cpp b/indra/newview/emeraldboobutils.cpp index 12b784a7e..6d1238976 100644 --- a/indra/newview/emeraldboobutils.cpp +++ b/indra/newview/emeraldboobutils.cpp @@ -186,4 +186,4 @@ EmeraldBoobState EmeraldBoobUtils::idleUpdate(const EmeraldGlobalBoobConfig &con newState.boobGrav = llclamp(newState.boobGrav, -1.5f, 2.0f); return newState; -} \ No newline at end of file +} diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index f6ce2fe3b..9f8561835 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -2771,4 +2771,4 @@ void LLPanelObject::onPasteRotClip(void* user_data) calcp->setVar(LLCalc::Y_ROT, mClipboardRot.mV[VY]); calcp->setVar(LLCalc::Z_ROT, mClipboardRot.mV[VZ]); self->sendRotation(FALSE); -} \ No newline at end of file +} diff --git a/indra/newview/llphysicsmotion.cpp b/indra/newview/llphysicsmotion.cpp index e9a127cf3..793f3fb1b 100644 --- a/indra/newview/llphysicsmotion.cpp +++ b/indra/newview/llphysicsmotion.cpp @@ -770,4 +770,4 @@ void LLPhysicsMotion::reset() mCharacter->setVisualParamWeight((*iter).mParam,(*iter).mParam->getDefaultWeight()); } } -} \ No newline at end of file +} diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index d26c7a64b..1c7a57abc 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -1789,4 +1789,4 @@ void LLPreviewGesture::onDonePreview(LLMultiGesture* gesture, void* data) self->mPreviewGesture = NULL; self->refresh(); -} \ No newline at end of file +} diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index ad54f5009..7b6bab72e 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -2669,4 +2669,4 @@ void LLLiveLSLEditor::saveAs() fclose(fp); fp = NULL; } -// \ No newline at end of file +// diff --git a/indra/newview/llsavedsettingsglue.cpp b/indra/newview/llsavedsettingsglue.cpp index 0e58b5f89..72879837f 100644 --- a/indra/newview/llsavedsettingsglue.cpp +++ b/indra/newview/llsavedsettingsglue.cpp @@ -182,4 +182,4 @@ void LLSavedSettingsGlue::setColor4(const std::string &name, LLColor4 value) gSavedSettings.setColor4(name, value); else gSavedPerAccountSettings.setColor4(name, value); -}*/ \ No newline at end of file +}*/ diff --git a/indra/newview/qtoolalign.cpp b/indra/newview/qtoolalign.cpp index 166c93f6a..b40faef40 100644 --- a/indra/newview/qtoolalign.cpp +++ b/indra/newview/qtoolalign.cpp @@ -626,4 +626,4 @@ void QToolAlign::align() LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_POSITION); -} \ No newline at end of file +} diff --git a/indra/newview/qtoolalign.h b/indra/newview/qtoolalign.h index 168453da4..1bef073ba 100644 --- a/indra/newview/qtoolalign.h +++ b/indra/newview/qtoolalign.h @@ -46,4 +46,4 @@ private: BOOL mForce; }; -#endif // Q_QTOOLALIGN_H \ No newline at end of file +#endif // Q_QTOOLALIGN_H diff --git a/indra/newview/scriptcounter.h b/indra/newview/scriptcounter.h index 3e1f305fd..b1c737189 100644 --- a/indra/newview/scriptcounter.h +++ b/indra/newview/scriptcounter.h @@ -75,4 +75,4 @@ private: static bool doDelete; static std::stringstream sstr; static int countingDone; -}; \ No newline at end of file +}; diff --git a/indra/newview/wlfPanel_AdvSettings.cpp b/indra/newview/wlfPanel_AdvSettings.cpp index 757853c61..aef9e3276 100644 --- a/indra/newview/wlfPanel_AdvSettings.cpp +++ b/indra/newview/wlfPanel_AdvSettings.cpp @@ -178,4 +178,4 @@ void wlfPanel_AdvSettings::onChangePresetName(LLUICtrl* ctrl, void * userData) current_preset = combo_box->getSelectedValue().asString(); LLWaterParamManager::instance()->loadPreset(current_preset); } -} \ No newline at end of file +} From e0b0fa4f58f83ea4c2c666f55637dfc994c66d15 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Tue, 10 May 2011 04:27:57 +0200 Subject: [PATCH 12/21] Use AIFilePicker everywhere. Also upgrade the file picker filters with the new extensions found in the orginal file picker code of Singularity. Also improve AIFilePicker a bit: added hasFilename() and now deleting the statemachine automatically by default: it's no longer needed to call deleteMe from the callback. --- indra/llvfs/lldir.cpp | 6 +- indra/newview/floaterlocalassetbrowse.cpp | 22 +- indra/newview/floaterlocalassetbrowse.h | 2 + indra/newview/llassetuploadresponders.cpp | 6 +- indra/newview/llfilepicker.cpp | 19 + indra/newview/llfilepicker.h | 4 - indra/newview/llfloaterblacklist.cpp | 27 +- indra/newview/llfloaterblacklist.h | 5 + indra/newview/llfloatercustomize.cpp | 34 +- indra/newview/llfloatercustomize.h | 3 + indra/newview/llfloaterfriends.cpp | 32 +- indra/newview/llfloaterfriends.h | 3 + indra/newview/llfloaterregioninfo.cpp | 39 +- indra/newview/llfloaterregioninfo.h | 3 + indra/newview/llfloatervfs.cpp | 31 +- indra/newview/llfloatervfs.h | 4 + indra/newview/llinventoryactions.cpp | 18 +- indra/newview/llinventorybackup.cpp | 125 +++--- indra/newview/llinventorybackup.h | 9 +- indra/newview/llinventorybridge.cpp | 33 +- indra/newview/llinventorybridge.h | 3 +- indra/newview/llpanelavatar.cpp | 17 +- indra/newview/llpanelavatar.h | 5 + indra/newview/llpanelpick.cpp | 35 +- indra/newview/llpanelpick.h | 5 +- indra/newview/llpreviewanim.cpp | 29 +- indra/newview/llpreviewanim.h | 3 + indra/newview/llpreviewnotecard.cpp | 22 +- indra/newview/llpreviewnotecard.h | 2 + indra/newview/llpreviewscript.cpp | 39 +- indra/newview/llpreviewscript.h | 3 + indra/newview/llpreviewsound.cpp | 28 +- indra/newview/llpreviewsound.h | 3 + indra/newview/llpreviewtexture.cpp | 18 +- indra/newview/llpreviewtexture.h | 2 + indra/newview/lluploaddialog.cpp | 4 - indra/newview/llviewermenu.cpp | 43 ++- indra/newview/llviewermenufile.cpp | 359 ++++++++---------- indra/newview/llviewermenufile.h | 2 - indra/newview/llviewerobjectbackup.cpp | 35 +- indra/newview/llviewerobjectbackup.h | 2 + indra/newview/llviewerwindow.cpp | 20 +- indra/newview/llviewerwindow.h | 4 +- indra/newview/statemachine/aifilepicker.cpp | 81 +++- indra/newview/statemachine/aifilepicker.h | 42 +- indra/newview/statemachine/aistatemachine.cpp | 19 +- .../filepicker/basic_plugin_filepicker.cpp | 56 ++- indra/plugins/filepicker/llfilepicker.cpp | 316 +++++++++++++-- indra/plugins/filepicker/llfilepicker.h | 39 +- 49 files changed, 1097 insertions(+), 564 deletions(-) diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 29b50d7bc..28fcd7751 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -524,7 +524,11 @@ std::string LLDir::getTempFilename() const std::string LLDir::getScrubbedFileName(const std::string uncleanFileName) { std::string name(uncleanFileName); - const std::string illegalChars(getForbiddenFileChars()); + std::string illegalChars(getForbiddenFileChars()); +#if LL_LINUX || LL_SOLARIS + // Spaces in filenames are REALLY annoying on UNIX. + illegalChars += ' '; +#endif // replace any illegal file chars with and underscore '_' for( unsigned int i = 0; i < illegalChars.length(); i++ ) { diff --git a/indra/newview/floaterlocalassetbrowse.cpp b/indra/newview/floaterlocalassetbrowse.cpp index 76fa162b1..c643c636d 100644 --- a/indra/newview/floaterlocalassetbrowse.cpp +++ b/indra/newview/floaterlocalassetbrowse.cpp @@ -55,7 +55,7 @@ this feature is still a work in progress. #include #include "llviewertexturelist.h" #include "llviewerobjectlist.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "llviewermenufile.h" #include "llfloaterimagepreview.h" #include "llfile.h" @@ -453,23 +453,27 @@ LocalAssetBrowser::~LocalAssetBrowser() void LocalAssetBrowser::AddBitmap() { - LLFilePicker& picker = LLFilePicker::instance(); - if ( !picker.getMultipleOpenFiles(LLFilePicker::FFLOAD_IMAGE) ) - { return; } + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(FFLOAD_IMAGE, "", "image", true); + filepicker->run(boost::bind(&LocalAssetBrowser::AddBitmap_continued, filepicker)); +} + +void LocalAssetBrowser::AddBitmap_continued(AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) + return; bool change_happened = false; - std::string filename = picker.getFirstFile(); - while( !filename.empty() ) + std::vector const& filenames(filepicker->getFilenames()); + for(std::vector::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) { - LocalBitmap* unit = new LocalBitmap( filename ); + LocalBitmap* unit = new LocalBitmap(*filename); if ( unit->getIfValidBool() ) { loaded_bitmaps.push_back( unit ); change_happened = true; } - - filename = picker.getNextFile(); } if ( change_happened ) diff --git a/indra/newview/floaterlocalassetbrowse.h b/indra/newview/floaterlocalassetbrowse.h index 6ee1c75a3..42620e314 100644 --- a/indra/newview/floaterlocalassetbrowse.h +++ b/indra/newview/floaterlocalassetbrowse.h @@ -169,6 +169,7 @@ class LocalBitmap */ +class AIFilePicker; class LocalAssetBrowser { @@ -181,6 +182,7 @@ class LocalAssetBrowser static void setLayerUpdated(bool toggle) { mLayerUpdated = toggle; } static void setSculptUpdated(bool toggle) { mSculptUpdated = toggle; } static void AddBitmap(void); + static void AddBitmap_continued(AIFilePicker* filepicker); static void DelBitmap( std::vector, S32 column = BITMAPLIST_COL_ID ); /* UpdateTextureCtrlList was made public cause texturectrl requests it once on spawn diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp index 042a53632..2992e60bd 100644 --- a/indra/newview/llassetuploadresponders.cpp +++ b/indra/newview/llassetuploadresponders.cpp @@ -293,7 +293,7 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) view->getPanel()->setSelection(content["new_inventory_item"].asUUID(), TAKE_FOCUS_NO); if((LLAssetType::AT_TEXTURE == asset_type || LLAssetType::AT_SOUND == asset_type) - && LLFilePicker::instance().getFileCount() <= FILE_COUNT_DISPLAY_THRESHOLD) + /* FIXME: && LLFilePicker::instance().getFileCount() <= FILE_COUNT_DISPLAY_THRESHOLD */) { view->getPanel()->openSelected(); } @@ -309,7 +309,8 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) // remove the "Uploading..." message LLUploadDialog::modalUploadFinished(); - + +#if 0 // FIXME: This needs to be done in some other way. // *FIX: This is a pretty big hack. What this does is check the // file picker if there are any more pending uploads. If so, // upload that file. @@ -339,6 +340,7 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) everyone_perms, display_name, callback, expected_upload_cost, userdata); } +#endif } LLSendTexLayerResponder::LLSendTexLayerResponder(const LLSD& post_data, diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index fb327e5a6..2a325c0bb 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -788,6 +788,15 @@ Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *c result = false; } } + else if (filter == FFLOAD_XML) + { + if (fileInfo.filetype != 'XML ' && + (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("xml"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) + ) + { + result = false; + } + } #ifdef _CORY_TESTING else if (filter == FFLOAD_GEOMETRY) { @@ -1327,6 +1336,12 @@ static std::string add_bvh_filter_to_gtkchooser(GtkWindow *picker) LLTrans::getString("animation_files") + " (*.bvh)"); } +static std::string add_xml_filter_to_gtkchooser(GtkWindow *picker) +{ + return add_simple_mime_filter_to_gtkchooser(picker, "text/xml", + LLTrans::getString("xml_file") + " (*.xml)"); +} + static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker) { GtkFileFilter *gfilter = gtk_file_filter_new(); @@ -1454,6 +1469,9 @@ BOOL LLFilePickerBase::getOpenFile( ELoadFilter filter ) case FFLOAD_IMAGE: filtername = add_imageload_filter_to_gtkchooser(picker); break; + case FFLOAD_XML: + filtername = add_xml_filter_to_gtkchooser(picker); + break; default:; break; } @@ -1530,6 +1548,7 @@ BOOL LLFilePickerBase::getOpenFile( ELoadFilter filter ) case FFLOAD_WAV: filename += ".wav"; break; case FFLOAD_IMAGE: filename += ".tga"; break; case FFLOAD_ANIM: filename += ".bvh"; break; + case FFLOAD_XML: filename += ".xml"; break; default: break; } mFiles.push_back(filename); diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index 8063a7068..3be94f8e0 100644 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -185,13 +185,9 @@ private: #if LL_DARWIN NavDialogCreationOptions mNavOptions; - std::vector mFileVector; - UInt32 mFileIndex; OSStatus doNavChooseDialog(ELoadFilter filter); OSStatus doNavSaveDialog(ESaveFilter filter, const std::string& filename); - void getFilePath(SInt32 index); - void getFileName(SInt32 index); static Boolean navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode); #endif diff --git a/indra/newview/llfloaterblacklist.cpp b/indra/newview/llfloaterblacklist.cpp index e565f1434..ff76ec407 100644 --- a/indra/newview/llfloaterblacklist.cpp +++ b/indra/newview/llfloaterblacklist.cpp @@ -7,7 +7,7 @@ #include "llsdserialize.h" #include "llscrolllistctrl.h" #include "llcheckboxctrl.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "llviewerwindow.h" #include "llviewercontrol.h" #include "lldate.h" @@ -241,10 +241,17 @@ void LLFloaterBlacklist::saveToDisk() //static void LLFloaterBlacklist::onClickSave(void* user_data) { - LLFilePicker& file_picker = LLFilePicker::instance(); - if(file_picker.getSaveFile( LLFilePicker::FFSAVE_BLACKLIST, LLDir::getScrubbedFileName("untitled.blacklist"))) + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open("untitled.blacklist", FFSAVE_BLACKLIST); + filepicker->run(boost::bind(&LLFloaterBlacklist::onClickSave_continued, filepicker)); +} + +//static +void LLFloaterBlacklist::onClickSave_continued(AIFilePicker* filepicker) +{ + if (filepicker->hasFilename()) { - std::string file_name = file_picker.getFirstFile(); + std::string file_name = filepicker->getFilename(); llofstream export_file(file_name); LLSD data; for(std::map::iterator iter = blacklist_entries.begin(); iter != blacklist_entries.end(); ++iter) @@ -259,10 +266,16 @@ void LLFloaterBlacklist::onClickSave(void* user_data) //static void LLFloaterBlacklist::onClickLoad(void* user_data) { - LLFilePicker& file_picker = LLFilePicker::instance(); - if(file_picker.getOpenFile(LLFilePicker::FFLOAD_BLACKLIST)) + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(FFLOAD_BLACKLIST); + filepicker->run(boost::bind(&LLFloaterBlacklist::onClickLoad_continued, filepicker)); +} + +void LLFloaterBlacklist::onClickLoad_continued(AIFilePicker* filepicker) +{ + if (filepicker->hasFilename()) { - std::string file_name = file_picker.getFirstFile(); + std::string file_name = filepicker->getFilename(); llifstream xml_file(file_name); if(!xml_file.is_open()) return; LLSD data; diff --git a/indra/newview/llfloaterblacklist.h b/indra/newview/llfloaterblacklist.h index c35185721..c718fd71d 100644 --- a/indra/newview/llfloaterblacklist.h +++ b/indra/newview/llfloaterblacklist.h @@ -2,6 +2,9 @@ #ifndef LL_LLFLOATERBLACKLIST_H #define LL_LLFLOATERBLACKLIST_H #include "llfloater.h" + +class AIFilePicker; + class LLFloaterBlacklist : LLFloater { public: @@ -42,7 +45,9 @@ private: static void onClickAdd(void* user_data); static void onClickClear(void* user_data); static void onClickSave(void* user_data); + static void onClickSave_continued(AIFilePicker* filepicker); static void onClickLoad(void* user_data); + static void onClickLoad_continued(AIFilePicker* filepicker); static void onClickCopyUUID(void* user_data); static void onClickRemove(void* user_data); diff --git a/indra/newview/llfloatercustomize.cpp b/indra/newview/llfloatercustomize.cpp index 6a313c2bd..b8e23f387 100644 --- a/indra/newview/llfloatercustomize.cpp +++ b/indra/newview/llfloatercustomize.cpp @@ -75,7 +75,7 @@ #include "llviewercontrol.h" #include "lluictrlfactory.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "hippogridmanager.h" using namespace LLVOAvatarDefines; @@ -1846,14 +1846,20 @@ void LLFloaterCustomize::setCurrentWearableType( EWearableType type ) // reX: new function void LLFloaterCustomize::onBtnImport( void* userdata ) { - LLFilePicker& file_picker = LLFilePicker::instance(); - if( !file_picker.getOpenFile( LLFilePicker::FFLOAD_XML ) ) - { - // User canceled import. - return; - } + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(FFLOAD_XML); + filepicker->run(boost::bind(&LLFloaterCustomize::onBtnImport_continued, filepicker)); +} - const std::string filename = file_picker.getFirstFile(); +void LLFloaterCustomize::onBtnImport_continued(AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) + { + // User canceled import. + return; + } + + const std::string filename = filepicker->getFilename(); FILE* fp = LLFile::fopen(filename, "rb"); @@ -1900,8 +1906,14 @@ void LLFloaterCustomize::onBtnImport( void* userdata ) // reX: new function void LLFloaterCustomize::onBtnExport( void* userdata ) { - LLFilePicker& file_picker = LLFilePicker::instance(); - if( !file_picker.getSaveFile( LLFilePicker::FFSAVE_XML ) ) + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open("", FFSAVE_XML); + filepicker->run(boost::bind(&LLFloaterCustomize::onBtnExport_continued, filepicker)); +} + +void LLFloaterCustomize::onBtnExport_continued(AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) { // User canceled export. return; @@ -1910,7 +1922,7 @@ void LLFloaterCustomize::onBtnExport( void* userdata ) LLViewerInventoryItem* item; BOOL is_modifiable; - const std::string filename = file_picker.getFirstFile(); + const std::string filename = filepicker->getFilename(); FILE* fp = LLFile::fopen(filename, "wb"); diff --git a/indra/newview/llfloatercustomize.h b/indra/newview/llfloatercustomize.h index ceb49af01..84af37f70 100644 --- a/indra/newview/llfloatercustomize.h +++ b/indra/newview/llfloatercustomize.h @@ -64,6 +64,7 @@ class LLVisualParam; class LLVisualParamReset; class LLWearableSaveAsDialog; class LLPanelEditWearable; +class AIFilePicker; ///////////////////////////////////////////////////////////////////// // LLFloaterCustomize @@ -110,7 +111,9 @@ public: static void onBtnMakeOutfit( void* userdata ); static void onMakeOutfitCommit( LLMakeOutfitDialog* dialog, void* userdata ); static void onBtnImport( void* userdata ); + static void onBtnImport_continued(AIFilePicker* filepicker); static void onBtnExport( void* userdata ); + static void onBtnExport_continued(AIFilePicker* filepicker); static void onTabChanged( void* userdata, bool from_click ); static void onTabPrecommit( void* userdata, bool from_click ); diff --git a/indra/newview/llfloaterfriends.cpp b/indra/newview/llfloaterfriends.cpp index 255547933..c210caa1c 100644 --- a/indra/newview/llfloaterfriends.cpp +++ b/indra/newview/llfloaterfriends.cpp @@ -67,7 +67,7 @@ #include "llvoiceclient.h" #include "llsdserialize.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "llviewermenufile.h" #include "llviewermenu.h" @@ -1080,17 +1080,22 @@ void LLPanelFriends::onClickRemove(void* user_data) void LLPanelFriends::onClickExport(void* user_data) { - LLPanelFriends* panelp = (LLPanelFriends*)user_data; std::string agn; gAgent.getName(agn); - std::string filename = agn+".friendlist"; - LLFilePicker& picker = LLFilePicker::instance(); - if(!picker.getSaveFile( LLFilePicker::FFSAVE_ALL, filename ) ) + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(agn + ".friendlist", FFSAVE_ALL); + filepicker->run(boost::bind(&LLPanelFriends::onClickExport_continued, user_data, filepicker)); +} + +void LLPanelFriends::onClickExport_continued(void* user_data, AIFilePicker* filepicker) +{ + if(!filepicker->hasFilename()) { // User canceled save. return; } - filename = picker.getFirstFile(); + std::string const filename = filepicker->getFilename(); + LLPanelFriends* panelp = (LLPanelFriends*)user_data; std::vector selected = panelp->mFriendsList->getAllData();//->getAllSelected(); LLSD llsd; @@ -1126,19 +1131,22 @@ void LLPanelFriends::onClickExport(void* user_data) bool LLPanelFriends::merging; void LLPanelFriends::onClickImport(void* user_data) +{ + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(); + filepicker->run(boost::bind(&LLPanelFriends::onClickImport_filepicker_continued, filepicker)); +} + //THIS CODE IS DESIGNED SO THAT EXP/IMP BETWEEN GRIDS WILL FAIL //because assuming someone having the same name on another grid is the same person is generally a bad idea //i might add the option to query the user as to intelligently detecting matching names on a alternative grid // jcool410 +void LLPanelFriends::onClickImport_filepicker_continued(AIFilePicker* filepicker) { - //LLPanelFriends* panelp = (LLPanelFriends*)user_data; - //is_agent_friend( - - const std::string filename = upload_pick((void*)LLFilePicker::FFLOAD_ALL); - - if (filename.empty()) + if (!filepicker->hasFilename()) return; + std::string filename = filepicker->getFilename(); llifstream importer(filename); LLSD data; LLSDSerialize::fromXMLDocument(data, importer); diff --git a/indra/newview/llfloaterfriends.h b/indra/newview/llfloaterfriends.h index 20c996d23..e6223cfb2 100644 --- a/indra/newview/llfloaterfriends.h +++ b/indra/newview/llfloaterfriends.h @@ -45,6 +45,7 @@ class LLFriendObserver; class LLRelationship; class LLScrollListItem; class LLScrollListCtrl; +class AIFilePicker; /** * @class LLPanelFriends @@ -144,7 +145,9 @@ private: static void onClickAddFriend(void* user_data); static void onClickRemove(void* user_data); static void onClickExport(void* user_data); + static void onClickExport_continued(void* user_data, AIFilePicker* filepicker); static void onClickImport(void* user_data); + static void onClickImport_filepicker_continued(AIFilePicker* filepicker); public: static void FriendImportState(LLUUID id, bool accepted); private: diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 03f616af3..994fe3ad3 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -53,7 +53,7 @@ #include "llbutton.h" #include "llcheckboxctrl.h" #include "llcombobox.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "llfloaterdaycycle.h" #include "llfloatergodtools.h" // for send_sim_wide_deletes() #include "llfloatertopobjects.h" // added to fix SL-32336 @@ -1324,41 +1324,50 @@ void LLPanelRegionTerrainInfo::onChangeSunHour(LLUICtrl* ctrl, void*) // static void LLPanelRegionTerrainInfo::onClickDownloadRaw(void* data) { - LLFilePicker& picker = LLFilePicker::instance(); - if (!picker.getSaveFile(LLFilePicker::FFSAVE_RAW, "terrain.raw")) + LLPanelRegionTerrainInfo* self = (LLPanelRegionTerrainInfo*)data; + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open("terrain.raw", FFSAVE_RAW); + filepicker->run(boost::bind(&LLPanelRegionTerrainInfo::onClickUploadRaw_continued, self, filepicker)); +} + +void LLPanelRegionTerrainInfo::onClickDownloadRaw_continued(AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) { - llwarns << "No file" << llendl; return; } - std::string filepath = picker.getFirstFile(); + std::string filepath = filepicker->getFilename(); gXferManager->expectFileForRequest(filepath); - LLPanelRegionTerrainInfo* self = (LLPanelRegionTerrainInfo*)data; strings_t strings; strings.push_back("download filename"); strings.push_back(filepath); LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); - self->sendEstateOwnerMessage(gMessageSystem, "terrain", invoice, strings); + sendEstateOwnerMessage(gMessageSystem, "terrain", invoice, strings); } // static void LLPanelRegionTerrainInfo::onClickUploadRaw(void* data) { - LLFilePicker& picker = LLFilePicker::instance(); - if (!picker.getOpenFile(LLFilePicker::FFLOAD_RAW)) - { - llwarns << "No file" << llendl; + LLPanelRegionTerrainInfo* self = (LLPanelRegionTerrainInfo*)data; + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(FFLOAD_RAW); + filepicker->run(boost::bind(&LLPanelRegionTerrainInfo::onClickUploadRaw_continued, self, filepicker)); +} + +void LLPanelRegionTerrainInfo::onClickUploadRaw_continued(AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) return; - } - std::string filepath = picker.getFirstFile(); + + std::string filepath = filepicker->getFilename(); gXferManager->expectFileForTransfer(filepath); - LLPanelRegionTerrainInfo* self = (LLPanelRegionTerrainInfo*)data; strings_t strings; strings.push_back("upload filename"); strings.push_back(filepath); LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); - self->sendEstateOwnerMessage(gMessageSystem, "terrain", invoice, strings); + sendEstateOwnerMessage(gMessageSystem, "terrain", invoice, strings); LLNotifications::instance().add("RawUploadStarted"); } diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index fd0d9ce63..5871e3525 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -51,6 +51,7 @@ class LLNameListCtrl; class LLSliderCtrl; class LLSpinCtrl; class LLTextBox; +class AIFilePicker; class LLPanelRegionGeneralInfo; class LLPanelRegionDebugInfo; @@ -242,7 +243,9 @@ protected: static void onChangeSunHour(LLUICtrl* ctrl, void*); static void onClickDownloadRaw(void*); + void onClickDownloadRaw_continued(AIFilePicker* filepicker); static void onClickUploadRaw(void*); + void onClickUploadRaw_continued(AIFilePicker* filepicker); static void onClickBakeTerrain(void*); bool callbackBakeTerrain(const LLSD& notification, const LLSD& response); }; diff --git a/indra/newview/llfloatervfs.cpp b/indra/newview/llfloatervfs.cpp index 2db5b3459..0192e76b9 100644 --- a/indra/newview/llfloatervfs.cpp +++ b/indra/newview/llfloatervfs.cpp @@ -4,7 +4,7 @@ #include "lluictrlfactory.h" #include "llscrolllistctrl.h" #include "llcheckboxctrl.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "lllocalinventory.h" #include "llviewerwindow.h" #include "llassetconverter.h" @@ -229,25 +229,32 @@ void LLFloaterVFS::setEditEnabled(bool enabled) childSetEnabled("reload_btn", enabled); // WORKS! childSetEnabled("remove_btn", enabled); } + // static void LLFloaterVFS::onClickAdd(void* user_data) { - LLFloaterVFS* floaterp = (LLFloaterVFS*)user_data; - if(!floaterp) return; - LLUUID asset_id; - LLAssetType::EType asset_type = LLAssetType::AT_NONE; - asset_id.generate(); - LLFilePicker& file_picker = LLFilePicker::instance(); - if(file_picker.getOpenFile(LLFilePicker::FFLOAD_ALL)) + if(!user_data) return; + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(); + filepicker->run(boost::bind(&LLFloaterVFS::onClickAdd_continued, user_data, filepicker)); +} + +// static +void LLFloaterVFS::onClickAdd_continued(void* user_data, AIFilePicker* filepicker) +{ + LLFloaterVFS* self = (LLFloaterVFS*)user_data; + if (filepicker->hasFilename()) { - std::string file_name = file_picker.getFirstFile(); + std::string file_name = filepicker->getFilename(); std::string temp_filename = file_name + ".tmp"; - asset_type = LLAssetConverter::convert(file_name, temp_filename); + LLAssetType::EType asset_type = LLAssetConverter::convert(file_name, temp_filename); if(asset_type == LLAssetType::AT_NONE) { // todo: show a warning return; } + LLUUID asset_id; + asset_id.generate(); S32 file_size; LLAPRFile fp; fp.open(temp_filename, LL_APR_RB, LLAPRFile::global, &file_size); @@ -302,8 +309,8 @@ void LLFloaterVFS::onClickAdd(void* user_data) file.mID = asset_id; file.mType = asset_type; file.mName = gDirUtilp->getBaseFileName(file_name, true); - floaterp->add(file); - /*if(floaterp->getChild("create_pretend_item")->get()) + self->add(file); + /*if(self->getChild("create_pretend_item")->get()) { LLLocalInventory::addItem(file.mName, (int)file.mType, file.mID, true); }*/ diff --git a/indra/newview/llfloatervfs.h b/indra/newview/llfloatervfs.h index 7218b1090..47d1d8755 100644 --- a/indra/newview/llfloatervfs.h +++ b/indra/newview/llfloatervfs.h @@ -3,6 +3,9 @@ #define LL_LLFLOATERVFS_H #include "llfloater.h" #include "llassettype.h" + +class AIFilePicker; + class LLFloaterVFS : LLFloater { typedef struct @@ -27,6 +30,7 @@ public: void reloadEntry(entry file); void removeEntry(); static void onClickAdd(void* user_data); + static void onClickAdd_continued(void* user_data, AIFilePicker* filepicker); static void onClickClear(void* user_data); static void onClickReloadAll(void* user_data); static void onCommitFileList(LLUICtrl* ctrl, void* user_data); diff --git a/indra/newview/llinventoryactions.cpp b/indra/newview/llinventoryactions.cpp index b770a531d..8b04a1744 100644 --- a/indra/newview/llinventoryactions.cpp +++ b/indra/newview/llinventoryactions.cpp @@ -91,6 +91,7 @@ #include "llinventorybackup.h" //#include "llcheats.h" //#include "llnotecardmagic.h" +#include "statemachine/aifilepicker.h" // const std::string NEW_LSL_NAME = "New Script"; // *TODO:Translate? (probably not) @@ -569,14 +570,19 @@ class LLLoadInvCacheFloater : public inventory_listener_t { LLInventoryModel* model = mPtr->getPanel()->getModel(); if(!model) return false; - LLFilePicker& file_picker = LLFilePicker::instance(); - if(file_picker.getOpenFile( LLFilePicker::FFLOAD_INVGZ )) - { - std::string file_name = file_picker.getFirstFile(); - LLLocalInventory::loadInvCache(file_name); - } + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(FFLOAD_INVGZ, "", "invgz"); + filepicker->run(boost::bind(&LLLoadInvCacheFloater::filepicker_callback, this, filepicker)); return true; } + + void filepicker_callback(AIFilePicker* filepicker) + { + if(filepicker->hasFilename()) + { + LLLocalInventory::loadInvCache(filepicker->getFilename()); + } + } }; class LLRefreshInvModel : public inventory_listener_t diff --git a/indra/newview/llinventorybackup.cpp b/indra/newview/llinventorybackup.cpp index a5ce38e59..c6f687272 100644 --- a/indra/newview/llinventorybackup.cpp +++ b/indra/newview/llinventorybackup.cpp @@ -4,7 +4,7 @@ #include "llinventorybackup.h" #include "llinventorymodel.h" #include "llviewerinventory.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "lldirpicker.h" #include "llviewertexturelist.h" // gTextureList #include "llagent.h" // gAgent @@ -158,64 +158,64 @@ bool LLInventoryBackup::itemIsFolder(LLInventoryItem* item) } // static -LLFilePicker::ESaveFilter LLInventoryBackup::getSaveFilter(LLInventoryItem* item) +ESaveFilter LLInventoryBackup::getSaveFilter(LLInventoryItem* item) { LLAssetType::EType type = item->getType(); EWearableType wear = (EWearableType)(item->getFlags() & 0xFF); switch(type) { case LLAssetType::AT_TEXTURE: - return LLFilePicker::FFSAVE_TGA; + return FFSAVE_TGA; case LLAssetType::AT_SOUND: - return LLFilePicker::FFSAVE_OGG; + return FFSAVE_OGG; case LLAssetType::AT_SCRIPT: case LLAssetType::AT_LSL_TEXT: - return LLFilePicker::FFSAVE_LSL; + return FFSAVE_LSL; case LLAssetType::AT_ANIMATION: - return LLFilePicker::FFSAVE_ANIMATN; + return FFSAVE_ANIMATN; case LLAssetType::AT_GESTURE: - return LLFilePicker::FFSAVE_GESTURE; + return FFSAVE_GESTURE; case LLAssetType::AT_NOTECARD: - return LLFilePicker::FFSAVE_NOTECARD; + return FFSAVE_NOTECARD; case LLAssetType::AT_LANDMARK: - return LLFilePicker::FFSAVE_LANDMARK; + return FFSAVE_LANDMARK; case LLAssetType::AT_BODYPART: case LLAssetType::AT_CLOTHING: switch(wear) { case WT_EYES: - return LLFilePicker::FFSAVE_EYES; + return FFSAVE_EYES; case WT_GLOVES: - return LLFilePicker::FFSAVE_GLOVES; + return FFSAVE_GLOVES; case WT_HAIR: - return LLFilePicker::FFSAVE_HAIR; + return FFSAVE_HAIR; case WT_JACKET: - return LLFilePicker::FFSAVE_JACKET; + return FFSAVE_JACKET; case WT_PANTS: - return LLFilePicker::FFSAVE_PANTS; + return FFSAVE_PANTS; case WT_SHAPE: - return LLFilePicker::FFSAVE_SHAPE; + return FFSAVE_SHAPE; case WT_SHIRT: - return LLFilePicker::FFSAVE_SHIRT; + return FFSAVE_SHIRT; case WT_SHOES: - return LLFilePicker::FFSAVE_SHOES; + return FFSAVE_SHOES; case WT_SKIN: - return LLFilePicker::FFSAVE_SKIN; + return FFSAVE_SKIN; case WT_SKIRT: - return LLFilePicker::FFSAVE_SKIRT; + return FFSAVE_SKIRT; case WT_SOCKS: - return LLFilePicker::FFSAVE_SOCKS; + return FFSAVE_SOCKS; case WT_UNDERPANTS: - return LLFilePicker::FFSAVE_UNDERPANTS; + return FFSAVE_UNDERPANTS; case WT_UNDERSHIRT: - return LLFilePicker::FFSAVE_UNDERSHIRT; + return FFSAVE_UNDERSHIRT; case WT_PHYSICS: - return LLFilePicker::FFSAVE_PHYSICS; + return FFSAVE_PHYSICS; default: - return LLFilePicker::FFSAVE_ALL; + return FFSAVE_ALL; } default: - return LLFilePicker::FFSAVE_ALL; + return FFSAVE_ALL; } } @@ -352,28 +352,9 @@ void LLInventoryBackup::imageCallback(BOOL success, return; } - LLFilePicker& file_picker = LLFilePicker::instance(); - if( !file_picker.getSaveFile( getSaveFilter(item), LLDir::getScrubbedFileName(item->getName())) ) - { - // User canceled or we failed to acquire save file. - return; - } - // remember the user-approved/edited file name. - std::string filename = file_picker.getFirstFile(); - - LLPointer image_tga = new LLImageTGA; - if( !image_tga->encode( src ) ) - { - LLSD args; - args["ERROR_MESSAGE"] = "Couldn't encode file."; - LLNotifications::instance().add("ErrorMessage", args); - } - else if( !image_tga->save( filename ) ) - { - LLSD args; - args["ERROR_MESSAGE"] = "Couldn't write file."; - LLNotifications::instance().add("ErrorMessage", args); - } + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(LLDir::getScrubbedFileName(item->getName()), getSaveFilter(item)); + filepicker->run(boost::bind(&LLInventoryBackup::imageCallback_continued, src, filepicker)); } else { @@ -381,6 +362,28 @@ void LLInventoryBackup::imageCallback(BOOL success, } } +void LLInventoryBackup::imageCallback_continued(LLImageRaw* src, AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) + return; + + std::string filename = filepicker->getFilename(); + + LLPointer image_tga = new LLImageTGA; + if( !image_tga->encode( src ) ) + { + LLSD args; + args["ERROR_MESSAGE"] = "Couldn't encode file."; + LLNotifications::instance().add("ErrorMessage", args); + } + else if( !image_tga->save( filename ) ) + { + LLSD args; + args["ERROR_MESSAGE"] = "Couldn't write file."; + LLNotifications::instance().add("ErrorMessage", args); + } +} + // static void LLInventoryBackup::assetCallback(LLVFS *vfs, const LLUUID& asset_uuid, @@ -402,7 +405,7 @@ void LLInventoryBackup::assetCallback(LLVFS *vfs, LLVFile file(vfs, asset_uuid, type, LLVFile::READ); S32 size = file.getSize(); - char* buffer = new char[size]; + char* buffer = new char[size]; // Deleted in assetCallback_continued. if (buffer == NULL) { llerrs << "Memory Allocation Failed" << llendl; @@ -413,18 +416,22 @@ void LLInventoryBackup::assetCallback(LLVFS *vfs, // Write it back out... - LLFilePicker& file_picker = LLFilePicker::instance(); - if( !file_picker.getSaveFile( getSaveFilter(item), LLDir::getScrubbedFileName(item->getName())) ) - { - // User canceled or we failed to acquire save file. - return; - } - // remember the user-approved/edited file name. - std::string filename = file_picker.getFirstFile(); + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(LLDir::getScrubbedFileName(item->getName()), getSaveFilter(item)); + filepicker->run(boost::bind(&LLInventoryBackup::assetCallback_continued, buffer, size, filepicker)); +} - std::ofstream export_file(filename.c_str(), std::ofstream::binary); - export_file.write(buffer, size); - export_file.close(); +// static +void LLInventoryBackup::assetCallback_continued(char* buffer, S32 size, AIFilePicker* filepicker) +{ + if (filepicker->hasFilename()) + { + std::string filename = filepicker->getFilename(); + std::ofstream export_file(filename.c_str(), std::ofstream::binary); + export_file.write(buffer, size); + export_file.close(); + } + delete [] buffer; } // static diff --git a/indra/newview/llinventorybackup.h b/indra/newview/llinventorybackup.h index 98664a9a5..16d9a3ad1 100644 --- a/indra/newview/llinventorybackup.h +++ b/indra/newview/llinventorybackup.h @@ -11,11 +11,10 @@ #include "llviewerinventory.h" #include "llfolderview.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "llviewertexture.h" #include "llfloater.h" - class LLInventoryBackupOrder { public: @@ -94,10 +93,12 @@ private: }; +class AIFilePicker; + class LLInventoryBackup { public: - static LLFilePicker::ESaveFilter getSaveFilter(LLInventoryItem* item); + static ESaveFilter getSaveFilter(LLInventoryItem* item); static std::string getExtension(LLInventoryItem* item); static std::string getUniqueFilename(std::string filename, std::string extension); static std::string getUniqueDirname(std::string dirname); @@ -120,10 +121,12 @@ private: S32 discard_level, BOOL final, void* userdata); + static void imageCallback_continued(LLImageRaw* src, AIFilePicker* filepicker); static void assetCallback(LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); + static void assetCallback_continued(char* buffer, S32 size, AIFilePicker* filepicker); static void climb(LLInventoryCategory* cat, std::vector& cats, std::vector& items); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 3455caf19..aa6dc78d5 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -99,6 +99,7 @@ //#include "llcheats.h" #include "dofloaterhex.h" #include "hgfloatertexteditor.h" +#include "statemachine/aifilepicker.h" // // Editing wearables from inventory is an include-hungry feature -.- -SG @@ -1106,30 +1107,26 @@ void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, else if("reupload" == action) { LLInventoryItem* item = model->getItem(mUUID); - if(!item) return; - - LLFilePicker& picker = LLFilePicker::instance(); - std::string filename; - - switch(item->getType()) + if (item && item->getType() == LLAssetType::AT_TEXTURE) { - case LLAssetType::AT_TEXTURE: - if(!picker.getOpenFile(LLFilePicker::FFLOAD_IMAGE)) - return; - filename = picker.getFirstFile(); - if(!filename.empty()) - { - LLFloaterImagePreview* floaterp = new LLFloaterImagePreview(filename, item); - LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_image_preview.xml"); - } - break; - default: - break; + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(FFLOAD_IMAGE, "", "image"); + filepicker->run(boost::bind(&LLItemBridge::showFloaterImagePreview, item, filepicker)); } } // } +// static +void LLItemBridge::showFloaterImagePreview(LLInventoryItem* item, AIFilePicker* filepicker) +{ + if (filepicker->hasFilename()) + { + LLFloaterImagePreview* floaterp = new LLFloaterImagePreview(filepicker->getFilename(), item); + LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_image_preview.xml"); + } +} + void LLItemBridge::selectItem() { LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem(); diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index a3108d653..2a668e684 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -277,6 +277,7 @@ protected: LLInventoryType::EType mInvType; }; +class AIFilePicker; class LLItemBridge : public LLInvFVBridge { @@ -303,7 +304,7 @@ public: virtual BOOL hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } - + static void showFloaterImagePreview(LLInventoryItem* item, AIFilePicker* filepicker); // override for LLInvFVBridge virtual void clearDisplayName() { mDisplayName.clear(); } diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 52e812924..9a3add526 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -1213,20 +1213,27 @@ void LLPanelAvatarPicks::onClickNew(void* data) } //Pick import and export - RK +// static void LLPanelAvatarPicks::onClickImport(void* data) { LLPanelAvatarPicks* self = (LLPanelAvatarPicks*)data; - LLPanelPick* panel_pick = new LLPanelPick(FALSE); - LLTabContainer* tabs = self->getChild("picks tab"); + self->mPanelPick = new LLPanelPick(FALSE); + self->mPanelPick->importNewPick(&LLPanelAvatarPicks::onClickImport_continued, data); +} - bool import = panel_pick->importNewPick(); - if(tabs && import) +// static +void LLPanelAvatarPicks::onClickImport_continued(void* data, bool import) +{ + LLPanelAvatarPicks* self = (LLPanelAvatarPicks*)data; + LLTabContainer* tabs = self->getChild("picks tab"); + if(tabs && import && self->mPanelPick) { - tabs->addTabPanel(panel_pick, panel_pick->getPickName()); + tabs->addTabPanel(self->mPanelPick, self->mPanelPick->getPickName()); tabs->selectLastTab(); } } +// static void LLPanelAvatarPicks::onClickExport(void* data) { LLPanelAvatarPicks* self = (LLPanelAvatarPicks*)data; diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h index 24de55cb5..812da4571 100644 --- a/indra/newview/llpanelavatar.h +++ b/indra/newview/llpanelavatar.h @@ -57,6 +57,7 @@ class LLViewerObject; class LLMessageSystem; class LLIconCtrl; class LLMediaCtrl; +class LLPanelPick; enum EOnlineStatus { @@ -265,9 +266,13 @@ private: //Pick import and export - RK static void onClickImport(void* data); + static void onClickImport_continued(void* self, bool import); static void onClickExport(void* data); bool callbackDelete(const LLSD& notification, const LLSD& response); + + // Used to pass it from onClickImport to onClickImport_continued. + LLPanelPick* mPanelPick; }; diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index c7f31f1f9..9af829e9b 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -66,7 +66,7 @@ //For pick import and export - RK -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "llviewernetwork.h" #include "llsdserialize.h" #include "hippogridmanager.h" @@ -201,14 +201,19 @@ void LLPanelPick::initNewPick() } //Imports a new pick from an xml - RK -bool LLPanelPick::importNewPick() +void LLPanelPick::importNewPick(void (*callback)(void*, bool), void* data) { - LLFilePicker& file_picker = LLFilePicker::instance(); + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(FFLOAD_XML, "", "export"); + filepicker->run(boost::bind(&LLPanelPick::importNewPick_continued, this, callback, data, filepicker)); +} - if(!file_picker.getOpenFile(LLFilePicker::FFLOAD_XML)) return false;// User canceled load. - else +void LLPanelPick::importNewPick_continued(void (*callback)(void*, bool), void* data, AIFilePicker* filepicker) +{ + bool result = false; + if (filepicker->hasFilename()) { - std::string file = file_picker.getFirstFile(); + std::string file = filepicker->getFilename(); llifstream importer(file); LLSD data; @@ -231,19 +236,25 @@ bool LLPanelPick::importNewPick() mImporting = true; sendPickInfoUpdate(); - return true; + result = true; } + (*callback)(data, result); } //Exports a pick to an XML - RK void LLPanelPick::exportPick() { - LLFilePicker& file_picker = LLFilePicker::instance(); - - if(!file_picker.getSaveFile(LLFilePicker::FFSAVE_XML)) - return;// User canceled save. + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open("", FFSAVE_XML, "", "export"); + filepicker->run(boost::bind(&LLPanelPick::exportPick_continued, this, filepicker)); +} - std::string destination = file_picker.getFirstFile(); +void LLPanelPick::exportPick_continued(AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) + return; + + std::string destination = filepicker->getFilename(); LLSD datas; diff --git a/indra/newview/llpanelpick.h b/indra/newview/llpanelpick.h index 49b07924c..bfa702251 100644 --- a/indra/newview/llpanelpick.h +++ b/indra/newview/llpanelpick.h @@ -50,6 +50,7 @@ class LLTextEditor; class LLTextureCtrl; class LLUICtrl; class LLMessageSystem; +class AIFilePicker; class LLPanelPick : public LLPanel { @@ -70,8 +71,10 @@ public: void initNewPick(); //Pick import and export - RK - bool importNewPick(); + void importNewPick(void (*callback)(void*, bool), void* data); + void importNewPick_continued(void (*callback)(void*, bool), void* data, AIFilePicker* filepicker); void exportPick(); + void exportPick_continued(AIFilePicker* filepicker); // We need to know the creator id so the database knows which partition // to query for the pick data. diff --git a/indra/newview/llpreviewanim.cpp b/indra/newview/llpreviewanim.cpp index f9cb5f210..85349883b 100644 --- a/indra/newview/llpreviewanim.cpp +++ b/indra/newview/llpreviewanim.cpp @@ -40,7 +40,7 @@ #include "llvoavatar.h" #include "llagent.h" // gAgent #include "llkeyframemotion.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "lllineeditor.h" #include "lluictrlfactory.h" #include "lluictrlfactory.h" @@ -392,21 +392,22 @@ void LLPreviewAnim::gotAssetForSave(LLVFS *vfs, // Write it back out... - LLFilePicker& file_picker = LLFilePicker::instance(); - if( !file_picker.getSaveFile( LLFilePicker::FFSAVE_ANIMATN, LLDir::getScrubbedFileName(self->getItem()->getName())) ) - { - // User canceled or we failed to acquire save file. - return; - } - // remember the user-approved/edited file name. - std::string filename = file_picker.getFirstFile(); + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(LLDir::getScrubbedFileName(self->getItem()->getName()) + ".animatn", FFSAVE_ANIMATN); + filepicker->run(boost::bind(&LLPreviewAnim::gotAssetForSave_continued, buffer, size, filepicker)); +} - std::ofstream export_file(filename.c_str(), std::ofstream::binary); - export_file.write(buffer, size); - export_file.close(); - +// static +void LLPreviewAnim::gotAssetForSave_continued(char* buffer, S32 size, AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) + { + std::string filename = filepicker->getFilename(); + std::ofstream export_file(filename.c_str(), std::ofstream::binary); + export_file.write(buffer, size); + export_file.close(); + } delete[] buffer; - buffer = NULL; } // virtual diff --git a/indra/newview/llpreviewanim.h b/indra/newview/llpreviewanim.h index 115419fb6..3b9c53025 100644 --- a/indra/newview/llpreviewanim.h +++ b/indra/newview/llpreviewanim.h @@ -36,6 +36,8 @@ #include "llpreview.h" #include "llcharacter.h" +class AIFilePicker; + class LLPreviewAnim : public LLPreview { public: @@ -59,6 +61,7 @@ public: const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); + static void gotAssetForSave_continued(char* buffer, S32 size, AIFilePicker* filepicker); static void copyAnimID(void* userdata); // static void endAnimCallback( void *userdata ); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index c2c3e8869..6acdc32ba 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -61,7 +61,7 @@ #include "lllineeditor.h" #include "lluictrlfactory.h" // -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" // ///---------------------------------------------------------------------------- @@ -717,17 +717,18 @@ void LLPreviewNotecard::saveAs() if(item) { // gAssetStorage->getAssetData(item->getAssetUUID(), LLAssetType::AT_NOTECARD, LLPreviewNotecard::gotAssetForSave, this, TRUE); - default_filename = LLDir::getScrubbedFileName(item->getName()); + default_filename = LLDir::getScrubbedFileName(item->getName()) + ".notecard"; } - LLFilePicker& file_picker = LLFilePicker::instance(); - if( !file_picker.getSaveFile( LLFilePicker::FFSAVE_NOTECARD, default_filename ) ) - { - // User canceled or we failed to acquire save file. + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(default_filename, FFSAVE_NOTECARD); + filepicker->run(boost::bind(&LLPreviewNotecard::saveAs_continued, this, filepicker)); +} + +void LLPreviewNotecard::saveAs_continued(AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) return; - } - // remember the user-approved/edited file name. - std::string filename = file_picker.getFirstFile(); LLViewerTextEditor* editor = getChild("Notecard Editor"); @@ -738,8 +739,9 @@ void LLPreviewNotecard::saveAs() return; } - S32 size = buffer.length() + 1; + S32 size = buffer.length(); + std::string filename = filepicker->getFilename(); std::ofstream export_file(filename.c_str(), std::ofstream::binary); export_file.write(buffer.c_str(), size); export_file.close(); diff --git a/indra/newview/llpreviewnotecard.h b/indra/newview/llpreviewnotecard.h index c3bf0e2f9..04568bf65 100644 --- a/indra/newview/llpreviewnotecard.h +++ b/indra/newview/llpreviewnotecard.h @@ -46,6 +46,7 @@ class LLTextEditor; class LLViewerTextEditor; class LLButton; +class AIFilePicker; class LLPreviewNotecard : public LLPreview { @@ -128,6 +129,7 @@ protected: // virtual BOOL canSaveAs() const; virtual void saveAs(); + void saveAs_continued(AIFilePicker* filepicker); // }; diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 7b6bab72e..a58f1180e 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -54,6 +54,7 @@ #include "lltextbox.h" #include "lltooldraganddrop.h" #include "llvfile.h" +#include "statemachine/aifilepicker.h" #include "llagent.h" #include "llnotify.h" @@ -1700,23 +1701,23 @@ void LLPreviewLSL::saveAs() const LLInventoryItem *item = getItem(); if(item) { - default_filename = LLDir::getScrubbedFileName(item->getName()); + default_filename = LLDir::getScrubbedFileName(item->getName()) + ".lsl"; } - LLFilePicker& file_picker = LLFilePicker::instance(); - if( !file_picker.getSaveFile( LLFilePicker::FFSAVE_LSL, default_filename ) ) - { - // User canceled or we failed to acquire save file. + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(default_filename, FFSAVE_LSL); + filepicker->run(boost::bind(&LLPreviewLSL::saveAs_continued, this, filepicker)); +} + +void LLPreviewLSL::saveAs_continued(AIFilePicker* filepicker) +{ + if(!filepicker->hasFilename()) return; - } - // remember the user-approved/edited file name. - std::string filename = file_picker.getFirstFile(); std::string utf8text = mScriptEd->mEditor->getText(); - LLFILE* fp = LLFile::fopen(filename, "wb"); + LLFILE* fp = LLFile::fopen(filepicker->getFilename(), "wb"); fputs(utf8text.c_str(), fp); fclose(fp); - fp = NULL; } // /// --------------------------------------------------------------------------- @@ -2654,19 +2655,19 @@ void LLLiveLSLEditor::saveAs() default_filename = LLDir::getScrubbedFileName(item->getName()); } - LLFilePicker& file_picker = LLFilePicker::instance(); - if( !file_picker.getSaveFile( LLFilePicker::FFSAVE_LSL, default_filename ) ) - { - // User canceled or we failed to acquire save file. + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(default_filename, FFSAVE_LSL); + filepicker->run(boost::bind(&LLLiveLSLEditor::saveAs_continued, this, filepicker)); +} + +void LLLiveLSLEditor::saveAs_continued(AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) return; - } - // remember the user-approved/edited file name. - std::string filename = file_picker.getFirstFile(); std::string utf8text = mScriptEd->mEditor->getText(); - LLFILE* fp = LLFile::fopen(filename, "wb"); + LLFILE* fp = LLFile::fopen(filepicker->getFilename(), "wb"); fputs(utf8text.c_str(), fp); fclose(fp); - fp = NULL; } // diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 58959e445..aa03a6c2e 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -53,6 +53,7 @@ struct LLEntryAndEdCore; class LLMenuBarGL; class LLFloaterScriptSearch; class LLKeywordToken; +class AIFilePicker; // Inner, implementation class. LLPreviewScript and LLLiveLSLEditor each own one of these. class LLScriptEdCore : public LLPanel, public LLEventTimer @@ -189,6 +190,7 @@ protected: // virtual BOOL canSaveAs() const; virtual void saveAs(); + void saveAs_continued(AIFilePicker* filepicker); // static void onSearchReplace(void* userdata); @@ -261,6 +263,7 @@ protected: // virtual BOOL canSaveAs() const; virtual void saveAs(); + void saveAs_continued(AIFilePicker* filepicker); // static void onSearchReplace(void* userdata); diff --git a/indra/newview/llpreviewsound.cpp b/indra/newview/llpreviewsound.cpp index e40dddbd2..890979578 100644 --- a/indra/newview/llpreviewsound.cpp +++ b/indra/newview/llpreviewsound.cpp @@ -48,7 +48,7 @@ #include "llchat.h" #include "llfloaterchat.h" #include "llviewerwindow.h" // for alert -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" // for ambient play: #include "llviewerregion.h" // @@ -330,18 +330,22 @@ void LLPreviewSound::gotAssetForSave(LLVFS *vfs, // Write it back out... - LLFilePicker& file_picker = LLFilePicker::instance(); - if( !file_picker.getSaveFile( LLFilePicker::FFSAVE_OGG, LLDir::getScrubbedFileName(self->getItem()->getName())) ) - { - // User canceled or we failed to acquire save file. - return; - } - // remember the user-approved/edited file name. - std::string filename = file_picker.getFirstFile(); + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(LLDir::getScrubbedFileName(self->getItem()->getName()) + ".ogg", FFSAVE_OGG); + filepicker->run(boost::bind(&LLPreviewSound::gotAssetForSave_continued, buffer, size, filepicker)); +} - std::ofstream export_file(filename.c_str(), std::ofstream::binary); - export_file.write(buffer, size); - export_file.close(); +// static +void LLPreviewSound::gotAssetForSave_continued(char* buffer, S32 size, AIFilePicker* filepicker) +{ + if (filepicker->hasFilename()) + { + std::string filename = filepicker->getFilename(); + std::ofstream export_file(filename.c_str(), std::ofstream::binary); + export_file.write(buffer, size); + export_file.close(); + } + delete [] buffer; } // virtual diff --git a/indra/newview/llpreviewsound.h b/indra/newview/llpreviewsound.h index 535b233c9..bd8b9c0f0 100644 --- a/indra/newview/llpreviewsound.h +++ b/indra/newview/llpreviewsound.h @@ -35,6 +35,8 @@ #include "llpreview.h" +class AIFilePicker; + class LLPreviewSound : public LLPreview { public: @@ -59,6 +61,7 @@ public: const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); + static void gotAssetForSave_continued(char* buffer, S32 size, AIFilePicker* filepicker); // protected: diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index 8eb803401..2e52f3d47 100644 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -37,7 +37,7 @@ #include "llagent.h" #include "llbutton.h" #include "llcombobox.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "llimagetga.h" #include "llinventoryview.h" #include "llinventory.h" @@ -380,15 +380,19 @@ void LLPreviewTexture::saveAs() if( mLoadingFullImage ) return; - LLFilePicker& file_picker = LLFilePicker::instance(); const LLViewerInventoryItem* item = getItem() ; - if( !file_picker.getSaveFile( LLFilePicker::FFSAVE_TGA, item ? LLDir::getScrubbedFileName(item->getName()) : LLStringUtil::null) ) - { - // User canceled or we failed to acquire save file. + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(item ? LLDir::getScrubbedFileName(item->getName()) + ".tga" : LLStringUtil::null, FFSAVE_TGA, "", "image"); + filepicker->run(boost::bind(&LLPreviewTexture::saveAs_continued, this, item, filepicker)); +} + +void LLPreviewTexture::saveAs_continued(LLViewerInventoryItem const* item, AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) return; - } + // remember the user-approved/edited file name. - mSaveFileName = file_picker.getFirstFile(); + mSaveFileName = filepicker->getFilename(); mLoadingFullImage = TRUE; getWindow()->incBusyCount(); mImage->forceToSaveRawImage(0) ;//re-fetch the raw image if the old one is removed. diff --git a/indra/newview/llpreviewtexture.h b/indra/newview/llpreviewtexture.h index fb75876fc..ce26d4dd4 100644 --- a/indra/newview/llpreviewtexture.h +++ b/indra/newview/llpreviewtexture.h @@ -40,6 +40,7 @@ class LLComboBox; class LLImageRaw; +class AIFilePicker; class LLPreviewTexture : public LLPreview { @@ -64,6 +65,7 @@ public: virtual BOOL canSaveAs() const; virtual void saveAs(); + void saveAs_continued(LLViewerInventoryItem const* item, AIFilePicker* filepicker); virtual LLUUID getItemID(); virtual std::string getItemCreatorName(); virtual std::string getItemCreationDate(); diff --git a/indra/newview/lluploaddialog.cpp b/indra/newview/lluploaddialog.cpp index 2ba87f01b..a860e5303 100644 --- a/indra/newview/lluploaddialog.cpp +++ b/indra/newview/lluploaddialog.cpp @@ -161,10 +161,6 @@ void LLUploadDialog::setMessage( const std::string& msg) LLUploadDialog::~LLUploadDialog() { gFocusMgr.releaseFocusIfNeeded( this ); - -// LLFilePicker::instance().reset(); - - LLUploadDialog::sDialog = NULL; } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 873e6d1ae..2c898d300 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -69,6 +69,7 @@ #include "lltimer.h" #include "llvfile.h" #include "llvolumemgr.h" +#include "statemachine/aifilepicker.h" // newview includes #include "llagent.h" @@ -1223,19 +1224,21 @@ void init_debug_world_menu(LLMenuGL* menu) menu->createJumpKeys(); } - +static void handle_export_menus_to_xml_continued(AIFilePicker* filepicker); void handle_export_menus_to_xml(void*) { + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open("", FFSAVE_XML); + filepicker->run(boost::bind(&handle_export_menus_to_xml_continued, filepicker)); +} - LLFilePicker& picker = LLFilePicker::instance(); - if(!picker.getSaveFile(LLFilePicker::FFSAVE_XML)) +static void handle_export_menus_to_xml_continued(AIFilePicker* filepicker) +{ + if(!filepicker->hasFilename()) { - llwarns << "No file" << llendl; return; } - std::string filename = picker.getFirstFile(); - - llofstream out(filename); + llofstream out(filepicker->getFilename()); LLXMLNodePtr node = gMenuBarView->getXML(); node->writeToOstream(out); out.close(); @@ -8645,6 +8648,7 @@ const LLRect LLViewerMenuHolderGL::getMenuRect() const return LLRect(0, getRect().getHeight() - MENU_BAR_HEIGHT, getRect().getWidth(), STATUS_BAR_HEIGHT); } +static void handle_save_to_xml_continued(LLFloater* frontmost, AIFilePicker* filepicker); void handle_save_to_xml(void*) { LLFloater* frontmost = gFloaterView->getFrontmost(); @@ -8664,20 +8668,33 @@ void handle_save_to_xml(void*) LLStringUtil::replaceChar(default_name, ':', '_'); LLStringUtil::replaceChar(default_name, '"', '_'); - LLFilePicker& picker = LLFilePicker::instance(); - if (picker.getSaveFile(LLFilePicker::FFSAVE_XML, default_name)) + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(default_name, FFSAVE_XML); + filepicker->run(boost::bind(&handle_save_to_xml_continued, frontmost, filepicker)); +} + +static void handle_save_to_xml_continued(LLFloater* frontmost, AIFilePicker* filepicker) +{ + if (filepicker->hasFilename()) { - std::string filename = picker.getFirstFile(); + std::string filename = filepicker->getFilename(); LLUICtrlFactory::getInstance()->saveToXML(frontmost, filename); } } +static void handle_load_from_xml_continued(AIFilePicker* filepicker); void handle_load_from_xml(void*) { - LLFilePicker& picker = LLFilePicker::instance(); - if (picker.getOpenFile(LLFilePicker::FFLOAD_XML)) + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(FFLOAD_XML); + filepicker->run(boost::bind(&handle_load_from_xml_continued, filepicker)); +} + +static void handle_load_from_xml_continued(AIFilePicker* filepicker) +{ + if (filepicker->hasFilename()) { - std::string filename = picker.getFirstFile(); + std::string filename = filepicker->getFilename(); LLFloater* floater = new LLFloater("sample_floater"); LLUICtrlFactory::getInstance()->buildFloater(floater, filename); } diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 7e276e51f..a8d78cd8d 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -41,7 +41,7 @@ #include "llimagepng.h" #include "llimagebmp.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "llfloateranimpreview.h" #include "llfloaterbuycurrency.h" @@ -125,26 +125,26 @@ static std::string SLOBJECT_EXTENSIONS = "slobject"; #endif static std::string ALL_FILE_EXTENSIONS = "*.*"; -std::string build_extensions_string(LLFilePicker::ELoadFilter filter) +std::string build_extensions_string(ELoadFilter filter) { switch(filter) { #if LL_WINDOWS - case LLFilePicker::FFLOAD_IMAGE: + case FFLOAD_IMAGE: return IMAGE_EXTENSIONS; - case LLFilePicker::FFLOAD_WAV: + case FFLOAD_WAV: return SOUND_EXTENSIONS; - case LLFilePicker::FFLOAD_ANIM: + case FFLOAD_ANIM: return ANIM_EXTENSIONS; - case LLFilePicker::FFLOAD_SLOBJECT: + case FFLOAD_SLOBJECT: return SLOBJECT_EXTENSIONS; #ifdef _CORY_TESTING - case LLFilePicker::FFLOAD_GEOMETRY: + case FFLOAD_GEOMETRY: return GEOMETRY_EXTENSIONS; #endif - case LLFilePicker::FFLOAD_XML: + case FFLOAD_XML: return XML_EXTENSIONS; - case LLFilePicker::FFLOAD_ALL: + case FFLOAD_ALL: return ALL_FILE_EXTENSIONS; #endif default: @@ -152,48 +152,51 @@ std::string build_extensions_string(LLFilePicker::ELoadFilter filter) } } -/** - char* upload_pick(void* data) +class AIFileUpload { + protected: + AIFilePicker* mPicker; - If applicable, brings up a file chooser in which the user selects a file - to upload for a particular task. If the file is valid for the given action, - returns the string to the full path filename, else returns NULL. - Data is the load filter for the type of file as defined in LLFilePicker. + public: + AIFileUpload(void) : mPicker(NULL) { } + virtual ~AIFileUpload() { llassert(!mPicker); if (mPicker) { mPicker->abort(); mPicker = NULL; } } - Eventually I'd really like to have a built-in browser that gave you all - valid filetypes, default permissions, "Temp when available", and a single - upload button. Maybe let it show the contents of multiple folders. The main - purpose of this features is to deal with the problem of SL just up and - disconnecting if you take more than like 30 seconds to look for a file. -HgB -**/ -const std::string upload_pick(void* data) + public: + bool is_valid(std::string const& filename, ELoadFilter type); + void filepicker_callback(ELoadFilter type); + void start_filepicker(ELoadFilter type, char const* context); + + protected: + virtual void handle_event(std::string const& filename) = 0; +}; + +void AIFileUpload::start_filepicker(ELoadFilter filter, char const* context) { - if( gAgent.cameraMouselook() ) + if( gAgent.cameraMouselook() ) { gAgent.changeCameraToDefault(); // This doesn't seem necessary. JC // display(); } - LLFilePicker::ELoadFilter type; - if(data) - { - type = (LLFilePicker::ELoadFilter)((intptr_t)data); - } - else - { - type = LLFilePicker::FFLOAD_ALL; - } + llassert(!mPicker); + mPicker = new AIFilePicker; + mPicker->open(filter, "", context); + mPicker->run(boost::bind(&AIFileUpload::filepicker_callback, this, filter)); +} - LLFilePicker& picker = LLFilePicker::instance(); - if (!picker.getOpenFile(type)) +void AIFileUpload::filepicker_callback(ELoadFilter type) +{ + if (mPicker->hasFilename()) { - llinfos << "Couldn't import objects from file" << llendl; - return std::string(); + std::string filename = mPicker->getFilename(); + if (is_valid(filename, type)) + handle_event(filename); } + mPicker = NULL; +} - - const std::string& filename = picker.getFirstFile(); +bool AIFileUpload::is_valid(std::string const& filename, ELoadFilter type) +{ std::string ext = gDirUtilp->getExtension(filename); //strincmp doesn't like NULL pointers @@ -205,7 +208,7 @@ const std::string upload_pick(void* data) LLSD args; args["FILE"] = short_name; LLNotifications::instance().add("NoFileExtension", args); - return std::string(); + return false; } else { @@ -248,7 +251,7 @@ const std::string upload_pick(void* data) args["EXTENSION"] = ext; args["VALIDS"] = valid_extensions; LLNotifications::instance().add("InvalidFileExtension", args); - return NULL; + return false; } }//end else (non-null extension) @@ -257,7 +260,7 @@ const std::string upload_pick(void* data) //now we check to see //if the file is actually a valid image/sound/etc. //Consider completely disabling this, see how SL handles it. Maybe we can get full song uploads again! -HgB - if (type == LLFilePicker::FFLOAD_WAV) + if (type == FFLOAD_WAV) { // pre-qualify wavs to make sure the format is acceptable std::string error_msg; @@ -267,103 +270,68 @@ const std::string upload_pick(void* data) LLSD args; args["FILE"] = filename; LLNotifications::instance().add( error_msg, args ); - return std::string(); + return false; } }//end if a wave/sound file - return filename; + return true; } -class LLFileUploadImage : public view_listener_t +class LLFileUploadImage : public view_listener_t, public AIFileUpload { + public: bool handleEvent(LLPointer event, const LLSD& userdata) { - std::string filename = upload_pick((void *)LLFilePicker::FFLOAD_IMAGE); - if (!filename.empty()) - { - LLFloaterImagePreview* floaterp = new LLFloaterImagePreview(filename); - LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_image_preview.xml"); - } - return TRUE; + start_filepicker(FFLOAD_IMAGE, "image"); + return true; + } + + protected: + // Inherited from AIFileUpload. + /*virtual*/ void handle_event(std::string const& filename) + { + LLFloaterImagePreview* floaterp = new LLFloaterImagePreview(filename); + LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_image_preview.xml"); } }; -class LLFileUploadSound : public view_listener_t +class LLFileUploadSound : public view_listener_t, public AIFileUpload { bool handleEvent(LLPointer event, const LLSD& userdata) { - std::string filename = upload_pick((void*)LLFilePicker::FFLOAD_WAV); - if (!filename.empty()) - { - LLFloaterNameDesc* floaterp = new LLFloaterNameDesc(filename); - LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_sound_preview.xml"); - floaterp->childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload() )); - } + start_filepicker(FFLOAD_WAV, "sound"); return true; } + + protected: + // Inherited from AIFileUpload. + /*virtual*/ void handle_event(std::string const& filename) + { + LLFloaterNameDesc* floaterp = new LLFloaterNameDesc(filename); + LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_sound_preview.xml"); + floaterp->childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload() )); + } }; -class LLFileUploadAnim : public view_listener_t +class LLFileUploadAnim : public view_listener_t, public AIFileUpload { bool handleEvent(LLPointer event, const LLSD& userdata) { - const std::string filename = upload_pick((void*)LLFilePicker::FFLOAD_ANIM); - if (!filename.empty()) - { - LLFloaterAnimPreview* floaterp = new LLFloaterAnimPreview(filename); - LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_animation_preview.xml"); - } + start_filepicker(FFLOAD_ANIM, "animations"); return true; } + + protected: + // Inherited from AIFileUpload. + /*virtual*/ void handle_event(std::string const& filename) + { + LLFloaterAnimPreview* floaterp = new LLFloaterAnimPreview(filename); + LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_animation_preview.xml"); + } }; class LLFileUploadBulk : public view_listener_t { - static bool onConfirmBulkUploadTemp(const LLSD& notification, const LLSD& response ) - { - S32 option = LLNotification::getSelectedOption(notification, response); - BOOL enabled; - if(option == 0) // yes - enabled = TRUE; - else if(option == 1) //no - enabled = FALSE; - else //cancel - return false; - - LLFilePicker& picker = LLFilePicker::instance(); - if (picker.getMultipleOpenFiles()) - { - //const std::string& filename = picker.getFirstFile(); - std::string filename; - while(!(filename = picker.getNextFile()).empty()) - { - std::string name = gDirUtilp->getBaseFileName(filename, true); - - std::string asset_name = name; - LLStringUtil::replaceNonstandardASCII( asset_name, '?' ); - LLStringUtil::replaceChar(asset_name, '|', '?'); - LLStringUtil::stripNonprintable(asset_name); - LLStringUtil::trim(asset_name); - - std::string display_name = LLStringUtil::null; - LLAssetStorage::LLStoreAssetCallback callback = NULL; - S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); - void *userdata = NULL; - gSavedSettings.setBOOL("TemporaryUpload",enabled); - upload_new_resource(filename, asset_name, asset_name, 0, LLAssetType::AT_NONE, LLInventoryType::IT_NONE, - LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(), - display_name, - callback, expected_upload_cost, userdata); - - } - } - else - { - llinfos << "Couldn't import objects from file" << llendl; - gSavedSettings.setBOOL("TemporaryUpload",FALSE); - } - return false; - } bool handleEvent(LLPointer event, const LLSD& userdata) { if( gAgent.cameraMouselook() ) @@ -388,45 +356,59 @@ class LLFileUploadBulk : public view_listener_t msg.append(llformat("\nWARNING: Each upload costs L$%d if it's not temporary.",expected_upload_cost)); args["MESSAGE"] = msg; LLNotifications::instance().add("GenericAlertYesNoCancel", args, LLSD(), onConfirmBulkUploadTemp); - /* moved to the callback for the above - LLFilePicker& picker = LLFilePicker::instance(); - if (picker.getMultipleOpenFiles()) - { - // - - //const std::string& filename = picker.getFirstFile(); - std::string filename; - while(!(filename = picker.getNextFile()).empty()) - { - // - std::string name = gDirUtilp->getBaseFileName(filename, true); - - std::string asset_name = name; - LLStringUtil::replaceNonstandardASCII( asset_name, '?' ); - LLStringUtil::replaceChar(asset_name, '|', '?'); - LLStringUtil::stripNonprintable(asset_name); - LLStringUtil::trim(asset_name); - - std::string display_name = LLStringUtil::null; - LLAssetStorage::LLStoreAssetCallback callback = NULL; - S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); - void *userdata = NULL; - upload_new_resource(filename, asset_name, asset_name, 0, LLAssetType::AT_NONE, LLInventoryType::IT_NONE, - LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(), - display_name, - callback, expected_upload_cost, userdata); + return true; + } + + static bool onConfirmBulkUploadTemp(const LLSD& notification, const LLSD& response ) + { + S32 option = LLNotification::getSelectedOption(notification, response); + bool enabled; + if (option == 0) // yes + enabled = true; + else if(option == 1) // no + enabled = false; + else // cancel + return false; + + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(FFLOAD_ALL, "", "openfile", true); + filepicker->run(boost::bind(&LLFileUploadBulk::onConfirmBulkUploadTemp_continued, enabled, filepicker)); + return true; + } + + static void onConfirmBulkUploadTemp_continued(bool enabled, AIFilePicker* filepicker) + { + if (filepicker->hasFilename()) + { + std::vector const& file_names(filepicker->getFilenames()); + for (std::vector::const_iterator iter = file_names.begin(); iter != file_names.end(); ++iter) + { + std::string const& filename(*iter); + if (filename.empty()) + continue; + std::string name = gDirUtilp->getBaseFileName(filename, true); + + std::string asset_name = name; + LLStringUtil::replaceNonstandardASCII( asset_name, '?' ); + LLStringUtil::replaceChar(asset_name, '|', '?'); + LLStringUtil::stripNonprintable(asset_name); + LLStringUtil::trim(asset_name); + + std::string display_name = LLStringUtil::null; + LLAssetStorage::LLStoreAssetCallback callback = NULL; + S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); + void *userdata = NULL; + gSavedSettings.setBOOL("TemporaryUpload", enabled); + upload_new_resource(filename, asset_name, asset_name, 0, LLAssetType::AT_NONE, LLInventoryType::IT_NONE, + LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(), + display_name, callback, expected_upload_cost, userdata); - // *NOTE: Ew, we don't iterate over the file list here, - // we handle the next files in upload_done_callback() - // not anymore! } } else { - llinfos << "Couldn't import objects from file" << llendl; + gSavedSettings.setBOOL("TemporaryUpload", FALSE); } - */ - return true; } }; @@ -601,44 +583,39 @@ class LLFileQuit : public view_listener_t } }; -void handle_upload(void* data) -{ - const std::string filename = upload_pick(data); - if (!filename.empty()) - { - LLFloaterNameDesc* floaterp = new LLFloaterNameDesc(filename); - LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_name_description.xml"); - floaterp->childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload() )); - } -} - +static void handle_compress_image_continued(AIFilePicker* filepicker); void handle_compress_image(void*) { - LLFilePicker& picker = LLFilePicker::instance(); - if (picker.getMultipleOpenFiles(LLFilePicker::FFLOAD_IMAGE)) + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(FFLOAD_IMAGE, "", "openfile", true); + filepicker->run(boost::bind(&handle_compress_image_continued, filepicker)); +} + +static void handle_compress_image_continued(AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) + return; + + std::vector const& filenames(filepicker->getFilenames()); + for(std::vector::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) { - std::string infile = picker.getFirstFile(); - while (!infile.empty()) + std::string const& infile(*filename); + std::string outfile = infile + ".j2c"; + + llinfos << "Input: " << infile << llendl; + llinfos << "Output: " << outfile << llendl; + + BOOL success; + + success = LLViewerTextureList::createUploadFile(infile, outfile, IMG_CODEC_TGA); + + if (success) { - std::string outfile = infile + ".j2c"; - - llinfos << "Input: " << infile << llendl; - llinfos << "Output: " << outfile << llendl; - - BOOL success; - - success = LLViewerTextureList::createUploadFile(infile, outfile, IMG_CODEC_TGA); - - if (success) - { - llinfos << "Compression complete" << llendl; - } - else - { - llinfos << "Compression failed: " << LLImage::getLastError() << llendl; - } - - infile = picker.getNextFile(); + llinfos << "Compression complete" << llendl; + } + else + { + llinfos << "Compression failed: " << LLImage::getLastError() << llendl; } } } @@ -1137,32 +1114,6 @@ void upload_done_callback(const LLUUID& uuid, void* user_data, S32 result, LLExt LLUploadDialog::modalUploadFinished(); delete data; - - // *NOTE: This is a pretty big hack. What this does is check the - // file picker if there are any more pending uploads. If so, - // upload that file. - /* Agreed, let's not use it. -HgB - const std::string& next_file = LLFilePicker::instance().getNextFile(); - if(is_balance_sufficient && !next_file.empty()) - { - std::string asset_name = gDirUtilp->getBaseFileName(next_file, true); - LLStringUtil::replaceNonstandardASCII( asset_name, '?' ); - LLStringUtil::replaceChar(asset_name, '|', '?'); - LLStringUtil::stripNonprintable(asset_name); - LLStringUtil::trim(asset_name); - - std::string display_name = LLStringUtil::null; - LLAssetStorage::LLStoreAssetCallback callback = NULL; - void *userdata = NULL; - upload_new_resource(next_file, asset_name, asset_name, // file - 0, LLAssetType::AT_NONE, LLInventoryType::IT_NONE, - PERM_NONE, PERM_NONE, PERM_NONE, - display_name, - callback, - expected_upload_cost, // assuming next in a group of uploads is of roughly the same type, i.e. same upload cost - userdata); - } - */ } void upload_new_resource(const LLTransactionID &tid, LLAssetType::EType asset_type, diff --git a/indra/newview/llviewermenufile.h b/indra/newview/llviewermenufile.h index 0e03429e9..c92e82da3 100644 --- a/indra/newview/llviewermenufile.h +++ b/indra/newview/llviewermenufile.h @@ -78,6 +78,4 @@ void upload_new_resource(const LLTransactionID &tid, S32 expected_upload_cost, void *userdata); -const std::string upload_pick(void* data); - #endif diff --git a/indra/newview/llviewerobjectbackup.cpp b/indra/newview/llviewerobjectbackup.cpp index c944780af..51d0a4336 100644 --- a/indra/newview/llviewerobjectbackup.cpp +++ b/indra/newview/llviewerobjectbackup.cpp @@ -56,7 +56,7 @@ #include "llagent.h" #include "llappviewer.h" #include "llassetuploadresponders.h" -#include "llfilepicker.h" +#include "statemachine/aifilepicker.h" #include "llfloateranimpreview.h" #include "llfloaterbuycurrency.h" #include "llfloaterimagepreview.h" @@ -341,14 +341,20 @@ void LLObjectBackup::exportObject() mThisGroup.clear(); // Open the file save dialog - LLFilePicker& file_picker = LLFilePicker::instance(); - if (!file_picker.getSaveFile(LLFilePicker::FFSAVE_XML)) + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open("", FFSAVE_XML); + filepicker->run(boost::bind(&LLObjectBackup::exportObject_continued, this, filepicker)); +} + +void LLObjectBackup::exportObject_continued(AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) { // User canceled save. return; } - mFileName = file_picker.getCurFile(); + mFileName = filepicker->getFilename(); mFolder = gDirUtilp->getDirName(mFileName); mNonExportedTextures = TEXTURE_OK; @@ -733,14 +739,19 @@ void LLObjectBackup::importObject(bool upload) mRetexture = upload; // Open the file open dialog - LLFilePicker& file_picker = LLFilePicker::instance(); - if (!file_picker.getOpenFile(LLFilePicker::FFLOAD_XML)) - { - // User canceled save. - return; - } + AIFilePicker* filepicker = new AIFilePicker; + filepicker->open(FFLOAD_XML, "", "import"); + filepicker->run(boost::bind(&LLObjectBackup::importObject_continued, this, filepicker)); - std::string file_name = file_picker.getFirstFile().c_str(); + return; +} + +void LLObjectBackup::importObject_continued(AIFilePicker* filepicker) +{ + if (!filepicker->hasFilename()) + return; + + std::string file_name = filepicker->getFilename(); mFolder = gDirUtilp->getDirName(file_name); llifstream import_file(file_name); LLSDSerialize::fromXML(mLLSD, import_file); @@ -762,7 +773,7 @@ void LLObjectBackup::importObject(bool upload) if (mObjects <= 0) { LLSD args; - args["MESSAGE"] = std::string("Object import failed.\nThe XML file has an incompatble format or does not contain any objects."); + args["MESSAGE"] = std::string("Object import failed.\nThe XML file has an incompatible format or does not contain any objects."); LLNotifications::instance().add("GenericAlert", args); llwarns << "Trying to import illegal XML object file." << llendl; return; diff --git a/indra/newview/llviewerobjectbackup.h b/indra/newview/llviewerobjectbackup.h index 46ca5f916..5ae33c382 100644 --- a/indra/newview/llviewerobjectbackup.h +++ b/indra/newview/llviewerobjectbackup.h @@ -56,9 +56,11 @@ public: // Import entry point void importObject(bool upload=FALSE); + void importObject_continued(AIFilePicker* filepicker); // Export entry point void exportObject(); + void exportObject_continued(AIFilePicker* filepicker); // Update map from texture worker void updateMap(LLUUID uploaded_asset); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 2e684633c..2e256db6b 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -4015,23 +4015,22 @@ void LLViewerWindow::saveImageNumbered(LLPointer image) { std::string proposed_name( sSnapshotBaseName ); - // getSaveFile will append an appropriate extension to the proposed name, based on the ESaveFilter constant passed in. + // AIFilePicker will append an appropriate extension to the proposed name, based on the ESaveFilter constant passed in. // pick a directory in which to save - AIFilePicker* filepicker = new AIFilePicker; // Deleted in LLViewerWindow::saveImageNumbered_filepicker_callback + AIFilePicker* filepicker = new AIFilePicker; // Deleted in LLViewerWindow::saveImageNumbered_continued1 filepicker->open(proposed_name, pick_type, "", "snapshot"); - filepicker->run(boost::bind(&LLViewerWindow::saveImageNumbered_filepicker_callback, this, image, extension, filepicker, _1)); + filepicker->run(boost::bind(&LLViewerWindow::saveImageNumbered_continued1, this, image, extension, filepicker)); return; } - // LLViewerWindow::sSnapshotBaseName and LLViewerWindow::sSnapshotDir already known. Go straight to saveImageNumbered_continued. - saveImageNumbered_continued(image, extension); + // LLViewerWindow::sSnapshotBaseName and LLViewerWindow::sSnapshotDir already known. Go straight to saveImageNumbered_continued2. + saveImageNumbered_continued2(image, extension); } -void LLViewerWindow::saveImageNumbered_filepicker_callback(LLPointer image, std::string const& extension, AIFilePicker* filepicker, bool success) +void LLViewerWindow::saveImageNumbered_continued1(LLPointer image, std::string const& extension, AIFilePicker* filepicker) { - llassert((bool)*filepicker == success); - if (success && !filepicker->isCanceled()) + if (filepicker->hasFilename()) { // Copy the directory + file name std::string filepath = filepicker->getFilename(); @@ -4039,12 +4038,11 @@ void LLViewerWindow::saveImageNumbered_filepicker_callback(LLPointergetBaseFileName(filepath, true); LLViewerWindow::sSnapshotDir = gDirUtilp->getDirName(filepath); - saveImageNumbered_continued(image, extension); + saveImageNumbered_continued2(image, extension); } - filepicker->deleteMe(); } -void LLViewerWindow::saveImageNumbered_continued(LLPointer image, std::string const& extension) +void LLViewerWindow::saveImageNumbered_continued2(LLPointer image, std::string const& extension) { // Look for an unused file name std::string filepath; diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index cbf3f0187..dec5a177c 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -296,8 +296,8 @@ public: BOOL isSnapshotLocSet() const { return ! sSnapshotDir.empty(); } void resetSnapshotLoc() const { sSnapshotDir.clear(); } void saveImageNumbered(LLPointer image); - void saveImageNumbered_filepicker_callback(LLPointer image, std::string const& extension, AIFilePicker* filepicker, bool success); - void saveImageNumbered_continued(LLPointer image, std::string const& extension); + void saveImageNumbered_continued1(LLPointer image, std::string const& extension, AIFilePicker* filepicker); + void saveImageNumbered_continued2(LLPointer image, std::string const& extension); // Reset the directory where snapshots are saved. // Client will open directory picker on next snapshot save. diff --git a/indra/newview/statemachine/aifilepicker.cpp b/indra/newview/statemachine/aifilepicker.cpp index 33ef0c16c..e0588a990 100644 --- a/indra/newview/statemachine/aifilepicker.cpp +++ b/indra/newview/statemachine/aifilepicker.cpp @@ -133,8 +133,14 @@ void AIFilePicker::open(ELoadFilter filter, std::string const& default_path, std case FFLOAD_RAW: mFilter = "raw"; break; - case FFLOAD_TEXT: - mFilter = "text"; + case FFLOAD_INVGZ: + mFilter = "invgz"; + break; + case FFLOAD_AO: + mFilter = "ao"; + break; + case FFLOAD_BLACKLIST: + mFilter = "blacklist"; break; } } @@ -188,15 +194,75 @@ void AIFilePicker::open(std::string const& filename, ESaveFilter filter, std::st case FFSAVE_JPEG: mFilter = "jpeg"; break; - case FFSAVE_HPA: - mFilter = "hpa"; + case FFSAVE_ANIMATN: + mFilter = "animatn"; break; - case FFSAVE_TEXT: - mFilter = "text"; + case FFSAVE_OGG: + mFilter = "ogg"; + break; + case FFSAVE_NOTECARD: + mFilter = "notecard"; + break; + case FFSAVE_GESTURE: + mFilter = "gesture"; break; case FFSAVE_LSL: mFilter = "lsl"; break; + case FFSAVE_SHAPE: + mFilter = "shape"; + break; + case FFSAVE_SKIN: + mFilter = "skin"; + break; + case FFSAVE_HAIR: + mFilter = "hair"; + break; + case FFSAVE_EYES: + mFilter = "eyes"; + break; + case FFSAVE_SHIRT: + mFilter = "shirt"; + break; + case FFSAVE_PANTS: + mFilter = "pants"; + break; + case FFSAVE_SHOES: + mFilter = "shoes"; + break; + case FFSAVE_SOCKS: + mFilter = "socks"; + break; + case FFSAVE_JACKET: + mFilter = "jacket"; + break; + case FFSAVE_GLOVES: + mFilter = "gloves"; + break; + case FFSAVE_UNDERSHIRT: + mFilter = "undershirt"; + break; + case FFSAVE_UNDERPANTS: + mFilter = "underpants"; + break; + case FFSAVE_SKIRT: + mFilter = "skirt"; + break; + case FFSAVE_INVGZ: + mFilter = "invgz"; + break; + case FFSAVE_LANDMARK: + mFilter = "landmark"; + break; + case FFSAVE_AO: + mFilter = "ao"; + break; + case FFSAVE_BLACKLIST: + mFilter = "blacklist"; + break; + case FFSAVE_PHYSICS: + mFilter = "physics"; + break; } } @@ -320,6 +386,9 @@ void AIFilePicker::finish_impl(void) mPluginManager = NULL; } mFilter.clear(); // Check that open is called before calling run (again). + // The default behavior is to delete the plugin. This can be overridden in + // the callback by calling run() again. + deleteMe(); } // This function is called when a new message is received from the plugin. diff --git a/indra/newview/statemachine/aifilepicker.h b/indra/newview/statemachine/aifilepicker.h index b1caf4701..d916055fe 100644 --- a/indra/newview/statemachine/aifilepicker.h +++ b/indra/newview/statemachine/aifilepicker.h @@ -45,7 +45,9 @@ enum ELoadFilter FFLOAD_XML, FFLOAD_SLOBJECT, FFLOAD_RAW, - FFLOAD_TEXT + FFLOAD_INVGZ, + FFLOAD_AO, + FFLOAD_BLACKLIST }; enum ESaveFilter @@ -62,9 +64,29 @@ enum ESaveFilter FFSAVE_J2C, FFSAVE_PNG, FFSAVE_JPEG, - FFSAVE_HPA, - FFSAVE_TEXT, - FFSAVE_LSL + FFSAVE_ANIMATN, + FFSAVE_OGG, + FFSAVE_NOTECARD, + FFSAVE_GESTURE, + FFSAVE_LSL, + FFSAVE_SHAPE, + FFSAVE_SKIN, + FFSAVE_HAIR, + FFSAVE_EYES, + FFSAVE_SHIRT, + FFSAVE_PANTS, + FFSAVE_SHOES, + FFSAVE_SOCKS, + FFSAVE_JACKET, + FFSAVE_GLOVES, + FFSAVE_UNDERSHIRT, + FFSAVE_UNDERPANTS, + FFSAVE_SKIRT, + FFSAVE_INVGZ, + FFSAVE_LANDMARK, + FFSAVE_AO, + FFSAVE_BLACKLIST, + FFSAVE_PHYSICS }; /* @@ -108,12 +130,9 @@ new AIFilePicker Termination happens by receiving the "canceled" or "done" message, which sets the state to AIFilePicker_canceled or AIFilePicker_done respectively, causing a call to AIStateMachine::finish(), which calls - AIFilePicker::finish_impl which destroys the plugin (mPluginBase) - and the plugin manager (mPluginManager). - - AIStateMachine::finish() also calls the registered callback function, - which should call AIStateMachine::deleteMe(), causing the AIFilePicker - to be deleted. + AIFilePicker::finish_impl which destroys the plugin (mPluginBase), + the plugin manager (mPluginManager) and calls AIStateMachine::deleteMe() + causing the AIFilePicker to be deleted. */ @@ -140,6 +159,7 @@ public: void open(ELoadFilter filter = FFLOAD_ALL, std::string const& default_path = "", std::string const& context = "openfile", bool multiple = false); bool isCanceled(void) const { return mCanceled; } + bool hasFilename(void) const { return *this && !mCanceled; } std::string const& getFilename(void) const; std::string getFolder(void) const; std::vector const& getFilenames(void) const { return mFilenames; } @@ -175,7 +195,7 @@ private: std::string get_folder(std::string const& default_path, std::string const& context); protected: - // Call deleteMe(), not delete. + // Call finish() (or abort()), not delete. /*virtual*/ ~AIFilePicker() { LL_DEBUGS("Plugin") << "Calling AIFilePicker::~AIFilePicker()" << LL_ENDL; } // Handle initializing the object. diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp index ca1b5c61e..ce1a4149e 100644 --- a/indra/newview/statemachine/aistatemachine.cpp +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -168,8 +168,16 @@ void AIStateMachine::finish(void) { DoutEntering(dc::statemachine, "AIStateMachine::finish() [" << (void*)this << "]"); llassert(mState == bs_run || mState == bs_abort); + // It is possible that mIdle is false when abort or finish was called from + // outside multiplex_impl. However, that only may be done by the main thread. + llassert(!mIdle || is_main_thread()); + if (!mIdle) + idle(); mState = bs_finish; finish_impl(); + // Did finish_impl call deleteMe? Then that is only the default. Remember it. + bool default_delete = (mState == bs_deleted); + mState = bs_finish; if (mParent) { // It is possible that the parent is not running when the parent is in fact aborting and called @@ -190,22 +198,21 @@ void AIStateMachine::finish(void) } // Set this already to bs_initialize now, so that (bool)*this evaluates to true. mState = bs_initialize; - // It is possible that mIdle is false when abort or finish was called from - // outside multiplex_impl. However, that only may be done by the main thread. - llassert(!mIdle || is_main_thread()); - if (!mIdle) - idle(); if (mCallback) { mCallback->callback(!mAborted); // This can/may call deleteMe(), in which case the whole AIStateMachine will be deleted from the mainloop. delete mCallback; mCallback = NULL; } + // Restore the request for deletion if we weren't started again from the callback. + if (default_delete && mState == bs_initialize) + mState = bs_deleted; } void AIStateMachine::deleteMe(void) { - llassert(mIdle && mState == bs_initialize); + // Should only be called from finish(). + llassert(mIdle && (mState == bs_initialize || mState == bs_finish)); mState = bs_deleted; } diff --git a/indra/plugins/filepicker/basic_plugin_filepicker.cpp b/indra/plugins/filepicker/basic_plugin_filepicker.cpp index b30131479..b954e27b4 100644 --- a/indra/plugins/filepicker/basic_plugin_filepicker.cpp +++ b/indra/plugins/filepicker/basic_plugin_filepicker.cpp @@ -87,12 +87,52 @@ static LLFilePicker::ESaveFilter str2savefilter(std::string const& filter) return LLFilePicker::FFSAVE_PNG; else if (filter == "jpeg") return LLFilePicker::FFSAVE_JPEG; - else if (filter == "hpa") - return LLFilePicker::FFSAVE_HPA; - else if (filter == "text") - return LLFilePicker::FFSAVE_TEXT; + else if (filter == "animatn") + return LLFilePicker::FFSAVE_ANIMATN; + else if (filter == "ogg") + return LLFilePicker::FFSAVE_OGG; + else if (filter == "notecard") + return LLFilePicker::FFSAVE_NOTECARD; + else if (filter == "gesture") + return LLFilePicker::FFSAVE_GESTURE; else if (filter == "lsl") return LLFilePicker::FFSAVE_LSL; + else if (filter == "shape") + return LLFilePicker::FFSAVE_SHAPE; + else if (filter == "skin") + return LLFilePicker::FFSAVE_SKIN; + else if (filter == "hair") + return LLFilePicker::FFSAVE_HAIR; + else if (filter == "eyes") + return LLFilePicker::FFSAVE_EYES; + else if (filter == "shirt") + return LLFilePicker::FFSAVE_SHIRT; + else if (filter == "pants") + return LLFilePicker::FFSAVE_PANTS; + else if (filter == "shoes") + return LLFilePicker::FFSAVE_SHOES; + else if (filter == "socks") + return LLFilePicker::FFSAVE_SOCKS; + else if (filter == "jacket") + return LLFilePicker::FFSAVE_JACKET; + else if (filter == "gloves") + return LLFilePicker::FFSAVE_GLOVES; + else if (filter == "undershirt") + return LLFilePicker::FFSAVE_UNDERSHIRT; + else if (filter == "underpants") + return LLFilePicker::FFSAVE_UNDERPANTS; + else if (filter == "skirt") + return LLFilePicker::FFSAVE_SKIRT; + else if (filter == "invgz") + return LLFilePicker::FFSAVE_INVGZ; + else if (filter == "landmark") + return LLFilePicker::FFSAVE_LANDMARK; + else if (filter == "ao") + return LLFilePicker::FFSAVE_AO; + else if (filter == "blacklist") + return LLFilePicker::FFSAVE_BLACKLIST; + else if (filter == "physics") + return LLFilePicker::FFSAVE_PHYSICS; else return LLFilePicker::FFSAVE_ALL; } @@ -116,8 +156,12 @@ static LLFilePicker::ELoadFilter str2loadfilter(std::string const& filter) return LLFilePicker::FFLOAD_SLOBJECT; else if (filter == "raw") return LLFilePicker::FFLOAD_RAW; - else if (filter == "text") - return LLFilePicker::FFLOAD_TEXT; + else if (filter == "invgz") + return LLFilePicker::FFLOAD_INVGZ; + else if (filter == "ao") + return LLFilePicker::FFLOAD_AO; + else if (filter == "blacklist") + return LLFilePicker::FFLOAD_BLACKLIST; else return LLFilePicker::FFLOAD_ALL; } diff --git a/indra/plugins/filepicker/llfilepicker.cpp b/indra/plugins/filepicker/llfilepicker.cpp index b9c616195..8028665e3 100644 --- a/indra/plugins/filepicker/llfilepicker.cpp +++ b/indra/plugins/filepicker/llfilepicker.cpp @@ -129,8 +129,13 @@ namespace LLWindowSDL { LLFilePicker LLFilePicker::sInstance; #if LL_WINDOWS -#define SOUND_FILTER L"Sounds (*.wav)\0*.wav\0" -#define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.tga;*.bmp;*.jpg;*.jpeg;*.png\0" +// +#define SOUND_FILTER L"Sounds (*.wav; *.ogg)\0*.wav;*.ogg\0" +#define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png; *.jp2; *.j2k; *.j2c)\0*.tga;*.bmp;*.jpg;*.jpeg;*.png;*.jp2;*.j2k;*.j2c\0" +#define INVGZ_FILTER L"Inv cache (*.inv; *.inv.gz)\0*.inv;*.inv.gz\0" +#define AO_FILTER L"Animation Override (*.ao)\0*.ao\0" +#define BLACKLIST_FILTER L"Asset Blacklist (*.blacklist)\0*.blacklist\0" +// #define ANIM_FILTER L"Animations (*.bvh)\0*.bvh\0" #ifdef _CORY_TESTING #define GEOMETRY_FILTER L"SL Geometry (*.slg)\0*.slg\0" @@ -264,6 +269,20 @@ bool LLFilePickerBase::setupFilter(ELoadFilter filter) mOFN.lpstrFilter = RAW_FILTER \ L"\0"; break; + // + case FFLOAD_INVGZ: + mOFN.lpstrFilter = INVGZ_FILTER \ + L"\0"; + break; + case FFLOAD_AO: + mOFN.lpstrFilter = AO_FILTER \ + L"\0"; + break; + case FFLOAD_BLACKLIST: + mOFN.lpstrFilter = BLACKLIST_FILTER \ + L"\0"; + break; + // default: res = FALSE; break; @@ -401,28 +420,6 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena L"Targa, Bitmap Images (*.tga; *.bmp)\0*.tga;*.bmp\0" \ L"\0"; break; - case FFSAVE_LSL: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.lsl", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - mOFN.lpstrDefExt = L"lsl"; - mOFN.lpstrFilter = - L"LSL Files (*.lsl)\0*.lsl\0" - L"Text files (*.txt)\0*.txt\0" - L"\0"; - break; - case FFSAVE_TEXT: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.txt", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - mOFN.lpstrDefExt = L"txt"; - mOFN.lpstrFilter = - L"Text files (*.txt)\0*.txt\0" - L"RTF Files (*.rtf)\0*.rtf\0" - L"\0"; - break; case FFSAVE_WAV: if (filename.empty()) { @@ -466,11 +463,11 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena case FFSAVE_JPEG: if (filename.empty()) { - wcsncpy( mFilesW,L"untitled.jpeg", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + wcsncpy( mFilesW,L"untitled.jpg", 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: @@ -545,10 +542,241 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena L"Compressed Images (*.j2c)\0*.j2c\0" \ L"\0"; break; + // + case FFSAVE_ANIMATN: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.animatn", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"animatn"; + mOFN.lpstrFilter = + L"SL Animations (*.animatn)\0*.animatn\0" \ + L"\0"; + break; + case FFSAVE_OGG: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.ogg", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"ogg"; + mOFN.lpstrFilter = + L"Ogg (*.ogg)\0*.ogg\0" \ + L"\0"; + break; + case FFSAVE_NOTECARD: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.notecard", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"notecard"; + mOFN.lpstrFilter = + L"Notecards (*.notecard)\0*.notecard\0" \ + L"\0"; + break; + case FFSAVE_GESTURE: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.gesture", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"gesture"; + mOFN.lpstrFilter = + L"Gestures (*.gesture)\0*.gesture\0" \ + L"\0"; + break; + case FFSAVE_LSL: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.lsl", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"lsl"; + mOFN.lpstrFilter = + L"LSL (*.lsl)\0*.lsl\0" \ + L"\0"; + break; + case FFSAVE_SHAPE: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.shape", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"shape"; + mOFN.lpstrFilter = + L"Shapes (*.shape)\0*.shape\0" \ + L"\0"; + break; + case FFSAVE_SKIN: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.skin", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"skin"; + mOFN.lpstrFilter = + L"Skins (*.skin)\0*.skin\0" \ + L"\0"; + break; + case FFSAVE_HAIR: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.hair", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"hair"; + mOFN.lpstrFilter = + L"Hair (*.hair)\0*.hair\0" \ + L"\0"; + break; + case FFSAVE_EYES: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.eyes", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"eyes"; + mOFN.lpstrFilter = + L"Eyes (*.eyes)\0*.eyes\0" \ + L"\0"; + break; + case FFSAVE_SHIRT: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.shirt", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"shirt"; + mOFN.lpstrFilter = + L"Shirts (*.shirt)\0*.shirt\0" \ + L"\0"; + break; + case FFSAVE_PANTS: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.pants", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"pants"; + mOFN.lpstrFilter = + L"Pants (*.pants)\0*.pants\0" \ + L"\0"; + break; + case FFSAVE_SHOES: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.shoes", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"shoes"; + mOFN.lpstrFilter = + L"Shoes (*.shoes)\0*.shoes\0" \ + L"\0"; + break; + case FFSAVE_SOCKS: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.socks", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"socks"; + mOFN.lpstrFilter = + L"Socks (*.socks)\0*.socks\0" \ + L"\0"; + break; + case FFSAVE_JACKET: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.jacket", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"jacket"; + mOFN.lpstrFilter = + L"Jackets (*.jacket)\0*.jacket\0" \ + L"\0"; + break; + case FFSAVE_GLOVES: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.gloves", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"gloves"; + mOFN.lpstrFilter = + L"Gloves (*.gloves)\0*.gloves\0" \ + L"\0"; + break; + case FFSAVE_UNDERSHIRT: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.undershirt", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"undershirt"; + mOFN.lpstrFilter = + L"Undershirts (*.undershirt)\0*.undershirt\0" \ + L"\0"; + break; + case FFSAVE_UNDERPANTS: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.underpants", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"underpants"; + mOFN.lpstrFilter = + L"Underpants (*.underpants)\0*.underpants\0" \ + L"\0"; + break; + case FFSAVE_SKIRT: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.skirt", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"skirt"; + mOFN.lpstrFilter = + L"Skirts (*.skirt)\0*.skirt\0" \ + L"\0"; + break; + case FFSAVE_LANDMARK: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.landmark", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"landmark"; + mOFN.lpstrFilter = + L"Landmarks (*.landmark)\0*.landmark\0" \ + L"\0"; + break; + case FFSAVE_AO: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.ao", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"ao"; + mOFN.lpstrFilter = + L"Animation overrides (*.ao)\0*.ao\0" \ + L"\0"; + break; + case FFSAVE_INVGZ: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.inv", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L".inv"; + mOFN.lpstrFilter = + L"InvCache (*.inv)\0*.inv\0" \ + L"\0"; + break; + case FFSAVE_BLACKLIST: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.blacklist", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L".blacklist"; + mOFN.lpstrFilter = + L"Asset Blacklists (*.blacklist)\0*.blacklist\0" \ + L"\0"; + break; + case FFSAVE_PHYSICS: + if(filename.empty()) + { + wcsncpy( mFilesW,L"untitled.phy", FILENAME_BUFFER_SIZE); + } + mOFN.lpstrDefExt = L"phy"; + mOFN.lpstrFilter = + L"Landmarks (*.phy)\0*.phy\0" \ + L"\0"; + break; + // default: return FALSE; } - mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE; mOFN.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST; @@ -604,16 +832,12 @@ Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *c { if (fileInfo.filetype != 'JPEG' && fileInfo.filetype != 'JPG ' && fileInfo.filetype != 'BMP ' && fileInfo.filetype != 'TGA ' && - fileInfo.filetype != 'TIFF' && fileInfo.filetype != 'PSD ' && fileInfo.filetype != 'BMPf' && fileInfo.filetype != 'TPIC' && fileInfo.filetype != 'PNG ' && (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("jpeg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && CFStringCompare(fileInfo.extension, CFSTR("jpg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && CFStringCompare(fileInfo.extension, CFSTR("bmp"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && CFStringCompare(fileInfo.extension, CFSTR("tga"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && - CFStringCompare(fileInfo.extension, CFSTR("psd"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && - CFStringCompare(fileInfo.extension, CFSTR("tiff"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && - CFStringCompare(fileInfo.extension, CFSTR("tif"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && CFStringCompare(fileInfo.extension, CFSTR("png"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) ) { @@ -639,6 +863,15 @@ Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *c result = false; } } + else if (filter == FFLOAD_XML) + { + if (fileInfo.filetype != 'XML' && + (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("xml"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) + ) + { + result = false; + } + } #ifdef _CORY_TESTING else if (filter == FFLOAD_GEOMETRY) { @@ -1158,13 +1391,8 @@ static std::string add_simple_mime_filter_to_gtkchooser(GtkWindow *picker, static std::string add_wav_filter_to_gtkchooser(GtkWindow *picker) { - GtkFileFilter *gfilter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(gfilter, "*.wav"); - gtk_file_filter_add_mime_type(gfilter,"audio/x-wav");//not working - - std::string filtername = LLTrans::getString("sound_files") + " (*.wav)"; - add_common_filters_to_gtkchooser(gfilter, picker, filtername); - return filtername; + return add_simple_mime_filter_to_gtkchooser(picker, "audio/x-wav", + LLTrans::getString("sound_files") + " (*.wav)"); } static std::string add_bvh_filter_to_gtkchooser(GtkWindow *picker) @@ -1173,6 +1401,12 @@ static std::string add_bvh_filter_to_gtkchooser(GtkWindow *picker) LLTrans::getString("animation_files") + " (*.bvh)"); } +static std::string add_xml_filter_to_gtkchooser(GtkWindow *picker) +{ + return add_simple_mime_filter_to_gtkchooser(picker, "text/xml", + LLTrans::getString("xml_file") + " (*.xml)"); +} + static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker) { GtkFileFilter *gfilter = gtk_file_filter_new(); @@ -1300,6 +1534,9 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder case FFLOAD_IMAGE: filtername = add_imageload_filter_to_gtkchooser(picker); break; + case FFLOAD_XML: + filtername = add_xml_filter_to_gtkchooser(picker); + break; default:; break; } @@ -1376,6 +1613,7 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder case FFLOAD_WAV: filename += ".wav"; break; case FFLOAD_IMAGE: filename += ".tga"; break; case FFLOAD_ANIM: filename += ".bvh"; break; + case FFLOAD_XML: filename += ".xml"; break; default: break; } mFiles.push_back(filename); diff --git a/indra/plugins/filepicker/llfilepicker.h b/indra/plugins/filepicker/llfilepicker.h index d14612112..4e1a52a7e 100644 --- a/indra/plugins/filepicker/llfilepicker.h +++ b/indra/plugins/filepicker/llfilepicker.h @@ -88,7 +88,11 @@ public: FFLOAD_XML = 6, FFLOAD_SLOBJECT = 7, FFLOAD_RAW = 8, - FFLOAD_TEXT = 9, + // + FFLOAD_INVGZ = 9, + FFLOAD_AO = 10, + FFLOAD_BLACKLIST = 11 + // }; enum ESaveFilter @@ -108,9 +112,32 @@ public: FFSAVE_J2C = 12, FFSAVE_PNG = 13, FFSAVE_JPEG = 14, - FFSAVE_HPA = 15, - FFSAVE_TEXT = 16, - FFSAVE_LSL = 17 + // + FFSAVE_ANIMATN = 15, + FFSAVE_OGG = 16, + FFSAVE_NOTECARD = 17, + FFSAVE_GESTURE = 18, + FFSAVE_LSL = 19, + // good grief + FFSAVE_SHAPE = 20, + FFSAVE_SKIN = 21, + FFSAVE_HAIR = 22, + FFSAVE_EYES = 23, + FFSAVE_SHIRT = 24, + FFSAVE_PANTS = 25, + FFSAVE_SHOES = 26, + FFSAVE_SOCKS = 27, + FFSAVE_JACKET = 28, + FFSAVE_GLOVES = 29, + FFSAVE_UNDERSHIRT = 30, + FFSAVE_UNDERPANTS = 31, + FFSAVE_SKIRT = 32, + FFSAVE_INVGZ = 33, + FFSAVE_LANDMARK = 34, + FFSAVE_AO = 35, + FFSAVE_BLACKLIST = 36, + FFSAVE_PHYSICS = 37, + // }; // open the dialog. This is a modal operation @@ -164,13 +191,9 @@ private: #if LL_DARWIN NavDialogCreationOptions mNavOptions; - std::vector mFileVector; - UInt32 mFileIndex; OSStatus doNavChooseDialog(ELoadFilter filter); OSStatus doNavSaveDialog(ESaveFilter filter, const std::string& filename); - void getFilePath(SInt32 index); - void getFileName(SInt32 index); static Boolean navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode); #endif // LL_DARWIN From a6cb676d4a07bfdd449ddf2d13c81bf2cc7aa73c Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 11 May 2011 03:01:34 +0200 Subject: [PATCH 13/21] Add AIDirPicker and use it. Remove indra/newview/ll{dir,file}picker.{h,cpp}. Also removed some code from the Mac/windows code in indra/plugins/filepicker/llfilepicker.cpp that shouldn't be in there anymore (send_agent_pause/resume and updating the LLFrameTimer stuff). --- indra/newview/CMakeLists.txt | 5 +- indra/newview/llassetuploadresponders.cpp | 7 +- indra/newview/llfilepicker.cpp | 1587 ----------------- indra/newview/llfilepicker.h | 230 --- indra/newview/llfloaterreporter.cpp | 1 - indra/newview/llfloatervfsexplorer.cpp | 1 - indra/newview/llinventorybackup.cpp | 27 +- indra/newview/llinventorybackup.h | 3 + indra/newview/llpanelnetwork.cpp | 18 +- indra/newview/llpanelnetwork.h | 3 + indra/newview/llprefsim.cpp | 18 +- indra/newview/lltexturectrl.cpp | 1 - indra/newview/llviewermenu.h | 3 - indra/newview/llviewermenufile.cpp | 4 +- indra/newview/llviewermessage.cpp | 1 - indra/newview/statemachine/aidirpicker.h | 77 + indra/newview/statemachine/aifilepicker.cpp | 6 +- indra/newview/statemachine/aifilepicker.h | 3 +- indra/plugins/filepicker/CMakeLists.txt | 6 +- .../filepicker/basic_plugin_filepicker.cpp | 19 +- indra/plugins/filepicker/legacy.cpp | 112 ++ indra/plugins/filepicker/legacy.h | 79 + .../filepicker}/lldirpicker.cpp | 26 +- .../filepicker}/lldirpicker.h | 0 indra/plugins/filepicker/llfilepicker.cpp | 132 +- indra/plugins/filepicker/llfilepicker.h | 36 +- 26 files changed, 364 insertions(+), 2041 deletions(-) delete mode 100644 indra/newview/llfilepicker.cpp delete mode 100644 indra/newview/llfilepicker.h create mode 100644 indra/newview/statemachine/aidirpicker.h create mode 100644 indra/plugins/filepicker/legacy.cpp create mode 100644 indra/plugins/filepicker/legacy.h rename indra/{newview => plugins/filepicker}/lldirpicker.cpp (92%) rename indra/{newview => plugins/filepicker}/lldirpicker.h (100%) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 3599a1d3d..340989a05 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -132,7 +132,6 @@ set(viewer_SOURCE_FILES lldebugmessagebox.cpp lldebugview.cpp lldelayedgestureerror.cpp - lldirpicker.cpp lldrawable.cpp lldrawpoolalpha.cpp lldrawpoolavatar.cpp @@ -154,7 +153,6 @@ set(viewer_SOURCE_FILES llface.cpp llfasttimerview.cpp llfeaturemanager.cpp - llfilepicker.cpp llfirstuse.cpp llflexibleobject.cpp llfloaterabout.cpp @@ -609,7 +607,6 @@ set(viewer_HEADER_FILES lldebugmessagebox.h lldebugview.h lldelayedgestureerror.h - lldirpicker.h lldrawable.h lldrawpool.h lldrawpoolalpha.h @@ -632,7 +629,6 @@ set(viewer_HEADER_FILES llface.h llfasttimerview.h llfeaturemanager.h - llfilepicker.h llfirstuse.h llflexibleobject.h llfloaterabout.h @@ -1017,6 +1013,7 @@ set(statemachine_SOURCE_FILES set(statemachine_HEADER_FILES statemachine/aistatemachine.h statemachine/aifilepicker.h + statemachine/aidirpicker.h ) list(APPEND viewer_SOURCE_FILES ${statemachine_SOURCE_FILES}) list(APPEND viewer_HEADER_FILES ${statemachine_HEADER_FILES}) diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp index 2992e60bd..d910ab4b2 100644 --- a/indra/newview/llassetuploadresponders.cpp +++ b/indra/newview/llassetuploadresponders.cpp @@ -38,7 +38,6 @@ #include "llagent.h" #include "llcompilequeue.h" #include "llfloaterbuycurrency.h" -#include "llfilepicker.h" #include "llnotify.h" #include "llinventorymodel.h" #include "llinventoryview.h" @@ -129,7 +128,7 @@ void LLAssetUploadResponder::error(U32 statusNum, const std::string& reason) break; } LLUploadDialog::modalUploadFinished(); - LLFilePicker::instance().reset(); // unlock file picker when bulk upload fails + //AIFIXME? LLFilePicker::instance().reset(); // unlock file picker when bulk upload fails } //virtual @@ -293,7 +292,7 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) view->getPanel()->setSelection(content["new_inventory_item"].asUUID(), TAKE_FOCUS_NO); if((LLAssetType::AT_TEXTURE == asset_type || LLAssetType::AT_SOUND == asset_type) - /* FIXME: && LLFilePicker::instance().getFileCount() <= FILE_COUNT_DISPLAY_THRESHOLD */) + /* AIFIXME: && LLFilePicker::instance().getFileCount() <= FILE_COUNT_DISPLAY_THRESHOLD */) { view->getPanel()->openSelected(); } @@ -310,7 +309,7 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) // remove the "Uploading..." message LLUploadDialog::modalUploadFinished(); -#if 0 // FIXME: This needs to be done in some other way. +#if 0 // AIFIXME: This needs to be done in some other way. // *FIX: This is a pretty big hack. What this does is check the // file picker if there are any more pending uploads. If so, // upload that file. diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp deleted file mode 100644 index 2a325c0bb..000000000 --- a/indra/newview/llfilepicker.cpp +++ /dev/null @@ -1,1587 +0,0 @@ -/** - * @file llfilepicker.cpp - * @brief OS-specific file picker - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llfilepicker.h" -#include "llworld.h" -#include "llviewerwindow.h" -#include "llkeyboard.h" -#include "lldir.h" -#include "llframetimer.h" -#include "lltrans.h" - -#if LL_SDL -#include "llwindowsdl.h" // for some X/GTK utils to help with filepickers -#endif // LL_SDL - -// -// Globals -// - -LLFilePicker LLFilePicker::sInstance; - -#if LL_WINDOWS -// -#define SOUND_FILTER L"Sounds (*.wav; *.ogg)\0*.wav;*.ogg\0" -#define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg; *.png; *.jp2; *.j2k; *.j2c)\0*.tga;*.bmp;*.jpg;*.jpeg;*.png;*.jp2;*.j2k;*.j2c\0" -#define INVGZ_FILTER L"Inv cache (*.inv; *.inv.gz)\0*.inv;*.inv.gz\0" -#define AO_FILTER L"Animation Override (*.ao)\0*.ao\0" -#define BLACKLIST_FILTER L"Asset Blacklist (*.blacklist)\0*.blacklist\0" -// -#define ANIM_FILTER L"Animations (*.bvh)\0*.bvh\0" -#ifdef _CORY_TESTING -#define GEOMETRY_FILTER L"SL Geometry (*.slg)\0*.slg\0" -#endif -#define XML_FILTER L"XML files (*.xml)\0*.xml\0" -#define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0" -#define RAW_FILTER L"RAW files (*.raw)\0*.raw\0" -#endif - -// -// Implementation -// -LLFilePickerBase::LLFilePickerBase() - : mCurrentFile(0), - mLocked(FALSE) - -{ - reset(); - -#if LL_WINDOWS - mOFN.lStructSize = sizeof(OPENFILENAMEW); - mOFN.hwndOwner = NULL; // Set later - mOFN.hInstance = NULL; - mOFN.lpstrCustomFilter = NULL; - mOFN.nMaxCustFilter = 0; - mOFN.lpstrFile = NULL; // set in open and close - mOFN.nMaxFile = LL_MAX_PATH; - mOFN.lpstrFileTitle = NULL; - mOFN.nMaxFileTitle = 0; - mOFN.lpstrInitialDir = NULL; - mOFN.lpstrTitle = NULL; - mOFN.Flags = 0; // set in open and close - mOFN.nFileOffset = 0; - mOFN.nFileExtension = 0; - mOFN.lpstrDefExt = NULL; - mOFN.lCustData = 0L; - mOFN.lpfnHook = NULL; - mOFN.lpTemplateName = NULL; -#endif - -#if LL_DARWIN - memset(&mNavOptions, 0, sizeof(mNavOptions)); - OSStatus error = NavGetDefaultDialogCreationOptions(&mNavOptions); - if (error == noErr) - { - mNavOptions.modality = kWindowModalityAppModal; - } -#endif -} - -const std::string LLFilePickerBase::getFirstFile() -{ - mCurrentFile = 0; - return getNextFile(); -} - -const std::string LLFilePickerBase::getNextFile() -{ - if (mCurrentFile >= getFileCount()) - { - mLocked = FALSE; - return std::string(); - } - else - { - return mFiles[mCurrentFile++]; - } -} - -const std::string LLFilePickerBase::getCurFile() -{ - if (mCurrentFile >= getFileCount()) - { - mLocked = FALSE; - return std::string(); - } - else - { - return mFiles[mCurrentFile]; - } -} - -void LLFilePickerBase::reset() -{ - mLocked = FALSE; - mFiles.clear(); - mCurrentFile = 0; -} - -#if LL_WINDOWS - -BOOL LLFilePickerBase::setupFilter(ELoadFilter filter) -{ - BOOL res = TRUE; - switch (filter) - { - case FFLOAD_ALL: - mOFN.lpstrFilter = L"All Files (*.*)\0*.*\0" \ - SOUND_FILTER \ - IMAGE_FILTER \ - ANIM_FILTER \ - L"\0"; - break; - case FFLOAD_WAV: - mOFN.lpstrFilter = SOUND_FILTER \ - L"\0"; - break; - case FFLOAD_IMAGE: - mOFN.lpstrFilter = IMAGE_FILTER \ - L"\0"; - break; - case FFLOAD_ANIM: - mOFN.lpstrFilter = ANIM_FILTER \ - L"\0"; - break; -#ifdef _CORY_TESTING - case FFLOAD_GEOMETRY: - mOFN.lpstrFilter = GEOMETRY_FILTER \ - L"\0"; - break; -#endif - case FFLOAD_XML: - mOFN.lpstrFilter = XML_FILTER \ - L"\0"; - break; - case FFLOAD_SLOBJECT: - mOFN.lpstrFilter = SLOBJECT_FILTER \ - L"\0"; - break; - case FFLOAD_RAW: - mOFN.lpstrFilter = RAW_FILTER \ - L"\0"; - break; - // - case FFLOAD_INVGZ: - mOFN.lpstrFilter = INVGZ_FILTER \ - L"\0"; - break; - - case FFLOAD_AO: - mOFN.lpstrFilter = AO_FILTER \ - L"\0"; - break; - - case FFLOAD_BLACKLIST: - mOFN.lpstrFilter = BLACKLIST_FILTER \ - L"\0"; - break; - // - default: - res = FALSE; - break; - } - return res; -} - -BOOL LLFilePickerBase::getOpenFile(ELoadFilter filter) -{ - if( mLocked ) - { - return FALSE; - } - BOOL success = FALSE; - - // don't provide default file selection - mFilesW[0] = '\0'; - - mOFN.hwndOwner = (HWND)gViewerWindow->getPlatformWindow(); - mOFN.lpstrFile = mFilesW; - mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE; - mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR ; - mOFN.nFilterIndex = 1; - - setupFilter(filter); - - // Modal, so pause agent - send_agent_pause(); - - reset(); - - // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!! - success = GetOpenFileName(&mOFN); - if (success) - { - std::string filename = utf16str_to_utf8str(llutf16string(mFilesW)); - mFiles.push_back(filename); - } - send_agent_resume(); - - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); - return success; -} - -BOOL LLFilePickerBase::getMultipleOpenFiles(ELoadFilter filter) -{ - if( mLocked ) - { - return FALSE; - } - BOOL success = FALSE; - - // don't provide default file selection - mFilesW[0] = '\0'; - - mOFN.hwndOwner = (HWND)gViewerWindow->getPlatformWindow(); - mOFN.lpstrFile = mFilesW; - mOFN.nFilterIndex = 1; - mOFN.nMaxFile = FILENAME_BUFFER_SIZE; - mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | - OFN_EXPLORER | OFN_ALLOWMULTISELECT; - - setupFilter(filter); - - reset(); - - // Modal, so pause agent - send_agent_pause(); - // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!! - success = GetOpenFileName(&mOFN); // pauses until ok or cancel. - if( success ) - { - // The getopenfilename api doesn't tell us if we got more than - // one file, so we have to test manually by checking string - // lengths. - if( wcslen(mOFN.lpstrFile) > mOFN.nFileOffset ) /*Flawfinder: ignore*/ - { - std::string filename = utf16str_to_utf8str(llutf16string(mFilesW)); - mFiles.push_back(filename); - } - else - { - mLocked = TRUE; - WCHAR* tptrw = mFilesW; - std::string dirname; - while(1) - { - if (*tptrw == 0 && *(tptrw+1) == 0) // double '\0' - break; - if (*tptrw == 0) - tptrw++; // shouldn't happen? - std::string filename = utf16str_to_utf8str(llutf16string(tptrw)); - if (dirname.empty()) - dirname = filename + "\\"; - else - mFiles.push_back(dirname + filename); - tptrw += filename.size(); - } - } - } - send_agent_resume(); - - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); - return success; -} - -BOOL LLFilePickerBase::getSaveFile(ESaveFilter filter, const std::string& filename) -{ - if( mLocked ) - { - return FALSE; - } - BOOL success = FALSE; - - mOFN.lpstrFile = mFilesW; - if (!filename.empty()) - { - llutf16string tstring = utf8str_to_utf16str(filename); - wcsncpy(mFilesW, tstring.c_str(), FILENAME_BUFFER_SIZE); } /*Flawfinder: ignore*/ - else - { - mFilesW[0] = '\0'; - } - mOFN.hwndOwner = (HWND)gViewerWindow->getPlatformWindow(); - - switch( filter ) - { - case FFSAVE_ALL: - mOFN.lpstrDefExt = NULL; - mOFN.lpstrFilter = - L"All Files (*.*)\0*.*\0" \ - L"WAV Sounds (*.wav)\0*.wav\0" \ - L"Targa, Bitmap Images (*.tga; *.bmp)\0*.tga;*.bmp\0" \ - L"\0"; - break; - case FFSAVE_WAV: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.wav", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - mOFN.lpstrDefExt = L"wav"; - mOFN.lpstrFilter = - L"WAV Sounds (*.wav)\0*.wav\0" \ - L"\0"; - break; - case FFSAVE_TGA: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.tga", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - mOFN.lpstrDefExt = L"tga"; - mOFN.lpstrFilter = - L"Targa Images (*.tga)\0*.tga\0" \ - L"\0"; - break; - case FFSAVE_BMP: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.bmp", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - mOFN.lpstrDefExt = L"bmp"; - mOFN.lpstrFilter = - L"Bitmap Images (*.bmp)\0*.bmp\0" \ - L"\0"; - break; - case FFSAVE_PNG: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.png", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - mOFN.lpstrDefExt = L"png"; - mOFN.lpstrFilter = - L"PNG Images (*.png)\0*.png\0" \ - L"\0"; - break; - case FFSAVE_JPEG: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.jpeg", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - mOFN.lpstrDefExt = L"jpg"; - mOFN.lpstrFilter = - L"JPEG Images (*.jpg *.jpeg)\0*.jpg;*.jpeg\0" \ - L"\0"; - break; - case FFSAVE_AVI: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.avi", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - mOFN.lpstrDefExt = L"avi"; - mOFN.lpstrFilter = - L"AVI Movie File (*.avi)\0*.avi\0" \ - L"\0"; - break; - case FFSAVE_ANIM: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.xaf", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - mOFN.lpstrDefExt = L"xaf"; - mOFN.lpstrFilter = - L"XAF Anim File (*.xaf)\0*.xaf\0" \ - L"\0"; - break; -#ifdef _CORY_TESTING - case FFSAVE_GEOMETRY: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.slg", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - mOFN.lpstrDefExt = L"slg"; - mOFN.lpstrFilter = - L"SLG SL Geometry File (*.slg)\0*.slg\0" \ - L"\0"; - break; -#endif - case FFSAVE_XML: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.xml", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - - mOFN.lpstrDefExt = L"xml"; - mOFN.lpstrFilter = - L"XML File (*.xml)\0*.xml\0" \ - L"\0"; - break; - case FFSAVE_COLLADA: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.collada", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - mOFN.lpstrDefExt = L"collada"; - mOFN.lpstrFilter = - L"COLLADA File (*.collada)\0*.collada\0" \ - L"\0"; - break; - case FFSAVE_RAW: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.raw", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ - } - mOFN.lpstrDefExt = L"raw"; - mOFN.lpstrFilter = RAW_FILTER \ - L"\0"; - break; - case FFSAVE_J2C: - if (filename.empty()) - { - wcsncpy( mFilesW,L"untitled.j2c", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"j2c"; - mOFN.lpstrFilter = - L"Compressed Images (*.j2c)\0*.j2c\0" \ - L"\0"; - break; - // - case FFSAVE_ANIMATN: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.animatn", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"animatn"; - mOFN.lpstrFilter = - L"SL Animations (*.animatn)\0*.animatn\0" \ - L"\0"; - break; - case FFSAVE_OGG: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.ogg", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"ogg"; - mOFN.lpstrFilter = - L"Ogg (*.ogg)\0*.ogg\0" \ - L"\0"; - break; - case FFSAVE_NOTECARD: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.notecard", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"notecard"; - mOFN.lpstrFilter = - L"Notecards (*.notecard)\0*.notecard\0" \ - L"\0"; - break; - case FFSAVE_GESTURE: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.gesture", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"gesture"; - mOFN.lpstrFilter = - L"Gestures (*.gesture)\0*.gesture\0" \ - L"\0"; - break; - case FFSAVE_LSL: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.lsl", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"lsl"; - mOFN.lpstrFilter = - L"LSL (*.lsl)\0*.lsl\0" \ - L"\0"; - break; - case FFSAVE_SHAPE: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.shape", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"shape"; - mOFN.lpstrFilter = - L"Shapes (*.shape)\0*.shape\0" \ - L"\0"; - break; - case FFSAVE_SKIN: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.skin", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"skin"; - mOFN.lpstrFilter = - L"Skins (*.skin)\0*.skin\0" \ - L"\0"; - break; - case FFSAVE_HAIR: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.hair", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"hair"; - mOFN.lpstrFilter = - L"Hair (*.hair)\0*.hair\0" \ - L"\0"; - break; - case FFSAVE_EYES: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.eyes", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"eyes"; - mOFN.lpstrFilter = - L"Eyes (*.eyes)\0*.eyes\0" \ - L"\0"; - break; - case FFSAVE_SHIRT: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.shirt", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"shirt"; - mOFN.lpstrFilter = - L"Shirts (*.shirt)\0*.shirt\0" \ - L"\0"; - break; - case FFSAVE_PANTS: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.pants", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"pants"; - mOFN.lpstrFilter = - L"Pants (*.pants)\0*.pants\0" \ - L"\0"; - break; - case FFSAVE_SHOES: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.shoes", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"shoes"; - mOFN.lpstrFilter = - L"Shoes (*.shoes)\0*.shoes\0" \ - L"\0"; - break; - case FFSAVE_SOCKS: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.socks", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"socks"; - mOFN.lpstrFilter = - L"Socks (*.socks)\0*.socks\0" \ - L"\0"; - break; - case FFSAVE_JACKET: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.jacket", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"jacket"; - mOFN.lpstrFilter = - L"Jackets (*.jacket)\0*.jacket\0" \ - L"\0"; - break; - case FFSAVE_GLOVES: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.gloves", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"gloves"; - mOFN.lpstrFilter = - L"Gloves (*.gloves)\0*.gloves\0" \ - L"\0"; - break; - case FFSAVE_UNDERSHIRT: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.undershirt", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"undershirt"; - mOFN.lpstrFilter = - L"Undershirts (*.undershirt)\0*.undershirt\0" \ - L"\0"; - break; - case FFSAVE_UNDERPANTS: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.underpants", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"underpants"; - mOFN.lpstrFilter = - L"Underpants (*.underpants)\0*.underpants\0" \ - L"\0"; - break; - case FFSAVE_SKIRT: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.skirt", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"skirt"; - mOFN.lpstrFilter = - L"Skirts (*.skirt)\0*.skirt\0" \ - L"\0"; - break; - case FFSAVE_LANDMARK: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.landmark", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"landmark"; - mOFN.lpstrFilter = - L"Landmarks (*.landmark)\0*.landmark\0" \ - L"\0"; - break; - case FFSAVE_AO: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.ao", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"ao"; - mOFN.lpstrFilter = - L"Animation overrides (*.ao)\0*.ao\0" \ - L"\0"; - break; - case FFSAVE_INVGZ: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.inv", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L".inv"; - mOFN.lpstrFilter = - L"InvCache (*.inv)\0*.inv\0" \ - L"\0"; - break; - case FFSAVE_BLACKLIST: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.blacklist", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L".blacklist"; - mOFN.lpstrFilter = - L"Asset Blacklists (*.blacklist)\0*.blacklist\0" \ - L"\0"; - break; - case FFSAVE_PHYSICS: - if(filename.empty()) - { - wcsncpy( mFilesW,L"untitled.phy", FILENAME_BUFFER_SIZE); - } - mOFN.lpstrDefExt = L"phy"; - mOFN.lpstrFilter = - L"Landmarks (*.phy)\0*.phy\0" \ - L"\0"; - break; - // - default: - return FALSE; - } - - - mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE; - mOFN.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST; - - reset(); - - // Modal, so pause agent - send_agent_pause(); - { - // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!! - success = GetSaveFileName(&mOFN); - if (success) - { - std::string filename = utf16str_to_utf8str(llutf16string(mFilesW)); - mFiles.push_back(filename); - } - gKeyboard->resetKeys(); - } - send_agent_resume(); - - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); - return success; -} - -#elif LL_DARWIN - -Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode) -{ - Boolean result = true; - ELoadFilter filter = *((ELoadFilter*) callBackUD); - OSStatus error = noErr; - - if (filterMode == kNavFilteringBrowserList && filter != FFLOAD_ALL && (theItem->descriptorType == typeFSRef || theItem->descriptorType == typeFSS)) - { - // navInfo is only valid for typeFSRef and typeFSS - NavFileOrFolderInfo *navInfo = (NavFileOrFolderInfo*) info; - if (!navInfo->isFolder) - { - AEDesc desc; - error = AECoerceDesc(theItem, typeFSRef, &desc); - if (error == noErr) - { - FSRef fileRef; - error = AEGetDescData(&desc, &fileRef, sizeof(fileRef)); - if (error == noErr) - { - LSItemInfoRecord fileInfo; - error = LSCopyItemInfoForRef(&fileRef, kLSRequestExtension | kLSRequestTypeCreator, &fileInfo); - if (error == noErr) - { - if (filter == FFLOAD_IMAGE) - { - if (fileInfo.filetype != 'JPEG' && fileInfo.filetype != 'JPG ' && - fileInfo.filetype != 'BMP ' && fileInfo.filetype != 'TGA ' && - fileInfo.filetype != 'BMPf' && fileInfo.filetype != 'TPIC' && - fileInfo.filetype != 'PNG ' && - (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("jpeg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && - CFStringCompare(fileInfo.extension, CFSTR("jpg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && - CFStringCompare(fileInfo.extension, CFSTR("bmp"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && - CFStringCompare(fileInfo.extension, CFSTR("tga"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && - CFStringCompare(fileInfo.extension, CFSTR("png"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) - ) - { - result = false; - } - } - else if (filter == FFLOAD_WAV) - { - if (fileInfo.filetype != 'WAVE' && fileInfo.filetype != 'WAV ' && - (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("wave"), kCFCompareCaseInsensitive) != kCFCompareEqualTo && - CFStringCompare(fileInfo.extension, CFSTR("wav"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) - ) - { - result = false; - } - } - else if (filter == FFLOAD_ANIM) - { - if (fileInfo.filetype != 'BVH ' && - (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("bvh"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) - ) - { - result = false; - } - } - else if (filter == FFLOAD_XML) - { - if (fileInfo.filetype != 'XML ' && - (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("xml"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) - ) - { - result = false; - } - } -#ifdef _CORY_TESTING - else if (filter == FFLOAD_GEOMETRY) - { - if (fileInfo.filetype != 'SLG ' && - (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("slg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) - ) - { - result = false; - } - } -#endif - else if (filter == FFLOAD_SLOBJECT) - { - llwarns << "*** navOpenFilterProc: FFLOAD_SLOBJECT NOT IMPLEMENTED ***" << llendl; - } - else if (filter == FFLOAD_RAW) - { - if (fileInfo.filetype != '\?\?\?\?' && - (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("raw"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) - ) - { - result = false; - } - } - - if (fileInfo.extension) - { - CFRelease(fileInfo.extension); - } - } - } - AEDisposeDesc(&desc); - } - } - } - return result; -} - -OSStatus LLFilePickerBase::doNavChooseDialog(ELoadFilter filter) -{ - OSStatus error = noErr; - NavDialogRef navRef = NULL; - NavReplyRecord navReply; - - memset(&navReply, 0, sizeof(navReply)); - - // NOTE: we are passing the address of a local variable here. - // This is fine, because the object this call creates will exist for less than the lifetime of this function. - // (It is destroyed by NavDialogDispose() below.) - error = NavCreateChooseFileDialog(&mNavOptions, NULL, NULL, NULL, navOpenFilterProc, (void*)(&filter), &navRef); - - gViewerWindow->mWindow->beforeDialog(); - - if (error == noErr) - error = NavDialogRun(navRef); - - gViewerWindow->mWindow->afterDialog(); - - if (error == noErr) - error = NavDialogGetReply(navRef, &navReply); - - if (navRef) - NavDialogDispose(navRef); - - if (error == noErr && navReply.validRecord) - { - SInt32 count = 0; - SInt32 index; - - // AE indexes are 1 based... - error = AECountItems(&navReply.selection, &count); - for (index = 1; index <= count; index++) - { - FSRef fsRef; - AEKeyword theAEKeyword; - DescType typeCode; - Size actualSize = 0; - char path[MAX_PATH]; /*Flawfinder: ignore*/ - - memset(&fsRef, 0, sizeof(fsRef)); - error = AEGetNthPtr(&navReply.selection, index, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize); - - if (error == noErr) - error = FSRefMakePath(&fsRef, (UInt8*) path, sizeof(path)); - - if (error == noErr) - mFiles.push_back(std::string(path)); - } - } - - return error; -} - -OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string& filename) -{ - OSStatus error = noErr; - NavDialogRef navRef = NULL; - NavReplyRecord navReply; - - memset(&navReply, 0, sizeof(navReply)); - - // Setup the type, creator, and extension - OSType type, creator; - CFStringRef extension = NULL; - switch (filter) - { - case FFSAVE_WAV: - type = 'WAVE'; - creator = 'TVOD'; - extension = CFSTR(".wav"); - break; - - case FFSAVE_TGA: - type = 'TPIC'; - creator = 'prvw'; - extension = CFSTR(".tga"); - break; - - case FFSAVE_BMP: - type = 'BMPf'; - creator = 'prvw'; - extension = CFSTR(".bmp"); - break; - case FFSAVE_JPEG: - type = 'JPEG'; - creator = 'prvw'; - extension = CFSTR(".jpeg"); - break; - case FFSAVE_PNG: - type = 'PNG '; - creator = 'prvw'; - extension = CFSTR(".png"); - break; - case FFSAVE_AVI: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; - extension = CFSTR(".mov"); - break; - - case FFSAVE_ANIM: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; - extension = CFSTR(".xaf"); - break; - -#ifdef _CORY_TESTING - case FFSAVE_GEOMETRY: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; - extension = CFSTR(".slg"); - break; -#endif - case FFSAVE_RAW: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; - extension = CFSTR(".raw"); - break; - - case FFSAVE_J2C: - type = '\?\?\?\?'; - creator = 'prvw'; - extension = CFSTR(".j2c"); - break; - - case FFSAVE_ALL: - default: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; - extension = CFSTR(""); - break; - } - - // Create the dialog - error = NavCreatePutFileDialog(&mNavOptions, type, creator, NULL, NULL, &navRef); - if (error == noErr) - { - CFStringRef nameString = NULL; - bool hasExtension = true; - - // Create a CFString of the initial file name - if (!filename.empty()) - nameString = CFStringCreateWithCString(NULL, filename.c_str(), kCFStringEncodingUTF8); - else - nameString = CFSTR("Untitled"); - - // Add the extension if one was not provided - if (nameString && !CFStringHasSuffix(nameString, extension)) - { - CFStringRef tempString = nameString; - hasExtension = false; - nameString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), tempString, extension); - CFRelease(tempString); - } - - // Set the name in the dialog - if (nameString) - { - error = NavDialogSetSaveFileName(navRef, nameString); - CFRelease(nameString); - } - else - { - error = paramErr; - } - } - - gViewerWindow->mWindow->beforeDialog(); - - // Run the dialog - if (error == noErr) - error = NavDialogRun(navRef); - - gViewerWindow->mWindow->afterDialog(); - - if (error == noErr) - error = NavDialogGetReply(navRef, &navReply); - - if (navRef) - NavDialogDispose(navRef); - - if (error == noErr && navReply.validRecord) - { - SInt32 count = 0; - - // AE indexes are 1 based... - error = AECountItems(&navReply.selection, &count); - if (count > 0) - { - // Get the FSRef to the containing folder - FSRef fsRef; - AEKeyword theAEKeyword; - DescType typeCode; - Size actualSize = 0; - - memset(&fsRef, 0, sizeof(fsRef)); - error = AEGetNthPtr(&navReply.selection, 1, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize); - - if (error == noErr) - { - char path[PATH_MAX]; /*Flawfinder: ignore*/ - char newFileName[SINGLE_FILENAME_BUFFER_SIZE]; /*Flawfinder: ignore*/ - - error = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX); - if (error == noErr) - { - if (CFStringGetCString(navReply.saveFileName, newFileName, sizeof(newFileName), kCFStringEncodingUTF8)) - { - mFiles.push_back(std::string(path) + "/" + std::string(newFileName)); - } - else - { - error = paramErr; - } - } - else - { - error = paramErr; - } - } - } - } - - return error; -} - -BOOL LLFilePickerBase::getOpenFile(ELoadFilter filter) -{ - if( mLocked ) - return FALSE; - - BOOL success = FALSE; - - OSStatus error = noErr; - - reset(); - - mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; - // Modal, so pause agent - send_agent_pause(); - { - error = doNavChooseDialog(filter); - } - send_agent_resume(); - if (error == noErr) - { - if (getFileCount()) - success = true; - } - - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); - return success; -} - -BOOL LLFilePickerBase::getMultipleOpenFiles(ELoadFilter filter) -{ - if( mLocked ) - return FALSE; - - BOOL success = FALSE; - - OSStatus error = noErr; - - reset(); - - mNavOptions.optionFlags |= kNavAllowMultipleFiles; - // Modal, so pause agent - send_agent_pause(); - { - error = doNavChooseDialog(filter); - } - send_agent_resume(); - if (error == noErr) - { - if (getFileCount()) - success = true; - if (getFileCount() > 1) - mLocked = TRUE; - } - - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); - return success; -} - -BOOL LLFilePickerBase::getSaveFile(ESaveFilter filter, const std::string& filename) -{ - if( mLocked ) - return FALSE; - BOOL success = FALSE; - OSStatus error = noErr; - - reset(); - - mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; - - // Modal, so pause agent - send_agent_pause(); - { - error = doNavSaveDialog(filter, filename); - } - send_agent_resume(); - if (error == noErr) - { - if (getFileCount()) - success = true; - } - - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); - return success; -} - -#elif LL_LINUX || LL_SOLARIS - -# if LL_GTK - -// static -void LLFilePickerBase::add_to_selectedfiles(gpointer data, gpointer user_data) -{ - // We need to run g_filename_to_utf8 in the user's locale - std::string saved_locale(setlocale(LC_ALL, NULL)); - setlocale(LC_ALL, ""); - - LLFilePickerBase* picker = (LLFilePickerBase*) user_data; - GError *error = NULL; - gchar* filename_utf8 = g_filename_to_utf8((gchar*)data, - -1, NULL, NULL, &error); - if (error) - { - // *FIXME. - // This condition should really be notified to the user, e.g. - // through a message box. Just logging it is inappropriate. - - // g_filename_display_name is ideal, but >= glib 2.6, so: - // a hand-rolled hacky makeASCII which disallows control chars - std::string display_name; - for (const gchar *str = (const gchar *)data; *str; str++) - { - display_name += (char)((*str >= 0x20 && *str <= 0x7E) ? *str : '?'); - } - llwarns << "g_filename_to_utf8 failed on \"" << display_name << "\": " << error->message << llendl; - } - - if (filename_utf8) - { - picker->mFiles.push_back(std::string(filename_utf8)); - lldebugs << "ADDED FILE " << filename_utf8 << llendl; - g_free(filename_utf8); - } - - setlocale(LC_ALL, saved_locale.c_str()); -} - -// static -void LLFilePickerBase::chooser_responder(GtkWidget *widget, gint response, gpointer user_data) -{ - LLFilePicker* picker = (LLFilePicker*)user_data; - - lldebugs << "GTK DIALOG RESPONSE " << response << llendl; - - if (response == GTK_RESPONSE_ACCEPT) - { - GSList *file_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget)); - g_slist_foreach(file_list, (GFunc)add_to_selectedfiles, user_data); - g_slist_foreach(file_list, (GFunc)g_free, NULL); - g_slist_free (file_list); - } - - // set the default path for this usage context. - picker->mContextToPathMap[picker->mCurContextName] = - gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(widget)); - - gtk_widget_destroy(widget); - gtk_main_quit(); -} - - -GtkWindow* LLFilePickerBase::buildFilePicker(bool is_save, bool is_folder, std::string context) -{ - if (LLWindowSDL::ll_try_gtk_init()) - { - GtkWidget *win = NULL; - GtkFileChooserAction pickertype = - is_save? - (is_folder? - GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER : - GTK_FILE_CHOOSER_ACTION_SAVE) : - (is_folder? - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER : - GTK_FILE_CHOOSER_ACTION_OPEN); - - win = gtk_file_chooser_dialog_new(NULL, NULL, - pickertype, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - is_folder ? - GTK_STOCK_APPLY : - (is_save ? - GTK_STOCK_SAVE : - GTK_STOCK_OPEN), - GTK_RESPONSE_ACCEPT, - (gchar *)NULL); - mCurContextName = context; - - // get the default path for this usage context if it's been - // seen before. - std::map::iterator - this_path = mContextToPathMap.find(context); - if (this_path != mContextToPathMap.end()) - { - gtk_file_chooser_set_current_folder - (GTK_FILE_CHOOSER(win), - this_path->second.c_str()); - } - -# if LL_X11 - // Make GTK tell the window manager to associate this - // dialog with our non-GTK raw X11 window, which should try - // to keep it on top etc. - Window XWindowID = LLWindowSDL::get_SDL_XWindowID(); - if (None != XWindowID) - { - gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin - GdkWindow *gdkwin = gdk_window_foreign_new(XWindowID); - gdk_window_set_transient_for(GTK_WIDGET(win)->window, - gdkwin); - } - else - { - llwarns << "Hmm, couldn't get xwid to use for transient." << llendl; - } -# endif //LL_X11 - - g_signal_connect (GTK_FILE_CHOOSER(win), - "response", - G_CALLBACK(LLFilePickerBase::chooser_responder), - this); - - gtk_window_set_modal(GTK_WINDOW(win), TRUE); - - /* GTK 2.6: if (is_folder) - gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(win), - TRUE); */ - - return GTK_WINDOW(win); - } - else - { - return NULL; - } -} - -static void add_common_filters_to_gtkchooser(GtkFileFilter *gfilter, - GtkWindow *picker, - std::string filtername) -{ - gtk_file_filter_set_name(gfilter, filtername.c_str()); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), - gfilter); - GtkFileFilter *allfilter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(allfilter, "*"); - gtk_file_filter_set_name(allfilter, LLTrans::getString("all_files").c_str()); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), allfilter); - gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(picker), gfilter); -} - -static std::string add_simple_pattern_filter_to_gtkchooser(GtkWindow *picker, - std::string pattern, - std::string filtername) -{ - GtkFileFilter *gfilter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(gfilter, pattern.c_str()); - add_common_filters_to_gtkchooser(gfilter, picker, filtername); - return filtername; -} - -static std::string add_simple_mime_filter_to_gtkchooser(GtkWindow *picker, - std::string mime, - std::string filtername) -{ - GtkFileFilter *gfilter = gtk_file_filter_new(); - gtk_file_filter_add_mime_type(gfilter, mime.c_str()); - add_common_filters_to_gtkchooser(gfilter, picker, filtername); - return filtername; -} - -static std::string add_wav_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_mime_filter_to_gtkchooser(picker, "audio/x-wav", - LLTrans::getString("sound_files") + " (*.wav)"); -} - -static std::string add_bvh_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_pattern_filter_to_gtkchooser(picker, "*.bvh", - LLTrans::getString("animation_files") + " (*.bvh)"); -} - -static std::string add_xml_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_mime_filter_to_gtkchooser(picker, "text/xml", - LLTrans::getString("xml_file") + " (*.xml)"); -} - -static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker) -{ - GtkFileFilter *gfilter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(gfilter, "*.tga"); - gtk_file_filter_add_mime_type(gfilter, "image/jpeg"); - gtk_file_filter_add_mime_type(gfilter, "image/png"); - gtk_file_filter_add_mime_type(gfilter, "image/bmp"); - std::string filtername = LLTrans::getString("image_files") + " (*.tga; *.bmp; *.jpg; *.png)"; - add_common_filters_to_gtkchooser(gfilter, picker, filtername); - return filtername; -} - - -BOOL LLFilePickerBase::getSaveFile( ESaveFilter filter, const std::string& filename ) -{ - BOOL rtn = FALSE; - - gViewerWindow->mWindow->beforeDialog(); - - reset(); - - GtkWindow* picker = buildFilePicker(true, false, "savefile"); - - if (picker) - { - std::string suggest_name = "untitled"; - std::string suggest_ext = ""; - std::string caption = LLTrans::getString("save_file_verb") + " "; - switch (filter) - { - case FFSAVE_WAV: - caption += add_wav_filter_to_gtkchooser(picker); - suggest_ext = ".wav"; - break; - case FFSAVE_TGA: - caption += add_simple_pattern_filter_to_gtkchooser - (picker, "*.tga", LLTrans::getString("targa_image_files") + " (*.tga)"); - suggest_ext = ".tga"; - break; - case FFSAVE_BMP: - caption += add_simple_mime_filter_to_gtkchooser - (picker, "image/bmp", LLTrans::getString("bitmap_image_files") + " (*.bmp)"); - suggest_ext = ".bmp"; - break; - case FFSAVE_AVI: - caption += add_simple_mime_filter_to_gtkchooser - (picker, "video/x-msvideo", - LLTrans::getString("avi_movie_file") + " (*.avi)"); - suggest_ext = ".avi"; - break; - case FFSAVE_ANIM: - caption += add_simple_pattern_filter_to_gtkchooser - (picker, "*.xaf", LLTrans::getString("xaf_animation_file") + " (*.xaf)"); - suggest_ext = ".xaf"; - break; - case FFSAVE_XML: - caption += add_simple_pattern_filter_to_gtkchooser - (picker, "*.xml", LLTrans::getString("xml_file") + " (*.xml)"); - suggest_ext = ".xml"; - break; - case FFSAVE_RAW: - caption += add_simple_pattern_filter_to_gtkchooser - (picker, "*.raw", LLTrans::getString("raw_file") + " (*.raw)"); - suggest_ext = ".raw"; - break; - case FFSAVE_J2C: - caption += add_simple_mime_filter_to_gtkchooser - (picker, "images/jp2", - LLTrans::getString("compressed_image_files") + " (*.j2c)"); - suggest_ext = ".j2c"; - break; - default:; - break; - } - - gtk_window_set_title(GTK_WINDOW(picker), caption.c_str()); - - if (filename.empty()) - { - suggest_name += suggest_ext; - - gtk_file_chooser_set_current_name - (GTK_FILE_CHOOSER(picker), - suggest_name.c_str()); - } - else - { - gtk_file_chooser_set_current_name - (GTK_FILE_CHOOSER(picker), filename.c_str()); - } - - gtk_widget_show_all(GTK_WIDGET(picker)); - gtk_main(); - - rtn = (getFileCount() == 1); - } - - gViewerWindow->mWindow->afterDialog(); - - return rtn; -} - -BOOL LLFilePickerBase::getOpenFile( ELoadFilter filter ) -{ - BOOL rtn = FALSE; - - gViewerWindow->mWindow->beforeDialog(); - - reset(); - - GtkWindow* picker = buildFilePicker(false, false, "openfile"); - - if (picker) - { - std::string caption = LLTrans::getString("load_file_verb") + " "; - std::string filtername = ""; - switch (filter) - { - case FFLOAD_WAV: - filtername = add_wav_filter_to_gtkchooser(picker); - break; - case FFLOAD_ANIM: - filtername = add_bvh_filter_to_gtkchooser(picker); - break; - case FFLOAD_IMAGE: - filtername = add_imageload_filter_to_gtkchooser(picker); - break; - case FFLOAD_XML: - filtername = add_xml_filter_to_gtkchooser(picker); - break; - default:; - break; - } - - caption += filtername; - - gtk_window_set_title(GTK_WINDOW(picker), caption.c_str()); - - gtk_widget_show_all(GTK_WIDGET(picker)); - gtk_main(); - - rtn = (getFileCount() == 1); - } - - gViewerWindow->mWindow->afterDialog(); - - return rtn; -} - -BOOL LLFilePickerBase::getMultipleOpenFiles( ELoadFilter filter ) -{ - BOOL rtn = FALSE; - - gViewerWindow->mWindow->beforeDialog(); - - reset(); - - GtkWindow* picker = buildFilePicker(false, false, "openfile"); - - if (picker) - { - gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(picker), - TRUE); - - gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("load_files").c_str()); - - gtk_widget_show_all(GTK_WIDGET(picker)); - gtk_main(); - rtn = !mFiles.empty(); - } - - gViewerWindow->mWindow->afterDialog(); - - return rtn; -} - -# else // LL_GTK - -// Hacky stubs designed to facilitate fake getSaveFile and getOpenFile with -// static results, when we don't have a real filepicker. - -BOOL LLFilePickerBase::getSaveFile( ESaveFilter filter, const std::string& filename ) -{ - reset(); - - llinfos << "getSaveFile suggested filename is [" << filename - << "]" << llendl; - if (!filename.empty()) - { - mFiles.push_back(gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + filename); - return TRUE; - } - return FALSE; -} - -BOOL LLFilePickerBase::getOpenFile( ELoadFilter filter ) -{ - reset(); - - // HACK: Static filenames for 'open' until we implement filepicker - std::string filename = gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter() + "upload"; - switch (filter) - { - case FFLOAD_WAV: filename += ".wav"; break; - case FFLOAD_IMAGE: filename += ".tga"; break; - case FFLOAD_ANIM: filename += ".bvh"; break; - case FFLOAD_XML: filename += ".xml"; break; - default: break; - } - mFiles.push_back(filename); - llinfos << "getOpenFile: Will try to open file: " << hackyfilename << llendl; - return TRUE; -} - -BOOL LLFilePickerBase::getMultipleOpenFiles( ELoadFilter filter ) -{ - reset(); - return FALSE; -} - -#endif // LL_GTK - -#else // not implemented - -BOOL LLFilePickerBase::getSaveFile( ESaveFilter filter, const std::string& filename ) -{ - reset(); - return FALSE; -} - -BOOL LLFilePickerBase::getOpenFile( ELoadFilter filter ) -{ - reset(); - return FALSE; -} - -BOOL LLFilePickerBase::getMultipleOpenFiles( ELoadFilter filter ) -{ - reset(); - return FALSE; -} - -#endif diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h deleted file mode 100644 index 3be94f8e0..000000000 --- a/indra/newview/llfilepicker.h +++ /dev/null @@ -1,230 +0,0 @@ -/** - * @file llfilepicker.h - * @brief OS-specific file picker - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -// OS specific file selection dialog. This is implemented as a -// singleton class, so call the instance() method to get the working -// instance. When you call getMultipleOpenFile(), it locks the picker -// until you iterate to the end of the list of selected files with -// getNextFile() or call reset(). - -#ifndef LL_LLFILEPICKER_H -#define LL_LLFILEPICKER_H - -#include "stdtypes.h" - -#if LL_DARWIN -#include - -// AssertMacros.h does bad things. -#undef verify -#undef check -#undef require - -#include -#include "llstring.h" - -#endif - -// Need commdlg.h for OPENFILENAMEA -#ifdef LL_WINDOWS -#include -#endif - -// mostly for Linux, possible on others -#if LL_GTK -# include "gtk/gtk.h" -#endif // LL_GTK - -// also mostly for Linux, for some X11-specific filepicker usability tweaks -#if LL_X11 -#include "SDL/SDL_syswm.h" -#endif - -// This class is used as base class of a singleton and is therefore not -// allowed to have any static members or static local variables! -class LLFilePickerBase -{ -public: - enum ELoadFilter - { - FFLOAD_ALL = 1, - FFLOAD_WAV = 2, - FFLOAD_IMAGE = 3, - FFLOAD_ANIM = 4, -#ifdef _CORY_TESTING - FFLOAD_GEOMETRY = 5, -#endif - FFLOAD_XML = 6, - FFLOAD_SLOBJECT = 7, - FFLOAD_RAW = 8, - // - FFLOAD_INVGZ = 9, - FFLOAD_AO = 10, - FFLOAD_BLACKLIST = 11 - // - }; - - enum ESaveFilter - { - FFSAVE_ALL = 1, - FFSAVE_WAV = 3, - FFSAVE_TGA = 4, - FFSAVE_BMP = 5, - FFSAVE_AVI = 6, - FFSAVE_ANIM = 7, -#ifdef _CORY_TESTING - FFSAVE_GEOMETRY = 8, -#endif - FFSAVE_XML = 9, - FFSAVE_COLLADA = 10, - FFSAVE_RAW = 11, - FFSAVE_J2C = 12, - FFSAVE_PNG = 13, - FFSAVE_JPEG = 14, - // - FFSAVE_ANIMATN = 15, - FFSAVE_OGG = 16, - FFSAVE_NOTECARD = 17, - FFSAVE_GESTURE = 18, - FFSAVE_LSL = 19, - // good grief - FFSAVE_SHAPE = 20, - FFSAVE_SKIN = 21, - FFSAVE_HAIR = 22, - FFSAVE_EYES = 23, - FFSAVE_SHIRT = 24, - FFSAVE_PANTS = 25, - FFSAVE_SHOES = 26, - FFSAVE_SOCKS = 27, - FFSAVE_JACKET = 28, - FFSAVE_GLOVES = 29, - FFSAVE_UNDERSHIRT = 30, - FFSAVE_UNDERPANTS = 31, - FFSAVE_SKIRT = 32, - FFSAVE_INVGZ = 33, - FFSAVE_LANDMARK = 34, - FFSAVE_AO = 35, - FFSAVE_BLACKLIST = 36, - FFSAVE_PHYSICS = 37, - // - }; - - // open the dialog. This is a modal operation - BOOL getSaveFile( ESaveFilter filter = FFSAVE_ALL, const std::string& filename = LLStringUtil::null ); - BOOL getOpenFile( ELoadFilter filter = FFLOAD_ALL ); - BOOL getMultipleOpenFiles( ELoadFilter filter = FFLOAD_ALL ); - - // Get the filename(s) found. getFirstFile() sets the pointer to - // the start of the structure and allows the start of iteration. - const std::string getFirstFile(); - - // getNextFile() increments the internal representation and - // returns the next file specified by the user. Returns NULL when - // no more files are left. Further calls to getNextFile() are - // undefined. - const std::string getNextFile(); - - // This utility function extracts the current file name without - // doing any incrementing. - const std::string getCurFile(); - - // Returns the index of the current file. - S32 getCurFileNum() const { return mCurrentFile; } - - S32 getFileCount() const { return (S32)mFiles.size(); } - - // See llvfs/lldir.h : getBaseFileName and getDirName to extract base or directory names - - // clear any lists of buffers or whatever, and make sure the file - // picker isn't locked. - void reset(); - -private: - enum - { - SINGLE_FILENAME_BUFFER_SIZE = 1024, - //FILENAME_BUFFER_SIZE = 65536 - FILENAME_BUFFER_SIZE = 65000 - }; - -#if LL_WINDOWS - OPENFILENAMEW mOFN; // for open and save dialogs - WCHAR mFilesW[FILENAME_BUFFER_SIZE]; - - BOOL setupFilter(ELoadFilter filter); -#endif - -#if LL_DARWIN - NavDialogCreationOptions mNavOptions; - - OSStatus doNavChooseDialog(ELoadFilter filter); - OSStatus doNavSaveDialog(ESaveFilter filter, const std::string& filename); - static Boolean navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode); -#endif - -#if LL_GTK - static void add_to_selectedfiles(gpointer data, gpointer user_data); - static void chooser_responder(GtkWidget *widget, gint response, gpointer user_data); - // we remember the last path that was accessed for a particular usage - std::map mContextToPathMap; - std::string mCurContextName; -#endif - - std::vector mFiles; - S32 mCurrentFile; - BOOL mLocked; - BOOL mMultiFile; - -protected: -#if LL_GTK - GtkWindow* buildFilePicker(bool is_save, bool is_folder, - std::string context = "generic"); -#endif - -protected: - LLFilePickerBase(); -}; - -// True singleton, private constructors (and no friends). -class LLFilePicker : public LLFilePickerBase -{ -public: - // calling this before main() is undefined - static LLFilePicker& instance( void ) { return sInstance; } - -private: - static LLFilePicker sInstance; - - LLFilePicker() { } -}; - -#endif diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 6e524a05f..72720692f 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -76,7 +76,6 @@ #include "llviewerwindow.h" #include "llviewertexturelist.h" #include "llworldmap.h" -#include "llfilepicker.h" #include "llfloateravatarpicker.h" #include "lldir.h" #include "llselectmgr.h" diff --git a/indra/newview/llfloatervfsexplorer.cpp b/indra/newview/llfloatervfsexplorer.cpp index 6f6384a37..99662b9d9 100644 --- a/indra/newview/llfloatervfsexplorer.cpp +++ b/indra/newview/llfloatervfsexplorer.cpp @@ -5,7 +5,6 @@ #include "lluictrlfactory.h" #include "llscrolllistctrl.h" #include "llcheckboxctrl.h" -#include "llfilepicker.h" #include "llvfs.h" #include "lllocalinventory.h" #include "llviewerwindow.h" diff --git a/indra/newview/llinventorybackup.cpp b/indra/newview/llinventorybackup.cpp index c6f687272..416c82065 100644 --- a/indra/newview/llinventorybackup.cpp +++ b/indra/newview/llinventorybackup.cpp @@ -5,7 +5,7 @@ #include "llinventorymodel.h" #include "llviewerinventory.h" #include "statemachine/aifilepicker.h" -#include "lldirpicker.h" +#include "statemachine/aidirpicker.h" #include "llviewertexturelist.h" // gTextureList #include "llagent.h" // gAgent #include "llviewerwindow.h" // gViewerWindow @@ -120,36 +120,41 @@ void LLFloaterInventoryBackupSettings::onClickNext(void* userdata) } // Get dir name - LLDirPicker& picker = LLDirPicker::instance(); - std::string filename = "New Folder"; - if (!picker.getDir(&filename)) + AIDirPicker* dirpicker = new AIDirPicker("New Folder"); + dirpicker->run(boost::bind(&LLFloaterInventoryBackupSettings::onClickNext_continued, userdata, dirpicker)); +} + +// static +void LLFloaterInventoryBackupSettings::onClickNext_continued(void* userdata, AIDirPicker* dirpicker) +{ + LLFloaterInventoryBackupSettings* floater = (LLFloaterInventoryBackupSettings*)userdata; + LLInventoryBackupOrder* order = floater->mOrder; + + if (!dirpicker->hasDirname()) { floater->close(); return; } - filename = picker.getDirName(); + std::string dirname = dirpicker->getDirname(); // Make local directory tree - LLFile::mkdir(filename); + LLFile::mkdir(dirname); std::vector::iterator _cat_iter = order->mCats.begin(); std::vector::iterator _cat_end = order->mCats.end(); for( ; _cat_iter != _cat_end; ++_cat_iter) { - std::string path = filename + OS_SEP + LLInventoryBackup::getPath(*_cat_iter, order->mCats); + std::string path = dirname + OS_SEP + LLInventoryBackup::getPath(*_cat_iter, order->mCats); LLFile::mkdir(path); } // Go go backup floater - LLFloaterInventoryBackup* backup_floater = new LLFloaterInventoryBackup(filename, order->mCats, order->mItems); + LLFloaterInventoryBackup* backup_floater = new LLFloaterInventoryBackup(dirname, order->mCats, order->mItems); backup_floater->center(); // Close myself floater->close(); } - - - // static bool LLInventoryBackup::itemIsFolder(LLInventoryItem* item) { diff --git a/indra/newview/llinventorybackup.h b/indra/newview/llinventorybackup.h index 16d9a3ad1..2ee0c197c 100644 --- a/indra/newview/llinventorybackup.h +++ b/indra/newview/llinventorybackup.h @@ -15,6 +15,8 @@ #include "llviewertexture.h" #include "llfloater.h" +class AIDirPicker; + class LLInventoryBackupOrder { public: @@ -44,6 +46,7 @@ public: LLFloaterInventoryBackupSettings(LLInventoryBackupOrder* order); BOOL postBuild(void); static void onClickNext(void* userdata); + static void onClickNext_continued(void* userdata, AIDirPicker* dirpicker); LLInventoryBackupOrder* mOrder; virtual ~LLFloaterInventoryBackupSettings(); diff --git a/indra/newview/llpanelnetwork.cpp b/indra/newview/llpanelnetwork.cpp index 6e66628eb..6bc34989a 100644 --- a/indra/newview/llpanelnetwork.cpp +++ b/indra/newview/llpanelnetwork.cpp @@ -39,7 +39,7 @@ // project includes #include "llcheckboxctrl.h" #include "llradiogroup.h" -#include "lldirpicker.h" +#include "statemachine/aidirpicker.h" #include "lluictrlfactory.h" #include "llviewercontrol.h" #include "llviewerwindow.h" @@ -151,18 +151,24 @@ void LLPanelNetwork::onClickClearCache(void*) // static void LLPanelNetwork::onClickSetCache(void* user_data) { - LLPanelNetwork* self = (LLPanelNetwork*)user_data; - std::string cur_name(gSavedSettings.getString("CacheLocation")); std::string proposed_name(cur_name); - LLDirPicker& picker = LLDirPicker::instance(); - if (! picker.getDir(&proposed_name ) ) + AIDirPicker* dirpicker = new AIDirPicker(proposed_name, "cachelocation"); + dirpicker->run(boost::bind(&LLPanelNetwork::onClickSetCache_continued, user_data, dirpicker)); +} + +// static +void LLPanelNetwork::onClickSetCache_continued(void* user_data, AIDirPicker* dirpicker) +{ + if (!dirpicker->hasDirname()) { return; //Canceled! } - std::string dir_name = picker.getDirName(); + LLPanelNetwork* self = (LLPanelNetwork*)user_data; + std::string cur_name(gSavedSettings.getString("CacheLocation")); + std::string dir_name = dirpicker->getDirname(); if (!dir_name.empty() && dir_name != cur_name) { self->childSetText("cache_location", dir_name); diff --git a/indra/newview/llpanelnetwork.h b/indra/newview/llpanelnetwork.h index a5e2269a4..30dc471e6 100644 --- a/indra/newview/llpanelnetwork.h +++ b/indra/newview/llpanelnetwork.h @@ -35,6 +35,8 @@ #include "llpanel.h" +class AIDirPicker; + class LLPanelNetwork : public LLPanel { public: @@ -49,6 +51,7 @@ public: private: static void onClickClearCache(void*); static void onClickSetCache(void*); + static void onClickSetCache_continued(void* user_data, AIDirPicker* dirpicker); static void onClickResetCache(void*); static void onCommitPort(LLUICtrl* ctrl, void*); static void onCommitSocks5ProxyEnabled(LLUICtrl* ctrl, void* data); diff --git a/indra/newview/llprefsim.cpp b/indra/newview/llprefsim.cpp index 8c1f71b4b..62dc37726 100644 --- a/indra/newview/llprefsim.cpp +++ b/indra/newview/llprefsim.cpp @@ -45,7 +45,7 @@ #include "llviewernetwork.h" #include "lluictrlfactory.h" -#include "lldirpicker.h" +#include "statemachine/aidirpicker.h" #include "hippogridmanager.h" @@ -53,6 +53,8 @@ #include "rlvhandler.h" // [/RLVa:KB] +class AIDirPicker; + class LLPrefsIMImpl : public LLPanel { public: @@ -67,6 +69,7 @@ public: void enableHistory(); static void onClickLogPath(void* user_data); + static void onClickLogPath_continued(void* user_data, AIDirPicker* dirpicker); static void onCommitLogging(LLUICtrl* ctrl, void* user_data); protected: @@ -301,13 +304,20 @@ void LLPrefsIMImpl::onClickLogPath(void* user_data) std::string proposed_name(self->childGetText("log_path_string")); - LLDirPicker& picker = LLDirPicker::instance(); - if (!picker.getDir(&proposed_name ) ) + AIDirPicker* dirpicker = new AIDirPicker(proposed_name); + dirpicker->run(boost::bind(&LLPrefsIMImpl::onClickLogPath_continued, user_data, dirpicker)); +} + +// static +void LLPrefsIMImpl::onClickLogPath_continued(void* user_data, AIDirPicker* dirpicker) +{ + if (!dirpicker->hasDirname()) { return; //Canceled! } - self->childSetText("log_path_string", picker.getDirName()); + LLPrefsIMImpl* self=(LLPrefsIMImpl*)user_data; + self->childSetText("log_path_string", dirpicker->getDirname()); } diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 319f1cb57..e92a5185c 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -74,7 +74,6 @@ // tag: vaa emerald local_asset_browser [begin] #include "floaterlocalassetbrowse.h" #include "llscrolllistctrl.h" -#include "llfilepicker.h" #define LOCALLIST_COL_ID 1 // tag: vaa emerald local_asset_browser [end] diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index e9fedf37d..a6d96c08c 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -35,9 +35,6 @@ #include "llmenugl.h" -//newview includes -#include "llfilepicker.h" - class LLUICtrl; class LLView; class LLParcelSelection; diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index a8d78cd8d..0a3254ea0 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -420,7 +420,7 @@ void upload_error(const std::string& error_message, const std::string& label, co { lldebugs << "unable to remove temp file" << llendl; } - LLFilePicker::instance().reset(); + //AIFIXME? LLFilePicker::instance().reset(); } class LLFileEnableCloseWindow : public view_listener_t @@ -965,7 +965,7 @@ void upload_new_resource(const std::string& src_filename, std::string name, { lldebugs << "unable to remove temp file" << llendl; } - LLFilePicker::instance().reset(); + //AIFIXME? LLFilePicker::instance().reset(); } } // diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 99844508f..60a2be5a3 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -49,7 +49,6 @@ #include "llchat.h" #include "lldbstrings.h" #include "lleconomy.h" -#include "llfilepicker.h" #include "llfocusmgr.h" #include "llfollowcamparams.h" #include "llinstantmessage.h" diff --git a/indra/newview/statemachine/aidirpicker.h b/indra/newview/statemachine/aidirpicker.h new file mode 100644 index 000000000..452f42b3e --- /dev/null +++ b/indra/newview/statemachine/aidirpicker.h @@ -0,0 +1,77 @@ +/** + * @file aidirpicker.h + * @brief Directory picker State machine + * + * Copyright (c) 2011, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * 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. + * + * CHANGELOG + * and additional copyright holders. + * + * 10/05/2011 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AIDIRPICKER_H +#define AIDIRPICKER_H + +#include "aifilepicker.h" + +// A directory picker state machine. +// +// Before calling run(), call open() to pass needed parameters. +// +// When the state machine finishes, call hasDirname to check +// whether or not getDirname will be valid. +// +// Objects of this type can be reused multiple times, see +// also the documentation of AIStateMachine. +class AIDirPicker : protected AIFilePicker { +public: + // Allow to pass the arguments to open upon creation. + // + // The starting directory that the user will be in when the directory picker opens + // will be the same as the directory used the last time the directory picker was + // opened with the same context. If the directory picker was never opened before + // with the given context, the starting directory will be set to default_path + // unless that is the empty string, in which case it will be equal to the + // directory used the last time the file- or dirpicker was opened with context + // "openfile". + AIDirPicker(std::string const& default_path = "", std::string const& context = "openfile") { open(default_path, context); } + + // This should only be called if you want to re-use the AIDirPicker after it finished + // (from the callback function, followed by a call to 'run'). Normally it is not used. + void open(std::string const& default_path = "", std::string const& context = "openfile") { AIFilePicker::open(DF_DIRECTORY, default_path, context); } + + bool hasDirname(void) const { return hasFilename(); } + std::string const& getDirname(void) const { return getFilename(); } + +public: + // Basically all public members of AIStateMachine could made accessible here, + // but I don't think others will ever be needed (not even these, actually). + using AIStateMachine::state_type; + using AIFilePicker::isCanceled; + using AIStateMachine::run; + +protected: + // Call finish() (or abort()), not delete. + /*virtual*/ ~AIDirPicker() { LL_DEBUGS("Plugin") << "Calling AIDirPicker::~AIDirPicker()" << LL_ENDL; } +}; + +#endif diff --git a/indra/newview/statemachine/aifilepicker.cpp b/indra/newview/statemachine/aifilepicker.cpp index e0588a990..e3a09009d 100644 --- a/indra/newview/statemachine/aifilepicker.cpp +++ b/indra/newview/statemachine/aifilepicker.cpp @@ -107,6 +107,9 @@ void AIFilePicker::open(ELoadFilter filter, std::string const& default_path, std mOpenType = multiple ? load_multiple : load; switch(filter) { + case DF_DIRECTORY: + mFilter = "directory"; + break; case FFLOAD_ALL: mFilter = "all"; break; @@ -314,7 +317,8 @@ void AIFilePicker::multiplex_impl(void) static char const* key_str[] = { "all_files", "sound_files", "animation_files", "image_files", "save_file_verb", "targa_image_files", "bitmap_image_files", "avi_movie_file", "xaf_animation_file", - "xml_file", "raw_file", "compressed_image_files", "load_file_verb", "load_files" + "xml_file", "raw_file", "compressed_image_files", "load_file_verb", "load_files", + "choose_the_directory" }; LLSD dictionary; for (int key = 0; key < sizeof(key_str) / sizeof(key_str[0]); ++key) diff --git a/indra/newview/statemachine/aifilepicker.h b/indra/newview/statemachine/aifilepicker.h index d916055fe..a1709eeb8 100644 --- a/indra/newview/statemachine/aifilepicker.h +++ b/indra/newview/statemachine/aifilepicker.h @@ -38,6 +38,7 @@ enum ELoadFilter { + DF_DIRECTORY, FFLOAD_ALL, FFLOAD_WAV, FFLOAD_IMAGE, @@ -175,7 +176,7 @@ public: private: LLPointer mPluginManager; //!< Pointer to the plugin manager. - // FIXME: this should be a separate, thread-safe singleton. + // AIFIXME: this should be a separate, thread-safe singleton. typedef std::map context_map_type; //!< Type of mContextMap. context_map_type mContextMap; //!< Map context (ie, "snapshot" or "image") to last used folder. std::string mContext; //!< Some key to indicate the context (remembers the folder per key). diff --git a/indra/plugins/filepicker/CMakeLists.txt b/indra/plugins/filepicker/CMakeLists.txt index d3b233e01..13714e89f 100644 --- a/indra/plugins/filepicker/CMakeLists.txt +++ b/indra/plugins/filepicker/CMakeLists.txt @@ -28,12 +28,16 @@ if(NOT WORD_SIZE EQUAL 32) endif (NOT WORD_SIZE EQUAL 32) set(basic_plugin_filepicker_SOURCE_FILES - basic_plugin_filepicker.cpp + basic_plugin_filepicker.cpp + legacy.cpp llfilepicker.cpp + lldirpicker.cpp ) set(basic_plugin_filepicker_HEADER_FILES + legacy.h llfilepicker.h + lldirpicker.h ) set_source_files_properties(${basic_plugin_filepicker_HEADER_FILES} diff --git a/indra/plugins/filepicker/basic_plugin_filepicker.cpp b/indra/plugins/filepicker/basic_plugin_filepicker.cpp index b954e27b4..1201c3f71 100644 --- a/indra/plugins/filepicker/basic_plugin_filepicker.cpp +++ b/indra/plugins/filepicker/basic_plugin_filepicker.cpp @@ -36,6 +36,7 @@ #include "linden_common.h" #include "basic_plugin_base.h" #include "llfilepicker.h" +#include "lldirpicker.h" class FilepickerPlugin : public BasicPluginBase { @@ -229,9 +230,14 @@ void FilepickerPlugin::receiveMessage(char const* message_string) std::string type = message_in.getValue("type"); std::string filter = message_in.getValue("filter"); std::string folder = message_in.getValue("folder"); + bool get_directory = (filter == "directory"); bool canceled; - if (type == "save") + if (get_directory) + { + canceled = !LLDirPicker::instance().getDir(&folder); + } + else if (type == "save") { canceled = !LLFilePicker::instance().getSaveFile(str2savefilter(filter), message_in.getValue("default"), folder); } @@ -254,9 +260,16 @@ void FilepickerPlugin::receiveMessage(char const* message_string) LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_BASIC, "done"); message.setValue("perseus", "unblock"); LLSD filenames; - for (std::string filename = LLFilePicker::instance().getFirstFile(); !filename.empty(); filename = LLFilePicker::instance().getNextFile()) + if (get_directory) { - filenames.append(filename); + filenames.append(LLDirPicker::instance().getDirName()); + } + else + { + for (std::string filename = LLFilePicker::instance().getFirstFile(); !filename.empty(); filename = LLFilePicker::instance().getNextFile()) + { + filenames.append(filename); + } } message.setValueLLSD("filenames", filenames); sendMessage(message); diff --git a/indra/plugins/filepicker/legacy.cpp b/indra/plugins/filepicker/legacy.cpp new file mode 100644 index 000000000..f89862061 --- /dev/null +++ b/indra/plugins/filepicker/legacy.cpp @@ -0,0 +1,112 @@ +/** + * @file legacy.cpp + * @brief Helper stubs to keep the picker files as much as possible equal to their original. + * + * Copyright (c) 2011, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * 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. + * + * CHANGELOG + * and additional copyright holders. + * + * 11/05/2011 + * - Initial version, written by Aleric Inglewood @ SL + */ + +#include "legacy.h" +#include "basic_plugin_base.h" // For PLS_INFOS etc. + +// Translation map. +translation_map_type translation_map; + +namespace translation +{ + std::string getString(char const* key) + { + translation_map_type::iterator iter = translation_map.find(key); + return (iter != translation_map.end()) ? iter->second : key; + } + + void add(std::string const& key, std::string const& translation) + { + PLS_DEBUGS << "Adding translation \"" << key << "\" --> \"" << translation << "\"" << PLS_ENDL; + translation_map[key] = translation; + } +} + +#if LL_GTK +namespace LLWindowSDL { + bool ll_try_gtk_init(void) + { + static BOOL done_gtk_diag = FALSE; + static BOOL gtk_is_good = FALSE; + static BOOL done_setlocale = FALSE; + static BOOL tried_gtk_init = FALSE; + + if (!done_setlocale) + { + PLS_INFOS << "Starting GTK Initialization." << PLS_ENDL; + //maybe_lock_display(); + gtk_disable_setlocale(); + //maybe_unlock_display(); + done_setlocale = TRUE; + } + + if (!tried_gtk_init) + { + tried_gtk_init = TRUE; + if (!g_thread_supported ()) g_thread_init (NULL); + //maybe_lock_display(); + gtk_is_good = gtk_init_check(NULL, NULL); + //maybe_unlock_display(); + if (!gtk_is_good) + PLS_WARNS << "GTK Initialization failed." << PLS_ENDL; + } + if (gtk_is_good && !done_gtk_diag) + { + PLS_INFOS << "GTK Initialized." << PLS_ENDL; + PLS_INFOS << "- Compiled against GTK version " + << GTK_MAJOR_VERSION << "." + << GTK_MINOR_VERSION << "." + << GTK_MICRO_VERSION << PLS_ENDL; + PLS_INFOS << "- Running against GTK version " + << gtk_major_version << "." + << gtk_minor_version << "." + << gtk_micro_version << PLS_ENDL; + //maybe_lock_display(); + const gchar* gtk_warning = gtk_check_version( + GTK_MAJOR_VERSION, + GTK_MINOR_VERSION, + GTK_MICRO_VERSION); + //maybe_unlock_display(); + if (gtk_warning) + { + PLS_WARNS << "- GTK COMPATIBILITY WARNING: " << gtk_warning << PLS_ENDL; + gtk_is_good = FALSE; + } + else + { + PLS_INFOS << "- GTK version is good." << PLS_ENDL; + } + done_gtk_diag = TRUE; + } + return gtk_is_good; + } +} +#endif + diff --git a/indra/plugins/filepicker/legacy.h b/indra/plugins/filepicker/legacy.h new file mode 100644 index 000000000..253a99280 --- /dev/null +++ b/indra/plugins/filepicker/legacy.h @@ -0,0 +1,79 @@ +/** + * @file legacy.h + * @brief Declarations of legacy.cpp. + * + * Copyright (c) 2011, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * 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. + * + * CHANGELOG + * and additional copyright holders. + * + * 11/05/2011 + * - Initial version, written by Aleric Inglewood @ SL + */ + +#include +#include +#include "stdtypes.h" // BOOL + +// Translation map. +typedef std::map translation_map_type; +extern translation_map_type translation_map; + +namespace translation +{ + std::string getString(char const* key); + void add(std::string const& key, std::string const& translation); +} + +#if LL_GTK +namespace LLWindowSDL { + bool ll_try_gtk_init(void); +} +#endif + +// A temporary hack to minimize the number of changes from the original llfilepicker.cpp. +#define LLTrans translation + +#if LL_DARWIN +#include + +// AssertMacros.h does bad things. +#undef verify +#undef check +#undef require + +#include "llstring.h" +#endif + +// Need commdlg.h for OPENFILENAMEA +#ifdef LL_WINDOWS +#include +#endif + +// mostly for Linux, possible on others +#if LL_GTK +# include "gtk/gtk.h" +#endif // LL_GTK + +// also mostly for Linux, for some X11-specific filepicker usability tweaks +#if LL_X11 +#include "SDL/SDL_syswm.h" +#endif + diff --git a/indra/newview/lldirpicker.cpp b/indra/plugins/filepicker/lldirpicker.cpp similarity index 92% rename from indra/newview/lldirpicker.cpp rename to indra/plugins/filepicker/lldirpicker.cpp index 3840b52bd..39dff05e9 100644 --- a/indra/newview/lldirpicker.cpp +++ b/indra/plugins/filepicker/lldirpicker.cpp @@ -30,16 +30,12 @@ * $/LicenseInfo$ */ -#include "llviewerprecompiledheaders.h" - +#include "linden_common.h" #include "lldirpicker.h" -//#include "llviewermessage.h" -#include "llworld.h" -#include "llviewerwindow.h" -#include "llkeyboard.h" -#include "lldir.h" -#include "llframetimer.h" -#include "lltrans.h" +#include "llpreprocessor.h" +#include "llerror.h" +#include "basic_plugin_base.h" // For PLS_INFOS etc. +#include "legacy.h" #if LL_LINUX || LL_SOLARIS # include "llfilepicker.h" @@ -77,9 +73,6 @@ BOOL LLDirPicker::getDir(std::string* filename) } BOOL success = FALSE; - // Modal, so pause agent - send_agent_pause(); - BROWSEINFO bi; memset(&bi, 0, sizeof(bi)); @@ -109,10 +102,6 @@ BOOL LLDirPicker::getDir(std::string* filename) ::OleUninitialize(); - send_agent_resume(); - - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); return success; } @@ -237,20 +226,15 @@ BOOL LLDirPicker::getDir(std::string* filename) // mNavOptions.saveFileName - // Modal, so pause agent - send_agent_pause(); { error = doNavChooseDialog(); } - send_agent_resume(); if (error == noErr) { if (mDir.length() > 0) success = true; } - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); return success; } diff --git a/indra/newview/lldirpicker.h b/indra/plugins/filepicker/lldirpicker.h similarity index 100% rename from indra/newview/lldirpicker.h rename to indra/plugins/filepicker/lldirpicker.h diff --git a/indra/plugins/filepicker/llfilepicker.cpp b/indra/plugins/filepicker/llfilepicker.cpp index 8028665e3..48069e88c 100644 --- a/indra/plugins/filepicker/llfilepicker.cpp +++ b/indra/plugins/filepicker/llfilepicker.cpp @@ -35,92 +35,7 @@ #include "llpreprocessor.h" #include "llerror.h" #include "basic_plugin_base.h" // For PLS_INFOS etc. - -#if LL_SDL -#include "llwindowsdl.h" // for some X/GTK utils to help with filepickers -#endif // LL_SDL - -// Translation map. -typedef std::map translation_map_type; -translation_map_type translation_map; - -// A temporary hack to minimize the number of changes from the original llfilepicker.cpp. -#define LLTrans translation -namespace translation -{ - std::string getString(char const* key) - { - translation_map_type::iterator iter = translation_map.find(key); - return (iter != translation_map.end()) ? iter->second : key; - } - - void add(std::string const& key, std::string const& translation) - { - PLS_DEBUGS << "Adding translation \"" << key << "\" --> \"" << translation << "\"" << PLS_ENDL; - translation_map[key] = translation; - } -} - -#if LL_GTK -namespace LLWindowSDL { - bool ll_try_gtk_init(void) - { - static BOOL done_gtk_diag = FALSE; - static BOOL gtk_is_good = FALSE; - static BOOL done_setlocale = FALSE; - static BOOL tried_gtk_init = FALSE; - - if (!done_setlocale) - { - PLS_INFOS << "Starting GTK Initialization." << PLS_ENDL; - //maybe_lock_display(); - gtk_disable_setlocale(); - //maybe_unlock_display(); - done_setlocale = TRUE; - } - - if (!tried_gtk_init) - { - tried_gtk_init = TRUE; - if (!g_thread_supported ()) g_thread_init (NULL); - //maybe_lock_display(); - gtk_is_good = gtk_init_check(NULL, NULL); - //maybe_unlock_display(); - if (!gtk_is_good) - PLS_WARNS << "GTK Initialization failed." << PLS_ENDL; - } - if (gtk_is_good && !done_gtk_diag) - { - PLS_INFOS << "GTK Initialized." << PLS_ENDL; - PLS_INFOS << "- Compiled against GTK version " - << GTK_MAJOR_VERSION << "." - << GTK_MINOR_VERSION << "." - << GTK_MICRO_VERSION << PLS_ENDL; - PLS_INFOS << "- Running against GTK version " - << gtk_major_version << "." - << gtk_minor_version << "." - << gtk_micro_version << PLS_ENDL; - //maybe_lock_display(); - const gchar* gtk_warning = gtk_check_version( - GTK_MAJOR_VERSION, - GTK_MINOR_VERSION, - GTK_MICRO_VERSION); - //maybe_unlock_display(); - if (gtk_warning) - { - PLS_WARNS << "- GTK COMPATIBILITY WARNING: " << gtk_warning << PLS_ENDL; - gtk_is_good = FALSE; - } - else - { - PLS_INFOS << "- GTK version is good." << PLS_ENDL; - } - done_gtk_diag = TRUE; - } - return gtk_is_good; - } -} -#endif +#include "legacy.h" // // Globals @@ -290,7 +205,7 @@ bool LLFilePickerBase::setupFilter(ELoadFilter filter) return res; } -// FIXME: Use folder +// AIFIXME: Use folder bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -309,9 +224,6 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder setupFilter(filter); - // Modal, so pause agent - send_agent_pause(); - reset(); // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!! @@ -321,14 +233,11 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder std::string filename = utf16str_to_utf8str(llutf16string(mFilesW)); mFiles.push_back(filename); } - send_agent_resume(); - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); return success; } -// FIXME: Use folder +// AIFIXME: Use folder bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -350,8 +259,6 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons reset(); - // Modal, so pause agent - send_agent_pause(); // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!! success = GetOpenFileName(&mOFN); // pauses until ok or cancel. if( success ) @@ -384,14 +291,11 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons } } } - send_agent_resume(); - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); return success; } -// FIXME: Use folder +// AIFIXME: Use folder bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) { if( mLocked ) @@ -783,8 +687,6 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena reset(); - // Modal, so pause agent - send_agent_pause(); { // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!! success = GetSaveFileName(&mOFN); @@ -795,10 +697,7 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena } gKeyboard->resetKeys(); } - send_agent_resume(); - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); return success; } @@ -1137,7 +1036,7 @@ OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string return error; } -// FIXME: Use folder +// AIFIXME: Use folder bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -1150,24 +1049,19 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder reset(); mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; - // Modal, so pause agent - send_agent_pause(); { error = doNavChooseDialog(filter); } - send_agent_resume(); if (error == noErr) { if (getFileCount()) success = true; } - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); return success; } -// FIXME: Use folder +// AIFIXME: Use folder bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -1180,12 +1074,9 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons reset(); mNavOptions.optionFlags |= kNavAllowMultipleFiles; - // Modal, so pause agent - send_agent_pause(); { error = doNavChooseDialog(filter); } - send_agent_resume(); if (error == noErr) { if (getFileCount()) @@ -1194,12 +1085,10 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons mLocked = TRUE; } - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); return success; } -// FIXME: Use folder +// AIFIXME: Use folder bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) { if( mLocked ) @@ -1211,20 +1100,15 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; - // Modal, so pause agent - send_agent_pause(); { error = doNavSaveDialog(filter, filename); } - send_agent_resume(); if (error == noErr) { if (getFileCount()) success = true; } - // Account for the fact that the app has been stalled. - LLFrameTimer::updateFrameTime(); return success; } @@ -1601,7 +1485,7 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena return FALSE; } -// FIXME: Use folder +// AIFIXME: Use folder bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) { reset(); diff --git a/indra/plugins/filepicker/llfilepicker.h b/indra/plugins/filepicker/llfilepicker.h index 4e1a52a7e..f8e17719e 100644 --- a/indra/plugins/filepicker/llfilepicker.h +++ b/indra/plugins/filepicker/llfilepicker.h @@ -39,37 +39,8 @@ #ifndef LL_LLFILEPICKER_H #define LL_LLFILEPICKER_H -#include "stdtypes.h" -#include +#include "legacy.h" #include -#include - -#if LL_DARWIN -#include - -// AssertMacros.h does bad things. -#undef verify -#undef check -#undef require - -#include "llstring.h" - -#endif - -// Need commdlg.h for OPENFILENAMEA -#ifdef LL_WINDOWS -#include -#endif - -// mostly for Linux, possible on others -#if LL_GTK -# include "gtk/gtk.h" -#endif // LL_GTK - -// also mostly for Linux, for some X11-specific filepicker usability tweaks -#if LL_X11 -#include "SDL/SDL_syswm.h" -#endif // This class is used as base class of a singleton and is therefore not // allowed to have any static members or static local variables! @@ -246,9 +217,4 @@ private: LLFilePicker() { } }; -namespace translation -{ - void add(std::string const& key, std::string const& translation); -} - #endif From 38b33328e604d32a5bf62dda10062bd50a58d07d Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Wed, 11 May 2011 03:47:42 +0200 Subject: [PATCH 14/21] Redo the copyconstructor hack for AI*Access objects. The previous hack wasn't thread-safe: read-only access would access the reference counter multiple times at the same time, which therefore would have to be thread-local to ever work. The current solution just disables the calls to lock/unlock for copyconstructed objects, which works if the copyconstructed object isn't used anymore after the original is destructed. This is the case then the copy construction only happens upon passing a temporary to a function, which is the case. --- indra/llcommon/aithreadsafe.h | 43 +++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/indra/llcommon/aithreadsafe.h b/indra/llcommon/aithreadsafe.h index 795c15e0a..810aa8c2f 100644 --- a/indra/llcommon/aithreadsafe.h +++ b/indra/llcommon/aithreadsafe.h @@ -39,6 +39,11 @@ // g++ 4.2.x (and before?) have the bug that when you try to pass a temporary // to a function taking a const reference, it still calls the copy constructor. // Define this to hack around that. +// Note that the chosen solution ONLY works for copying an AI*Access object that +// is passed to a function: the lifetime of the copied object must not be longer +// than the original (or at least, it shouldn't be used anymore after the +// original is destructed). This will be guaranteed if the code also compiles +// on a compiler that doesn't need this hack. #define AI_NEED_ACCESS_CC (defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ < 3)) || (__GNUC__ < 4))) template struct AIReadAccessConst; @@ -71,10 +76,6 @@ protected: // Accessors. T const* ptr() const { return reinterpret_cast(mMemory); } T* ptr() { return reinterpret_cast(mMemory); } - -#if AI_NEED_ACCESS_CC - int mAccessCopyCount; -#endif }; /** @@ -246,11 +247,11 @@ struct AIReadAccessConst AIReadAccessConst(AIThreadSafe const& wrapper) : mWrapper(const_cast&>(wrapper)), mState(readlocked) +#if AI_NEED_ACCESS_CC + , mIsCopyConstructed(false) +#endif { mWrapper.mRWLock.rdlock(); -#if AI_NEED_ACCESS_CC - mWrapper.mAccessCopyCount = 1; -#endif } //! Destruct the AI*Access object. @@ -258,7 +259,7 @@ struct AIReadAccessConst ~AIReadAccessConst() { #if AI_NEED_ACCESS_CC - if (--(this->mWrapper.mAccessCopyCount) > 0) return; + if (mIsCopyConstructed) return; #endif if (mState == readlocked) mWrapper.mRWLock.rdunlock(); @@ -282,13 +283,14 @@ protected: AIThreadSafe& mWrapper; //!< Reference to the object that we provide access to. state_type const mState; //!< The lock state that mWrapper is in. -#if !AI_NEED_ACCESS_CC +#if AI_NEED_ACCESS_CC + bool mIsCopyConstructed; +public: + AIReadAccessConst(AIReadAccessConst const& orig) : mWrapper(orig.mWrapper), mState(orig.mState), mIsCopyConstructed(true) { } +#else private: // Disallow copy constructing directly. AIReadAccessConst(AIReadAccessConst const&); -#else -public: - AIReadAccessConst(AIReadAccessConst const& orig) : mWrapper(orig.mWrapper), mState(orig.mState) { mWrapper.mAccessCopyCount++; } #endif }; @@ -482,11 +484,11 @@ struct AIAccess { //! Construct a AIAccess from a non-constant AIThreadSafeSimple. AIAccess(AIThreadSafeSimple& wrapper) : mWrapper(wrapper) +#if AI_NEED_ACCESS_CC + , mIsCopyConstructed(false) +#endif { this->mWrapper.mMutex.lock(); -#if AI_NEED_ACCESS_CC - this->mWrapper.mAccessCopyCount = 1; -#endif } //! Access the underlaying object for (read and) write access. @@ -498,7 +500,7 @@ struct AIAccess ~AIAccess() { #if AI_NEED_ACCESS_CC - if (--(this->mWrapper.mAccessCopyCount) > 0) return; + if (mIsCopyConstructed) return; #endif this->mWrapper.mMutex.unlock(); } @@ -506,13 +508,14 @@ struct AIAccess protected: AIThreadSafeSimple& mWrapper; //!< Reference to the object that we provide access to. -#if !AI_NEED_ACCESS_CC +#if AI_NEED_ACCESS_CC + bool mIsCopyConstructed; +public: + AIAccess(AIAccess const& orig) : mWrapper(orig.mWrapper), mIsCopyConstructed(true) { } +#else private: // Disallow copy constructing directly. AIAccess(AIAccess const&); -#else -public: - AIAccess(AIAccess const& orig) : mWrapper(orig.mWrapper) { this->mWrapper.mAccessCopyCount++; } #endif }; From 5f72cbb10319db5e68ca210013cfddaf3d83d770 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Thu, 12 May 2011 18:22:51 +0200 Subject: [PATCH 15/21] Add support for flushing messages from plugin to viewer. Actually flush messages before terminating a plugin (upon the shutdown message) and flush messages in the file- and dirpicker before opening the blocking dialog. Flush debug messages too (deeper into the code, just prior to the actual blocking call). Also, fix the context folder map to be a thread-safe singleton and *attempt* to add support for default folders to windows and Mac. The latter might even not compile yet and definitely have to be tested (and fixed): Opening a DirPicker in preferences --> Network and Set the directory location of the cache. It should open a Dialog window where you are already in the folder that is the current cache directory setting (you can click Cancel after verifying that this worked). And, start to upload an image, select a file is some directory (other than what it starts in). You can omit the actual upload by clicking cancel in the preview. Then upload again and now it should start in the same folder as that you were just in. Possibly you need to first open a file picker elsewhere with a different context though, or windows might choose to open in the last folder anyway while the code doesn't really work. Uploading a sound before the second texture upload should do the trick. --- indra/llplugin/llpluginmessagepipe.cpp | 49 +++++-- indra/llplugin/llpluginmessagepipe.h | 6 +- indra/llplugin/llpluginprocesschild.cpp | 11 ++ indra/llplugin/llpluginprocessparent.cpp | 4 + indra/llplugin/llpluginprocessparent.h | 3 +- indra/newview/statemachine/aifilepicker.cpp | 24 +++- indra/newview/statemachine/aifilepicker.h | 7 +- .../plugins/base_basic/basic_plugin_base.cpp | 17 +++ indra/plugins/base_basic/basic_plugin_base.h | 13 +- .../filepicker/basic_plugin_filepicker.cpp | 6 +- indra/plugins/filepicker/legacy.cpp | 2 +- indra/plugins/filepicker/lldirpicker.cpp | 72 ++++++---- indra/plugins/filepicker/lldirpicker.h | 2 +- indra/plugins/filepicker/llfilepicker.cpp | 124 +++++++++++++----- indra/plugins/filepicker/llfilepicker.h | 9 +- 15 files changed, 258 insertions(+), 91 deletions(-) diff --git a/indra/llplugin/llpluginmessagepipe.cpp b/indra/llplugin/llpluginmessagepipe.cpp index ebac3c52b..7aaa09372 100644 --- a/indra/llplugin/llpluginmessagepipe.cpp +++ b/indra/llplugin/llpluginmessagepipe.cpp @@ -89,6 +89,16 @@ bool LLPluginMessagePipeOwner::writeMessageRaw(const std::string &message) return result; } +bool LLPluginMessagePipeOwner::flushMessages(void) +{ + bool result = true; + if (mMessagePipe != NULL) + { + result = mMessagePipe->flushMessages(); + } + return result; +} + void LLPluginMessagePipeOwner::killMessagePipe(void) { if(mMessagePipe != NULL) @@ -163,26 +173,32 @@ bool LLPluginMessagePipe::pump(F64 timeout) return result; } -bool LLPluginMessagePipe::pumpOutput() +static apr_interval_time_t const flush_max_block_time = 10000000; // Even when flushing, give up after 10 seconds. +static apr_interval_time_t const flush_min_timeout = 1000; // When flushing, initially timeout after 1 ms. +static apr_interval_time_t const flush_max_timeout = 50000; // Never wait longer than 50 ms. + +// DO NOT SET 'flush' TO TRUE WHEN CALLED ON THE VIEWER SIDE! +// flush is only intended for plugin-side. +bool LLPluginMessagePipe::pumpOutput(bool flush) { bool result = true; if(mSocket) { - apr_status_t status; - apr_size_t size; + apr_interval_time_t flush_time_left_usec = flush_max_block_time; + apr_interval_time_t timeout_usec = flush ? flush_min_timeout : 0; LLMutexLock lock(&mOutputMutex); - if(!mOutput.empty()) + while(result && !mOutput.empty()) { // write any outgoing messages - size = (apr_size_t)mOutput.size(); + apr_size_t size = (apr_size_t)mOutput.size(); - setSocketTimeout(0); + setSocketTimeout(timeout_usec); // LL_INFOS("Plugin") << "before apr_socket_send, size = " << size << LL_ENDL; - status = apr_socket_send( + apr_status_t status = apr_socket_send( mSocket->getSocket(), (const char*)mOutput.data(), &size); @@ -193,12 +209,29 @@ bool LLPluginMessagePipe::pumpOutput() { // success mOutput = mOutput.substr(size); + break; } - else if(APR_STATUS_IS_EAGAIN(status)) + else if(APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) { // Socket buffer is full... // remove the written part from the buffer and try again later. mOutput = mOutput.substr(size); + if (!flush) + break; + flush_time_left_usec -= timeout_usec; + if (flush_time_left_usec <= 0) + { + result = false; + } + else if (size == 0) + { + // Nothing at all was written. Increment wait time. + timeout_usec = std::min(flush_max_timeout, 2 * timeout_usec); + } + else + { + timeout_usec = std::max(flush_min_timeout, timeout_usec / 2); + } } else if(APR_STATUS_IS_EOF(status)) { diff --git a/indra/llplugin/llpluginmessagepipe.h b/indra/llplugin/llpluginmessagepipe.h index 6eedca27f..1e71caf42 100644 --- a/indra/llplugin/llpluginmessagepipe.h +++ b/indra/llplugin/llpluginmessagepipe.h @@ -61,6 +61,8 @@ protected: bool canSendMessage(void); // call this to send a message over the pipe bool writeMessageRaw(const std::string &message); + // call this to attempt to flush all messages for 10 seconds long. + bool flushMessages(void); // call this to close the pipe void killMessagePipe(void); @@ -79,8 +81,10 @@ public: void clearOwner(void); bool pump(F64 timeout = 0.0f); - bool pumpOutput(); + bool pumpOutput(bool flush = false); bool pumpInput(F64 timeout = 0.0f); + + bool flushMessages(void) { return pumpOutput(true); } protected: void processInput(void); diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp index 3ba765bf6..fdcb4f6a8 100644 --- a/indra/llplugin/llpluginprocesschild.cpp +++ b/indra/llplugin/llpluginprocesschild.cpp @@ -556,11 +556,22 @@ void LLPluginProcessChild::receivePluginMessage(const std::string &message) } else if (message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL) { + bool flush = false; std::string message_name = parsed.getName(); if(message_name == "shutdown") { // The plugin is finished. setState(STATE_UNLOADING); + flush = true; + } + else if (message_name == "flush") + { + flush = true; + passMessage = false; + } + if (flush) + { + flushMessages(); } } } diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index fc94ee849..0d361324e 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -104,6 +104,7 @@ LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner) mDebug = false; mBlocked = false; mPolledInput = false; + mReceivedShutdown = false; mPollFD.client_data = NULL; mPollFDPool.create(); @@ -165,6 +166,8 @@ void LLPluginProcessParent::errorState(void) { if(mState < STATE_RUNNING) setState(STATE_LAUNCH_FAILURE); + else if (mReceivedShutdown) + setState(STATE_EXITING); else setState(STATE_ERROR); } @@ -1012,6 +1015,7 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) else if(message_name == "shutdown") { LL_INFOS("Plugin") << "received shutdown message" << LL_ENDL; + mReceivedShutdown = true; mOwner->receivedShutdown(); } else if(message_name == "shm_add_response") diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h index 1ec5ef4c3..65455950f 100644 --- a/indra/llplugin/llpluginprocessparent.h +++ b/indra/llplugin/llpluginprocessparent.h @@ -145,8 +145,8 @@ private: STATE_RUNNING, // STATE_LAUNCH_FAILURE, // Failure before plugin loaded STATE_ERROR, // generic bailout state - STATE_CLEANUP, // clean everything up STATE_EXITING, // Tried to kill process, waiting for it to exit + STATE_CLEANUP, // clean everything up STATE_DONE // }; @@ -183,6 +183,7 @@ private: bool mDebug; bool mBlocked; bool mPolledInput; + bool mReceivedShutdown; LLProcessLauncher mDebugger; diff --git a/indra/newview/statemachine/aifilepicker.cpp b/indra/newview/statemachine/aifilepicker.cpp index e3a09009d..f8da4da86 100644 --- a/indra/newview/statemachine/aifilepicker.cpp +++ b/indra/newview/statemachine/aifilepicker.cpp @@ -64,31 +64,41 @@ AIFilePicker::AIFilePicker(void) : mPluginManager(NULL), mCanceled(false) { } +// static +AITHREADSAFESIMPLE(AIFilePicker::context_map_type, AIFilePicker::sContextMap, ); + +// static void AIFilePicker::store_folder(std::string const& context, std::string const& folder) { if (!folder.empty()) { - mContextMap[context] = folder; + LL_DEBUGS("Plugin") << "AIFilePicker::mContextMap[\"" << context << "\"] = \"" << folder << '"' << LL_ENDL; + AIAccess(sContextMap)->operator[](context) = folder; } } +// static std::string AIFilePicker::get_folder(std::string const& default_path, std::string const& context) { - context_map_type::iterator iter = mContextMap.find(context); - if (iter != mContextMap.end()) + AIAccess wContextMap(sContextMap); + context_map_type::iterator iter = wContextMap->find(context); + if (iter != wContextMap->end()) { return iter->second; } else if (!default_path.empty()) { + LL_DEBUGS("Plugin") << "context \"" << context << "\" not found. Returning default_path \"" << default_path << "\"." << LL_ENDL; return default_path; } - else if ((iter = mContextMap.find("savefile")) != mContextMap.end()) + else if ((iter = wContextMap->find("savefile")) != wContextMap->end()) { + LL_DEBUGS("Plugin") << "context \"" << context << "\" not found and default_path empty. Returning context \"savefile\": \"" << iter->second << "\"." << LL_ENDL; return iter->second; } else { + LL_DEBUGS("Plugin") << "context \"" << context << "\" not found, default_path empty and context \"savefile\" not found. Returning \"$HOME\"." << LL_ENDL; // This is the last resort when all else failed. Open the file chooser in directory 'home'. char const* home = NULL; #if LL_WINDOWS @@ -103,7 +113,7 @@ std::string AIFilePicker::get_folder(std::string const& default_path, std::strin void AIFilePicker::open(ELoadFilter filter, std::string const& default_path, std::string const& context, bool multiple) { mContext = context; - mFolder = get_folder(default_path, context); + mFolder = AIFilePicker::get_folder(default_path, context); mOpenType = multiple ? load_multiple : load; switch(filter) { @@ -152,7 +162,7 @@ void AIFilePicker::open(std::string const& filename, ESaveFilter filter, std::st { mFilename = filename; mContext = context; - mFolder = get_folder(default_path, context); + mFolder = AIFilePicker::get_folder(default_path, context); mOpenType = save; switch(filter) { @@ -372,7 +382,7 @@ void AIFilePicker::multiplex_impl(void) case AIFilePicker_done: { // Store folder of first filename as context. - store_folder(mContext, getFolder()); + AIFilePicker::store_folder(mContext, getFolder()); finish(); } } diff --git a/indra/newview/statemachine/aifilepicker.h b/indra/newview/statemachine/aifilepicker.h index a1709eeb8..91a7bc819 100644 --- a/indra/newview/statemachine/aifilepicker.h +++ b/indra/newview/statemachine/aifilepicker.h @@ -176,9 +176,8 @@ public: private: LLPointer mPluginManager; //!< Pointer to the plugin manager. - // AIFIXME: this should be a separate, thread-safe singleton. typedef std::map context_map_type; //!< Type of mContextMap. - context_map_type mContextMap; //!< Map context (ie, "snapshot" or "image") to last used folder. + static AIThreadSafeSimple sContextMap; //!< Map context (ie, "snapshot" or "image") to last used folder. std::string mContext; //!< Some key to indicate the context (remembers the folder per key). // Input variables (cache variable between call to open and run). @@ -191,9 +190,9 @@ private: std::vector mFilenames; //!< Filesnames. // Store a folder for the given context. - void store_folder(std::string const& context, std::string const& folder); + static void store_folder(std::string const& context, std::string const& folder); // Return the last folder stored for 'context', or default_path if none, or context "savefile" if empty, or $HOME if none. - std::string get_folder(std::string const& default_path, std::string const& context); + static std::string get_folder(std::string const& default_path, std::string const& context); protected: // Call finish() (or abort()), not delete. diff --git a/indra/plugins/base_basic/basic_plugin_base.cpp b/indra/plugins/base_basic/basic_plugin_base.cpp index f791b5887..d1a656391 100755 --- a/indra/plugins/base_basic/basic_plugin_base.cpp +++ b/indra/plugins/base_basic/basic_plugin_base.cpp @@ -115,6 +115,20 @@ void BasicPluginBase::sendLogMessage(std::string const& message, LLPluginMessage logmessage.setValue("message", message); logmessage.setValueS32("log_level",level); mSendMessageFunction(logmessage.generate().c_str(), &mPluginInstance); + // Theoretically we should flush here (that's what PLS_ENDL means), but we don't because + // that interfers with how the code would act without debug messages. Instead, the developer + // is responsible to call flushMessages themselves at the appropriate places. +} + +/** + * Flush all messages to the viewer. + * + * This blocks if necessary, up till 10 seconds, before it gives up. + */ +void BasicPluginBase::flushMessages(void) +{ + LLPluginMessage logmessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "flush"); + mSendMessageFunction(logmessage.generate().c_str(), &mPluginInstance); } /** @@ -124,6 +138,9 @@ void BasicPluginBase::sendLogMessage(std::string const& message, LLPluginMessage */ void BasicPluginBase::sendShutdownMessage(void) { + // Do a double flush. First flush all messages so far, then send the shutdown message, + // which also will try to flush itself before terminating the process. + flushMessages(); LLPluginMessage shutdownmessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shutdown"); sendMessage(shutdownmessage); } diff --git a/indra/plugins/base_basic/basic_plugin_base.h b/indra/plugins/base_basic/basic_plugin_base.h index 492b408e7..042a27b63 100755 --- a/indra/plugins/base_basic/basic_plugin_base.h +++ b/indra/plugins/base_basic/basic_plugin_base.h @@ -36,16 +36,13 @@ #ifndef BASIC_PLUGIN_BASE_H #define BASIC_PLUGIN_BASE_H -#include - -#include - #include "linden_common.h" - #include "llplugininstance.h" #include "llpluginmessage.h" #include "llpluginmessageclasses.h" +#include + class BasicPluginBase { public: @@ -65,7 +62,10 @@ public: // Used for log messages. Use macros below. void sendLogMessage(std::string const& message, LLPluginMessage::LLPLUGIN_LOG_LEVEL level); - // Shoot down the whole process. + // Flush all messages to the viewer. + void flushMessages(void); + + // Flush all messages and then shoot down the whole process. void sendShutdownMessage(void); protected: @@ -127,6 +127,7 @@ int create_plugin( #define PLS_INFOS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_INFO, LL_DEBUG_PLUGIN_MESSAGES) #define PLS_WARNS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_WARN, 1) #define PLS_ERRS PLS_LOG_MESSAGE(LLPluginMessage::LOG_LEVEL_ERR, 1) +#define PLS_FLUSH do { BasicPluginBase::sPluginBase->flushMessages(); } while(0) #endif // BASIC_PLUGIN_BASE diff --git a/indra/plugins/filepicker/basic_plugin_filepicker.cpp b/indra/plugins/filepicker/basic_plugin_filepicker.cpp index 1201c3f71..7e3a4f0b0 100644 --- a/indra/plugins/filepicker/basic_plugin_filepicker.cpp +++ b/indra/plugins/filepicker/basic_plugin_filepicker.cpp @@ -232,10 +232,13 @@ void FilepickerPlugin::receiveMessage(char const* message_string) std::string folder = message_in.getValue("folder"); bool get_directory = (filter == "directory"); + // We about to completely block on running the modal File/Dir picker window, so flush any pending messages to the viewer. + flushMessages(); + bool canceled; if (get_directory) { - canceled = !LLDirPicker::instance().getDir(&folder); + canceled = !LLDirPicker::instance().getDir(folder); } else if (type == "save") { @@ -275,6 +278,7 @@ void FilepickerPlugin::receiveMessage(char const* message_string) sendMessage(message); } // We're done. Exit the whole application. + // This first flushes any messages before terminating the plugin. sendShutdownMessage(); } else diff --git a/indra/plugins/filepicker/legacy.cpp b/indra/plugins/filepicker/legacy.cpp index f89862061..e106923bf 100644 --- a/indra/plugins/filepicker/legacy.cpp +++ b/indra/plugins/filepicker/legacy.cpp @@ -28,8 +28,8 @@ * - Initial version, written by Aleric Inglewood @ SL */ -#include "legacy.h" #include "basic_plugin_base.h" // For PLS_INFOS etc. +#include "legacy.h" // Translation map. translation_map_type translation_map; diff --git a/indra/plugins/filepicker/lldirpicker.cpp b/indra/plugins/filepicker/lldirpicker.cpp index 39dff05e9..7ddcda6df 100644 --- a/indra/plugins/filepicker/lldirpicker.cpp +++ b/indra/plugins/filepicker/lldirpicker.cpp @@ -65,7 +65,15 @@ LLDirPicker::~LLDirPicker() // nothing } -BOOL LLDirPicker::getDir(std::string* filename) +static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) +{ + if (uMsg == BFFM_INITIALIZED) + { + SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData); + } +} + +BOOL LLDirPicker::getDir(std::string const& folder) { if( mLocked ) { @@ -73,34 +81,38 @@ BOOL LLDirPicker::getDir(std::string* filename) } BOOL success = FALSE; - BROWSEINFO bi; - memset(&bi, 0, sizeof(bi)); + BROWSEINFO bi; + memset(&bi, 0, sizeof(bi)); - bi.ulFlags = BIF_USENEWUI; - bi.hwndOwner = (HWND)gViewerWindow->getPlatformWindow(); - bi.lpszTitle = NULL; + bi.ulFlags = BIF_USENEWUI; + bi.hwndOwner = (HWND)gViewerWindow->getPlatformWindow(); + bi.lpszTitle = NULL; + llutf16string tstring = utf8str_to_utf16str(folder); + bi.lParam = (LPARAM)tstring.c_str(); + bi.lpfn = &BrowseCallbackProc; - ::OleInitialize(NULL); + ::OleInitialize(NULL); - LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi); + PLS_FLUSH; + LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi); - if(pIDL != NULL) - { - WCHAR buffer[_MAX_PATH] = {'\0'}; + if(pIDL != NULL) + { + WCHAR buffer[_MAX_PATH] = {'\0'}; - if(::SHGetPathFromIDList(pIDL, buffer) != 0) - { - // Set the string value. + if(::SHGetPathFromIDList(pIDL, buffer) != 0) + { + // Set the string value. - mDir = utf16str_to_utf8str(llutf16string(buffer)); - success = TRUE; - } + mDir = utf16str_to_utf8str(llutf16string(buffer)); + success = TRUE; + } - // free the item id list - CoTaskMemFree(pIDL); - } + // free the item id list + CoTaskMemFree(pIDL); + } - ::OleUninitialize(); + ::OleUninitialize(); return success; } @@ -147,9 +159,9 @@ pascal void LLDirPicker::doNavCallbackEvent(NavEventCallbackMessage callBackSele //Convert string to a FSSpec FSRef myFSRef; - const char* filename=sInstance.mFileName->c_str(); + const char* folder = sInstance.mFileName->c_str(); - error = FSPathMakeRef ((UInt8*)filename, &myFSRef, NULL); + error = FSPathMakeRef ((UInt8*)folder, &myFSRef, NULL); if (error != noErr) break; @@ -185,7 +197,10 @@ OSStatus LLDirPicker::doNavChooseDialog() gViewerWindow->mWindow->beforeDialog(); if (error == noErr) + { + PLS_FLUSH; error = NavDialogRun(navRef); + } gViewerWindow->mWindow->afterDialog(); @@ -216,13 +231,13 @@ OSStatus LLDirPicker::doNavChooseDialog() return error; } -BOOL LLDirPicker::getDir(std::string* filename) +BOOL LLDirPicker::getDir(std::string const& folder) { if( mLocked ) return FALSE; BOOL success = FALSE; OSStatus error = noErr; - mFileName = filename; + mFileName = folder; // mNavOptions.saveFileName @@ -266,14 +281,15 @@ void LLDirPicker::reset() LLFilePickerBase::reset(); } -BOOL LLDirPicker::getDir(std::string* filename) +BOOL LLDirPicker::getDir(std::string const& folder) { reset(); - GtkWindow* picker = buildFilePicker(false, true, "dirpicker"); + GtkWindow* picker = buildFilePicker(false, true, folder); if (picker) { gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("choose_the_directory").c_str()); gtk_widget_show_all(GTK_WIDGET(picker)); + PLS_FLUSH; gtk_main(); return (!getFirstFile().empty()); } @@ -301,7 +317,7 @@ void LLDirPicker::reset() { } -BOOL LLDirPicker::getDir(std::string* filename) +BOOL LLDirPicker::getDir(std::string* folder) { return FALSE; } diff --git a/indra/plugins/filepicker/lldirpicker.h b/indra/plugins/filepicker/lldirpicker.h index cdee1d094..034bcdf91 100644 --- a/indra/plugins/filepicker/lldirpicker.h +++ b/indra/plugins/filepicker/lldirpicker.h @@ -71,7 +71,7 @@ public: // calling this before main() is undefined static LLDirPicker& instance( void ) { return sInstance; } - BOOL getDir(std::string* filename); + BOOL getDir(std::string const& filename); std::string getDirName(); // clear any lists of buffers or whatever, and make sure the dir diff --git a/indra/plugins/filepicker/llfilepicker.cpp b/indra/plugins/filepicker/llfilepicker.cpp index 48069e88c..066a8340c 100644 --- a/indra/plugins/filepicker/llfilepicker.cpp +++ b/indra/plugins/filepicker/llfilepicker.cpp @@ -205,7 +205,6 @@ bool LLFilePickerBase::setupFilter(ELoadFilter filter) return res; } -// AIFIXME: Use folder bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -214,9 +213,9 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder } bool success = FALSE; - // don't provide default file selection + llutf16string tstring = utf8str_to_utf16str(folder); + mOFN.lpstrInitialDir = (LPCTSTR)tstring.data(); mFilesW[0] = '\0'; - mOFN.lpstrFile = mFilesW; mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE; mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR ; @@ -237,7 +236,6 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder return success; } -// AIFIXME: Use folder bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -246,9 +244,9 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons } bool success = FALSE; - // don't provide default file selection + llutf16string tstring = utf8str_to_utf16str(folder); + mOFN.lpstrInitialDir = (LPCTSTR)tstring.data(); mFilesW[0] = '\0'; - mOFN.lpstrFile = mFilesW; mOFN.nFilterIndex = 1; mOFN.nMaxFile = FILENAME_BUFFER_SIZE; @@ -295,7 +293,6 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons return success; } -// AIFIXME: Use folder bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) { if( mLocked ) @@ -304,11 +301,13 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena } bool success = FALSE; + llutf16string tstring = utf8str_to_utf16str(folder); + mOFN.lpstrInitialDir = (LPCTSTR)tstring.data(); mOFN.lpstrFile = mFilesW; if (!filename.empty()) { llutf16string tstring = utf8str_to_utf16str(filename); - wcsncpy(mFilesW, tstring.c_str(), FILENAME_BUFFER_SIZE); } /*Flawfinder: ignore*/ + wcsncpy(mFilesW, tstring.data(), FILENAME_BUFFER_SIZE); } /*Flawfinder: ignore*/ else { mFilesW[0] = '\0'; @@ -705,8 +704,9 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode) { + LLFilePickerBase* picker = (LLFilePickerBase*)callBackUD; Boolean result = true; - ELoadFilter filter = *((ELoadFilter*) callBackUD); + ELoadFilter filter = picker->getLoadFilter(); OSStatus error = noErr; if (filterMode == kNavFilteringBrowserList && filter != FFLOAD_ALL && (theItem->descriptorType == typeFSRef || theItem->descriptorType == typeFSS)) @@ -788,7 +788,7 @@ Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *c } else if (filter == FFLOAD_RAW) { - if (fileInfo.filetype != '\?\?\?\?' && + if (fileInfo.filetype != L'\?\?\?\?' && (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("raw"), kCFCompareCaseInsensitive) != kCFCompareEqualTo)) ) { @@ -809,23 +809,70 @@ Boolean LLFilePickerBase::navOpenFilterProc(AEDesc *theItem, void *info, void *c return result; } -OSStatus LLFilePickerBase::doNavChooseDialog(ELoadFilter filter) +//static +pascal void LLFilePickerBase::doNavCallbackEvent(NavEventCallbackMessage callBackSelector, + NavCBRecPtr callBackParms, void* callBackUD) +{ + switch(callBackSelector) + { + case kNavCBStart: + { + LLFilePickerBase* picker = reinterpret_cast(callBackUD); + if (picker->getFolder().empty()) break; + + OSStatus error = noErr; + AEDesc theLocation = {typeNull, NULL}; + FSSpec outFSSpec; + + //Convert string to a FSSpec + FSRef myFSRef; + + const char* folder = picker->getFolder().c_str(); + + error = FSPathMakeRef ((UInt8*)folder, &myFSRef, NULL); + + if (error != noErr) break; + + error = FSGetCatalogInfo (&myFSRef, kFSCatInfoNone, NULL, NULL, &outFSSpec, NULL); + + if (error != noErr) break; + + error = AECreateDesc(typeFSS, &outFSSpec, sizeof(FSSpec), &theLocation); + + if (error != noErr) break; + + error = NavCustomControl(callBackParms->context, + kNavCtlSetLocation, (void*)&theLocation); + + } + } +} + +OSStatus LLFilePickerBase::doNavChooseDialog(ELoadFilter filter, std::string const& folder) { OSStatus error = noErr; NavDialogRef navRef = NULL; NavReplyRecord navReply; memset(&navReply, 0, sizeof(navReply)); + + mLoadFilter = filter; + mFolder = folder; + + NavEventUPP eventProc = NewNavEventUPP(doNavCallbackEvent); // NOTE: we are passing the address of a local variable here. // This is fine, because the object this call creates will exist for less than the lifetime of this function. // (It is destroyed by NavDialogDispose() below.) - error = NavCreateChooseFileDialog(&mNavOptions, NULL, NULL, NULL, navOpenFilterProc, (void*)(&filter), &navRef); + error = NavCreateChooseFileDialog(&mNavOptions, NULL, eventProc, NULL, navOpenFilterProc, (void*)this, &navRef); //gViewerWindow->mWindow->beforeDialog(); if (error == noErr) + { + PLS_FLUSH; error = NavDialogRun(navRef); + } //gViewerWindow->mWindow->afterDialog(); @@ -860,11 +907,14 @@ OSStatus LLFilePickerBase::doNavChooseDialog(ELoadFilter filter) mFiles.push_back(std::string(path)); } } + + if (eventProc) + DisposeNavEventUPP(eventProc); return error; } -OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string& filename) +OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, std::string const& filename, std::string const& folder) { OSStatus error = noErr; NavDialogRef navRef = NULL; @@ -905,46 +955,49 @@ OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string extension = CFSTR(".png"); break; case FFSAVE_AVI: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(".mov"); break; case FFSAVE_ANIM: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(".xaf"); break; #ifdef _CORY_TESTING case FFSAVE_GEOMETRY: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(".slg"); break; #endif case FFSAVE_RAW: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(".raw"); break; case FFSAVE_J2C: - type = '\?\?\?\?'; + type = L'\?\?\?\?'; creator = 'prvw'; extension = CFSTR(".j2c"); break; case FFSAVE_ALL: default: - type = '\?\?\?\?'; - creator = '\?\?\?\?'; + type = L'\?\?\?\?'; + creator = L'\?\?\?\?'; extension = CFSTR(""); break; } + + NavEventUPP eventUPP = NewNavEventUPP(navSetDefaultFolderProc); + mFolder = folder; // Create the dialog - error = NavCreatePutFileDialog(&mNavOptions, type, creator, NULL, NULL, &navRef); + error = NavCreatePutFileDialog(&mNavOptions, type, creator, eventUPP, (void*)this, &navRef); if (error == noErr) { CFStringRef nameString = NULL; @@ -981,7 +1034,10 @@ OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string // Run the dialog if (error == noErr) + { + PLS_FLUSH; error = NavDialogRun(navRef); + } //gViewerWindow->mWindow->afterDialog(); @@ -1033,10 +1089,12 @@ OSStatus LLFilePickerBase::doNavSaveDialog(ESaveFilter filter, const std::string } } + if (eventUPP) + DisposeNavEventUPP(eventUPP); + return error; } -// AIFIXME: Use folder bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -1050,7 +1108,7 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; { - error = doNavChooseDialog(filter); + error = doNavChooseDialog(filter, folder); } if (error == noErr) { @@ -1061,7 +1119,6 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder return success; } -// AIFIXME: Use folder bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string const& folder) { if( mLocked ) @@ -1075,7 +1132,7 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons mNavOptions.optionFlags |= kNavAllowMultipleFiles; { - error = doNavChooseDialog(filter); + error = doNavChooseDialog(filter, folder); } if (error == noErr) { @@ -1088,7 +1145,6 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons return success; } -// AIFIXME: Use folder bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder) { if( mLocked ) @@ -1101,7 +1157,7 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena mNavOptions.optionFlags &= ~kNavAllowMultipleFiles; { - error = doNavSaveDialog(filter, filename); + error = doNavSaveDialog(filter, filename, folder); } if (error == noErr) { @@ -1203,6 +1259,7 @@ GtkWindow* LLFilePickerBase::buildFilePicker(bool is_save, bool is_folder, std:: if (!folder.empty()) { + PLS_DEBUGS << "Calling gtk_file_chooser_set_current_folder(\"" << folder << "\")." << PLS_ENDL; gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(win), folder.c_str()); @@ -1225,6 +1282,7 @@ GtkWindow* LLFilePickerBase::buildFilePicker(bool is_save, bool is_folder, std:: G_CALLBACK(LLFilePickerBase::chooser_responder), this); + PLS_FLUSH; gtk_window_set_modal(GTK_WINDOW(win), TRUE); /* GTK 2.6: if (is_folder) @@ -1383,6 +1441,7 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena } gtk_widget_show_all(GTK_WIDGET(picker)); + PLS_FLUSH; gtk_main(); rtn = (getFileCount() == 1); @@ -1430,6 +1489,7 @@ bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder gtk_window_set_title(GTK_WINDOW(picker), caption.c_str()); gtk_widget_show_all(GTK_WIDGET(picker)); + PLS_FLUSH; gtk_main(); rtn = (getFileCount() == 1); @@ -1458,6 +1518,7 @@ bool LLFilePickerBase::getMultipleLoadFiles(ELoadFilter filter, std::string cons gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("load_files").c_str()); gtk_widget_show_all(GTK_WIDGET(picker)); + PLS_FLUSH; gtk_main(); rtn = !mFiles.empty(); } @@ -1485,7 +1546,6 @@ bool LLFilePickerBase::getSaveFile(ESaveFilter filter, std::string const& filena return FALSE; } -// AIFIXME: Use folder bool LLFilePickerBase::getLoadFile(ELoadFilter filter, std::string const& folder) { reset(); diff --git a/indra/plugins/filepicker/llfilepicker.h b/indra/plugins/filepicker/llfilepicker.h index f8e17719e..9c8db8e8a 100644 --- a/indra/plugins/filepicker/llfilepicker.h +++ b/indra/plugins/filepicker/llfilepicker.h @@ -166,6 +166,9 @@ private: OSStatus doNavChooseDialog(ELoadFilter filter); OSStatus doNavSaveDialog(ESaveFilter filter, const std::string& filename); static Boolean navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode); + static pascal void LLFilePickerBase::doNavCallbackEvent(NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, void* callBackUD); + ELoadFilter getLoadFilter(void) const { return mLoadFilter; } + std::string const& getFolder(void) const { return mFolder; } #endif // LL_DARWIN #if LL_GTK @@ -194,7 +197,11 @@ private: S32 mCurrentFile; bool mLocked; bool mMultiFile; - +#if LL_DARWIN + ELoadFilter mLoadFilter; + std::string mFolder; +#endif + protected: #if LL_GTK GtkWindow* buildFilePicker(bool is_save, bool is_folder, std::string const& folder); From ea2cc3e0dedc262b0ff760495c7de44092963b58 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Fri, 13 May 2011 23:50:56 +0200 Subject: [PATCH 16/21] Make FilePicker context sensitive default paths persistent over logins. Also, make newview/statemachine a separate project. --- indra/CMakeLists.txt | 1 + indra/cmake/AIStateMachine.cmake | 4 ++ indra/llcommon/llstl.h | 1 + indra/llplugin/llpluginclassmedia.h | 7 +- indra/newview/CMakeLists.txt | 15 +---- indra/newview/llappviewer.cpp | 4 ++ indra/newview/llstartup.cpp | 7 ++ indra/newview/llviewercontrol.cpp | 10 +++ indra/newview/llviewercontrol.h | 7 +- indra/newview/llviewerpluginmanager.cpp | 10 +++ indra/newview/llviewerpluginmanager.h | 11 ++- indra/newview/statemachine/CMakeLists.txt | 41 ++++++++++++ indra/newview/statemachine/aifilepicker.cpp | 67 +++++++++++++++++-- indra/newview/statemachine/aifilepicker.h | 7 +- indra/newview/statemachine/aistatemachine.cpp | 9 +-- indra/newview/statemachine/aistatemachine.h | 1 + 16 files changed, 168 insertions(+), 34 deletions(-) create mode 100644 indra/cmake/AIStateMachine.cmake create mode 100644 indra/newview/statemachine/CMakeLists.txt diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 55e2c1e87..c0e8dc408 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -106,6 +106,7 @@ if (VIEWER) add_dependencies(viewer solaris-crash-logger) endif (LINUX) + add_subdirectory(${VIEWER_PREFIX}newview/statemachine) add_subdirectory(${VIEWER_PREFIX}newview) add_dependencies(viewer secondlife-bin) endif (VIEWER) diff --git a/indra/cmake/AIStateMachine.cmake b/indra/cmake/AIStateMachine.cmake new file mode 100644 index 000000000..c1bda40c4 --- /dev/null +++ b/indra/cmake/AIStateMachine.cmake @@ -0,0 +1,4 @@ +# -*- cmake -*- + +set(AISTATEMACHINE_INCLUDE_DIRS statemachine) +set(AISTATEMACHINE_LIBRARIES statemachine) diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h index 788617953..18842f276 100644 --- a/indra/llcommon/llstl.h +++ b/indra/llcommon/llstl.h @@ -39,6 +39,7 @@ #include #include #include +#include "stdtypes.h" // llcommon/stdtypes.h, needed for S32 and U32. // Use to compare the first element only of a pair // e.g. typedef std::set, compare_pair > some_pair_set_t; diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index 1996c6588..2882de180 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -37,7 +37,6 @@ #define LL_LLPLUGINCLASSMEDIA_H #include "llpluginclassbasic.h" -#include "llgltypes.h" #include "llrect.h" #include "v4color.h" @@ -232,9 +231,9 @@ protected: bool mTextureParamsReceived; // the mRequestedTexture* fields are only valid when this is true S32 mRequestedTextureDepth; - LLGLenum mRequestedTextureInternalFormat; - LLGLenum mRequestedTextureFormat; - LLGLenum mRequestedTextureType; + U32 mRequestedTextureInternalFormat; + U32 mRequestedTextureFormat; + U32 mRequestedTextureType; bool mRequestedTextureSwapBytes; bool mRequestedTextureCoordsOpenGL; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 340989a05..2b4a4ca31 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -22,6 +22,7 @@ include(LLInventory) include(LLMath) include(LLMessage) include(LLPlugin) +include(AIStateMachine) include(LLPrimitive) include(LLRender) include(LLUI) @@ -43,6 +44,7 @@ if (WINDOWS) endif (WINDOWS) include_directories( + ${CMAKE_SOURCE_DIR}/newview ${DBUSGLIB_INCLUDE_DIRS} ${HUNSPELL_INCLUDE_DIR} ${ELFIO_INCLUDE_DIR} @@ -1006,18 +1008,6 @@ set(viewer_HEADER_FILES source_group("CMake Rules" FILES ViewerInstall.cmake) -set(statemachine_SOURCE_FILES - statemachine/aistatemachine.cpp - statemachine/aifilepicker.cpp - ) -set(statemachine_HEADER_FILES - statemachine/aistatemachine.h - statemachine/aifilepicker.h - statemachine/aidirpicker.h - ) -list(APPEND viewer_SOURCE_FILES ${statemachine_SOURCE_FILES}) -list(APPEND viewer_HEADER_FILES ${statemachine_HEADER_FILES}) - if (DARWIN) LIST(APPEND viewer_SOURCE_FILES llappviewermacosx.cpp) @@ -1434,6 +1424,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLINVENTORY_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLPLUGIN_LIBRARIES} + ${AISTATEMACHINE_LIBRARIES} ${LLPRIMITIVE_LIBRARIES} ${LLRENDER_LIBRARIES} ${FREETYPE_LIBRARIES} diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 420a9ae67..a2a4ed386 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -72,6 +72,7 @@ #include "llmutelist.h" #include "llurldispatcher.h" #include "llurlhistory.h" +#include "statemachine/aifilepicker.h" #include "llfirstuse.h" #include "llrender.h" #include "llfont.h" @@ -1353,6 +1354,9 @@ bool LLAppViewer::cleanup() // Save URL history file LLURLHistory::saveFile("url_history.xml"); + // Save file- and dirpicker {context, default paths} map. + AIFilePicker::saveFile("filepicker_contexts.xml"); + // save mute list. gMuteList used to also be deleted here too. LLMuteList::getInstance()->cache(gAgent.getID()); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index e08bc301a..07c8de068 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -54,6 +54,7 @@ #include "hippogridmanager.h" #include "hippolimits.h" #include "floaterao.h" +#include "statemachine/aifilepicker.h" #include "llares.h" #include "llcachename.h" @@ -636,6 +637,12 @@ bool idle_startup() LLStartUp::handleSocksProxy(false); + //------------------------------------------------- + // Load file- and dirpicker {context, default path} map. + //------------------------------------------------- + + AIFilePicker::loadFile("filepicker_contexts.xml"); + //------------------------------------------------- // Init audio, which may be needed for prefs dialog // or audio cues in connection UI. diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index de65b97af..c5889106f 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -810,6 +810,16 @@ template <> eControlType get_control_type(const LLSD& in, LLSD& out) return TYPE_LLSD; } +void onCommitControlSetting_gSavedSettings(LLUICtrl* ctrl, void* name) +{ + gSavedSettings.setValue((const char*)name,ctrl->getValue()); +} + +void onCommitControlSetting_gSavedPerAccountSettings(LLUICtrl* ctrl, void* name) +{ + gSavedPerAccountSettings.setValue((const char*)name,ctrl->getValue()); +} + #if TEST_CACHED_CONTROL #define DECL_LLCC(T, V) static LLCachedControl mySetting_##T("TestCachedControl"#T, V) diff --git a/indra/newview/llviewercontrol.h b/indra/newview/llviewercontrol.h index 8835146ad..f22f96f27 100644 --- a/indra/newview/llviewercontrol.h +++ b/indra/newview/llviewercontrol.h @@ -35,9 +35,10 @@ #include #include "llcontrol.h" -#include "lluictrl.h" #include "aithreadsafe.h" +class LLUICtrl; + // Enabled this definition to compile a 'hacked' viewer that // allows a hacked godmode to be toggled on and off. #define TOGGLE_HACKED_GODLIKE_VIEWER @@ -75,8 +76,8 @@ bool handleCloudSettingsChanged(const LLSD& newvalue); //A template would be a little awkward to use here.. so.. a preprocessor macro. Alas. onCommitControlSetting(gSavedSettings) etc. -inline void onCommitControlSetting_gSavedSettings(LLUICtrl* ctrl, void* name) {gSavedSettings.setValue((const char*)name,ctrl->getValue());} -inline void onCommitControlSetting_gSavedPerAccountSettings(LLUICtrl* ctrl, void* name) {gSavedPerAccountSettings.setValue((const char*)name,ctrl->getValue());} +void onCommitControlSetting_gSavedSettings(LLUICtrl* ctrl, void* name); +void onCommitControlSetting_gSavedPerAccountSettings(LLUICtrl* ctrl, void* name); #define onCommitControlSetting(controlgroup) onCommitControlSetting_##controlgroup //#define TEST_CACHED_CONTROL 1 diff --git a/indra/newview/llviewerpluginmanager.cpp b/indra/newview/llviewerpluginmanager.cpp index 58ee33585..fdd48736e 100644 --- a/indra/newview/llviewerpluginmanager.cpp +++ b/indra/newview/llviewerpluginmanager.cpp @@ -32,6 +32,7 @@ #include "llviewerprecompiledheaders.h" #include "llviewerpluginmanager.h" +#include "llnotifications.h" void LLViewerPluginManager::destroyPlugin() { @@ -54,3 +55,12 @@ void LLViewerPluginManager::update() return; } } + +void LLViewerPluginManager::send_plugin_failure_warning(std::string const& plugin_basename) +{ + LL_WARNS("Plugin") << "plugin intialization failed for plugin: " << plugin_basename << LL_ENDL; + LLSD args; + args["MIME_TYPE"] = plugin_basename; // FIXME: Use different notification. + LLNotifications::instance().add("NoPlugin", args); +} + diff --git a/indra/newview/llviewerpluginmanager.h b/indra/newview/llviewerpluginmanager.h index b56d11eee..6525a5403 100644 --- a/indra/newview/llviewerpluginmanager.h +++ b/indra/newview/llviewerpluginmanager.h @@ -39,7 +39,6 @@ #include "lldir.h" #include "llfile.h" #include "llviewercontrol.h" -#include "llnotifications.h" #include "llpluginclassbasic.h" class LLViewerPluginManager : public LLRefCount @@ -65,6 +64,10 @@ public: // Return pointer to plugin. LLPluginClassBasic* getPlugin(void) const { return mPluginBase; } +private: + // Called from createPlugin. + void send_plugin_failure_warning(std::string const& plugin_basename); + protected: LLPluginClassBasic* mPluginBase; //!< Pointer to the base class of the underlaying plugin. }; @@ -104,11 +107,7 @@ LLPluginClassBasic* LLViewerPluginManager::createPlugin(T* user_data) if (mPluginBase) return mPluginBase; - LL_WARNS("Plugin") << "plugin intialization failed for plugin: " << PLUGIN_TYPE::plugin_basename() << LL_ENDL; - LLSD args; - args["MIME_TYPE"] = PLUGIN_TYPE::plugin_basename(); // FIXME: Use different notification. - LLNotifications::instance().add("NoPlugin", args); - + send_plugin_failure_warning(PLUGIN_TYPE::plugin_basename()); return NULL; } diff --git a/indra/newview/statemachine/CMakeLists.txt b/indra/newview/statemachine/CMakeLists.txt new file mode 100644 index 000000000..a7738290a --- /dev/null +++ b/indra/newview/statemachine/CMakeLists.txt @@ -0,0 +1,41 @@ +# -*- cmake -*- + +project(statemachine) + +include(00-Common) +include(LLCommon) +include(LLPlugin) +include(LLMessage) # This is needed by LLPlugin. +include(LLMath) +include(LLVFS) +include(LLXML) + +include_directories( + ${CMAKE_SOURCE_DIR}/newview + ${LLCOMMON_INCLUDE_DIRS} + ${LLPLUGIN_INCLUDE_DIRS} + ${LLMESSAGE_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLVFS_INCLUDE_DIRS} + ${LLXML_INCLUDE_DIRS} + ) + +set(statemachine_SOURCE_FILES + aistatemachine.cpp + aifilepicker.cpp + ) + +set(statemachine_HEADER_FILES + CMakeLists.txt + aistatemachine.h + aifilepicker.h + aidirpicker.h + ) + +set_source_files_properties(${statemachine_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND statemachine_SOURCE_FILES ${statemachine_HEADER_FILES}) + +add_library (statemachine ${statemachine_SOURCE_FILES}) +add_dependencies(statemachine prepare) diff --git a/indra/newview/statemachine/aifilepicker.cpp b/indra/newview/statemachine/aifilepicker.cpp index f8da4da86..29f83c782 100644 --- a/indra/newview/statemachine/aifilepicker.cpp +++ b/indra/newview/statemachine/aifilepicker.cpp @@ -28,14 +28,14 @@ * Initial version, written by Aleric Inglewood @ SL */ -#include "../llviewerprecompiledheaders.h" -#include "../llviewermedia.h" -#include "../lltrans.h" +#include "linden_common.h" +#include "lltrans.h" #include "llpluginclassmedia.h" #include "llpluginmessageclasses.h" +#include "llsdserialize.h" #include "aifilepicker.h" #if LL_WINDOWS -#include "../llviewerwindow.h" +#include "llviewerwindow.h" #endif #if LL_GTK && LL_X11 #include "llwindowsdl.h" @@ -456,3 +456,62 @@ std::string AIFilePicker::getFolder(void) const return gDirUtilp->getDirName(getFilename()); } +// static +bool AIFilePicker::loadFile(std::string const& filename) +{ + LLSD data; + std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); + llifstream file(filepath); + if (file.is_open()) + { + llinfos << "Loading filepicker context file at \"" << filepath << "\"." << llendl; + LLSDSerialize::fromXML(data, file); + } + + if (!data.isMap()) + { + llinfos << "File missing, ill-formed, or simply undefined; not changing the file (" << filepath << ")." << llendl; + return false; + } + + AIAccess wContextMap(sContextMap); + + for (LLSD::map_const_iterator iter = data.beginMap(); iter != data.endMap(); ++iter) + { + wContextMap->insert(context_map_type::value_type(iter->first, iter->second.asString())); + } + + return true; +} + +// static +bool AIFilePicker::saveFile(std::string const& filename) +{ + AIAccess wContextMap(sContextMap); + if (wContextMap->empty()) + return false; + + LLSD context; + + for (context_map_type::iterator iter = wContextMap->begin(); iter != wContextMap->end(); ++iter) + { + context[iter->first] = iter->second; + } + + std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); + llofstream file; + file.open(filepath.c_str()); + if (!file.is_open()) + { + llwarns << "Unable to open filepicker context file for save: \"" << filepath << "\"." << llendl; + return false; + } + + LLSDSerialize::toPrettyXML(context, file); + + file.close(); + llinfos << "Saved default paths to \"" << filepath << "\"." << llendl; + + return true; +} + diff --git a/indra/newview/statemachine/aifilepicker.h b/indra/newview/statemachine/aifilepicker.h index 91a7bc819..ee2013306 100644 --- a/indra/newview/statemachine/aifilepicker.h +++ b/indra/newview/statemachine/aifilepicker.h @@ -33,7 +33,7 @@ #include "aistatemachine.h" #include "llpluginclassmedia.h" -#include "../llviewermedia.h" +#include "llviewerpluginmanager.h" #include enum ELoadFilter @@ -165,6 +165,11 @@ public: std::string getFolder(void) const; std::vector const& getFilenames(void) const { return mFilenames; } + // Load the sContextMap from disk. + static bool loadFile(std::string const& filename); + // Save the sContextMap to disk. + static bool saveFile(std::string const& filename); + private: friend class AIPluginFilePicker; diff --git a/indra/newview/statemachine/aistatemachine.cpp b/indra/newview/statemachine/aistatemachine.cpp index ce1a4149e..2136d0eb4 100644 --- a/indra/newview/statemachine/aistatemachine.cpp +++ b/indra/newview/statemachine/aistatemachine.cpp @@ -28,17 +28,18 @@ * Initial version, written by Aleric Inglewood @ SL */ -#include "../llviewerprecompiledheaders.h" +#include "linden_common.h" #include -#include "../llcallbacklist.h" -#include "../llviewercontrol.h" - +#include "llcallbacklist.h" +#include "llcontrol.h" #include "llfasttimer.h" #include "aithreadsafe.h" #include "aistatemachine.h" +extern LLControlGroup gSavedSettings; + // Local variables. namespace { struct QueueElementComp; diff --git a/indra/newview/statemachine/aistatemachine.h b/indra/newview/statemachine/aistatemachine.h index 4b25b957d..fe3010d14 100644 --- a/indra/newview/statemachine/aistatemachine.h +++ b/indra/newview/statemachine/aistatemachine.h @@ -33,6 +33,7 @@ #include "aithreadsafe.h" #include "llfasttimer.h" +#include //! // A AIStateMachine is a base class that allows derived classes to From 4c9d9d44a45bb77a9c32671c880189bc4ea2f4eb Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sat, 14 May 2011 01:34:27 +0200 Subject: [PATCH 17/21] Add 'Save As...' back to snapshot floater. --- indra/newview/llfloatersnapshot.cpp | 2 +- indra/newview/skins/default/xui/en-us/floater_snapshot.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index 2d4ed9939..2f9539fd1 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -1404,7 +1404,7 @@ void LLFloaterSnapshot::Impl::onClickDiscard(void* data) // static void LLFloaterSnapshot::Impl::onCommitSave(LLUICtrl* ctrl, void* data) { - if (ctrl->getValue().asString() == "save as") + if (ctrl->getValue().asString() == "saveas") { gViewerWindow->resetSnapshotLoc(); } diff --git a/indra/newview/skins/default/xui/en-us/floater_snapshot.xml b/indra/newview/skins/default/xui/en-us/floater_snapshot.xml index 65e7d0fae..5b86ce319 100644 --- a/indra/newview/skins/default/xui/en-us/floater_snapshot.xml +++ b/indra/newview/skins/default/xui/en-us/floater_snapshot.xml @@ -32,6 +32,7 @@ list_position="below" mouse_opaque="true" name="save_btn" tool_tip="Save image to a file" width="105"> Save + Save As...