summaryrefslogtreecommitdiff
path: root/indra/llwindow
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llwindow')
-rw-r--r--indra/llwindow/CMakeLists.txt13
-rw-r--r--indra/llwindow/llgamecontrol.cpp2122
-rw-r--r--indra/llwindow/llgamecontrol.h347
-rw-r--r--indra/llwindow/llgamecontroltranslator.cpp275
-rw-r--r--indra/llwindow/llgamecontroltranslator.h93
-rw-r--r--indra/llwindow/llkeyboard.cpp31
-rw-r--r--indra/llwindow/llkeyboard.h20
-rw-r--r--indra/llwindow/llkeyboardheadless.cpp11
-rw-r--r--indra/llwindow/llkeyboardheadless.h19
-rw-r--r--indra/llwindow/llkeyboardmacosx.cpp13
-rw-r--r--indra/llwindow/llkeyboardmacosx.h16
-rw-r--r--indra/llwindow/llkeyboardsdl.cpp20
-rw-r--r--indra/llwindow/llkeyboardsdl.h22
-rw-r--r--indra/llwindow/llkeyboardwin32.cpp12
-rw-r--r--indra/llwindow/llkeyboardwin32.h19
-rw-r--r--indra/llwindow/llsdl.cpp102
-rw-r--r--indra/llwindow/llsdl.h30
-rw-r--r--indra/llwindow/llwindow.cpp47
-rw-r--r--indra/llwindow/llwindow.h45
-rw-r--r--indra/llwindow/llwindowheadless.h115
-rw-r--r--indra/llwindow/llwindowmacosx.cpp44
-rw-r--r--indra/llwindow/llwindowmacosx.h36
-rw-r--r--indra/llwindow/llwindowmesaheadless.h114
-rw-r--r--indra/llwindow/llwindowsdl.cpp1267
-rw-r--r--indra/llwindow/llwindowsdl.h74
-rw-r--r--indra/llwindow/llwindowwin32.cpp44
-rw-r--r--indra/llwindow/llwindowwin32.h156
27 files changed, 3755 insertions, 1352 deletions
diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt
index 075e17235a..e86ef2d578 100644
--- a/indra/llwindow/CMakeLists.txt
+++ b/indra/llwindow/CMakeLists.txt
@@ -18,11 +18,15 @@ include(LLWindow)
include(UI)
include(ViewerMiscLibs)
include(GLM)
+include(SDL2)
set(llwindow_SOURCE_FILES
llcursortypes.cpp
+ llgamecontrol.cpp
+ llgamecontroltranslator.cpp
llkeyboard.cpp
llkeyboardheadless.cpp
+ llsdl.cpp
llwindowheadless.cpp
llwindowcallbacks.cpp
llwindow.cpp
@@ -32,8 +36,11 @@ set(llwindow_HEADER_FILES
CMakeLists.txt
llcursortypes.h
+ llgamecontrol.h
+ llgamecontroltranslator.h
llkeyboard.h
llkeyboardheadless.h
+ llsdl.h
llwindowheadless.h
llwindowcallbacks.h
)
@@ -58,7 +65,7 @@ set(llwindow_LINK_LIBRARIES
ll::glm
ll::glext
ll::uilibraries
- ll::SDL
+ ll::SDL2
ll::zlib-ng
)
@@ -173,11 +180,11 @@ endif (llwindow_HEADER_FILES)
${viewer_SOURCE_FILES}
)
-if (SDL_FOUND)
+if (SDL2_FOUND)
set_property(TARGET llwindow
PROPERTY COMPILE_DEFINITIONS LL_SDL=1
)
-endif (SDL_FOUND)
+endif (SDL2_FOUND)
target_link_libraries (llwindow ${llwindow_LINK_LIBRARIES})
target_include_directories(llwindow INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/indra/llwindow/llgamecontrol.cpp b/indra/llwindow/llgamecontrol.cpp
new file mode 100644
index 0000000000..9d3c854ca2
--- /dev/null
+++ b/indra/llwindow/llgamecontrol.cpp
@@ -0,0 +1,2122 @@
+/**
+ * @file llgamecontrol.h
+ * @brief GameController detection and management
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llgamecontrol.h"
+
+#include <algorithm>
+#include <chrono>
+#include <unordered_map>
+
+#include "SDL2/SDL.h"
+#include "SDL2/SDL_gamecontroller.h"
+#include "SDL2/SDL_joystick.h"
+
+#include "indra_constants.h"
+#include "llfile.h"
+#include "llgamecontroltranslator.h"
+#include "llsd.h"
+
+namespace std
+{
+ string to_string(const SDL_JoystickGUID& guid)
+ {
+ char buffer[33] = { 0 };
+ SDL_JoystickGetGUIDString(guid, buffer, sizeof(guid));
+ return buffer;
+ }
+
+ string to_string(SDL_JoystickType type)
+ {
+ switch (type)
+ {
+ case SDL_JOYSTICK_TYPE_GAMECONTROLLER:
+ return "GAMECONTROLLER";
+ case SDL_JOYSTICK_TYPE_WHEEL:
+ return "WHEEL";
+ case SDL_JOYSTICK_TYPE_ARCADE_STICK:
+ return "ARCADE_STICK";
+ case SDL_JOYSTICK_TYPE_FLIGHT_STICK:
+ return "FLIGHT_STICK";
+ case SDL_JOYSTICK_TYPE_DANCE_PAD:
+ return "DANCE_PAD";
+ case SDL_JOYSTICK_TYPE_GUITAR:
+ return "GUITAR";
+ case SDL_JOYSTICK_TYPE_DRUM_KIT:
+ return "DRUM_KIT";
+ case SDL_JOYSTICK_TYPE_ARCADE_PAD:
+ return "ARCADE_PAD";
+ case SDL_JOYSTICK_TYPE_THROTTLE:
+ return "THROTTLE";
+ default:;
+ }
+ return "UNKNOWN";
+ }
+
+ string to_string(SDL_GameControllerType type)
+ {
+ switch (type)
+ {
+ case SDL_CONTROLLER_TYPE_XBOX360:
+ return "XBOX360";
+ case SDL_CONTROLLER_TYPE_XBOXONE:
+ return "XBOXONE";
+ case SDL_CONTROLLER_TYPE_PS3:
+ return "PS3";
+ case SDL_CONTROLLER_TYPE_PS4:
+ return "PS4";
+ case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
+ return "NINTENDO_SWITCH_PRO";
+ case SDL_CONTROLLER_TYPE_VIRTUAL:
+ return "VIRTUAL";
+ case SDL_CONTROLLER_TYPE_PS5:
+ return "PS5";
+ case SDL_CONTROLLER_TYPE_AMAZON_LUNA:
+ return "AMAZON_LUNA";
+ case SDL_CONTROLLER_TYPE_GOOGLE_STADIA:
+ return "GOOGLE_STADIA";
+ case SDL_CONTROLLER_TYPE_NVIDIA_SHIELD:
+ return "NVIDIA_SHIELD";
+ case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
+ return "NINTENDO_SWITCH_JOYCON_LEFT";
+ case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
+ return "NINTENDO_SWITCH_JOYCON_RIGHT";
+ case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
+ return "NINTENDO_SWITCH_JOYCON_PAIR";
+ default:;
+ }
+ return "UNKNOWN";
+ }
+}
+
+// Util for dumping SDL_JoystickGUID info
+std::ostream& operator<<(std::ostream& out, SDL_JoystickGUID& guid)
+{
+ return out << std::to_string(guid);
+}
+
+// Util for dumping SDL_JoystickType type name
+std::ostream& operator<<(std::ostream& out, SDL_JoystickType type)
+{
+ return out << std::to_string(type);
+}
+
+// Util for dumping SDL_GameControllerType type name
+std::ostream& operator<<(std::ostream& out, SDL_GameControllerType type)
+{
+ return out << std::to_string(type);
+}
+
+namespace std
+{
+ string to_string(SDL_Joystick* joystick)
+ {
+ if (!joystick)
+ {
+ return "nullptr";
+ }
+
+ std::stringstream ss;
+
+ ss << "{id:" << SDL_JoystickInstanceID(joystick);
+ SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
+ ss << ",guid:'" << guid << "'";
+ ss << ",type:'" << SDL_JoystickGetType(joystick) << "'";
+ ss << ",name:'" << ll_safe_string(SDL_JoystickName(joystick)) << "'";
+ ss << ",vendor:" << SDL_JoystickGetVendor(joystick);
+ ss << ",product:" << SDL_JoystickGetProduct(joystick);
+ if (U16 version = SDL_JoystickGetProductVersion(joystick))
+ {
+ ss << ",version:" << version;
+ }
+ if (const char* serial = SDL_JoystickGetSerial(joystick))
+ {
+ ss << ",serial:'" << serial << "'";
+ }
+ ss << ",num_axes:" << SDL_JoystickNumAxes(joystick);
+ ss << ",num_balls:" << SDL_JoystickNumBalls(joystick);
+ ss << ",num_hats:" << SDL_JoystickNumHats(joystick);
+ ss << ",num_buttons:" << SDL_JoystickNumHats(joystick);
+ ss << "}";
+
+ return ss.str();
+ }
+
+ string to_string(SDL_GameController* controller)
+ {
+ if (!controller)
+ {
+ return "nullptr";
+ }
+
+ stringstream ss;
+
+ ss << "{type:'" << SDL_GameControllerGetType(controller) << "'";
+ ss << ",name:'" << ll_safe_string(SDL_GameControllerName(controller)) << "'";
+ ss << ",vendor:" << SDL_GameControllerGetVendor(controller);
+ ss << ",product:" << SDL_GameControllerGetProduct(controller);
+ if (U16 version = SDL_GameControllerGetProductVersion(controller))
+ {
+ ss << ",version:" << version;
+ }
+ if (const char* serial = SDL_GameControllerGetSerial(controller))
+ {
+ ss << ",serial:'" << serial << "'";
+ }
+ ss << "}";
+
+ return ss.str();
+ }
+}
+
+// Util for dumping SDL_Joystick info
+std::ostream& operator<<(std::ostream& out, SDL_Joystick* joystick)
+{
+ return out << std::to_string(joystick);
+}
+
+// Util for dumping SDL_GameController info
+std::ostream& operator<<(std::ostream& out, SDL_GameController* controller)
+{
+ return out << std::to_string(controller);
+}
+
+std::string LLGameControl::InputChannel::getLocalName() const
+{
+ // HACK: we hard-code English channel names, but
+ // they should be loaded from localized XML config files.
+
+ if ((mType == LLGameControl::InputChannel::TYPE_AXIS) && (mIndex < NUM_AXES))
+ {
+ return "AXIS_" + std::to_string((U32)mIndex) +
+ (mSign < 0 ? "-" : mSign > 0 ? "+" : "");
+ }
+
+ if ((mType == LLGameControl::InputChannel::TYPE_BUTTON) && (mIndex < NUM_BUTTONS))
+ {
+ return "BUTTON_" + std::to_string((U32)mIndex);
+ }
+
+ return "NONE";
+}
+
+std::string LLGameControl::InputChannel::getRemoteName() const
+{
+ // HACK: we hard-code English channel names, but
+ // they should be loaded from localized XML config files.
+ std::string name = " ";
+ // GAME_CONTROL_AXIS_LEFTX, GAME_CONTROL_BUTTON_A, etc
+ if (mType == LLGameControl::InputChannel::TYPE_AXIS)
+ {
+ switch (mIndex)
+ {
+ case 0:
+ name = "GAME_CONTROL_AXIS_LEFTX";
+ break;
+ case 1:
+ name = "GAME_CONTROL_AXIS_LEFTY";
+ break;
+ case 2:
+ name = "GAME_CONTROL_AXIS_RIGHTX";
+ break;
+ case 3:
+ name = "GAME_CONTROL_AXIS_RIGHTY";
+ break;
+ case 4:
+ name = "GAME_CONTROL_AXIS_PADDLELEFT";
+ break;
+ case 5:
+ name = "GAME_CONTROL_AXIS_PADDLERIGHT";
+ break;
+ default:
+ break;
+ }
+ }
+ else if (mType == LLGameControl::InputChannel::TYPE_BUTTON)
+ {
+ switch(mIndex)
+ {
+ case 0:
+ name = "GAME_CONTROL_BUTTON_A";
+ break;
+ case 1:
+ name = "GAME_CONTROL_BUTTON_B";
+ break;
+ case 2:
+ name = "GAME_CONTROL_BUTTON_X";
+ break;
+ case 3:
+ name = "GAME_CONTROL_BUTTON_Y";
+ break;
+ case 4:
+ name = "GAME_CONTROL_BUTTON_BACK";
+ break;
+ case 5:
+ name = "GAME_CONTROL_BUTTON_GUIDE";
+ break;
+ case 6:
+ name = "GAME_CONTROL_BUTTON_START";
+ break;
+ case 7:
+ name = "GAME_CONTROL_BUTTON_LEFTSTICK";
+ break;
+ case 8:
+ name = "GAME_CONTROL_BUTTON_RIGHTSTICK";
+ break;
+ case 9:
+ name = "GAME_CONTROL_BUTTON_LEFTSHOULDER";
+ break;
+ case 10:
+ name = "GAME_CONTROL_BUTTON_RIGHTSHOULDER";
+ break;
+ case 11:
+ name = "GAME_CONTROL_BUTTON_DPAD_UP";
+ break;
+ case 12:
+ name = "GAME_CONTROL_BUTTON_DPAD_DOWN";
+ break;
+ case 13:
+ name = "GAME_CONTROL_BUTTON_DPAD_LEFT";
+ break;
+ case 14:
+ name = "GAME_CONTROL_BUTTON_DPAD_RIGHT";
+ break;
+ case 15:
+ name = "GAME_CONTROL_BUTTON_MISC1";
+ break;
+ case 16:
+ name = "GAME_CONTROL_BUTTON_PADDLE1";
+ break;
+ case 17:
+ name = "GAME_CONTROL_BUTTON_PADDLE2";
+ break;
+ case 18:
+ name = "GAME_CONTROL_BUTTON_PADDLE3";
+ break;
+ case 19:
+ name = "GAME_CONTROL_BUTTON_PADDLE4";
+ break;
+ case 20:
+ name = "GAME_CONTROL_BUTTON_TOUCHPAD";
+ break;
+ default:
+ break;
+ }
+ }
+ return name;
+}
+
+
+// internal class for managing list of controllers and per-controller state
+class LLGameControllerManager
+{
+public:
+ using ActionToChannelMap = std::map< std::string, LLGameControl::InputChannel >;
+ LLGameControllerManager();
+
+ static void getDefaultMappings(std::vector<std::pair<std::string, LLGameControl::InputChannel>>& agent_channels,
+ std::vector<LLGameControl::InputChannel>& flycam_channels);
+ void getDefaultMappings(std::vector<std::pair<std::string, LLGameControl::InputChannel>>& mappings);
+ void initializeMappingsByDefault();
+ void resetDeviceOptionsToDefaults();
+ void loadDeviceOptionsFromSettings();
+ void saveDeviceOptionsToSettings() const;
+ void setDeviceOptions(const std::string& guid, const LLGameControl::Options& options);
+
+ void addController(SDL_JoystickID id, const std::string& guid, const std::string& name);
+ void removeController(SDL_JoystickID id);
+
+ void onAxis(SDL_JoystickID id, U8 axis, S16 value);
+ void onButton(SDL_JoystickID id, U8 button, bool pressed);
+
+ void clearAllStates();
+
+ void accumulateInternalState();
+ void computeFinalState();
+
+ LLGameControl::ActionNameType getActionNameType(const std::string& action) const;
+ LLGameControl::InputChannel getChannelByAction(const std::string& action) const;
+ LLGameControl::InputChannel getFlycamChannelByAction(const std::string& action) const;
+
+ bool updateActionMap(const std::string& name, LLGameControl::InputChannel channel);
+ U32 computeInternalActionFlags();
+ void getFlycamInputs(std::vector<F32>& inputs_out);
+ void setExternalInput(U32 action_flags, U32 buttons);
+
+ U32 getMappedFlags() const { return mActionTranslator.getMappedFlags(); }
+
+ void clear();
+
+ std::string getAnalogMappings() const;
+ std::string getBinaryMappings() const;
+ std::string getFlycamMappings() const;
+
+ void setAnalogMappings(const std::string& mappings);
+ void setBinaryMappings(const std::string& mappings);
+ void setFlycamMappings(const std::string& mappings);
+
+private:
+ void updateFlycamMap(const std::string& action, LLGameControl::InputChannel channel);
+
+ std::list<LLGameControl::Device> mDevices; // all connected devices
+ using device_it = std::list<LLGameControl::Device>::iterator;
+ device_it findDevice(SDL_JoystickID id)
+ {
+ return std::find_if(mDevices.begin(), mDevices.end(),
+ [id](LLGameControl::Device& device)
+ {
+ return device.getJoystickID() == id;
+ });
+ }
+
+ LLGameControl::State mExternalState;
+ LLGameControlTranslator mActionTranslator;
+ std::map<std::string, LLGameControl::ActionNameType> mActions;
+ std::vector <std::string> mAnalogActions;
+ std::vector <std::string> mBinaryActions;
+ std::vector <std::string> mFlycamActions;
+ std::vector<LLGameControl::InputChannel> mFlycamChannels;
+ std::vector<S32> mAxesAccumulator;
+ U32 mButtonAccumulator { 0 };
+ U32 mLastActiveFlags { 0 };
+ U32 mLastFlycamActionFlags { 0 };
+
+ friend class LLGameControl;
+};
+
+// local globals
+namespace
+{
+ LLGameControl* g_gameControl = nullptr;
+ LLGameControllerManager g_manager;
+
+ // The GameControlInput message is sent via UDP which is lossy.
+ // Since we send the only the list of pressed buttons the receiving
+ // side can compute the difference between subsequent states to
+ // find button-down/button-up events.
+ //
+ // To reduce the likelihood of buttons being stuck "pressed" forever
+ // on the receiving side (for lost final packet) we resend the last
+ // data state. However, to keep the ambient resend bandwidth low we
+ // expand the resend period at a geometric rate.
+ //
+ constexpr U64 MSEC_PER_NSEC = 1000000;
+ constexpr U64 FIRST_RESEND_PERIOD = 100 * MSEC_PER_NSEC;
+ constexpr U64 RESEND_EXPANSION_RATE = 10;
+ LLGameControl::State g_outerState; // from controller devices
+ LLGameControl::State g_innerState; // state from gAgent
+ LLGameControl::State g_finalState; // sum of inner and outer
+ U64 g_lastSend = 0;
+ U64 g_nextResendPeriod = FIRST_RESEND_PERIOD;
+
+ bool g_enabled = false;
+ bool g_sendToServer = false;
+ bool g_controlAgent = false;
+ bool g_translateAgentActions = false;
+ LLGameControl::AgentControlMode g_agentControlMode = LLGameControl::CONTROL_MODE_AVATAR;
+
+ std::map<std::string, std::string> g_deviceOptions;
+
+ std::function<bool(const std::string&)> s_loadBoolean;
+ std::function<void(const std::string&, bool)> s_saveBoolean;
+ std::function<std::string(const std::string&)> s_loadString;
+ std::function<void(const std::string&, const std::string&)> s_saveString;
+ std::function<LLSD(const std::string&)> s_loadObject;
+ std::function<void(const std::string&, LLSD&)> s_saveObject;
+ std::function<void()> s_updateUI;
+
+ std::string SETTING_ENABLE("EnableGameControl");
+ std::string SETTING_SENDTOSERVER("GameControlToServer");
+ std::string SETTING_CONTROLAGENT("GameControlToAgent");
+ std::string SETTING_TRANSLATEACTIONS("AgentToGameControl");
+ std::string SETTING_AGENTCONTROLMODE("AgentControlMode");
+ std::string SETTING_ANALOGMAPPINGS("AnalogChannelMappings");
+ std::string SETTING_BINARYMAPPINGS("BinaryChannelMappings");
+ std::string SETTING_FLYCAMMAPPINGS("FlycamChannelMappings");
+ std::string SETTING_KNOWNCONTROLLERS("KnownGameControllers");
+
+ std::string ENUM_AGENTCONTROLMODE_FLYCAM("flycam");
+ std::string ENUM_AGENTCONTROLMODE_NONE("none");
+
+ LLGameControl::AgentControlMode convertStringToAgentControlMode(const std::string& mode)
+ {
+ if (mode == ENUM_AGENTCONTROLMODE_NONE)
+ return LLGameControl::CONTROL_MODE_NONE;
+ if (mode == ENUM_AGENTCONTROLMODE_FLYCAM)
+ return LLGameControl::CONTROL_MODE_FLYCAM;
+ // All values except NONE and FLYCAM are treated as default (AVATAR)
+ return LLGameControl::CONTROL_MODE_AVATAR;
+ }
+
+ const std::string& convertAgentControlModeToString(LLGameControl::AgentControlMode mode)
+ {
+ if (mode == LLGameControl::CONTROL_MODE_NONE)
+ return ENUM_AGENTCONTROLMODE_NONE;
+ if (mode == LLGameControl::CONTROL_MODE_FLYCAM)
+ return ENUM_AGENTCONTROLMODE_FLYCAM;
+ // All values except NONE and FLYCAM are treated as default (AVATAR)
+ return LLStringUtil::null;
+ }
+
+ const std::string& getDeviceOptionsString(const std::string& guid)
+ {
+ const auto& it = g_deviceOptions.find(guid);
+ return it == g_deviceOptions.end() ? LLStringUtil::null : it->second;
+ }
+}
+
+LLGameControl::~LLGameControl()
+{
+ terminate();
+}
+
+LLGameControl::State::State()
+: mButtons(0)
+{
+ mAxes.resize(NUM_AXES, 0);
+ mPrevAxes.resize(NUM_AXES, 0);
+}
+
+void LLGameControl::State::clear()
+{
+ std::fill(mAxes.begin(), mAxes.end(), 0);
+
+ // DO NOT clear mPrevAxes because those are managed by external logic.
+ //std::fill(mPrevAxes.begin(), mPrevAxes.end(), 0);
+
+ mButtons = 0;
+}
+
+bool LLGameControl::State::onButton(U8 button, bool pressed)
+{
+ U32 old_buttons = mButtons;
+ if (button < NUM_BUTTONS)
+ {
+ if (pressed)
+ {
+ mButtons |= (0x01 << button);
+ }
+ else
+ {
+ mButtons &= ~(0x01 << button);
+ }
+ }
+ return mButtons != old_buttons;
+}
+
+LLGameControl::Device::Device(int joystickID, const std::string& guid, const std::string& name)
+: mJoystickID(joystickID)
+, mGUID(guid)
+, mName(name)
+{
+}
+
+LLGameControl::Options::Options()
+{
+ mAxisOptions.resize(NUM_AXES);
+ mAxisMap.resize(NUM_AXES);
+ mButtonMap.resize(NUM_BUTTONS);
+ resetToDefaults();
+}
+
+void LLGameControl::Options::resetToDefaults()
+{
+ for (size_t i = 0; i < NUM_AXES; ++i)
+ {
+ mAxisOptions[i].resetToDefaults();
+ mAxisMap[i] = (U8)i;
+ }
+ for (size_t i = 0; i < NUM_BUTTONS; ++i)
+ {
+ mButtonMap[i] = (U8)i;
+ }
+}
+
+U8 LLGameControl::Options::mapAxis(U8 axis) const
+{
+ if (axis >= NUM_AXES)
+ {
+ LL_WARNS("SDL2") << "Invalid input axis: " << axis << LL_ENDL;
+ return axis;
+ }
+ return mAxisMap[axis];
+}
+
+U8 LLGameControl::Options::mapButton(U8 button) const
+{
+ if (button >= NUM_BUTTONS)
+ {
+ LL_WARNS("SDL2") << "Invalid input button: " << button << LL_ENDL;
+ return button;
+ }
+ return mButtonMap[button];
+}
+
+S16 LLGameControl::Options::fixAxisValue(U8 axis, S16 value) const
+{
+ if (axis >= NUM_AXES)
+ {
+ LL_WARNS("SDL2") << "Invalid input axis: " << axis << LL_ENDL;
+ }
+ else
+ {
+ value = mAxisOptions[axis].computeModifiedValue(value);
+ }
+ return value;
+}
+
+std::string LLGameControl::Options::AxisOptions::saveToString() const
+{
+ std::list<std::string> options;
+
+ if (mMultiplier == -1)
+ {
+ options.push_back("invert:1");
+ }
+ if (mDeadZone)
+ {
+ options.push_back(llformat("dead_zone:%u", mDeadZone));
+ }
+ if (mOffset)
+ {
+ options.push_back(llformat("offset:%d", mOffset));
+ }
+
+ std::string result = LLStringUtil::join(options);
+
+ return result.empty() ? result : "{" + result + "}";
+}
+
+// Parse string "{key:value,key:{key:value,key:value}}" and fill the map
+static bool parse(std::map<std::string, std::string>& result, std::string source)
+{
+ result.clear();
+
+ LLStringUtil::trim(source);
+ if (source.empty())
+ return true;
+
+ if (source.front() != '{' || source.back() != '}')
+ return false;
+
+ source = source.substr(1, source.size() - 2);
+
+ LLStringUtil::trim(source);
+ if (source.empty())
+ return true;
+
+ // Split the string "key:value" and add the pair to the map
+ auto split = [&](const std::string& pair) -> bool
+ {
+ size_t pos = pair.find(':');
+ if (!pos || pos == std::string::npos)
+ return false;
+ std::string key = pair.substr(0, pos);
+ std::string value = pair.substr(pos + 1);
+ LLStringUtil::trim(key);
+ LLStringUtil::trim(value);
+ if (key.empty() || value.empty())
+ return false;
+ result[key] = value;
+ return true;
+ };
+
+ U32 depth = 0;
+ size_t offset = 0;
+ while (true)
+ {
+ size_t pos = source.find_first_of(depth ? "{}" : ",{}", offset);
+ if (pos == std::string::npos)
+ {
+ return !depth && split(source);
+ }
+ if (source[pos] == ',')
+ {
+ if (!split(source.substr(0, pos)))
+ return false;
+ source = source.substr(pos + 1);
+ offset = 0;
+ }
+ else if (source[pos] == '{')
+ {
+ depth++;
+ offset = pos + 1;
+ }
+ else if (depth) // Assume '}' here
+ {
+ depth--;
+ offset = pos + 1;
+ }
+ else
+ {
+ return false; // Extra '}' found
+ }
+ }
+
+ return true;
+}
+
+void LLGameControl::Options::AxisOptions::loadFromString(std::string options)
+{
+ resetToDefaults();
+
+ if (options.empty())
+ return;
+
+ std::map<std::string, std::string> pairs;
+ if (!parse(pairs, options))
+ {
+ LL_WARNS("SDL2") << "Invalid axis options: '" << options << "'" << LL_ENDL;
+ }
+
+ mMultiplier = 1;
+ std::string invert = pairs["invert"];
+ if (!invert.empty())
+ {
+ if (invert == "1")
+ {
+ mMultiplier = -1;
+ }
+ else
+ {
+ LL_WARNS("SDL2") << "Invalid invert value: '" << invert << "'" << LL_ENDL;
+ }
+ }
+
+ std::string dead_zone = pairs["dead_zone"];
+ if (!dead_zone.empty())
+ {
+ size_t number = std::stoull(dead_zone);
+ if (number <= MAX_AXIS_DEAD_ZONE && std::to_string(number) == dead_zone)
+ {
+ mDeadZone = (U16)number;
+ }
+ else
+ {
+ LL_WARNS("SDL2") << "Invalid dead_zone value: '" << dead_zone << "'" << LL_ENDL;
+ }
+ }
+
+ std::string offset = pairs["offset"];
+ if (!offset.empty())
+ {
+ S32 number = std::stoi(offset);
+ if (abs(number) > MAX_AXIS_OFFSET || std::to_string(number) != offset)
+ {
+ LL_WARNS("SDL2") << "Invalid offset value: '" << offset << "'" << LL_ENDL;
+ }
+ else
+ {
+ mOffset = (S16)number;
+ }
+ }
+}
+
+std::string LLGameControl::Options::saveToString(const std::string& name, bool force_empty) const
+{
+ return stringifyDeviceOptions(name, mAxisOptions, mAxisMap, mButtonMap, force_empty);
+}
+
+bool LLGameControl::Options::loadFromString(std::string& name, std::string options)
+{
+ resetToDefaults();
+ return LLGameControl::parseDeviceOptions(options, name, mAxisOptions, mAxisMap, mButtonMap);
+}
+
+bool LLGameControl::Options::loadFromString(std::string options)
+{
+ resetToDefaults();
+ std::string dummy_name;
+ return LLGameControl::parseDeviceOptions(options, dummy_name, mAxisOptions, mAxisMap, mButtonMap);
+}
+
+LLGameControllerManager::LLGameControllerManager()
+{
+ mAxesAccumulator.resize(LLGameControl::NUM_AXES, 0);
+
+ mAnalogActions = { "push", "slide", "jump", "turn", "look" };
+ mBinaryActions = { "toggle_run", "toggle_fly", "toggle_flycam", "stop" };
+ mFlycamActions = { "advance", "pan", "rise", "pitch", "yaw", "zoom" };
+
+ // Collect all known action names with their types in one container
+ for (const std::string& name : mAnalogActions)
+ {
+ mActions[name] = LLGameControl::ACTION_NAME_ANALOG;
+ mActions[name + "+"] = LLGameControl::ACTION_NAME_ANALOG_POS;
+ mActions[name + "-"] = LLGameControl::ACTION_NAME_ANALOG_NEG;
+ }
+ for (const std::string& name : mBinaryActions)
+ {
+ mActions[name] = LLGameControl::ACTION_NAME_BINARY;
+ }
+ for (const std::string& name : mFlycamActions)
+ {
+ mActions[name] = LLGameControl::ACTION_NAME_FLYCAM;
+ }
+
+ // Here we build an invariant map between the named agent actions
+ // and control bit sent to the server. This map will be used,
+ // in combination with the action->InputChannel map below,
+ // to maintain an inverse map from control bit masks to GameControl data.
+ LLGameControlTranslator::ActionToMaskMap actionMasks =
+ {
+ // Analog actions (pairs)
+ { "push+", AGENT_CONTROL_AT_POS | AGENT_CONTROL_FAST_AT },
+ { "push-", AGENT_CONTROL_AT_NEG | AGENT_CONTROL_FAST_AT },
+ { "slide+", AGENT_CONTROL_LEFT_POS | AGENT_CONTROL_FAST_LEFT },
+ { "slide-", AGENT_CONTROL_LEFT_NEG | AGENT_CONTROL_FAST_LEFT },
+ { "jump+", AGENT_CONTROL_UP_POS | AGENT_CONTROL_FAST_UP },
+ { "jump-", AGENT_CONTROL_UP_NEG | AGENT_CONTROL_FAST_UP },
+ { "turn+", AGENT_CONTROL_YAW_POS },
+ { "turn-", AGENT_CONTROL_YAW_NEG },
+ { "look+", AGENT_CONTROL_PITCH_POS },
+ { "look-", AGENT_CONTROL_PITCH_NEG },
+ // Button actions
+ { "stop", AGENT_CONTROL_STOP },
+ // These are HACKs. We borrow some AGENT_CONTROL bits for "unrelated" features.
+ // Not a problem because these bits are only used internally.
+ { "toggle_run", AGENT_CONTROL_NUDGE_AT_POS }, // HACK
+ { "toggle_fly", AGENT_CONTROL_FLY }, // HACK
+ { "toggle_flycam", AGENT_CONTROL_NUDGE_AT_NEG }, // HACK
+ };
+ mActionTranslator.setAvailableActionMasks(actionMasks);
+
+ initializeMappingsByDefault();
+}
+
+// static
+void LLGameControllerManager::getDefaultMappings(
+ std::vector<std::pair<std::string, LLGameControl::InputChannel>>& agent_channels,
+ std::vector<LLGameControl::InputChannel>& flycam_channels)
+{
+ // Here we build a list of pairs between named agent actions and
+ // GameControl channels. Note: we only supply the non-signed names
+ // (e.g. "push" instead of "push+" and "push-") because mActionTranslator
+ // automatially expands action names as necessary.
+ using type = LLGameControl::InputChannel::Type;
+ agent_channels =
+ {
+ // Analog actions (associated by common name - without '+' or '-')
+ { "push", { type::TYPE_AXIS, LLGameControl::AXIS_LEFTY, 1 } },
+ { "slide", { type::TYPE_AXIS, LLGameControl::AXIS_LEFTX, 1 } },
+ { "jump", { type::TYPE_AXIS, LLGameControl::AXIS_TRIGGERLEFT, 1 } },
+ { "turn", { type::TYPE_AXIS, LLGameControl::AXIS_RIGHTX, 1 } },
+ { "look", { type::TYPE_AXIS, LLGameControl::AXIS_RIGHTY, 1 } },
+ // Button actions (associated by name)
+ { "toggle_run", { type::TYPE_BUTTON, LLGameControl::BUTTON_LEFTSHOULDER } },
+ { "toggle_fly", { type::TYPE_BUTTON, LLGameControl::BUTTON_DPAD_UP } },
+ { "toggle_flycam", { type::TYPE_BUTTON, LLGameControl::BUTTON_RIGHTSHOULDER } },
+ { "stop", { type::TYPE_BUTTON, LLGameControl::BUTTON_LEFTSTICK } }
+ };
+
+ // Flycam actions don't need bitwise translation, so we maintain the map
+ // of channels here directly rather than using an LLGameControlTranslator.
+ flycam_channels =
+ {
+ // Flycam actions (associated just by an order index)
+ { type::TYPE_AXIS, LLGameControl::AXIS_LEFTY, 1 }, // advance
+ { type::TYPE_AXIS, LLGameControl::AXIS_LEFTX, 1 }, // pan
+ { type::TYPE_AXIS, LLGameControl::AXIS_TRIGGERRIGHT, 1 }, // rise
+ { type::TYPE_AXIS, LLGameControl::AXIS_RIGHTY, -1 }, // pitch
+ { type::TYPE_AXIS, LLGameControl::AXIS_RIGHTX, 1 }, // yaw
+ { type::TYPE_NONE, 0 } // zoom
+ };
+}
+
+void LLGameControllerManager::getDefaultMappings(std::vector<std::pair<std::string, LLGameControl::InputChannel>>& mappings)
+{
+ // Join two different data structures into the one
+ std::vector<LLGameControl::InputChannel> flycam_channels;
+ getDefaultMappings(mappings, flycam_channels);
+ for (size_t i = 0; i < flycam_channels.size(); ++i)
+ {
+ mappings.emplace_back(mFlycamActions[i], flycam_channels[i]);
+ }
+}
+
+void LLGameControllerManager::initializeMappingsByDefault()
+{
+ std::vector<std::pair<std::string, LLGameControl::InputChannel>> agent_channels;
+ getDefaultMappings(agent_channels, mFlycamChannels);
+ mActionTranslator.setMappings(agent_channels);
+}
+
+void LLGameControllerManager::resetDeviceOptionsToDefaults()
+{
+ for (LLGameControl::Device& device : mDevices)
+ {
+ device.resetOptionsToDefaults();
+ }
+}
+
+void LLGameControllerManager::loadDeviceOptionsFromSettings()
+{
+ for (LLGameControl::Device& device : mDevices)
+ {
+ device.loadOptionsFromString(getDeviceOptionsString(device.getGUID()));
+ }
+}
+
+void LLGameControllerManager::saveDeviceOptionsToSettings() const
+{
+ for (const LLGameControl::Device& device : mDevices)
+ {
+ std::string options = device.saveOptionsToString();
+ if (options.empty())
+ {
+ g_deviceOptions.erase(device.getGUID());
+ }
+ else
+ {
+ g_deviceOptions[device.getGUID()] = options;
+ }
+ }
+}
+
+void LLGameControllerManager::setDeviceOptions(const std::string& guid, const LLGameControl::Options& options)
+{
+ // find Device by name
+ for (LLGameControl::Device& device : mDevices)
+ {
+ if (device.getGUID() == guid)
+ {
+ device.mOptions = options;
+ return;
+ }
+ }
+}
+
+void LLGameControllerManager::addController(SDL_JoystickID id, const std::string& guid, const std::string& name)
+{
+ llassert(id >= 0);
+
+ for (const LLGameControl::Device& device : mDevices)
+ {
+ if (device.getJoystickID() == id)
+ {
+ LL_WARNS("SDL2") << "device with id=" << id << " was already added"
+ << ", guid: '" << device.getGUID() << "'"
+ << ", name: '" << device.getName() << "'"
+ << LL_ENDL;
+ return;
+ }
+ }
+
+ mDevices.emplace_back(id, guid, name).loadOptionsFromString(getDeviceOptionsString(guid));
+}
+
+void LLGameControllerManager::removeController(SDL_JoystickID id)
+{
+ LL_INFOS("SDL2") << "joystick id: " << id << LL_ENDL;
+
+ mDevices.remove_if([id](LLGameControl::Device& device)
+ {
+ return device.getJoystickID() == id;
+ });
+}
+
+void LLGameControllerManager::onAxis(SDL_JoystickID id, U8 axis, S16 value)
+{
+ device_it it = findDevice(id);
+ if (it == mDevices.end())
+ {
+ LL_WARNS("SDL2") << "Unknown device: joystick=0x" << std::hex << id << std::dec
+ << " axis=" << (S32)axis
+ << " value=" << (S32)value << LL_ENDL;
+ return;
+ }
+
+ // Map axis using device-specific settings
+ // or leave the value unchanged
+ U8 mapped_axis = it->mOptions.mapAxis(axis);
+ if (mapped_axis != axis)
+ {
+ LL_DEBUGS("SDL2") << "Axis mapped: joystick=0x" << std::hex << id << std::dec
+ << " input axis i=" << (S32)axis
+ << " mapped axis i=" << (S32)mapped_axis << LL_ENDL;
+ axis = mapped_axis;
+ }
+
+ if (axis >= LLGameControl::NUM_AXES)
+ {
+ LL_WARNS("SDL2") << "Unknown axis: joystick=0x" << std::hex << id << std::dec
+ << " axis=" << (S32)(axis)
+ << " value=" << (S32)(value) << LL_ENDL;
+ return;
+ }
+
+ // Fix value using device-specific settings
+ // or leave the value unchanged
+ S16 fixed_value = it->mOptions.fixAxisValue(axis, value);
+ if (fixed_value != value)
+ {
+ LL_DEBUGS("SDL2") << "Value fixed: joystick=0x" << std::hex << id << std::dec
+ << " axis i=" << (S32)axis
+ << " input value=" << (S32)value
+ << " fixed value=" << (S32)fixed_value << LL_ENDL;
+ value = fixed_value;
+ }
+
+ // Note: the RAW analog joysticks provide NEGATIVE X,Y values for LEFT,FORWARD
+ // whereas those directions are actually POSITIVE in SL's local right-handed
+ // reference frame. Therefore we implicitly negate those axes here where
+ // they are extracted from SDL, before being used anywhere.
+ if (axis < SDL_CONTROLLER_AXIS_TRIGGERLEFT)
+ {
+ // Note: S16 value is in range [-32768, 32767] which means
+ // the negative range has an extra possible value. We need
+ // to add (or subtract) one during negation.
+ if (value < 0)
+ {
+ value = - (value + 1);
+ }
+ else if (value > 0)
+ {
+ value = (-value) - 1;
+ }
+ }
+
+ LL_DEBUGS("SDL2") << "joystick=0x" << std::hex << id << std::dec
+ << " axis=" << (S32)(axis)
+ << " value=" << (S32)(value) << LL_ENDL;
+ it->mState.mAxes[axis] = value;
+}
+
+void LLGameControllerManager::onButton(SDL_JoystickID id, U8 button, bool pressed)
+{
+ device_it it = findDevice(id);
+ if (it == mDevices.end())
+ {
+ LL_WARNS("SDL2") << "Unknown device: joystick=0x" << std::hex << id << std::dec
+ << " button i=" << (S32)button << LL_ENDL;
+ return;
+ }
+
+ // Map button using device-specific settings
+ // or leave the value unchanged
+ U8 mapped_button = it->mOptions.mapButton(button);
+ if (mapped_button != button)
+ {
+ LL_DEBUGS("SDL2") << "Button mapped: joystick=0x" << std::hex << id << std::dec
+ << " input button i=" << (S32)button
+ << " mapped button i=" << (S32)mapped_button << LL_ENDL;
+ button = mapped_button;
+ }
+
+ if (button >= LLGameControl::NUM_BUTTONS)
+ {
+ LL_WARNS("SDL2") << "Unknown button: joystick=0x" << std::hex << id << std::dec
+ << " button i=" << (S32)button << LL_ENDL;
+ return;
+ }
+
+ if (it->mState.onButton(button, pressed))
+ {
+ LL_DEBUGS("SDL2") << "joystick=0x" << std::hex << id << std::dec
+ << " button i=" << (S32)button
+ << " pressed=" << pressed << LL_ENDL;
+ }
+}
+
+void LLGameControllerManager::clearAllStates()
+{
+ for (auto& device : mDevices)
+ {
+ device.mState.clear();
+ }
+ mExternalState.clear();
+ mLastActiveFlags = 0;
+ mLastFlycamActionFlags = 0;
+}
+
+void LLGameControllerManager::accumulateInternalState()
+{
+ // clear the old state
+ std::fill(mAxesAccumulator.begin(), mAxesAccumulator.end(), 0);
+ mButtonAccumulator = 0;
+
+ // accumulate the controllers
+ for (const auto& device : mDevices)
+ {
+ mButtonAccumulator |= device.mState.mButtons;
+ for (size_t i = 0; i < LLGameControl::NUM_AXES; ++i)
+ {
+ // Note: we don't bother to clamp the axes yet
+ // because at this stage we haven't yet accumulated the "inner" state.
+ mAxesAccumulator[i] += (S32)device.mState.mAxes[i];
+ }
+ }
+}
+
+void LLGameControllerManager::computeFinalState()
+{
+ // We assume accumulateInternalState() has already been called and we will
+ // finish by accumulating "external" state (if enabled)
+ U32 old_buttons = g_finalState.mButtons;
+ g_finalState.mButtons = mButtonAccumulator;
+ if (g_translateAgentActions)
+ {
+ // accumulate from mExternalState
+ g_finalState.mButtons |= mExternalState.mButtons;
+ }
+ if (old_buttons != g_finalState.mButtons)
+ {
+ g_nextResendPeriod = 0; // packet needs to go out ASAP
+ }
+
+ // clamp the accumulated axes
+ for (size_t i = 0; i < LLGameControl::NUM_AXES; ++i)
+ {
+ S32 axis = mAxesAccumulator[i];
+ if (g_translateAgentActions)
+ {
+ // Note: we accumulate mExternalState onto local 'axis' variable
+ // rather than onto mAxisAccumulator[i] because the internal
+ // accumulated value is also used to drive the Flycam, and
+ // we don't want any external state leaking into that value.
+ axis += (S32)mExternalState.mAxes[i];
+ }
+ axis = (S16)std::min(std::max(axis, -32768), 32767);
+ // check for change
+ if (g_finalState.mAxes[i] != axis)
+ {
+ // When axis changes we explicitly update the corresponding prevAxis
+ // prior to storing axis. The only other place where prevAxis
+ // is updated in updateResendPeriod() which is explicitly called after
+ // a packet is sent. The result is: unchanged axes are included in
+ // first resend but not later ones.
+ g_finalState.mPrevAxes[i] = g_finalState.mAxes[i];
+ g_finalState.mAxes[i] = axis;
+ g_nextResendPeriod = 0; // packet needs to go out ASAP
+ }
+ }
+}
+
+LLGameControl::ActionNameType LLGameControllerManager::getActionNameType(const std::string& action) const
+{
+ auto it = mActions.find(action);
+ return it == mActions.end() ? LLGameControl::ACTION_NAME_UNKNOWN : it->second;
+}
+
+LLGameControl::InputChannel LLGameControllerManager::getChannelByAction(const std::string& action) const
+{
+ LLGameControl::InputChannel channel;
+ auto action_it = mActions.find(action);
+ if (action_it != mActions.end())
+ {
+ if (action_it->second == LLGameControl::ACTION_NAME_FLYCAM)
+ {
+ channel = getFlycamChannelByAction(action);
+ }
+ else
+ {
+ channel = mActionTranslator.getChannelByAction(action);
+ }
+ }
+ return channel;
+}
+
+LLGameControl::InputChannel LLGameControllerManager::getFlycamChannelByAction(const std::string& action) const
+{
+ auto flycam_it = std::find(mFlycamActions.begin(), mFlycamActions.end(), action);
+ llassert(flycam_it != mFlycamActions.end());
+ std::ptrdiff_t index = std::distance(mFlycamActions.begin(), flycam_it);
+ return mFlycamChannels[(std::size_t)index];
+}
+
+// Common implementation of getAnalogMappings(), getBinaryMappings() and getFlycamMappings()
+static std::string getMappings(const std::vector<std::string>& actions, LLGameControl::InputChannel::Type type,
+ std::function<LLGameControl::InputChannel(const std::string& action)> getChannel)
+{
+ std::list<std::string> mappings;
+
+ std::vector<std::pair<std::string, LLGameControl::InputChannel>> default_mappings;
+ LLGameControl::getDefaultMappings(default_mappings);
+
+ // Walk through the all known actions of the chosen type
+ for (const std::string& action : actions)
+ {
+ LLGameControl::InputChannel channel = getChannel(action);
+ // Only channels of the expected type should be stored
+ if (channel.mType == type)
+ {
+ bool mapping_differs = false;
+ for (const auto& pair : default_mappings)
+ {
+ if (pair.first == action)
+ {
+ mapping_differs = !channel.isEqual(pair.second);
+ break;
+ }
+ }
+ // Only mappings different from the default should be stored
+ if (mapping_differs)
+ {
+ mappings.push_back(action + ":" + channel.getLocalName());
+ }
+ }
+ }
+
+ std::string result = LLStringUtil::join(mappings);
+
+ return result;
+}
+
+std::string LLGameControllerManager::getAnalogMappings() const
+{
+ return getMappings(mAnalogActions, LLGameControl::InputChannel::TYPE_AXIS,
+ [&](const std::string& action) -> LLGameControl::InputChannel
+ {
+ return mActionTranslator.getChannelByAction(action + "+");
+ });
+}
+
+std::string LLGameControllerManager::getBinaryMappings() const
+{
+ return getMappings(mBinaryActions, LLGameControl::InputChannel::TYPE_BUTTON,
+ [&](const std::string& action) -> LLGameControl::InputChannel
+ {
+ return mActionTranslator.getChannelByAction(action);
+ });
+}
+
+std::string LLGameControllerManager::getFlycamMappings() const
+{
+ return getMappings(mFlycamActions, LLGameControl::InputChannel::TYPE_AXIS,
+ [&](const std::string& action) -> LLGameControl::InputChannel
+ {
+ return getFlycamChannelByAction(action);
+ });
+}
+
+// Common implementation of setAnalogMappings(), setBinaryMappings() and setFlycamMappings()
+static void setMappings(const std::string& mappings,
+ const std::vector<std::string>& actions, LLGameControl::InputChannel::Type type,
+ std::function<void(const std::string& action, LLGameControl::InputChannel channel)> updateMap)
+{
+ if (mappings.empty())
+ return;
+
+ std::map<std::string, std::string> pairs;
+ LLStringOps::splitString(mappings, ',', [&](const std::string& mapping)
+ {
+ std::size_t pos = mapping.find(':');
+ if (pos > 0 && pos != std::string::npos)
+ {
+ pairs[mapping.substr(0, pos)] = mapping.substr(pos + 1);
+ }
+ });
+
+ static const LLGameControl::InputChannel channelNone;
+
+ for (const std::string& action : actions)
+ {
+ auto it = pairs.find(action);
+ if (it != pairs.end())
+ {
+ LLGameControl::InputChannel channel = LLGameControl::getChannelByName(it->second);
+ if (channel.isNone() || channel.mType == type)
+ {
+ updateMap(action, channel);
+ continue;
+ }
+ }
+ updateMap(action, channelNone);
+ }
+}
+
+void LLGameControllerManager::setAnalogMappings(const std::string& mappings)
+{
+ setMappings(mappings, mAnalogActions, LLGameControl::InputChannel::TYPE_AXIS,
+ [&](const std::string& action, LLGameControl::InputChannel channel)
+ {
+ mActionTranslator.updateMap(action, channel);
+ });
+}
+
+void LLGameControllerManager::setBinaryMappings(const std::string& mappings)
+{
+ setMappings(mappings, mBinaryActions, LLGameControl::InputChannel::TYPE_BUTTON,
+ [&](const std::string& action, LLGameControl::InputChannel channel)
+ {
+ mActionTranslator.updateMap(action, channel);
+ });
+}
+
+void LLGameControllerManager::setFlycamMappings(const std::string& mappings)
+{
+ setMappings(mappings, mFlycamActions, LLGameControl::InputChannel::TYPE_AXIS,
+ [&](const std::string& action, LLGameControl::InputChannel channel)
+ {
+ updateFlycamMap(action, channel);
+ });
+}
+
+bool LLGameControllerManager::updateActionMap(const std::string& action, LLGameControl::InputChannel channel)
+{
+ auto action_it = mActions.find(action);
+ if (action_it == mActions.end())
+ {
+ LL_WARNS("SDL2") << "unmappable action='" << action << "'" << LL_ENDL;
+ return false;
+ }
+
+ if (action_it->second == LLGameControl::ACTION_NAME_FLYCAM)
+ {
+ updateFlycamMap(action, channel);
+ }
+ else
+ {
+ mActionTranslator.updateMap(action, channel);
+ }
+ return true;
+}
+
+void LLGameControllerManager::updateFlycamMap(const std::string& action, LLGameControl::InputChannel channel)
+{
+ auto flycam_it = std::find(mFlycamActions.begin(), mFlycamActions.end(), action);
+ llassert(flycam_it != mFlycamActions.end());
+ std::ptrdiff_t index = std::distance(mFlycamActions.begin(), flycam_it);
+ llassert(index >= 0 && (std::size_t)index < mFlycamChannels.size());
+ mFlycamChannels[(std::size_t)index] = channel;
+}
+
+U32 LLGameControllerManager::computeInternalActionFlags()
+{
+ // add up device inputs
+ accumulateInternalState();
+ if (g_controlAgent)
+ {
+ return mActionTranslator.computeFlagsFromState(mAxesAccumulator, mButtonAccumulator);
+ }
+ return 0;
+}
+
+void LLGameControllerManager::getFlycamInputs(std::vector<F32>& inputs)
+{
+ // The inputs are packed in the same order as they exist in mFlycamChannels:
+ //
+ // advance
+ // pan
+ // rise
+ // pitch
+ // yaw
+ // zoom
+ //
+ for (const auto& channel: mFlycamChannels)
+ {
+ S16 axis;
+ if (channel.mIndex == LLGameControl::AXIS_TRIGGERLEFT ||
+ channel.mIndex == LLGameControl::AXIS_TRIGGERRIGHT)
+ {
+ // TIED TRIGGER HACK: we assume the two triggers are paired together
+ S32 total_axis = mAxesAccumulator[LLGameControl::AXIS_TRIGGERLEFT]
+ - mAxesAccumulator[LLGameControl::AXIS_TRIGGERRIGHT];
+ if (channel.mIndex == LLGameControl::AXIS_TRIGGERRIGHT)
+ {
+ // negate previous math when TRIGGERRIGHT is positive channel
+ total_axis *= -1;
+ }
+ axis = S16(std::min(std::max(total_axis, -32768), 32767));
+ }
+ else
+ {
+ axis = S16(std::min(std::max(mAxesAccumulator[channel.mIndex], -32768), 32767));
+ }
+ // value arrives as S16 in range [-32768, 32767]
+ // so we scale positive and negative values by slightly different factors
+ // to try to map it to [-1, 1]
+ F32 input = F32(axis) / ((axis > 0.0f) ? 32767 : 32768) * channel.mSign;
+ inputs.push_back(input);
+ }
+}
+
+void LLGameControllerManager::setExternalInput(U32 action_flags, U32 buttons)
+{
+ if (g_translateAgentActions)
+ {
+ // HACK: these are the bits we can safely translate from control flags to GameControl
+ // Extracting LLGameControl::InputChannels that are mapped to other bits is a WIP.
+ // TODO: translate other bits to GameControl, which might require measure of gAgent
+ // state changes (e.g. sitting <--> standing, flying <--> not-flying, etc)
+ const U32 BITS_OF_INTEREST =
+ AGENT_CONTROL_AT_POS | AGENT_CONTROL_AT_NEG
+ | AGENT_CONTROL_LEFT_POS | AGENT_CONTROL_LEFT_NEG
+ | AGENT_CONTROL_UP_POS | AGENT_CONTROL_UP_NEG
+ | AGENT_CONTROL_YAW_POS | AGENT_CONTROL_YAW_NEG
+ | AGENT_CONTROL_PITCH_POS | AGENT_CONTROL_PITCH_NEG
+ | AGENT_CONTROL_STOP
+ | AGENT_CONTROL_FAST_AT
+ | AGENT_CONTROL_FAST_LEFT
+ | AGENT_CONTROL_FAST_UP;
+ action_flags &= BITS_OF_INTEREST;
+
+ U32 active_flags = action_flags & mActionTranslator.getMappedFlags();
+ if (active_flags != mLastActiveFlags)
+ {
+ mLastActiveFlags = active_flags;
+ mExternalState = mActionTranslator.computeStateFromFlags(action_flags);
+ mExternalState.mButtons |= buttons;
+ }
+ else
+ {
+ mExternalState.mButtons = buttons;
+ }
+ }
+ else
+ {
+ mExternalState.mButtons = buttons;
+ }
+}
+
+void LLGameControllerManager::clear()
+{
+ mDevices.clear();
+}
+
+U64 get_now_nsec()
+{
+ std::chrono::time_point<std::chrono::steady_clock> t0;
+ return (std::chrono::steady_clock::now() - t0).count();
+}
+
+void onJoystickDeviceAdded(const SDL_Event& event)
+{
+ SDL_JoystickGUID guid(SDL_JoystickGetDeviceGUID(event.cdevice.which));
+ SDL_JoystickType type(SDL_JoystickGetDeviceType(event.cdevice.which));
+ std::string name(ll_safe_string(SDL_JoystickNameForIndex(event.cdevice.which)));
+
+ LL_INFOS("SDL2") << "joystick {id:" << event.cdevice.which
+ << ",guid:'" << guid << "'"
+ << ",type:'" << type << "'"
+ << ",name:'" << name << "'"
+ << "}" << LL_ENDL;
+
+ if (SDL_Joystick* joystick = SDL_JoystickOpen(event.cdevice.which))
+ {
+ LL_INFOS("SDL2") << "joystick " << joystick << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("SDL2") << "Can't open joystick: " << SDL_GetError() << LL_ENDL;
+ }
+}
+
+void onJoystickDeviceRemoved(const SDL_Event& event)
+{
+ LL_INFOS("SDL2") << "joystick id: " << event.cdevice.which << LL_ENDL;
+}
+
+void onControllerDeviceAdded(const SDL_Event& event)
+{
+ std::string guid(std::to_string(SDL_JoystickGetDeviceGUID(event.cdevice.which)));
+ SDL_GameControllerType type(SDL_GameControllerTypeForIndex(event.cdevice.which));
+ std::string name(ll_safe_string(SDL_GameControllerNameForIndex(event.cdevice.which)));
+
+ LL_INFOS("SDL2") << "controller {id:" << event.cdevice.which
+ << ",guid:'" << guid << "'"
+ << ",type:'" << type << "'"
+ << ",name:'" << name << "'"
+ << "}" << LL_ENDL;
+
+ SDL_JoystickID id = SDL_JoystickGetDeviceInstanceID(event.cdevice.which);
+ if (id < 0)
+ {
+ LL_WARNS("SDL2") << "Can't get device instance ID: " << SDL_GetError() << LL_ENDL;
+ return;
+ }
+
+ SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which);
+ if (!controller)
+ {
+ LL_WARNS("SDL2") << "Can't open game controller: " << SDL_GetError() << LL_ENDL;
+ return;
+ }
+
+ g_manager.addController(id, guid, name);
+
+ // this event could happen while the preferences UI is open
+ // in which case we need to force it to update
+ s_updateUI();
+}
+
+void onControllerDeviceRemoved(const SDL_Event& event)
+{
+ LL_INFOS("SDL2") << "joystick id=" << event.cdevice.which << LL_ENDL;
+
+ SDL_JoystickID id = event.cdevice.which;
+ g_manager.removeController(id);
+
+ // this event could happen while the preferences UI is open
+ // in which case we need to force it to update
+ s_updateUI();
+}
+
+void onControllerButton(const SDL_Event& event)
+{
+ g_manager.onButton(event.cbutton.which, event.cbutton.button, event.cbutton.state == SDL_PRESSED);
+}
+
+void onControllerAxis(const SDL_Event& event)
+{
+ LL_DEBUGS("SDL2") << "joystick=0x" << std::hex << event.caxis.which << std::dec
+ << " axis=" << (S32)(event.caxis.axis)
+ << " value=" << (S32)(event.caxis.value) << LL_ENDL;
+ g_manager.onAxis(event.caxis.which, event.caxis.axis, event.caxis.value);
+}
+
+// static
+bool LLGameControl::isEnabled()
+{
+ return g_enabled;
+}
+
+void LLGameControl::setEnabled(bool enable)
+{
+ if (enable != g_enabled)
+ {
+ g_enabled = enable;
+ s_saveBoolean(SETTING_ENABLE, g_enabled);
+ }
+}
+
+// static
+bool LLGameControl::isInitialized()
+{
+ return g_gameControl != nullptr;
+}
+
+// static
+// TODO: find a cleaner way to provide callbacks to LLGameControl
+void LLGameControl::init(const std::string& gamecontrollerdb_path,
+ std::function<bool(const std::string&)> loadBoolean,
+ std::function<void(const std::string&, bool)> saveBoolean,
+ std::function<std::string(const std::string&)> loadString,
+ std::function<void(const std::string&, const std::string&)> saveString,
+ std::function<LLSD(const std::string&)> loadObject,
+ std::function<void(const std::string&, const LLSD&)> saveObject,
+ std::function<void()> updateUI)
+{
+ if (g_gameControl)
+ return;
+
+ llassert(loadBoolean);
+ llassert(saveBoolean);
+ llassert(loadString);
+ llassert(saveString);
+ llassert(loadObject);
+ llassert(saveObject);
+ llassert(updateUI);
+
+ int result = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_SENSOR);
+ if (result < 0)
+ {
+ // This error is critical, we stop working with SDL and return
+ LL_WARNS("SDL2") << "Error initializing GameController subsystems : " << SDL_GetError() << LL_ENDL;
+ return;
+ }
+
+ // The inability to read this file is not critical, we can continue working
+ if (!LLFile::isfile(gamecontrollerdb_path.c_str()))
+ {
+ LL_WARNS("SDL2") << "Device mapping db file not found: " << gamecontrollerdb_path << LL_ENDL;
+ }
+ else
+ {
+ int count = SDL_GameControllerAddMappingsFromFile(gamecontrollerdb_path.c_str());
+ if (count < 0)
+ {
+ LL_WARNS("SDL2") << "Error adding mappings from " << gamecontrollerdb_path << " : " << SDL_GetError() << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS("SDL2") << "Total " << count << " mappings added from " << gamecontrollerdb_path << LL_ENDL;
+ }
+ }
+
+ g_gameControl = LLGameControl::getInstance();
+
+ s_loadBoolean = loadBoolean;
+ s_saveBoolean = saveBoolean;
+ s_loadString = loadString;
+ s_saveString = saveString;
+ s_loadObject = loadObject;
+ s_saveObject = saveObject;
+ s_updateUI = updateUI;
+
+ loadFromSettings();
+}
+
+// static
+void LLGameControl::terminate()
+{
+ g_manager.clear();
+}
+
+// static
+const std::list<LLGameControl::Device>& LLGameControl::getDevices()
+{
+ return g_manager.mDevices;
+}
+
+//static
+const std::map<std::string, std::string>& LLGameControl::getDeviceOptions()
+{
+ return g_deviceOptions;
+}
+
+//static
+// returns 'true' if GameControlInput message needs to go out,
+// which will be the case for new data or resend. Call this right
+// before deciding to put a GameControlInput packet on the wire
+// or not.
+bool LLGameControl::computeFinalStateAndCheckForChanges()
+{
+ // Note: LLGameControllerManager::computeFinalState() modifies g_nextResendPeriod as a side-effect
+ g_manager.computeFinalState();
+
+ // should send input when:
+ // sending is enabled and
+ // g_lastSend has "expired"
+ // either because g_nextResendPeriod has been zeroed
+ // or the last send really has expired.
+ return g_enabled && g_sendToServer && (g_lastSend + g_nextResendPeriod < get_now_nsec());
+}
+
+// static
+void LLGameControl::clearAllStates()
+{
+ g_manager.clearAllStates();
+}
+
+// static
+void LLGameControl::processEvents(bool app_has_focus)
+{
+ // This method used by non-linux platforms which only use SDL for GameController input
+ SDL_Event event;
+ if (!app_has_focus)
+ {
+ // when SL window lacks focus: pump SDL events but ignore them
+ while (g_gameControl && SDL_PollEvent(&event))
+ {
+ // do nothing: SDL_PollEvent() is the operator
+ }
+ g_manager.clearAllStates();
+ return;
+ }
+
+ while (g_gameControl && SDL_PollEvent(&event))
+ {
+ handleEvent(event, app_has_focus);
+ }
+}
+
+void LLGameControl::handleEvent(const SDL_Event& event, bool app_has_focus)
+{
+ switch (event.type)
+ {
+ case SDL_JOYDEVICEADDED:
+ onJoystickDeviceAdded(event);
+ break;
+ case SDL_JOYDEVICEREMOVED:
+ onJoystickDeviceRemoved(event);
+ break;
+ case SDL_CONTROLLERDEVICEADDED:
+ onControllerDeviceAdded(event);
+ break;
+ case SDL_CONTROLLERDEVICEREMOVED:
+ onControllerDeviceRemoved(event);
+ break;
+ case SDL_CONTROLLERBUTTONDOWN:
+ /* FALLTHROUGH */
+ case SDL_CONTROLLERBUTTONUP:
+ if (app_has_focus)
+ {
+ onControllerButton(event);
+ }
+ break;
+ case SDL_CONTROLLERAXISMOTION:
+ if (app_has_focus)
+ {
+ onControllerAxis(event);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+// static
+const LLGameControl::State& LLGameControl::getState()
+{
+ return g_finalState;
+}
+
+// static
+LLGameControl::InputChannel LLGameControl::getActiveInputChannel()
+{
+ InputChannel input;
+
+ State state = g_finalState;
+ if (state.mButtons > 0)
+ {
+ // check buttons
+ input.mType = LLGameControl::InputChannel::TYPE_BUTTON;
+ for (U8 i = 0; i < 32; ++i)
+ {
+ if ((0x1 << i) & state.mButtons)
+ {
+ input.mIndex = i;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // scan axes
+ constexpr S16 threshold = std::numeric_limits<S16>::max() / 2;
+ for (U8 i = 0; i < 6; ++i)
+ {
+ if (abs(state.mAxes[i]) > threshold)
+ {
+ input.mType = LLGameControl::InputChannel::TYPE_AXIS;
+ // input.mIndex ultimately translates to a LLGameControl::KeyboardAxis
+ // which distinguishes between negative and positive directions
+ // so we must translate to axis index "i" according to the sign
+ // of the axis value.
+ input.mIndex = i;
+ input.mSign = state.mAxes[i] > 0 ? 1 : -1;
+ break;
+ }
+ }
+ }
+
+ return input;
+}
+
+// static
+void LLGameControl::getFlycamInputs(std::vector<F32>& inputs_out)
+{
+ return g_manager.getFlycamInputs(inputs_out);
+}
+
+// static
+void LLGameControl::setSendToServer(bool enable)
+{
+ g_sendToServer = enable;
+ s_saveBoolean(SETTING_SENDTOSERVER, g_sendToServer);
+}
+
+// static
+void LLGameControl::setControlAgent(bool enable)
+{
+ g_controlAgent = enable;
+ s_saveBoolean(SETTING_CONTROLAGENT, g_controlAgent);
+}
+
+// static
+void LLGameControl::setTranslateAgentActions(bool enable)
+{
+ g_translateAgentActions = enable;
+ s_saveBoolean(SETTING_TRANSLATEACTIONS, g_translateAgentActions);
+}
+
+// static
+void LLGameControl::setAgentControlMode(LLGameControl::AgentControlMode mode)
+{
+ g_agentControlMode = mode;
+ s_saveString(SETTING_AGENTCONTROLMODE, convertAgentControlModeToString(mode));
+}
+
+// static
+bool LLGameControl::getSendToServer()
+{
+ return g_sendToServer;
+}
+
+// static
+bool LLGameControl::getControlAgent()
+{
+ return g_controlAgent;
+}
+
+// static
+bool LLGameControl::getTranslateAgentActions()
+{
+ return g_translateAgentActions;
+}
+
+// static
+LLGameControl::AgentControlMode LLGameControl::getAgentControlMode()
+{
+ return g_agentControlMode;
+}
+
+// static
+LLGameControl::ActionNameType LLGameControl::getActionNameType(const std::string& action)
+{
+ return g_manager.getActionNameType(action);
+}
+
+// static
+bool LLGameControl::willControlAvatar()
+{
+ return g_enabled && g_controlAgent && g_agentControlMode == CONTROL_MODE_AVATAR;
+}
+
+// static
+//
+// Given a name like "AXIS_1-" or "BUTTON_5" returns the corresponding InputChannel
+// If the axis name lacks the +/- postfix it assumes '+' postfix.
+LLGameControl::InputChannel LLGameControl::getChannelByName(const std::string& name)
+{
+ LLGameControl::InputChannel channel;
+
+ // 'name' has two acceptable formats: AXIS_<index>[sign] or BUTTON_<index>
+ if (LLStringUtil::startsWith(name, "AXIS_"))
+ {
+ channel.mType = LLGameControl::InputChannel::Type::TYPE_AXIS;
+ // Decimal postfix is only one character
+ channel.mIndex = atoi(name.substr(5, 1).c_str());
+ // AXIS_n can have an optional +/- at index 6
+ // Assume positive axis when sign not provided
+ channel.mSign = name.back() == '-' ? -1 : 1;
+ }
+ else if (LLStringUtil::startsWith(name, "BUTTON_"))
+ {
+ channel.mType = LLGameControl::InputChannel::Type::TYPE_BUTTON;
+ // Decimal postfix is only one or two characters
+ channel.mIndex = atoi(name.substr(7).c_str());
+ }
+
+ return channel;
+}
+
+// static
+// Given an action_name like "push+", or "strafe-", returns the InputChannel
+// mapped to it if found, else channel.isNone() will be true.
+LLGameControl::InputChannel LLGameControl::getChannelByAction(const std::string& action)
+{
+ return g_manager.getChannelByAction(action);
+}
+
+// static
+bool LLGameControl::updateActionMap(const std::string& action, LLGameControl::InputChannel channel)
+{
+ return g_manager.updateActionMap(action, channel);
+}
+
+// static
+U32 LLGameControl::computeInternalActionFlags()
+{
+ return g_manager.computeInternalActionFlags();
+}
+
+// static
+void LLGameControl::setExternalInput(U32 action_flags, U32 buttons)
+{
+ g_manager.setExternalInput(action_flags, buttons);
+}
+
+//static
+void LLGameControl::updateResendPeriod()
+{
+ // we expect this method to be called right after data is sent
+ g_lastSend = get_now_nsec();
+ if (g_nextResendPeriod == 0)
+ {
+ g_nextResendPeriod = FIRST_RESEND_PERIOD;
+ }
+ else
+ {
+ // Reset mPrevAxes only on second resend or higher
+ // because when the joysticks are being used we expect a steady stream
+ // of recorrection data rather than sparse changes.
+ //
+ // (The above assumption is not necessarily true for "Actions" input
+ // (e.g. keyboard events). TODO: figure out what to do about this.)
+ //
+ // In other words: we want to include changed axes in the first resend
+ // so we only overwrite g_finalState.mPrevAxes on higher resends.
+ g_finalState.mPrevAxes = g_finalState.mAxes;
+ g_nextResendPeriod *= RESEND_EXPANSION_RATE;
+ }
+}
+
+// static
+std::string LLGameControl::stringifyAnalogMappings(getChannel_t getChannel)
+{
+ return getMappings(g_manager.mAnalogActions, InputChannel::TYPE_AXIS, getChannel);
+}
+
+// static
+std::string LLGameControl::stringifyBinaryMappings(getChannel_t getChannel)
+{
+ return getMappings(g_manager.mBinaryActions, InputChannel::TYPE_BUTTON, getChannel);
+}
+
+// static
+std::string LLGameControl::stringifyFlycamMappings(getChannel_t getChannel)
+{
+ return getMappings(g_manager.mFlycamActions, InputChannel::TYPE_AXIS, getChannel);
+}
+
+// static
+void LLGameControl::getDefaultMappings(std::vector<std::pair<std::string, LLGameControl::InputChannel>>& mappings)
+{
+ g_manager.getDefaultMappings(mappings);
+}
+
+// static
+bool LLGameControl::parseDeviceOptions(const std::string& options, std::string& name,
+ std::vector<LLGameControl::Options::AxisOptions>& axis_options,
+ std::vector<U8>& axis_map, std::vector<U8>& button_map)
+{
+ if (options.empty())
+ return false;
+
+ name.clear();
+ axis_options.resize(NUM_AXES);
+ axis_map.resize(NUM_AXES);
+ button_map.resize(NUM_BUTTONS);
+
+ for (size_t i = 0; i < NUM_AXES; ++i)
+ {
+ axis_options[i].resetToDefaults();
+ axis_map[i] = (U8)i;
+ }
+
+ for (size_t i = 0; i < NUM_BUTTONS; ++i)
+ {
+ button_map[i] = (U8)i;
+ }
+
+ std::map<std::string, std::string> pairs;
+ if (!parse(pairs, options))
+ {
+ LL_WARNS("SDL2") << "Invalid options: '" << options << "'" << LL_ENDL;
+ return false;
+ }
+
+ std::map<std::string, std::string> axis_string_options;
+ if (!parse(axis_string_options, pairs["axis_options"]))
+ {
+ LL_WARNS("SDL2") << "Invalid axis_options: '" << pairs["axis_options"] << "'" << LL_ENDL;
+ return false;
+ }
+
+ std::map<std::string, std::string> axis_string_map;
+ if (!parse(axis_string_map, pairs["axis_map"]))
+ {
+ LL_WARNS("SDL2") << "Invalid axis_map: '" << pairs["axis_map"] << "'" << LL_ENDL;
+ return false;
+ }
+
+ std::map<std::string, std::string> button_string_map;
+ if (!parse(button_string_map, pairs["button_map"]))
+ {
+ LL_WARNS("SDL2") << "Invalid button_map: '" << pairs["button_map"] << "'" << LL_ENDL;
+ return false;
+ }
+
+ name = pairs["name"];
+
+ for (size_t i = 0; i < NUM_AXES; ++i)
+ {
+ std::string key = std::to_string(i);
+
+ std::string one_axis_options = axis_string_options[key];
+ if (!one_axis_options.empty())
+ {
+ axis_options[i].loadFromString(one_axis_options);
+ }
+
+ std::string value = axis_string_map[key];
+ if (!value.empty())
+ {
+ size_t number = std::stoull(value);
+ if (number >= NUM_AXES || std::to_string(number) != value)
+ {
+ LL_WARNS("SDL2") << "Invalid axis mapping: " << i << "->" << value << LL_ENDL;
+ }
+ else
+ {
+ axis_map[i] = (U8)number;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < NUM_BUTTONS; ++i)
+ {
+ std::string value = button_string_map[std::to_string(i)];
+ if (!value.empty())
+ {
+ size_t number = std::stoull(value);
+ if (number >= NUM_BUTTONS || std::to_string(number) != value)
+ {
+ LL_WARNS("SDL2") << "Invalid button mapping: " << i << "->" << value << LL_ENDL;
+ }
+ else
+ {
+ button_map[i] = (U8)number;
+ }
+ }
+ }
+
+ return true;
+}
+
+// static
+std::string LLGameControl::stringifyDeviceOptions(const std::string& name,
+ const std::vector<LLGameControl::Options::AxisOptions>& axis_options,
+ const std::vector<U8>& axis_map, const std::vector<U8>& button_map,
+ bool force_empty)
+{
+ std::list<std::string> options;
+
+ auto opts2str = [](size_t i, const Options::AxisOptions& options) -> std::string
+ {
+ std::string string = options.saveToString();
+ return string.empty() ? string : llformat("%u:%s", i, string.c_str());
+ };
+
+ std::string axis_options_string = LLStringUtil::join<std::vector<Options::AxisOptions>, Options::AxisOptions>(axis_options, opts2str);
+ if (!axis_options_string.empty())
+ {
+ options.push_back("axis_options:{" + axis_options_string + "}");
+ }
+
+ auto map2str = [](size_t index, const U8& value) -> std::string
+ {
+ return value == index ? LLStringUtil::null : llformat("%u:%u", index, value);
+ };
+
+ std::string axis_map_string = LLStringUtil::join<std::vector<U8>, U8>(axis_map, map2str);
+ if (!axis_map_string.empty())
+ {
+ options.push_back("axis_map:{" + axis_map_string + "}");
+ }
+
+ std::string button_map_string = LLStringUtil::join<std::vector<U8>, U8>(button_map, map2str);
+ if (!button_map_string.empty())
+ {
+ options.push_back("button_map:{" + button_map_string + "}");
+ }
+
+ if (!force_empty && options.empty())
+ return LLStringUtil::null;
+
+ // Remove control characters [',', '{', '}'] from name
+ std::string safe_name;
+ safe_name.reserve(name.size());
+ for (char c : name)
+ {
+ if (c != ',' && c != '{' && c != '}')
+ {
+ safe_name.push_back(c);
+ }
+ }
+ options.push_front(llformat("name:%s", safe_name.c_str()));
+
+ std::string result = LLStringUtil::join(options);
+
+ return "{" + result + "}";
+}
+
+// static
+void LLGameControl::initByDefault()
+{
+ g_sendToServer = false;
+ g_controlAgent = false;
+ g_translateAgentActions = false;
+ g_agentControlMode = CONTROL_MODE_AVATAR;
+ g_manager.initializeMappingsByDefault();
+ g_manager.resetDeviceOptionsToDefaults();
+ g_deviceOptions.clear();
+}
+
+// static
+void LLGameControl::loadFromSettings()
+{
+ // In case of absence of the required setting the default value is assigned
+ g_enabled = s_loadBoolean(SETTING_ENABLE);
+ g_sendToServer = s_loadBoolean(SETTING_SENDTOSERVER);
+ g_controlAgent = s_loadBoolean(SETTING_CONTROLAGENT);
+ g_translateAgentActions = s_loadBoolean(SETTING_TRANSLATEACTIONS);
+ g_agentControlMode = convertStringToAgentControlMode(s_loadString(SETTING_AGENTCONTROLMODE));
+
+ g_manager.initializeMappingsByDefault();
+
+ // Load action-to-channel mappings
+ std::string analogMappings = s_loadString(SETTING_ANALOGMAPPINGS);
+ std::string binaryMappings = s_loadString(SETTING_BINARYMAPPINGS);
+ std::string flycamMappings = s_loadString(SETTING_FLYCAMMAPPINGS);
+ g_manager.setAnalogMappings(analogMappings);
+ g_manager.setBinaryMappings(binaryMappings);
+ g_manager.setFlycamMappings(flycamMappings);
+
+ // Load device-specific settings
+ g_deviceOptions.clear();
+ LLSD options = s_loadObject(SETTING_KNOWNCONTROLLERS);
+ for (auto it = options.beginMap(); it != options.endMap(); ++it)
+ {
+ g_deviceOptions.emplace(it->first, it->second);
+ }
+ g_manager.loadDeviceOptionsFromSettings();
+}
+
+// static
+void LLGameControl::saveToSettings()
+{
+ s_saveBoolean(SETTING_ENABLE, g_enabled);
+ s_saveBoolean(SETTING_SENDTOSERVER, g_sendToServer);
+ s_saveBoolean(SETTING_CONTROLAGENT, g_controlAgent);
+ s_saveBoolean(SETTING_TRANSLATEACTIONS, g_translateAgentActions);
+ s_saveString(SETTING_AGENTCONTROLMODE, convertAgentControlModeToString(g_agentControlMode));
+ s_saveString(SETTING_ANALOGMAPPINGS, g_manager.getAnalogMappings());
+ s_saveString(SETTING_BINARYMAPPINGS, g_manager.getBinaryMappings());
+ s_saveString(SETTING_FLYCAMMAPPINGS, g_manager.getFlycamMappings());
+
+ g_manager.saveDeviceOptionsToSettings();
+
+ // construct LLSD version of g_deviceOptions but only include non-empty values
+ LLSD deviceOptions = LLSD::emptyMap();
+ for (const auto& data_pair : g_deviceOptions)
+ {
+ if (!data_pair.second.empty())
+ {
+ LLSD value(data_pair.second);
+ deviceOptions.insert(data_pair.first, value);
+ }
+ }
+
+ s_saveObject(SETTING_KNOWNCONTROLLERS, deviceOptions);
+}
+
+// static
+void LLGameControl::setDeviceOptions(const std::string& guid, const Options& options)
+{
+ g_manager.setDeviceOptions(guid, options);
+}
diff --git a/indra/llwindow/llgamecontrol.h b/indra/llwindow/llgamecontrol.h
new file mode 100644
index 0000000000..a4b47cc47f
--- /dev/null
+++ b/indra/llwindow/llgamecontrol.h
@@ -0,0 +1,347 @@
+/**
+ * @file llgamecontrol.h
+ * @brief GameController detection and management
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, 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$
+ */
+
+#pragma once
+
+#include <vector>
+
+
+#include "llerror.h"
+#include "llsingleton.h"
+#include "stdtypes.h"
+#include "SDL2/SDL_events.h"
+
+// For reference, here are the RAW indices of the various input channels
+// of a standard XBox controller. Button (N) is numbered in parentheses,
+// whereas axisN has N+ and N- labels.
+//
+// leftpaddle rightpaddle
+// _______ _______
+// / 4+ '-. .-' 5+ \.
+// leftshoulder _(9)_________'-.____ ____.-'_________(10) rightshoulder
+// / _________ \_________/ \.
+// / / 1- \ (3) \.
+// | | | (4) (5) (6) Y |
+// | |0- (7) 0+| _________ (2)X B(1) |
+// | | | / 3- \ A |
+// | | 1+ | | | (0) |
+// | \_________/ |2- (8) 2+| |
+// | leftstick (11) | | |
+// | (13) (14) | 3+ | |
+// | (12) \_________/ |
+// | d-pad rightstick |
+// | ____________________ |
+// | / \ |
+// | / \ |
+// | / \ |
+// \__________/ \__________/
+//
+// Note: the analog joysticks provide NEGATIVE X,Y values for LEFT,FORWARD
+// whereas those directions are actually POSITIVE in SL's local right-handed
+// reference frame. This is why we implicitly negate those axes the moment
+// they are extracted from SDL, before being used anywhere. See the
+// implementation in LLGameControllerManager::onAxis().
+
+// LLGameControl is a singleton with pure static public interface
+class LLGameControl : public LLSingleton<LLGameControl>
+{
+ LLSINGLETON_EMPTY_CTOR(LLGameControl);
+ virtual ~LLGameControl();
+ LOG_CLASS(LLGameControl);
+
+public:
+ enum AgentControlMode
+ {
+ CONTROL_MODE_AVATAR,
+ CONTROL_MODE_FLYCAM,
+ CONTROL_MODE_NONE
+ };
+
+ enum ActionNameType
+ {
+ ACTION_NAME_UNKNOWN,
+ ACTION_NAME_ANALOG, // E.g., "push"
+ ACTION_NAME_ANALOG_POS, // E.g., "push+"
+ ACTION_NAME_ANALOG_NEG, // E.g., "push-"
+ ACTION_NAME_BINARY, // E.g., "stop"
+ ACTION_NAME_FLYCAM // E.g., "zoom"
+ };
+
+ enum KeyboardAxis : U8
+ {
+ AXIS_LEFTX,
+ AXIS_LEFTY,
+ AXIS_RIGHTX,
+ AXIS_RIGHTY,
+ AXIS_TRIGGERLEFT,
+ AXIS_TRIGGERRIGHT,
+ NUM_AXES
+ };
+
+ enum Button
+ {
+ BUTTON_A,
+ BUTTON_B,
+ BUTTON_X,
+ BUTTON_Y,
+ BUTTON_BACK,
+ BUTTON_GUIDE,
+ BUTTON_START,
+ BUTTON_LEFTSTICK,
+ BUTTON_RIGHTSTICK,
+ BUTTON_LEFTSHOULDER,
+ BUTTON_RIGHTSHOULDER, // 10
+ BUTTON_DPAD_UP,
+ BUTTON_DPAD_DOWN,
+ BUTTON_DPAD_LEFT,
+ BUTTON_DPAD_RIGHT,
+ BUTTON_MISC1,
+ BUTTON_PADDLE1,
+ BUTTON_PADDLE2,
+ BUTTON_PADDLE3,
+ BUTTON_PADDLE4,
+ BUTTON_TOUCHPAD, // 20
+ BUTTON_21,
+ BUTTON_22,
+ BUTTON_23,
+ BUTTON_24,
+ BUTTON_25,
+ BUTTON_26,
+ BUTTON_27,
+ BUTTON_28,
+ BUTTON_29,
+ BUTTON_30,
+ BUTTON_31,
+ NUM_BUTTONS
+ };
+
+ static const U16 MAX_AXIS_DEAD_ZONE = 16384;
+ static const U16 MAX_AXIS_OFFSET = 16384;
+
+ class InputChannel
+ {
+ public:
+ enum Type
+ {
+ TYPE_NONE,
+ TYPE_AXIS,
+ TYPE_BUTTON
+ };
+
+ InputChannel() {}
+ InputChannel(Type type, U8 index) : mType(type), mIndex(index) {}
+ InputChannel(Type type, U8 index, S32 sign) : mType(type), mSign(sign), mIndex(index) {}
+
+ // these methods for readability
+ bool isNone() const { return mType == TYPE_NONE; }
+ bool isAxis() const { return mType == TYPE_AXIS; }
+ bool isButton() const { return mType == TYPE_BUTTON; }
+
+ bool isEqual(const InputChannel& other)
+ {
+ return mType == other.mType && mSign == other.mSign && mIndex == other.mIndex;
+ }
+
+ std::string getLocalName() const; // AXIS_0-, AXIS_0+, BUTTON_0, NONE etc.
+ std::string getRemoteName() const; // GAME_CONTROL_AXIS_LEFTX, GAME_CONTROL_BUTTON_A, etc
+
+ Type mType { TYPE_NONE };
+ S32 mSign { 0 };
+ U8 mIndex { 255 };
+ };
+
+ // Options is a data structure for storing device-specific settings
+ class Options
+ {
+ public:
+ struct AxisOptions
+ {
+ S32 mMultiplier = 1;
+ U16 mDeadZone { 0 };
+ S16 mOffset { 0 };
+
+ void resetToDefaults()
+ {
+ mMultiplier = 1;
+ mDeadZone = 0;
+ mOffset = 0;
+ }
+
+ S16 computeModifiedValue(S16 raw_value) const
+ {
+ S32 new_value = ((S32)raw_value + S32(mOffset)) * mMultiplier;
+ return (S16)(std::clamp(new_value, -32768, 32767));
+ }
+
+ std::string saveToString() const;
+ void loadFromString(std::string options);
+ };
+
+ Options();
+
+ void resetToDefaults();
+
+ U8 mapAxis(U8 axis) const;
+ U8 mapButton(U8 button) const;
+
+ S16 fixAxisValue(U8 axis, S16 value) const;
+
+ std::string saveToString(const std::string& name, bool force_empty = false) const;
+ bool loadFromString(std::string& name, std::string options);
+ bool loadFromString(std::string options);
+
+ const std::vector<AxisOptions>& getAxisOptions() const { return mAxisOptions; }
+ std::vector<AxisOptions>& getAxisOptions() { return mAxisOptions; }
+ const std::vector<U8>& getAxisMap() const { return mAxisMap; }
+ std::vector<U8>& getAxisMap() { return mAxisMap; }
+ const std::vector<U8>& getButtonMap() const { return mButtonMap; }
+ std::vector<U8>& getButtonMap() { return mButtonMap; }
+
+ private:
+ std::vector<AxisOptions> mAxisOptions;
+ std::vector<U8> mAxisMap;
+ std::vector<U8> mButtonMap;
+ };
+
+ // State is a minimal class for storing axes and buttons values
+ class State
+ {
+ public:
+ State();
+ void clear();
+ bool onButton(U8 button, bool pressed);
+ std::vector<S16> mAxes; // [ -32768, 32767 ]
+ std::vector<S16> mPrevAxes; // value in last outgoing packet
+ U32 mButtons;
+ };
+
+ // Device is a data structure for describing any detected controller
+ class Device
+ {
+ const int mJoystickID { -1 };
+ const std::string mGUID;
+ const std::string mName;
+ Options mOptions;
+ State mState;
+
+ public:
+ Device(int joystickID, const std::string& guid, const std::string& name);
+ int getJoystickID() const { return mJoystickID; }
+ std::string getGUID() const { return mGUID; }
+ std::string getName() const { return mName; }
+ const Options& getOptions() const { return mOptions; }
+ const State& getState() const { return mState; }
+
+ void resetOptionsToDefaults() { mOptions.resetToDefaults(); }
+ std::string saveOptionsToString(bool force_empty = false) const { return mOptions.saveToString(mName, force_empty); }
+ void loadOptionsFromString(const std::string& options) { mOptions.loadFromString(options); }
+
+ friend class LLGameControllerManager;
+ };
+
+ static bool isEnabled();
+ static void setEnabled(bool enabled);
+
+ static bool isInitialized();
+ static void init(const std::string& gamecontrollerdb_path,
+ std::function<bool(const std::string&)> loadBoolean,
+ std::function<void(const std::string&, bool)> saveBoolean,
+ std::function<std::string(const std::string&)> loadString,
+ std::function<void(const std::string&, const std::string&)> saveString,
+ std::function<LLSD(const std::string&)> loadObject,
+ std::function<void(const std::string&, const LLSD&)> saveObject,
+ std::function<void()> updateUI);
+ static void terminate();
+
+ static const std::list<LLGameControl::Device>& getDevices();
+ static const std::map<std::string, std::string>& getDeviceOptions();
+
+ // returns 'true' if GameControlInput message needs to go out,
+ // which will be the case for new data or resend. Call this right
+ // before deciding to put a GameControlInput packet on the wire
+ // or not.
+ static bool computeFinalStateAndCheckForChanges();
+
+ static void clearAllStates();
+
+ static void processEvents(bool app_has_focus = true);
+ static void handleEvent(const SDL_Event& event, bool app_has_focus);
+ static const State& getState();
+ static InputChannel getActiveInputChannel();
+ static void getFlycamInputs(std::vector<F32>& inputs_out);
+
+ // these methods for accepting input from keyboard
+ static void setSendToServer(bool enable);
+ static void setControlAgent(bool enable);
+ static void setTranslateAgentActions(bool enable);
+ static void setAgentControlMode(AgentControlMode mode);
+
+ static bool getSendToServer();
+ static bool getControlAgent();
+ static bool getTranslateAgentActions();
+ static AgentControlMode getAgentControlMode();
+ static ActionNameType getActionNameType(const std::string& action);
+
+ static bool willControlAvatar();
+
+ // Given a name like "AXIS_1-" or "BUTTON_5" returns the corresponding InputChannel
+ // If the axis name lacks the +/- postfix it assumes '+' postfix.
+ static LLGameControl::InputChannel getChannelByName(const std::string& name);
+
+ // action_name = push+, strafe-, etc
+ static LLGameControl::InputChannel getChannelByAction(const std::string& action);
+
+ static bool updateActionMap(const std::string& action_name, LLGameControl::InputChannel channel);
+
+ // Keyboard presses produce action_flags which can be translated into State
+ // and game_control devices produce State which can be translated into action_flags.
+ // These methods help exchange such translations.
+ static U32 computeInternalActionFlags();
+ static void setExternalInput(U32 action_flags, U32 buttons_from_keys);
+
+ // call this after putting a GameControlInput packet on the wire
+ static void updateResendPeriod();
+
+ using getChannel_t = std::function<LLGameControl::InputChannel(const std::string& action)>;
+ static std::string stringifyAnalogMappings(getChannel_t getChannel);
+ static std::string stringifyBinaryMappings(getChannel_t getChannel);
+ static std::string stringifyFlycamMappings(getChannel_t getChannel);
+ static void getDefaultMappings(std::vector<std::pair<std::string, LLGameControl::InputChannel>>& mappings);
+
+ static bool parseDeviceOptions(const std::string& options, std::string& name,
+ std::vector<LLGameControl::Options::AxisOptions>& axis_options,
+ std::vector<U8>& axis_map, std::vector<U8>& button_map);
+ static std::string stringifyDeviceOptions(const std::string& name,
+ const std::vector<LLGameControl::Options::AxisOptions>& axis_options,
+ const std::vector<U8>& axis_map, const std::vector<U8>& button_map,
+ bool force_empty = false);
+
+ static void initByDefault();
+ static void loadFromSettings();
+ static void saveToSettings();
+ static void setDeviceOptions(const std::string& guid, const Options& options);
+};
+
diff --git a/indra/llwindow/llgamecontroltranslator.cpp b/indra/llwindow/llgamecontroltranslator.cpp
new file mode 100644
index 0000000000..84468ab657
--- /dev/null
+++ b/indra/llwindow/llgamecontroltranslator.cpp
@@ -0,0 +1,275 @@
+/**
+ * @file llgamecontroltranslator.cpp
+ * @brief LLGameControlTranslator class implementation
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, 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$
+ */
+
+/*
+ * App-wide preferences. Note that these are not per-user,
+ * because we need to load many preferences before we have
+ * a login name.
+ */
+
+#include "llgamecontroltranslator.h"
+#include "llsd.h"
+
+
+using ActionToMaskMap = LLGameControlTranslator::ActionToMaskMap;
+
+LLGameControlTranslator::LLGameControlTranslator()
+{
+}
+
+void LLGameControlTranslator::setAvailableActionMasks(ActionToMaskMap& action_to_mask)
+{
+ mActionToMask = std::move(action_to_mask);
+}
+
+LLGameControl::InputChannel LLGameControlTranslator::getChannelByAction(const std::string& action) const
+{
+ LLGameControl::InputChannel channel;
+ auto mask_itr = mActionToMask.find(action);
+ if (mask_itr != mActionToMask.end())
+ {
+ U32 mask = mask_itr->second;
+ auto channel_itr = mMaskToChannel.find(mask);
+ if (channel_itr != mMaskToChannel.end())
+ {
+ channel = channel_itr->second;
+ }
+ }
+ else
+ {
+ // It is expected that sometimes 'action' lacks the postfix '+' or '-'.
+ // When it is missing we append '+' and try again.
+ std::string action_plus = action;
+ action_plus.append("+");
+ mask_itr = mActionToMask.find(action_plus);
+ if (mask_itr != mActionToMask.end())
+ {
+ U32 mask = mask_itr->second;
+ auto channel_itr = mMaskToChannel.find(mask);
+ if (channel_itr != mMaskToChannel.end())
+ {
+ channel = channel_itr->second;
+ }
+ }
+ }
+ return channel;
+}
+
+void LLGameControlTranslator::setMappings(LLGameControlTranslator::NamedChannels& named_channels)
+{
+ mMaskToChannel.clear();
+ mMappedFlags = 0;
+ mPrevActiveFlags = 0;
+ mCachedState.clear();
+
+ for (const auto& named_channel : named_channels)
+ {
+ updateMap(named_channel.first, named_channel.second);
+ }
+}
+
+void LLGameControlTranslator::updateMap(const std::string& action, const LLGameControl::InputChannel& channel)
+{
+ // First, get the action name type
+ LLGameControl::ActionNameType actionNameType = LLGameControl::getActionNameType(action);
+ if (actionNameType == LLGameControl::ACTION_NAME_UNKNOWN ||
+ actionNameType == LLGameControl::ACTION_NAME_FLYCAM)
+ {
+ LL_WARNS("SDL2") << "unmappable action='" << action << "' (type=" << actionNameType << ")" << LL_ENDL;
+ return;
+ }
+
+ // Second, get the expected associated channel type (except of TYPE_NONE)
+ LLGameControl::InputChannel::Type expectedChannelType =
+ actionNameType == LLGameControl::ACTION_NAME_BINARY ?
+ LLGameControl::InputChannel::TYPE_BUTTON :
+ LLGameControl::InputChannel::TYPE_AXIS;
+ if (!channel.isNone() && (channel.mType != expectedChannelType))
+ {
+ LL_WARNS("SDL2") << "unmappable channel (type=" << channel.mType << ")"
+ << " for action='" << action << "' (type=" << actionNameType << ")" << LL_ENDL;
+ return;
+ }
+
+ if (actionNameType == LLGameControl::ACTION_NAME_ANALOG) // E.g., "push"
+ {
+ // Special (double) processing for analog action names
+ // sequentially adding '+' and '-' to the given action
+ updateMapInternal(action + "+", channel);
+
+ // For the channel type TYPE_NONE we can map the same channel
+ // In fact, the mapping will be removed from the mapping list
+ if (channel.isNone())
+ {
+ updateMapInternal(action + "-", channel);
+ }
+ else
+ {
+ // For the channel type except of TYPE_NONE we construct the other channel
+ // with the same type and index but with the opposite sign
+ LLGameControl::InputChannel other_channel(channel.mType, channel.mIndex, -channel.mSign);
+
+ // TIED TRIGGER HACK: this works for XBox and similar controllers,
+ // and those are pretty much the only supported devices right now
+ // however TODO: figure out how to do this better.
+ //
+ // AXIS_TRIGGERLEFT and AXIS_TRIGGERRIGHT are separate axes and most devices
+ // only allow them to read positive, not negative. When used for motion control
+ // they are typically paired together. We assume as much here when computing
+ // the other_channel.
+ if (channel.mIndex == LLGameControl::AXIS_TRIGGERLEFT)
+ {
+ other_channel.mIndex = LLGameControl::AXIS_TRIGGERRIGHT;
+ other_channel.mSign = 1;
+ }
+ else if (channel.mIndex == LLGameControl::AXIS_TRIGGERRIGHT)
+ {
+ other_channel.mIndex = LLGameControl::AXIS_TRIGGERLEFT;
+ other_channel.mSign = 1;
+ }
+ updateMapInternal(action + "-", other_channel);
+ }
+ }
+ else
+ {
+ // Simple (single) processing for other action name types
+ updateMapInternal(action, channel);
+ }
+
+ // recompute mMappedFlags
+ mMappedFlags = 0;
+ for (auto& pair : mMaskToChannel)
+ {
+ mMappedFlags |= pair.first;
+ }
+ mPrevActiveFlags = 0;
+ mCachedState.clear();
+}
+
+// Given external action_flags (i.e. raw avatar input)
+// compute the corresponding LLGameControl::State that would have produced those flags.
+// Note: "action flags" are similar to, but not quite the same as, "control flags".
+// "Action flags" are the raw input of avatar movement intent, whereas "control flags"
+// are the consequential set of instructions that are sent to the server for moving
+// the avatar character.
+const LLGameControl::State& LLGameControlTranslator::computeStateFromFlags(U32 action_flags)
+{
+ // translate action_flag bits to equivalent game controller state
+ // according to data in mMaskToChannel
+
+ // only bother to update mCachedState if active_flags have changed
+ U32 active_flags = action_flags & mMappedFlags;
+ if (active_flags != mPrevActiveFlags)
+ {
+ mCachedState.clear();
+ for (const auto& pair : mMaskToChannel)
+ {
+ U32 mask = pair.first;
+ if (mask == (mask & action_flags))
+ {
+ LLGameControl::InputChannel channel = pair.second;
+ if (channel.isAxis())
+ {
+ if (channel.mSign < 0)
+ {
+ mCachedState.mAxes[channel.mIndex] = std::numeric_limits<S16>::min();
+ }
+ else
+ {
+ mCachedState.mAxes[channel.mIndex] = std::numeric_limits<S16>::max();
+ }
+ }
+ else if (channel.isButton())
+ {
+ mCachedState.mButtons |= 0x01U << channel.mIndex;
+ }
+ }
+ }
+ mPrevActiveFlags = active_flags;
+ }
+ return mCachedState;
+}
+
+// Given LLGameControl::State (i.e. from a real controller)
+// compute corresponding action flags (e.g. for moving the avatar around)
+U32 LLGameControlTranslator::computeFlagsFromState(const std::vector<S32>& axes, U32 buttons)
+{
+ // HACK: supply hard-coded threshold for ON/OFF zones
+ constexpr S32 AXIS_THRESHOLD = 32768 / 8;
+ U32 action_flags = 0;
+ for (const auto& pair : mMaskToChannel)
+ {
+ // pair = { mask, channel }
+ const LLGameControl::InputChannel& channel = pair.second;
+ if (channel.isAxis())
+ {
+ if (channel.mSign < 0)
+ {
+ if (axes[channel.mIndex] < -AXIS_THRESHOLD)
+ {
+ action_flags |= pair.first;
+ }
+ }
+ else if (axes[channel.mIndex] > AXIS_THRESHOLD)
+ {
+ action_flags |= pair.first;
+ }
+ }
+ else if (channel.isButton())
+ {
+ U32 bit_set = buttons & (0x01U << channel.mIndex);
+ if (bit_set)
+ {
+ action_flags |= pair.first;
+ }
+ }
+ }
+ return action_flags;
+}
+
+void LLGameControlTranslator::updateMapInternal(const std::string& name, const LLGameControl::InputChannel& channel)
+{
+ auto action_it = mActionToMask.find(name);
+ llassert(action_it != mActionToMask.end());
+ U32 mask = action_it->second;
+ auto channel_it = mMaskToChannel.find(mask);
+ if (channel_it == mMaskToChannel.end())
+ {
+ // create new mapping
+ mMaskToChannel[mask] = channel;
+ }
+ else if (channel.isNone())
+ {
+ // remove old mapping
+ mMaskToChannel.erase(channel_it);
+ }
+ else
+ {
+ // update old mapping
+ channel_it->second = channel;
+ }
+}
+
diff --git a/indra/llwindow/llgamecontroltranslator.h b/indra/llwindow/llgamecontroltranslator.h
new file mode 100644
index 0000000000..533408014c
--- /dev/null
+++ b/indra/llwindow/llgamecontroltranslator.h
@@ -0,0 +1,93 @@
+/**
+ * @file llgamecontroltranslator.h
+ * @brief LLGameControlTranslator class definition
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, 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$
+ */
+
+#pragma once
+
+#include <map>
+
+#include "stdtypes.h"
+#include "llgamecontrol.h"
+
+
+// GameControl data is sent to the server to expose game controller input to LSL scripts,
+// however not everyone will have a game controller device. To allow keyboard users to provide
+// GameControl data we allow the User to configure equivalences between avatar actions
+// (i.e. "push forward", "strafe left", etc) and keyboard buttons to GameControl axes
+// and buttons.
+//
+// The LLGameControlTranslator stores the equivalences and translates avatar action_flags
+// and keyboard state into GameContrl data, and in some cases the other direction: from
+// LLGameControl::State into avatar action_flags.
+//
+
+class LLGameControlTranslator
+{
+public:
+
+ using ActionToMaskMap = std::map< std::string, U32 >; // < action : mask >
+ using MaskToChannelMap = std::map< U32, LLGameControl::InputChannel >; // < mask : channel >
+ using NamedChannel = std::pair < std::string , LLGameControl::InputChannel >;
+ using NamedChannels = std::vector< NamedChannel >;
+
+
+ LLGameControlTranslator();
+ void setAvailableActionMasks(ActionToMaskMap& action_to_mask);
+ LLGameControl::InputChannel getChannelByAction(const std::string& action) const;
+ void setMappings(NamedChannels& named_channels);
+ void updateMap(const std::string& action, const LLGameControl::InputChannel& channel);
+ // Note: to remove a mapping you can call updateMap() with a TYPE_NONE channel
+
+ // Given external action_flags (i.e. raw avatar input)
+ // compute the corresponding LLGameControl::State that would have produced those flags.
+ // Note: "action_flags" are similar to, but not quite the same as, "control_flags".
+ const LLGameControl::State& computeStateFromFlags(U32 action_flags);
+
+ // Given LLGameControl::State (i.e. from a real controller)
+ // compute corresponding action flags (e.g. for moving the avatar around)
+ U32 computeFlagsFromState(const std::vector<S32>& axes, U32 buttons);
+
+ U32 getMappedFlags() const { return mMappedFlags; }
+
+private:
+ void updateMapInternal(const std::string& name, const LLGameControl::InputChannel& channel);
+
+private:
+ // mActionToMask is an invarient map between the possible actions
+ // and the action bit masks. Only actions therein can have their
+ // bit masks mapped to channels.
+ ActionToMaskMap mActionToMask; // invariant map after init
+
+ // mMaskToChannel is a dynamic map between action bit masks
+ // and GameControl channels.
+ MaskToChannelMap mMaskToChannel; // dynamic map, per preference changes
+
+ // mCachedState is an optimization:
+ // it is only recomputed when external action_flags change
+ LLGameControl::State mCachedState;
+
+ U32 mMappedFlags { 0 };
+ U32 mPrevActiveFlags { 0 };
+};
diff --git a/indra/llwindow/llkeyboard.cpp b/indra/llwindow/llkeyboard.cpp
index a16c0a318a..cb0c312a1d 100644
--- a/indra/llwindow/llkeyboard.cpp
+++ b/indra/llwindow/llkeyboard.cpp
@@ -161,6 +161,7 @@ void LLKeyboard::resetKeyDownAndHandle()
mCallbacks->handleTranslatedKeyUp(i, mask);
}
}
+ mCurTranslatedKey = KEY_NONE;
}
// BUG this has to be called when an OS dialog is shown, otherwise modifier key state
@@ -226,7 +227,7 @@ LLKeyboard::NATIVE_KEY_TYPE LLKeyboard::inverseTranslateKey(const KEY translated
}
-bool LLKeyboard::handleTranslatedKeyDown(KEY translated_key, U32 translated_mask)
+bool LLKeyboard::handleTranslatedKeyDown(KEY translated_key, MASK translated_mask)
{
bool handled = false;
bool repeated = false;
@@ -254,7 +255,7 @@ bool LLKeyboard::handleTranslatedKeyDown(KEY translated_key, U32 translated_mask
}
-bool LLKeyboard::handleTranslatedKeyUp(KEY translated_key, U32 translated_mask)
+bool LLKeyboard::handleTranslatedKeyUp(KEY translated_key, MASK translated_mask)
{
bool handled = false;
if( mKeyLevel[translated_key] )
@@ -276,6 +277,32 @@ bool LLKeyboard::handleTranslatedKeyUp(KEY translated_key, U32 translated_mask)
}
+bool LLKeyboard::handleKeyDown(const NATIVE_KEY_TYPE key, const MASK mask)
+{
+ MASK translated_mask = updateModifiers(mask);
+ KEY translated_key = 0;
+ bool handled = false;
+ if(translateKey(key, &translated_key))
+ {
+ handled = handleTranslatedKeyDown(translated_key, translated_mask);
+ }
+ return handled;
+}
+
+
+bool LLKeyboard::handleKeyUp(const NATIVE_KEY_TYPE key, const MASK mask)
+{
+ MASK translated_mask = updateModifiers(mask);
+ KEY translated_key = 0;
+ bool handled = false;
+ if(translateKey(key, &translated_key))
+ {
+ handled = handleTranslatedKeyUp(translated_key, translated_mask);
+ }
+ return handled;
+}
+
+
void LLKeyboard::toggleInsertMode()
{
if (LL_KIM_INSERT == mInsertMode)
diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h
index d3c35b1ed4..c092f55685 100644
--- a/indra/llwindow/llkeyboard.h
+++ b/indra/llwindow/llkeyboard.h
@@ -55,10 +55,12 @@ class LLWindowCallbacks;
class LLKeyboard
{
public:
-#ifndef LL_SDL
- typedef U16 NATIVE_KEY_TYPE;
-#else
+#ifdef LL_USE_SDL_KEYBOARD
+ // linux relies on SDL2 which uses U32 for its native key type
typedef U32 NATIVE_KEY_TYPE;
+#else
+ // on non-linux platforms we can get by with a smaller native key type
+ typedef U16 NATIVE_KEY_TYPE;
#endif
LLKeyboard();
virtual ~LLKeyboard();
@@ -73,17 +75,14 @@ public:
bool getKeyRepeated(const KEY key) { return mKeyRepeated[key]; }
bool translateKey(const NATIVE_KEY_TYPE os_key, KEY *translated_key);
- NATIVE_KEY_TYPE inverseTranslateKey(const KEY translated_key);
- bool handleTranslatedKeyUp(KEY translated_key, U32 translated_mask); // Translated into "Linden" keycodes
- bool handleTranslatedKeyDown(KEY translated_key, U32 translated_mask); // Translated into "Linden" keycodes
+ NATIVE_KEY_TYPE inverseTranslateKey(const KEY translated_key);
+ bool handleTranslatedKeyUp(KEY translated_key, MASK translated_mask); // Translated into "Linden" keycodes
+ bool handleTranslatedKeyDown(KEY translated_key, MASK translated_mask); // Translated into "Linden" keycodes
virtual bool handleKeyUp(const NATIVE_KEY_TYPE key, MASK mask) = 0;
virtual bool handleKeyDown(const NATIVE_KEY_TYPE key, MASK mask) = 0;
-#ifdef LL_DARWIN
- // We only actually use this for macOS.
- virtual void handleModifier(MASK mask) = 0;
-#endif // LL_DARWIN
+ virtual void handleModifier(MASK mask) { }
// Asynchronously poll the control, alt, and shift keys and set the
// appropriate internal key masks.
@@ -113,6 +112,7 @@ public:
protected:
void addKeyName(KEY key, const std::string& name);
+ virtual MASK updateModifiers(const MASK mask) { return mask; }
protected:
std::map<NATIVE_KEY_TYPE, KEY> mTranslateKeyMap; // Map of translations from OS keys to Linden KEYs
diff --git a/indra/llwindow/llkeyboardheadless.cpp b/indra/llwindow/llkeyboardheadless.cpp
index ad8e42a412..0ca8c09f42 100644
--- a/indra/llwindow/llkeyboardheadless.cpp
+++ b/indra/llwindow/llkeyboardheadless.cpp
@@ -31,6 +31,16 @@
LLKeyboardHeadless::LLKeyboardHeadless()
{ }
+bool LLKeyboardHeadless::handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask)
+{
+ return false;
+}
+
+bool LLKeyboardHeadless::handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask)
+{
+ return false;
+}
+
void LLKeyboardHeadless::resetMaskKeys()
{ }
@@ -57,6 +67,7 @@ void LLKeyboardHeadless::scanKeyboard()
mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]);
}
}
+ mCurScanKey = KEY_NONE;
// Reset edges for next frame
for (S32 key = 0; key < KEY_COUNT; key++)
diff --git a/indra/llwindow/llkeyboardheadless.h b/indra/llwindow/llkeyboardheadless.h
index 439abaf25b..60c4d61e1c 100644
--- a/indra/llwindow/llkeyboardheadless.h
+++ b/indra/llwindow/llkeyboardheadless.h
@@ -33,20 +33,15 @@ class LLKeyboardHeadless : public LLKeyboard
{
public:
LLKeyboardHeadless();
- /*virtual*/ ~LLKeyboardHeadless() {};
+ ~LLKeyboardHeadless() {};
-#ifndef LL_SDL
- /*virtual*/ bool handleKeyUp(const U16 key, MASK mask) { return false; }
- /*virtual*/ bool handleKeyDown(const U16 key, MASK mask) { return false; }
-#else
- /*virtual*/ bool handleKeyUp(const U32 key, MASK mask) { return false; }
- /*virtual*/ bool handleKeyDown(const U32 key, MASK mask) { return false; }
-#endif
- /*virtual*/ void resetMaskKeys();
- /*virtual*/ MASK currentMask(bool for_mouse_event);
- /*virtual*/ void scanKeyboard();
+ bool handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override;
+ bool handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override;
+ void resetMaskKeys() override;
+ MASK currentMask(bool for_mouse_event) override;
+ void scanKeyboard() override;
#ifdef LL_DARWIN
- /*virtual*/ void handleModifier(MASK mask);
+ void handleModifier(MASK mask) override;
#endif
};
diff --git a/indra/llwindow/llkeyboardmacosx.cpp b/indra/llwindow/llkeyboardmacosx.cpp
index 89ff7c6d3f..1a403e5d94 100644
--- a/indra/llwindow/llkeyboardmacosx.cpp
+++ b/indra/llwindow/llkeyboardmacosx.cpp
@@ -162,7 +162,7 @@ LLKeyboardMacOSX::LLKeyboardMacOSX()
void LLKeyboardMacOSX::resetMaskKeys()
{
- U32 mask = getModifiers();
+ MASK mask = getModifiers();
// MBW -- XXX -- This mirrors the operation of the Windows version of resetMaskKeys().
// It looks a bit suspicious, as it won't correct for keys that have been released.
@@ -187,7 +187,7 @@ void LLKeyboardMacOSX::resetMaskKeys()
}
/*
-static bool translateKeyMac(const U16 key, const U32 mask, KEY &outKey, U32 &outMask)
+static bool translateKeyMac(const U16 key, const MASK mask, KEY &outKey, U32 &outMask)
{
// Translate the virtual keycode into the keycodes the keyboard system expects.
U16 virtualKey = (mask >> 24) & 0x0000007F;
@@ -203,7 +203,7 @@ void LLKeyboardMacOSX::handleModifier(MASK mask)
updateModifiers(mask);
}
-MASK LLKeyboardMacOSX::updateModifiers(const U32 mask)
+MASK LLKeyboardMacOSX::updateModifiers(const MASK mask)
{
// translate the mask
MASK out_mask = 0;
@@ -226,7 +226,7 @@ MASK LLKeyboardMacOSX::updateModifiers(const U32 mask)
return out_mask;
}
-bool LLKeyboardMacOSX::handleKeyDown(const U16 key, const U32 mask)
+bool LLKeyboardMacOSX::handleKeyDown(const U16 key, MASK mask)
{
KEY translated_key = 0;
U32 translated_mask = 0;
@@ -243,7 +243,7 @@ bool LLKeyboardMacOSX::handleKeyDown(const U16 key, const U32 mask)
}
-bool LLKeyboardMacOSX::handleKeyUp(const U16 key, const U32 mask)
+bool LLKeyboardMacOSX::handleKeyUp(const U16 key, MASK mask)
{
KEY translated_key = 0;
U32 translated_mask = 0;
@@ -262,7 +262,7 @@ bool LLKeyboardMacOSX::handleKeyUp(const U16 key, const U32 mask)
MASK LLKeyboardMacOSX::currentMask(bool for_mouse_event)
{
MASK result = MASK_NONE;
- U32 mask = getModifiers();
+ MASK mask = getModifiers();
if (mask & MAC_SHIFT_KEY) result |= MASK_SHIFT;
if (mask & MAC_CTRL_KEY) result |= MASK_CONTROL;
@@ -291,6 +291,7 @@ void LLKeyboardMacOSX::scanKeyboard()
mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]);
}
}
+ mCurScanKey = KEY_NONE;
// Reset edges for next frame
for (key = 0; key < KEY_COUNT; key++)
diff --git a/indra/llwindow/llkeyboardmacosx.h b/indra/llwindow/llkeyboardmacosx.h
index 92ab5c9a85..a5f59f3fba 100644
--- a/indra/llwindow/llkeyboardmacosx.h
+++ b/indra/llwindow/llkeyboardmacosx.h
@@ -42,17 +42,17 @@ class LLKeyboardMacOSX : public LLKeyboard
{
public:
LLKeyboardMacOSX();
- /*virtual*/ ~LLKeyboardMacOSX() {};
+ ~LLKeyboardMacOSX() {};
- /*virtual*/ bool handleKeyUp(const U16 key, MASK mask);
- /*virtual*/ bool handleKeyDown(const U16 key, MASK mask);
- /*virtual*/ void resetMaskKeys();
- /*virtual*/ MASK currentMask(bool for_mouse_event);
- /*virtual*/ void scanKeyboard();
- /*virtual*/ void handleModifier(MASK mask);
+ bool handleKeyUp(const U16 key, MASK mask) override;
+ bool handleKeyDown(const U16 key, MASK mask) override;
+ void resetMaskKeys() override;
+ MASK currentMask(bool for_mouse_event) override;
+ void scanKeyboard() override;
+ void handleModifier(MASK mask) override;
protected:
- MASK updateModifiers(const U32 mask);
+ MASK updateModifiers(const MASK mask) override;
void setModifierKeyLevel( KEY key, bool new_state );
bool translateNumpadKey( const U16 os_key, KEY *translated_key );
U16 inverseTranslateNumpadKey(const KEY translated_key);
diff --git a/indra/llwindow/llkeyboardsdl.cpp b/indra/llwindow/llkeyboardsdl.cpp
index 543882fc8f..b6666195a6 100644
--- a/indra/llwindow/llkeyboardsdl.cpp
+++ b/indra/llwindow/llkeyboardsdl.cpp
@@ -26,7 +26,7 @@
#include "linden_common.h"
#include "llkeyboardsdl.h"
#include "llwindowcallbacks.h"
-#include "SDL2/SDL.h"
+
#include "SDL2/SDL_keycode.h"
LLKeyboardSDL::LLKeyboardSDL()
@@ -42,7 +42,7 @@ LLKeyboardSDL::LLKeyboardSDL()
// <FS:ND> Looks like we need to map those despite of SDL_TEXTINPUT handling most of this, but without
// the translation lower->upper here accelerators will not work.
- U16 cur_char;
+ LLKeyboard::NATIVE_KEY_TYPE cur_char;
for (cur_char = 'A'; cur_char <= 'Z'; cur_char++)
{
mTranslateKeyMap[cur_char] = cur_char;
@@ -177,7 +177,7 @@ void LLKeyboardSDL::resetMaskKeys()
}
-MASK LLKeyboardSDL::updateModifiers(const U32 mask)
+MASK LLKeyboardSDL::updateModifiers(const MASK mask)
{
// translate the mask
MASK out_mask = MASK_NONE;
@@ -201,7 +201,7 @@ MASK LLKeyboardSDL::updateModifiers(const U32 mask)
}
-static U32 adjustNativekeyFromUnhandledMask(const U32 key, const U32 mask)
+U32 adjustNativekeyFromUnhandledMask(const LLKeyboard::NATIVE_KEY_TYPE key, const MASK mask)
{
// SDL doesn't automatically adjust the keysym according to
// whether NUMLOCK is engaged, so we massage the keysym manually.
@@ -226,11 +226,11 @@ static U32 adjustNativekeyFromUnhandledMask(const U32 key, const U32 mask)
}
-bool LLKeyboardSDL::handleKeyDown(const U32 key, const U32 mask)
+bool LLKeyboardSDL::handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, const MASK mask)
{
U32 adjusted_nativekey;
KEY translated_key = 0;
- U32 translated_mask = MASK_NONE;
+ MASK translated_mask = MASK_NONE;
bool handled = false;
adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask);
@@ -246,7 +246,7 @@ bool LLKeyboardSDL::handleKeyDown(const U32 key, const U32 mask)
}
-bool LLKeyboardSDL::handleKeyUp(const U32 key, const U32 mask)
+bool LLKeyboardSDL::handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, const MASK mask)
{
U32 adjusted_nativekey;
KEY translated_key = 0;
@@ -300,6 +300,7 @@ void LLKeyboardSDL::scanKeyboard()
mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]);
}
}
+ mCurScanKey = KEY_NONE;
// Reset edges for next frame
for (S32 key = 0; key < KEY_COUNT; key++)
@@ -314,12 +315,12 @@ void LLKeyboardSDL::scanKeyboard()
}
-bool LLKeyboardSDL::translateNumpadKey( const U32 os_key, KEY *translated_key)
+bool LLKeyboardSDL::translateNumpadKey( const LLKeyboard::NATIVE_KEY_TYPE os_key, KEY *translated_key)
{
return translateKey(os_key, translated_key);
}
-U16 LLKeyboardSDL::inverseTranslateNumpadKey(const KEY translated_key)
+LLKeyboard::NATIVE_KEY_TYPE LLKeyboardSDL::inverseTranslateNumpadKey(const KEY translated_key)
{
return inverseTranslateKey(translated_key);
}
@@ -497,7 +498,6 @@ enum class WindowsVK : U32
};
std::map< U32, U32 > mSDL2_to_Win;
-std::set< U32 > mIgnoreSDL2Keys;
U32 LLKeyboardSDL::mapSDL2toWin( U32 aSymbol )
{
diff --git a/indra/llwindow/llkeyboardsdl.h b/indra/llwindow/llkeyboardsdl.h
index 9ebff66865..7671e4c859 100644
--- a/indra/llwindow/llkeyboardsdl.h
+++ b/indra/llwindow/llkeyboardsdl.h
@@ -33,22 +33,22 @@ class LLKeyboardSDL : public LLKeyboard
{
public:
LLKeyboardSDL();
- /*virtual*/ ~LLKeyboardSDL() {};
+ ~LLKeyboardSDL() {};
- /*virtual*/ bool handleKeyUp(const U32 key, MASK mask);
- /*virtual*/ bool handleKeyDown(const U32 key, MASK mask);
- /*virtual*/ void resetMaskKeys();
- /*virtual*/ MASK currentMask(bool for_mouse_event);
- /*virtual*/ void scanKeyboard();
+ bool handleKeyUp(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override;
+ bool handleKeyDown(const LLKeyboard::NATIVE_KEY_TYPE key, MASK mask) override;
+ void resetMaskKeys() override;
+ MASK currentMask(bool for_mouse_event) override;
+ void scanKeyboard() override;
protected:
- MASK updateModifiers(const U32 mask);
+ MASK updateModifiers(const MASK mask) override;
void setModifierKeyLevel( KEY key, bool new_state );
- bool translateNumpadKey( const U32 os_key, KEY *translated_key );
- U16 inverseTranslateNumpadKey(const KEY translated_key);
+ bool translateNumpadKey( const LLKeyboard::NATIVE_KEY_TYPE os_key, KEY *translated_key );
+ LLKeyboard::NATIVE_KEY_TYPE inverseTranslateNumpadKey(const KEY translated_key);
private:
- std::map<U32, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys
- std::map<KEY, U32> mInvTranslateNumpadMap; // inverse of the above
+ std::map<LLKeyboard::NATIVE_KEY_TYPE, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys
+ std::map<KEY, LLKeyboard::NATIVE_KEY_TYPE> mInvTranslateNumpadMap; // inverse of the above
public:
static U32 mapSDL2toWin( U32 );
diff --git a/indra/llwindow/llkeyboardwin32.cpp b/indra/llwindow/llkeyboardwin32.cpp
index 8d6b8d9b93..c31ef5c9a3 100644
--- a/indra/llwindow/llkeyboardwin32.cpp
+++ b/indra/llwindow/llkeyboardwin32.cpp
@@ -182,7 +182,7 @@ void LLKeyboardWin32::resetMaskKeys()
//}
-MASK LLKeyboardWin32::updateModifiers()
+MASK LLKeyboardWin32::updateModifiers(const U32 mask)
{
//RN: this seems redundant, as we should have already received the appropriate
// messages for the modifier keys
@@ -191,8 +191,7 @@ MASK LLKeyboardWin32::updateModifiers()
// (keydown encoded in high order bit of short)
mKeyLevel[KEY_CAPSLOCK] = (GetKeyState(VK_CAPITAL) & 0x0001) != 0; // Low order bit carries the toggle state.
// Get mask for keyboard events
- MASK mask = currentMask(false);
- return mask;
+ return currentMask(false);
}
@@ -203,7 +202,7 @@ bool LLKeyboardWin32::handleKeyDown(const U16 key, MASK mask)
U32 translated_mask;
bool handled = false;
- translated_mask = updateModifiers();
+ translated_mask = updateModifiers(mask);
if (translateExtendedKey(key, mask, &translated_key))
{
@@ -220,7 +219,7 @@ bool LLKeyboardWin32::handleKeyUp(const U16 key, MASK mask)
U32 translated_mask;
bool handled = false;
- translated_mask = updateModifiers();
+ translated_mask = updateModifiers(mask);
if (translateExtendedKey(key, mask, &translated_key))
{
@@ -259,6 +258,7 @@ void LLKeyboardWin32::scanKeyboard()
mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]);
}
}
+ mCurScanKey = KEY_NONE;
// Reset edges for next frame
for (key = 0; key < KEY_COUNT; key++)
@@ -321,4 +321,4 @@ U16 LLKeyboardWin32::inverseTranslateExtendedKey(const KEY translated_key)
return inverseTranslateKey(converted_key);
}
-#endif
+#endif // LL_WINDOWS
diff --git a/indra/llwindow/llkeyboardwin32.h b/indra/llwindow/llkeyboardwin32.h
index d0dfc5cfdd..ea7bb4d866 100644
--- a/indra/llwindow/llkeyboardwin32.h
+++ b/indra/llwindow/llkeyboardwin32.h
@@ -37,18 +37,19 @@ class LLKeyboardWin32 : public LLKeyboard
{
public:
LLKeyboardWin32();
- /*virtual*/ ~LLKeyboardWin32() {};
+ ~LLKeyboardWin32() {};
- /*virtual*/ bool handleKeyUp(const U16 key, MASK mask);
- /*virtual*/ bool handleKeyDown(const U16 key, MASK mask);
- /*virtual*/ void resetMaskKeys();
- /*virtual*/ MASK currentMask(bool for_mouse_event);
- /*virtual*/ void scanKeyboard();
- bool translateExtendedKey(const U16 os_key, const MASK mask, KEY *translated_key);
- U16 inverseTranslateExtendedKey(const KEY translated_key);
+ bool handleKeyUp(const U16 key, MASK mask) override;
+ bool handleKeyDown(const U16 key, MASK mask) override;
+ void resetMaskKeys() override;
+ MASK currentMask(bool for_mouse_event) override;
+ void scanKeyboard() override;
+
+ bool translateExtendedKey(const U16 os_key, const MASK mask, KEY *translated_key);
+ U16 inverseTranslateExtendedKey(const KEY translated_key);
protected:
- MASK updateModifiers();
+ MASK updateModifiers(const MASK mask) override;
//void setModifierKeyLevel( KEY key, bool new_state );
private:
std::map<U16, KEY> mTranslateNumpadMap;
diff --git a/indra/llwindow/llsdl.cpp b/indra/llwindow/llsdl.cpp
new file mode 100644
index 0000000000..6161bd2972
--- /dev/null
+++ b/indra/llwindow/llsdl.cpp
@@ -0,0 +1,102 @@
+/**
+ * @file llsdl.cpp
+ * @brief SDL2 initialization
+ *
+ * $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$
+ */
+
+#include <initializer_list>
+#include <list>
+
+#include "SDL2/SDL.h"
+
+#include "llerror.h"
+#include "llwindow.h"
+
+void sdl_logger(void *userdata, int category, SDL_LogPriority priority, const char *message)
+{
+ LL_DEBUGS("SDL2") << "log='" << message << "'" << LL_ENDL;
+}
+
+void init_sdl()
+{
+ SDL_version c_sdl_version;
+ SDL_VERSION(&c_sdl_version);
+ LL_INFOS() << "Compiled against SDL "
+ << int(c_sdl_version.major) << "."
+ << int(c_sdl_version.minor) << "."
+ << int(c_sdl_version.patch) << LL_ENDL;
+ SDL_version r_sdl_version;
+ SDL_GetVersion(&r_sdl_version);
+ LL_INFOS() << "Running with SDL "
+ << int(r_sdl_version.major) << "."
+ << int(r_sdl_version.minor) << "."
+ << int(r_sdl_version.patch) << LL_ENDL;
+#ifdef LL_LINUX
+ // For linux we SDL_INIT_VIDEO and _AUDIO
+ std::initializer_list<std::tuple< char const*, char const * > > hintList =
+ {
+ {SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR,"0"},
+ {SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,"1"},
+ {SDL_HINT_IME_INTERNAL_EDITING,"1"}
+ };
+
+ for (auto hint: hintList)
+ {
+ SDL_SetHint(std::get<0>(hint), std::get<1>(hint));
+ }
+
+ std::initializer_list<std::tuple<uint32_t, char const*, bool>> initList=
+ { {SDL_INIT_VIDEO,"SDL_INIT_VIDEO", true},
+ {SDL_INIT_AUDIO,"SDL_INIT_AUDIO", false},
+ };
+#else
+ // For non-linux platforms we still SDL_INIT_VIDEO because it is a pre-requisite
+ // for SDL_INIT_GAMECONTROLLER.
+ std::initializer_list<std::tuple<uint32_t, char const*, bool>> initList=
+ { {SDL_INIT_VIDEO,"SDL_INIT_VIDEO", false},
+ };
+#endif // LL_LINUX
+ // We SDL_INIT_GAMECONTROLLER later in the startup process to make it
+ // more likely we'll catch initial SDL_CONTROLLERDEVICEADDED events.
+
+ for (auto subSystem : initList)
+ {
+ if (SDL_InitSubSystem(std::get<0>(subSystem)) < 0)
+ {
+ LL_WARNS() << "SDL_InitSubSystem for " << std::get<1>(subSystem) << " failed " << SDL_GetError() << LL_ENDL;
+
+ if (std::get<2>(subSystem))
+ {
+ OSMessageBox("SDL_Init() failure", "error", OSMB_OK);
+ return;
+ }
+ }
+ }
+
+ SDL_LogSetOutputFunction(&sdl_logger, nullptr);
+}
+
+void quit_sdl()
+{
+ SDL_Quit();
+}
diff --git a/indra/llwindow/llsdl.h b/indra/llwindow/llsdl.h
new file mode 100644
index 0000000000..9fc8de129c
--- /dev/null
+++ b/indra/llwindow/llsdl.h
@@ -0,0 +1,30 @@
+/**
+ * @file llsdl.h
+ * @brief SDL2 initialization
+ *
+ * $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$
+ */
+
+#pragma once
+
+void init_sdl();
+void quit_sdl();
diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp
index 378e633cd2..93ac58ca6f 100644
--- a/indra/llwindow/llwindow.cpp
+++ b/indra/llwindow/llwindow.cpp
@@ -27,18 +27,19 @@
#include "linden_common.h"
#include "llwindowheadless.h"
-#if LL_MESA_HEADLESS
-#include "llwindowmesaheadless.h"
-#elif LL_SDL
-#include "llwindowsdl.h"
-#elif LL_WINDOWS
+#if LL_WINDOWS
#include "llwindowwin32.h"
#elif LL_DARWIN
#include "llwindowmacosx.h"
+#elif LL_MESA_HEADLESS
+#include "llwindowmesaheadless.h"
+#elif LL_LINUX
+#include "llwindowsdl.h"
#endif
#include "llerror.h"
#include "llkeyboard.h"
+#include "llsdl.h"
#include "llwindowcallbacks.h"
@@ -72,13 +73,13 @@ S32 OSMessageBox(const std::string& text, const std::string& caption, U32 type)
S32 result = 0;
LL_WARNS() << "OSMessageBox: " << text << LL_ENDL;
-#if LL_MESA_HEADLESS // !!! *FIX: (?)
- return OSBTN_OK;
-#elif LL_WINDOWS
+#if LL_WINDOWS
result = OSMessageBoxWin32(text, caption, type);
#elif LL_DARWIN
result = OSMessageBoxMacOSX(text, caption, type);
-#elif LL_SDL
+#elif LL_MESA_HEADLESS // !!! *FIX: (?)
+ return OSBTN_OK;
+#elif LL_LINUX
result = OSMessageBoxSDL(text, caption, type);
#else
#error("OSMessageBox not implemented for this platform!")
@@ -263,7 +264,7 @@ std::vector<std::string> LLWindow::getDynamicFallbackFontList()
return LLWindowWin32::getDynamicFallbackFontList();
#elif LL_DARWIN
return LLWindowMacOSX::getDynamicFallbackFontList();
-#elif LL_SDL
+#elif LL_LINUX
return LLWindowSDL::getDynamicFallbackFontList();
#else
return std::vector<std::string>();
@@ -342,12 +343,12 @@ bool LLSplashScreen::isVisible()
// static
LLSplashScreen *LLSplashScreen::create()
{
-#if LL_MESA_HEADLESS || LL_SDL // !!! *FIX: (?)
- return 0;
-#elif LL_WINDOWS
+#if LL_WINDOWS
return new LLSplashScreenWin32;
#elif LL_DARWIN
return new LLSplashScreenMacOSX;
+#elif LL_MESA_HEADLESS || LL_LINUX // !!! *FIX: (?)
+ return 0;
#else
#error("LLSplashScreen not implemented on this platform!")
#endif
@@ -415,15 +416,8 @@ LLWindow* LLWindowManager::createWindow(
if (use_gl)
{
-#if LL_MESA_HEADLESS
- new_window = new LLWindowMesaHeadless(callbacks,
- title, name, x, y, width, height, flags,
- fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth);
-#elif LL_SDL
- new_window = new LLWindowSDL(callbacks,
- title, x, y, width, height, flags,
- fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples);
-#elif LL_WINDOWS
+ init_sdl();
+#if LL_WINDOWS
new_window = new LLWindowWin32(callbacks,
title, name, x, y, width, height, flags,
fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples, max_cores, max_gl_version);
@@ -431,6 +425,14 @@ LLWindow* LLWindowManager::createWindow(
new_window = new LLWindowMacOSX(callbacks,
title, name, x, y, width, height, flags,
fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples);
+#elif LL_MESA_HEADLESS
+ new_window = new LLWindowMesaHeadless(callbacks,
+ title, name, x, y, width, height, flags,
+ fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth);
+#elif LL_LINUX
+ new_window = new LLWindowSDL(callbacks,
+ title, name, x, y, width, height, flags,
+ fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples);
#endif
}
else
@@ -462,6 +464,7 @@ bool LLWindowManager::destroyWindow(LLWindow* window)
window->close();
sWindowList.erase(window);
+ quit_sdl();
delete window;
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
index fcc4fd863a..e74142c7df 100644
--- a/indra/llwindow/llwindow.h
+++ b/indra/llwindow/llwindow.h
@@ -24,8 +24,7 @@
* $/LicenseInfo$
*/
-#ifndef LL_LLWINDOW_H
-#define LL_LLWINDOW_H
+#pragma once
#include "llrect.h"
#include "llcoord.h"
@@ -33,6 +32,7 @@
#include "llcursortypes.h"
#include "llinstancetracker.h"
#include "llsd.h"
+#include "llsdl.h"
class LLSplashScreen;
class LLPreeditor;
@@ -63,16 +63,16 @@ public:
virtual void show() = 0;
virtual void hide() = 0;
virtual void close() = 0;
- virtual bool getVisible() = 0;
- virtual bool getMinimized() = 0;
- virtual bool getMaximized() = 0;
+ virtual bool getVisible() const = 0;
+ virtual bool getMinimized() const = 0;
+ virtual bool getMaximized() const = 0;
virtual bool maximize() = 0;
virtual void minimize() = 0;
virtual void restore() = 0;
- bool getFullscreen() { return mFullscreen; };
- virtual bool getPosition(LLCoordScreen *position) = 0;
- virtual bool getSize(LLCoordScreen *size) = 0;
- virtual bool getSize(LLCoordWindow *size) = 0;
+ virtual bool getFullscreen() const { return mFullscreen; };
+ virtual bool getPosition(LLCoordScreen *position) const = 0;
+ virtual bool getSize(LLCoordScreen *size) const = 0;
+ virtual bool getSize(LLCoordWindow *size) const = 0;
virtual bool setPosition(LLCoordScreen position) = 0;
bool setSize(LLCoordScreen size);
bool setSize(LLCoordWindow size);
@@ -93,7 +93,7 @@ public:
virtual bool setCursorPosition(LLCoordWindow position) = 0;
virtual bool getCursorPosition(LLCoordWindow *position) = 0;
#if LL_WINDOWS
- virtual bool getCursorDelta(LLCoordCommon* delta) = 0;
+ virtual bool getCursorDelta(LLCoordCommon* delta) const = 0;
#endif
virtual void showCursor() = 0;
virtual void hideCursor() = 0;
@@ -135,14 +135,14 @@ public:
virtual bool copyTextToPrimary(const LLWString &src);
virtual void flashIcon(F32 seconds) = 0;
- virtual F32 getGamma() = 0;
+ virtual F32 getGamma() const = 0;
virtual bool setGamma(const F32 gamma) = 0; // Set the gamma
virtual void setFSAASamples(const U32 fsaa_samples) = 0; //set number of FSAA samples
- virtual U32 getFSAASamples() = 0;
+ virtual U32 getFSAASamples() const = 0;
virtual bool restoreGamma() = 0; // Restore original gamma table (before updating gamma)
- virtual ESwapMethod getSwapMethod() { return mSwapMethod; }
+ ESwapMethod getSwapMethod() { return mSwapMethod; }
virtual void processMiscNativeEvents();
- virtual void gatherInput() = 0;
+ virtual void gatherInput(bool app_has_focus) = 0;
virtual void delayInputProcessing() = 0;
virtual void swapBuffers() = 0;
virtual void bringToFront() = 0;
@@ -151,12 +151,12 @@ public:
// handy coordinate space conversion routines
// NB: screen to window and vice verse won't work on width/height coordinate pairs,
// as the conversion must take into account left AND right border widths, etc.
- virtual bool convertCoords( LLCoordScreen from, LLCoordWindow *to) = 0;
- virtual bool convertCoords( LLCoordWindow from, LLCoordScreen *to) = 0;
- virtual bool convertCoords( LLCoordWindow from, LLCoordGL *to) = 0;
- virtual bool convertCoords( LLCoordGL from, LLCoordWindow *to) = 0;
- virtual bool convertCoords( LLCoordScreen from, LLCoordGL *to) = 0;
- virtual bool convertCoords( LLCoordGL from, LLCoordScreen *to) = 0;
+ virtual bool convertCoords( LLCoordScreen from, LLCoordWindow *to) const = 0;
+ virtual bool convertCoords( LLCoordWindow from, LLCoordScreen *to) const = 0;
+ virtual bool convertCoords( LLCoordWindow from, LLCoordGL *to) const = 0;
+ virtual bool convertCoords( LLCoordGL from, LLCoordWindow *to) const = 0;
+ virtual bool convertCoords( LLCoordScreen from, LLCoordGL *to) const = 0;
+ virtual bool convertCoords( LLCoordGL from, LLCoordScreen *to) const = 0;
// query supported resolutions
virtual LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) = 0;
@@ -189,7 +189,7 @@ public:
static std::vector<std::string> getDynamicFallbackFontList();
// Provide native key event data
- virtual LLSD getNativeKeyData() { return LLSD::emptyMap(); }
+ virtual LLSD getNativeKeyData() const { return LLSD::emptyMap(); }
// Get system UI size based on DPI (for 96 DPI UI size should be 1.0)
virtual F32 getSystemUISize() { return 1.0; }
@@ -206,7 +206,7 @@ public:
return false;
};
- virtual S32 getRefreshRate() { return mRefreshRate; }
+ virtual S32 getRefreshRate() const { return mRefreshRate; }
protected:
LLWindow(LLWindowCallbacks* callbacks, bool fullscreen, U32 flags);
virtual ~LLWindow();
@@ -328,4 +328,3 @@ extern const S32 gURLProtocolWhitelistCount;
extern const std::string gURLProtocolWhitelist[];
//extern const std::string gURLProtocolWhitelistHandler[];
-#endif // _LL_window_h_
diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h
index 5696b69a59..96654b8838 100644
--- a/indra/llwindow/llwindowheadless.h
+++ b/indra/llwindow/llwindowheadless.h
@@ -32,77 +32,70 @@
class LLWindowHeadless : public LLWindow
{
public:
- /*virtual*/ void show() override {}
- /*virtual*/ void hide() override {}
- /*virtual*/ void close() override {}
- /*virtual*/ bool getVisible() override {return false;}
- /*virtual*/ bool getMinimized() override {return false;}
- /*virtual*/ bool getMaximized() override {return false;}
- /*virtual*/ bool maximize() override {return false;}
- /*virtual*/ void minimize() override {}
- /*virtual*/ void restore() override {}
- // TODO: LLWindow::getFullscreen() is (intentionally?) NOT virtual.
- // Apparently the coder of LLWindowHeadless didn't realize that. Is it a
- // mistake to shadow the base-class method with an LLWindowHeadless
- // override when called on the subclass, yet call the base-class method
- // when indirecting through a polymorphic pointer or reference?
- bool getFullscreen() {return false;}
- /*virtual*/ bool getPosition(LLCoordScreen *position) override {return false;}
- /*virtual*/ bool getSize(LLCoordScreen *size) override {return false;}
- /*virtual*/ bool getSize(LLCoordWindow *size) override {return false;}
- /*virtual*/ bool setPosition(LLCoordScreen position) override {return false;}
- /*virtual*/ bool setSizeImpl(LLCoordScreen size) override {return false;}
- /*virtual*/ bool setSizeImpl(LLCoordWindow size) override {return false;}
- /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override {return false;}
+ void show() override {}
+ void hide() override {}
+ void close() override {}
+ bool getVisible() const override {return false;}
+ bool getMinimized() const override {return false;}
+ bool getMaximized() const override {return false;}
+ bool maximize() override {return false;}
+ void minimize() override {}
+ void restore() override {}
+ bool getFullscreen() const override {return false;};
+ bool getPosition(LLCoordScreen *position) const override {return false;}
+ bool getSize(LLCoordScreen *size) const override {return false;}
+ bool getSize(LLCoordWindow *size) const override {return false;}
+ bool setPosition(LLCoordScreen position) override {return false;}
+ bool setSizeImpl(LLCoordScreen size) override {return false;}
+ bool setSizeImpl(LLCoordWindow size) override {return false;}
+ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override {return false;}
void* createSharedContext() override { return nullptr; }
void makeContextCurrent(void*) override {}
void destroySharedContext(void*) override {}
- /*virtual*/ void toggleVSync(bool enable_vsync) override { }
- /*virtual*/ bool setCursorPosition(LLCoordWindow position) override {return false;}
- /*virtual*/ bool getCursorPosition(LLCoordWindow *position) override {return false;}
+ void toggleVSync(bool enable_vsync) override { }
+ bool setCursorPosition(LLCoordWindow position) override {return false;}
+ bool getCursorPosition(LLCoordWindow *position) override {return false;}
#if LL_WINDOWS
- /*virtual*/ bool getCursorDelta(LLCoordCommon* delta) override { return false; }
+ bool getCursorDelta(LLCoordCommon* delta) const override { return false; }
#endif
- /*virtual*/ void showCursor() override {}
- /*virtual*/ void hideCursor() override {}
- /*virtual*/ void showCursorFromMouseMove() override {}
- /*virtual*/ void hideCursorUntilMouseMove() override {}
- /*virtual*/ bool isCursorHidden() override {return false;}
- /*virtual*/ void updateCursor() override {}
- //virtual ECursorType getCursor() override { return mCurrentCursor; }
- /*virtual*/ void captureMouse() override {}
- /*virtual*/ void releaseMouse() override {}
- /*virtual*/ void setMouseClipping( bool b ) override {}
- /*virtual*/ bool isClipboardTextAvailable() override {return false; }
- /*virtual*/ bool pasteTextFromClipboard(LLWString &dst) override {return false; }
- /*virtual*/ bool copyTextToClipboard(const LLWString &src) override {return false; }
- /*virtual*/ void flashIcon(F32 seconds) override {}
- /*virtual*/ F32 getGamma() override {return 1.0f; }
- /*virtual*/ bool setGamma(const F32 gamma) override {return false; } // Set the gamma
- /*virtual*/ void setFSAASamples(const U32 fsaa_samples) override { }
- /*virtual*/ U32 getFSAASamples() override { return 0; }
- /*virtual*/ bool restoreGamma() override {return false; } // Restore original gamma table (before updating gamma)
- //virtual ESwapMethod getSwapMethod() override { return mSwapMethod; }
- /*virtual*/ void gatherInput() override {}
- /*virtual*/ void delayInputProcessing() override {}
- /*virtual*/ void swapBuffers() override;
+ void showCursor() override {}
+ void hideCursor() override {}
+ void showCursorFromMouseMove() override {}
+ void hideCursorUntilMouseMove() override {}
+ bool isCursorHidden() override {return false;}
+ void updateCursor() override {}
+ void captureMouse() override {}
+ void releaseMouse() override {}
+ void setMouseClipping( bool b ) override {}
+ bool isClipboardTextAvailable() override {return false; }
+ bool pasteTextFromClipboard(LLWString &dst) override {return false; }
+ bool copyTextToClipboard(const LLWString &src) override {return false; }
+ void flashIcon(F32 seconds) override {}
+ F32 getGamma() const override {return 1.0f; }
+ bool setGamma(const F32 gamma) override {return false; } // Set the gamma
+ void setFSAASamples(const U32 fsaa_samples) override { }
+ U32 getFSAASamples() const override { return 0; }
+ bool restoreGamma() override {return false; } // Restore original gamma table (before updating gamma)
+ void gatherInput(bool app_has_focus) override {}
+ void delayInputProcessing() override {}
+ void swapBuffers() override;
// handy coordinate space conversion routines
- /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override { return false; }
- /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override { return false; }
- /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordGL *to) override { return false; }
- /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordWindow *to) override { return false; }
- /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordGL *to) override { return false; }
- /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordScreen *to) override { return false; }
+ bool convertCoords(LLCoordScreen from, LLCoordWindow *to) const override { return false; }
+ bool convertCoords(LLCoordWindow from, LLCoordScreen *to) const override { return false; }
+ bool convertCoords(LLCoordWindow from, LLCoordGL *to) const override { return false; }
+ bool convertCoords(LLCoordGL from, LLCoordWindow *to) const override { return false; }
+ bool convertCoords(LLCoordScreen from, LLCoordGL *to) const override { return false; }
+ bool convertCoords(LLCoordGL from, LLCoordScreen *to) const override { return false; }
- /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override { return NULL; }
- /*virtual*/ F32 getNativeAspectRatio() override { return 1.0f; }
- /*virtual*/ F32 getPixelAspectRatio() override { return 1.0f; }
- /*virtual*/ void setNativeAspectRatio(F32 ratio) override {}
+ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override { return NULL; }
+ F32 getNativeAspectRatio() override { return 1.0f; }
+ F32 getPixelAspectRatio() override { return 1.0f; }
+ void setNativeAspectRatio(F32 ratio) override {}
- /*virtual*/ void *getPlatformWindow() override { return 0; }
- /*virtual*/ void bringToFront() override {}
+ void *getPlatformWindow() override { return 0; }
+ void bringToFront() override {}
LLWindowHeadless(LLWindowCallbacks* callbacks,
const std::string& title, const std::string& name,
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index e95ad4d970..1883c6c9c1 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -28,6 +28,7 @@
#include "llwindowmacosx.h"
+#include "llgamecontrol.h"
#include "llkeyboardmacosx.h"
#include "llwindowcallbacks.h"
#include "llpreeditor.h"
@@ -625,7 +626,7 @@ void LLWindowMacOSX::updateMouseDeltas(float* deltas)
}
}
-void LLWindowMacOSX::getMouseDeltas(float* delta)
+void LLWindowMacOSX::getMouseDeltas(float* delta) const
{
delta[0] = mCursorLastEventDeltaX;
delta[1] = mCursorLastEventDeltaY;
@@ -845,7 +846,7 @@ bool LLWindowMacOSX::isValid()
return (mWindow != NULL);
}
-bool LLWindowMacOSX::getVisible()
+bool LLWindowMacOSX::getVisible() const
{
bool result = false;
@@ -860,12 +861,12 @@ bool LLWindowMacOSX::getVisible()
return(result);
}
-bool LLWindowMacOSX::getMinimized()
+bool LLWindowMacOSX::getMinimized() const
{
return mMinimized;
}
-bool LLWindowMacOSX::getMaximized()
+bool LLWindowMacOSX::getMaximized() const
{
return mMaximized;
}
@@ -879,17 +880,13 @@ bool LLWindowMacOSX::maximize()
return mMaximized;
}
-bool LLWindowMacOSX::getFullscreen()
-{
- return mFullscreen;
-}
-
-void LLWindowMacOSX::gatherInput()
+void LLWindowMacOSX::gatherInput(bool app_has_focus)
{
updateCursor();
+ LLGameControl::processEvents(app_has_focus);
}
-bool LLWindowMacOSX::getPosition(LLCoordScreen *position)
+bool LLWindowMacOSX::getPosition(LLCoordScreen *position) const
{
S32 err = -1;
@@ -916,7 +913,7 @@ bool LLWindowMacOSX::getPosition(LLCoordScreen *position)
return (err == noErr);
}
-bool LLWindowMacOSX::getSize(LLCoordScreen *size)
+bool LLWindowMacOSX::getSize(LLCoordScreen *size) const
{
S32 err = -1;
@@ -942,7 +939,7 @@ bool LLWindowMacOSX::getSize(LLCoordScreen *size)
return (err == noErr);
}
-bool LLWindowMacOSX::getSize(LLCoordWindow *size)
+bool LLWindowMacOSX::getSize(LLCoordWindow *size) const
{
S32 err = -1;
@@ -1016,7 +1013,7 @@ void LLWindowMacOSX::restoreGLContext()
CGLSetCurrentContext(mContext);
}
-F32 LLWindowMacOSX::getGamma()
+F32 LLWindowMacOSX::getGamma() const
{
F32 result = 2.2; // Default to something sane
@@ -1050,7 +1047,7 @@ F32 LLWindowMacOSX::getGamma()
return result;
}
-U32 LLWindowMacOSX::getFSAASamples()
+U32 LLWindowMacOSX::getFSAASamples() const
{
return mFSAASamples;
}
@@ -1376,21 +1373,21 @@ LLWindow::LLWindowResolution* LLWindowMacOSX::getSupportedResolutions(S32 &num_r
return mSupportedResolutions;
}
-bool LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow *to)
+bool LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow *to) const
{
to->mX = from.mX;
to->mY = from.mY;
return true;
}
-bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to)
+bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to) const
{
to->mX = from.mX;
to->mY = from.mY;
return true;
}
-bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to)
+bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to) const
{
if(mWindow)
{
@@ -1409,7 +1406,7 @@ bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to)
return false;
}
-bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to)
+bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to) const
{
if(mWindow)
{
@@ -1428,14 +1425,14 @@ bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to)
return false;
}
-bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordGL *to)
+bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordGL *to) const
{
LLCoordWindow window_coord;
return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
}
-bool LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordScreen *to)
+bool LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordScreen *to) const
{
LLCoordWindow window_coord;
@@ -2321,7 +2318,7 @@ bool LLWindowMacOSX::getInputDevices(U32 device_type_filter,
return return_value;
}
-LLSD LLWindowMacOSX::getNativeKeyData()
+LLSD LLWindowMacOSX::getNativeKeyData() const
{
LLSD result = LLSD::emptyMap();
@@ -2505,6 +2502,7 @@ void LLWindowMacOSX::interruptLanguageTextInput()
commitCurrentPreedit(mGLView);
}
+// static
std::vector<std::string> LLWindowMacOSX::getDisplaysResolutionList()
{
std::vector<std::string> resolution_list;
@@ -2534,7 +2532,7 @@ std::vector<std::string> LLWindowMacOSX::getDisplaysResolutionList()
return resolution_list;
}
-//static
+// static
std::vector<std::string> LLWindowMacOSX::getDynamicFallbackFontList()
{
// Fonts previously in getFontListSans() have moved to fonts.xml.
diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h
index 211ae872c6..14a56a038e 100644
--- a/indra/llwindow/llwindowmacosx.h
+++ b/indra/llwindow/llwindowmacosx.h
@@ -47,16 +47,15 @@ public:
void show() override;
void hide() override;
void close() override;
- bool getVisible() override;
- bool getMinimized() override;
- bool getMaximized() override;
+ bool getVisible() const override;
+ bool getMinimized() const override;
+ bool getMaximized() const override;
bool maximize() override;
void minimize() override;
void restore() override;
- bool getFullscreen();
- bool getPosition(LLCoordScreen *position) override;
- bool getSize(LLCoordScreen *size) override;
- bool getSize(LLCoordWindow *size) override;
+ bool getPosition(LLCoordScreen *position) const override;
+ bool getSize(LLCoordScreen *size) const override;
+ bool getSize(LLCoordWindow *size) const override;
bool setPosition(LLCoordScreen position) override;
bool setSizeImpl(LLCoordScreen size) override;
bool setSizeImpl(LLCoordWindow size) override;
@@ -77,23 +76,22 @@ public:
bool pasteTextFromClipboard(LLWString &dst) override;
bool copyTextToClipboard(const LLWString & src) override;
void flashIcon(F32 seconds) override;
- F32 getGamma() override;
+ F32 getGamma() const override;
bool setGamma(const F32 gamma) override; // Set the gamma
- U32 getFSAASamples() override;
+ U32 getFSAASamples() const override;
void setFSAASamples(const U32 fsaa_samples) override;
bool restoreGamma() override; // Restore original gamma table (before updating gamma)
- ESwapMethod getSwapMethod() override { return mSwapMethod; }
- void gatherInput() override;
+ void gatherInput(bool app_has_focus) override;
void delayInputProcessing() override {};
void swapBuffers() override;
// handy coordinate space conversion routines
- bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override;
- bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override;
- bool convertCoords(LLCoordWindow from, LLCoordGL *to) override;
- bool convertCoords(LLCoordGL from, LLCoordWindow *to) override;
- bool convertCoords(LLCoordScreen from, LLCoordGL *to) override;
- bool convertCoords(LLCoordGL from, LLCoordScreen *to) override;
+ bool convertCoords(LLCoordScreen from, LLCoordWindow *to) const override;
+ bool convertCoords(LLCoordWindow from, LLCoordScreen *to) const override;
+ bool convertCoords(LLCoordWindow from, LLCoordGL *to) const override;
+ bool convertCoords(LLCoordGL from, LLCoordWindow *to) const override;
+ bool convertCoords(LLCoordScreen from, LLCoordGL *to) const override;
+ bool convertCoords(LLCoordGL from, LLCoordScreen *to) const override;
LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override;
F32 getNativeAspectRatio() override;
@@ -125,14 +123,14 @@ public:
static std::vector<std::string> getDynamicFallbackFontList();
// Provide native key event data
- LLSD getNativeKeyData() override;
+ LLSD getNativeKeyData() const override;
void* getWindow() { return mWindow; }
LLWindowCallbacks* getCallbacks() { return mCallbacks; }
LLPreeditor* getPreeditor() { return mPreeditor; }
void updateMouseDeltas(float* deltas);
- void getMouseDeltas(float* delta);
+ void getMouseDeltas(float* delta) const;
void handleDragNDrop(std::string url, LLWindowCallbacks::DragNDropAction action);
diff --git a/indra/llwindow/llwindowmesaheadless.h b/indra/llwindow/llwindowmesaheadless.h
index 0bf8c46a30..0a29ddfa5b 100644
--- a/indra/llwindow/llwindowmesaheadless.h
+++ b/indra/llwindow/llwindowmesaheadless.h
@@ -36,64 +36,64 @@
class LLWindowMesaHeadless : public LLWindow
{
public:
- /*virtual*/ void show() {};
- /*virtual*/ void hide() {};
- /*virtual*/ void close() {};
- /*virtual*/ bool getVisible() {return false;};
- /*virtual*/ bool getMinimized() {return false;};
- /*virtual*/ bool getMaximized() {return false;};
- /*virtual*/ bool maximize() {return false;};
- /*virtual*/ void minimize() {};
- /*virtual*/ void restore() {};
- /*virtual*/ bool getFullscreen() {return false;};
- /*virtual*/ bool getPosition(LLCoordScreen *position) {return false;};
- /*virtual*/ bool getSize(LLCoordScreen *size) {return false;};
- /*virtual*/ bool getSize(LLCoordWindow *size) {return false;};
- /*virtual*/ bool setPosition(LLCoordScreen position) {return false;};
- /*virtual*/ bool setSizeImpl(LLCoordScreen size) {return false;};
- /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL) {return false;};
- /*virtual*/ bool setCursorPosition(LLCoordWindow position) {return false;};
- /*virtual*/ bool getCursorPosition(LLCoordWindow *position) {return false;};
- /*virtual*/ void showCursor() {};
- /*virtual*/ void hideCursor() {};
- /*virtual*/ void showCursorFromMouseMove() {};
- /*virtual*/ void hideCursorUntilMouseMove() {};
- /*virtual*/ bool isCursorHidden() {return false;};
- /*virtual*/ void updateCursor() {};
- //virtual ECursorType getCursor() { return mCurrentCursor; };
- /*virtual*/ void captureMouse() {};
- /*virtual*/ void releaseMouse() {};
- /*virtual*/ void setMouseClipping( bool b ) {};
- /*virtual*/ bool isClipboardTextAvailable() {return false; };
- /*virtual*/ bool pasteTextFromClipboard(LLWString &dst) {return false; };
- /*virtual*/ bool copyTextToClipboard(const LLWString &src) {return false; };
- /*virtual*/ void flashIcon(F32 seconds) {};
- /*virtual*/ F32 getGamma() {return 1.0f; };
- /*virtual*/ bool setGamma(const F32 gamma) {return false; }; // Set the gamma
- /*virtual*/ bool restoreGamma() {return false; }; // Restore original gamma table (before updating gamma)
- /*virtual*/ void setFSAASamples(const U32 fsaa_samples) { /* FSAA not supported yet on Mesa headless.*/ }
- /*virtual*/ U32 getFSAASamples() { return 0; }
- //virtual ESwapMethod getSwapMethod() { return mSwapMethod; }
- /*virtual*/ void gatherInput() {};
- /*virtual*/ void delayInputProcessing() {};
- /*virtual*/ void swapBuffers();
- /*virtual*/ void restoreGLContext() {};
+ void show() override {};
+ void hide() override {};
+ void close() override {};
+ bool getVisible() override {return false;};
+ bool getMinimized() override {return false;};
+ bool getMaximized() override {return false;};
+ bool maximize() override {return false;};
+ void minimize() override {};
+ void restore() override {};
+ bool getFullscreen() override {return false;};
+ bool getPosition(LLCoordScreen *position) override {return false;};
+ bool getSize(LLCoordScreen *size) override {return false;};
+ bool getSize(LLCoordWindow *size) override {return false;};
+ bool setPosition(LLCoordScreen position) override {return false;};
+ bool setSizeImpl(LLCoordScreen size) override {return false;};
+ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL) override {return false;};
+ bool setCursorPosition(LLCoordWindow position) override {return false;};
+ bool getCursorPosition(LLCoordWindow *position) override {return false;};
+ void showCursor() override {};
+ void hideCursor() override {};
+ void showCursorFromMouseMove() override {};
+ void hideCursorUntilMouseMove() override {};
+ bool isCursorHidden() override {return false;};
+ void updateCursor() override {};
+ //ECursorType getCursor() override { return mCurrentCursor; };
+ void captureMouse() override {};
+ void releaseMouse() override {};
+ void setMouseClipping( bool b ) override {};
+ bool isClipboardTextAvailable() override {return false; };
+ bool pasteTextFromClipboard(LLWString &dst) override {return false; };
+ bool copyTextToClipboard(const LLWString &src) override {return false; };
+ void flashIcon(F32 seconds) override {};
+ F32 getGamma() override {return 1.0f; };
+ bool setGamma(const F32 gamma) override {return false; }; // Set the gamma
+ bool restoreGamma() override {return false; }; // Restore original gamma table (before updating gamma)
+ void setFSAASamples(const U32 fsaa_samples) override { /* FSAA not supported yet on Mesa headless.*/ }
+ U32 getFSAASamples() override { return 0; }
+ //ESwapMethod getSwapMethod() override { return mSwapMethod; }
+ void gatherInput(bool app_has_focus) override {};
+ void delayInputProcessing() override {};
+ void swapBuffers() override;
+ void restoreGLContext() override {};
// handy coordinate space conversion routines
- /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordWindow *to) { return false; };
- /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordScreen *to) { return false; };
- /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordGL *to) { return false; };
- /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordWindow *to) { return false; };
- /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordGL *to) { return false; };
- /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordScreen *to) { return false; };
+ bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override { return false; };
+ bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override { return false; };
+ bool convertCoords(LLCoordWindow from, LLCoordGL *to) override { return false; };
+ bool convertCoords(LLCoordGL from, LLCoordWindow *to) override { return false; };
+ bool convertCoords(LLCoordScreen from, LLCoordGL *to) override { return false; };
+ bool convertCoords(LLCoordGL from, LLCoordScreen *to) override { return false; };
- /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) { return NULL; };
- /*virtual*/ F32 getNativeAspectRatio() { return 1.0f; };
- /*virtual*/ F32 getPixelAspectRatio() { return 1.0f; };
- /*virtual*/ void setNativeAspectRatio(F32 ratio) {}
+ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override { return NULL; };
+ F32 getNativeAspectRatio() override { return 1.0f; };
+ F32 getPixelAspectRatio() override { return 1.0f; };
+ void setNativeAspectRatio(F32 ratio) override {}
- /*virtual*/ void *getPlatformWindow() { return 0; };
- /*virtual*/ void bringToFront() {};
+ void *getPlatformWindow() override { return 0; };
+ void bringToFront() override {};
LLWindowMesaHeadless(LLWindowCallbacks* callbacks,
const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height,
@@ -112,9 +112,9 @@ public:
LLSplashScreenMesaHeadless() {};
virtual ~LLSplashScreenMesaHeadless() {};
- /*virtual*/ void showImpl() {};
- /*virtual*/ void updateImpl(const std::string& mesg) {};
- /*virtual*/ void hideImpl() {};
+ void showImpl() override {};
+ void updateImpl(const std::string& mesg) override {};
+ void hideImpl() override {};
};
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
index 89f0d152c6..ea95a5aa2e 100644
--- a/indra/llwindow/llwindowsdl.cpp
+++ b/indra/llwindow/llwindowsdl.cpp
@@ -37,6 +37,7 @@
#include "llstring.h"
#include "lldir.h"
#include "llfindlocale.h"
+#include "llgamecontrol.h"
#ifdef LL_GLIB
#include <glib.h>
@@ -59,9 +60,6 @@ extern bool gDebugWindowProc;
const S32 MAX_NUM_RESOLUTIONS = 200;
-// static variable for ATI mouse cursor crash work-around:
-static bool ATIbug = false;
-
//
// LLWindowSDL
//
@@ -113,250 +111,8 @@ Display* LLWindowSDL::get_SDL_Display(void)
}
#endif // LL_X11
-#if LL_X11
-
-// Clipboard handing via native X11, base on the implementation in Cool VL by Henri Beauchamp
-
-namespace
-{
- std::array<Atom, 3> gSupportedAtoms;
-
- Atom XA_CLIPBOARD;
- Atom XA_TARGETS;
- Atom PVT_PASTE_BUFFER;
- long const MAX_PASTE_BUFFER_SIZE = 16383;
-
- void filterSelectionRequest( XEvent aEvent )
- {
- auto *display = LLWindowSDL::getSDLDisplay();
- auto &request = aEvent.xselectionrequest;
-
- XSelectionEvent reply { SelectionNotify, aEvent.xany.serial, aEvent.xany.send_event, display,
- request.requestor, request.selection, request.target,
- request.property,request.time };
-
- if (request.target == XA_TARGETS)
- {
- XChangeProperty(display, request.requestor, request.property,
- XA_ATOM, 32, PropModeReplace,
- (unsigned char *) &gSupportedAtoms.front(), gSupportedAtoms.size());
- }
- else if (std::find(gSupportedAtoms.begin(), gSupportedAtoms.end(), request.target) !=
- gSupportedAtoms.end())
- {
- std::string utf8;
- if (request.selection == XA_PRIMARY)
- utf8 = wstring_to_utf8str(gWindowImplementation->getPrimaryText());
- else
- utf8 = wstring_to_utf8str(gWindowImplementation->getSecondaryText());
-
- XChangeProperty(display, request.requestor, request.property,
- request.target, 8, PropModeReplace,
- (unsigned char *) utf8.c_str(), utf8.length());
- }
- else if (request.selection == XA_CLIPBOARD)
- {
- // Did not have what they wanted, so no property set
- reply.property = None;
- }
- else
- return;
-
- XSendEvent(request.display, request.requestor, False, NoEventMask, (XEvent *) &reply);
- XSync(display, False);
- }
-
- void filterSelectionClearRequest( XEvent aEvent )
- {
- auto &request = aEvent.xselectionrequest;
- if (request.selection == XA_PRIMARY)
- gWindowImplementation->clearPrimaryText();
- else if (request.selection == XA_CLIPBOARD)
- gWindowImplementation->clearSecondaryText();
- }
-
- int x11_clipboard_filter(void*, SDL_Event *evt)
- {
- Display *display = LLWindowSDL::getSDLDisplay();
- if (!display)
- return 1;
-
- if (evt->type != SDL_SYSWMEVENT)
- return 1;
-
- auto xevent = evt->syswm.msg->msg.x11.event;
-
- if (xevent.type == SelectionRequest)
- filterSelectionRequest( xevent );
- else if (xevent.type == SelectionClear)
- filterSelectionClearRequest( xevent );
- return 1;
- }
-
- bool grab_property(Display* display, Window window, Atom selection, Atom target)
- {
- if( !display )
- return false;
-
- maybe_lock_display();
-
- XDeleteProperty(display, window, PVT_PASTE_BUFFER);
- XFlush(display);
-
- XConvertSelection(display, selection, target, PVT_PASTE_BUFFER, window, CurrentTime);
-
- // Unlock the connection so that the SDL event loop may function
- maybe_unlock_display();
-
- const auto start{ SDL_GetTicks() };
- const auto end{ start + 1000 };
-
- XEvent xevent {};
- bool response = false;
-
- do
- {
- SDL_Event event {};
-
- // Wait for an event
- SDL_WaitEvent(&event);
-
- // If the event is a window manager event
- if (event.type == SDL_SYSWMEVENT)
- {
- xevent = event.syswm.msg->msg.x11.event;
-
- if (xevent.type == SelectionNotify && xevent.xselection.requestor == window)
- response = true;
- }
- } while (!response && SDL_GetTicks() < end );
-
- return response && xevent.xselection.property != None;
- }
-}
-
-void LLWindowSDL::initialiseX11Clipboard()
-{
- if (!mSDL_Display)
- return;
-
- SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
- SDL_SetEventFilter(x11_clipboard_filter, nullptr);
-
- maybe_lock_display();
-
- XA_CLIPBOARD = XInternAtom(mSDL_Display, "CLIPBOARD", False);
-
- gSupportedAtoms[0] = XInternAtom(mSDL_Display, "UTF8_STRING", False);
- gSupportedAtoms[1] = XInternAtom(mSDL_Display, "COMPOUND_TEXT", False);
- gSupportedAtoms[2] = XA_STRING;
-
- // TARGETS atom
- XA_TARGETS = XInternAtom(mSDL_Display, "TARGETS", False);
-
- // SL_PASTE_BUFFER atom
- PVT_PASTE_BUFFER = XInternAtom(mSDL_Display, "FS_PASTE_BUFFER", False);
-
- maybe_unlock_display();
-}
-
-bool LLWindowSDL::getSelectionText( Atom aSelection, Atom aType, LLWString &text )
-{
- if( !mSDL_Display )
- return false;
-
- if( !grab_property(mSDL_Display, mSDL_XWindowID, aSelection,aType ) )
- return false;
-
- maybe_lock_display();
-
- Atom type;
- int format{};
- unsigned long len{},remaining {};
- unsigned char* data = nullptr;
- int res = XGetWindowProperty(mSDL_Display, mSDL_XWindowID,
- PVT_PASTE_BUFFER, 0, MAX_PASTE_BUFFER_SIZE, False,
- AnyPropertyType, &type, &format, &len,
- &remaining, &data);
- if (data && len)
- {
- text = LLWString(
- utf8str_to_wstring(reinterpret_cast< char const *>( data ) )
- );
- XFree(data);
- }
-
- maybe_unlock_display();
- return res == Success;
-}
-
-bool LLWindowSDL::getSelectionText(Atom selection, LLWString& text)
-{
- if (!mSDL_Display)
- return false;
-
- maybe_lock_display();
-
- Window owner = XGetSelectionOwner(mSDL_Display, selection);
- if (owner == None)
- {
- if (selection == XA_PRIMARY)
- {
- owner = DefaultRootWindow(mSDL_Display);
- selection = XA_CUT_BUFFER0;
- }
- else
- {
- maybe_unlock_display();
- return false;
- }
- }
-
- maybe_unlock_display();
-
- for( Atom atom : gSupportedAtoms )
- {
- if(getSelectionText(selection, atom, text ) )
- return true;
- }
-
- return false;
-}
-
-bool LLWindowSDL::setSelectionText(Atom selection, const LLWString& text)
-{
- maybe_lock_display();
-
- if (selection == XA_PRIMARY)
- {
- std::string utf8 = wstring_to_utf8str(text);
- XStoreBytes(mSDL_Display, utf8.c_str(), utf8.length() + 1);
- mPrimaryClipboard = text;
- }
- else
- mSecondaryClipboard = text;
-
- XSetSelectionOwner(mSDL_Display, selection, mSDL_XWindowID, CurrentTime);
-
- auto owner = XGetSelectionOwner(mSDL_Display, selection);
-
- maybe_unlock_display();
-
- return owner == mSDL_XWindowID;
-}
-
-Display* LLWindowSDL::getSDLDisplay()
-{
- if (gWindowImplementation)
- return gWindowImplementation->mSDL_Display;
- return nullptr;
-}
-
-#endif
-
-
LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks,
- const std::string& title, S32 x, S32 y, S32 width,
+ const std::string& title, const std::string& name, S32 x, S32 y, S32 width,
S32 height, U32 flags,
bool fullscreen, bool clearBg,
bool enable_vsync, bool use_gl,
@@ -390,7 +146,7 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks,
mOriginalAspectRatio = 1024.0 / 768.0;
if (title.empty())
- mWindowTitle = "SDL Window"; // *FIX: (?)
+ mWindowTitle = "Second Life";
else
mWindowTitle = title;
@@ -409,10 +165,7 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks,
// Stash an object pointer for OSMessageBox()
gWindowImplementation = this;
-#if LL_X11
mFlashing = false;
- initialiseX11Clipboard();
-#endif // LL_X11
mKeyVirtualKey = 0;
mKeyModifiers = KMOD_NONE;
@@ -434,145 +187,6 @@ static SDL_Surface *Load_BMP_Resource(const char *basename)
return SDL_LoadBMP(path_buffer);
}
-#if LL_X11
-// This is an XFree86/XOrg-specific hack for detecting the amount of Video RAM
-// on this machine. It works by searching /var/log/var/log/Xorg.?.log or
-// /var/log/XFree86.?.log for a ': (VideoRAM ?|Memory): (%d+) kB' regex, where
-// '?' is the X11 display number derived from $DISPLAY
-static int x11_detect_VRAM_kb_fp(FILE *fp, const char *prefix_str)
-{
- const int line_buf_size = 1000;
- char line_buf[line_buf_size];
- while (fgets(line_buf, line_buf_size, fp))
- {
- //LL_DEBUGS() << "XLOG: " << line_buf << LL_ENDL;
-
- // Why the ad-hoc parser instead of using a regex? Our
- // favourite regex implementation - libboost_regex - is
- // quite a heavy and troublesome dependency for the client, so
- // it seems a shame to introduce it for such a simple task.
- // *FIXME: libboost_regex is a dependency now anyway, so we may
- // as well use it instead of this hand-rolled nonsense.
- const char *part1_template = prefix_str;
- const char part2_template[] = " kB";
- char *part1 = strstr(line_buf, part1_template);
- if (part1) // found start of matching line
- {
- part1 = &part1[strlen(part1_template)]; // -> after
- char *part2 = strstr(part1, part2_template);
- if (part2) // found end of matching line
- {
- // now everything between part1 and part2 is
- // supposed to be numeric, describing the
- // number of kB of Video RAM supported
- int rtn = 0;
- for (; part1 < part2; ++part1)
- {
- if (*part1 < '0' || *part1 > '9')
- {
- // unexpected char, abort parse
- rtn = 0;
- break;
- }
- rtn *= 10;
- rtn += (*part1) - '0';
- }
- if (rtn > 0)
- {
- // got the kB number. return it now.
- return rtn;
- }
- }
- }
- }
- return 0; // 'could not detect'
-}
-
-static int x11_detect_VRAM_kb()
-{
- std::string x_log_location("/var/log/");
- std::string fname;
- int rtn = 0; // 'could not detect'
- int display_num = 0;
- FILE *fp;
- char *display_env = getenv("DISPLAY"); // e.g. :0 or :0.0 or :1.0 etc
- // parse DISPLAY number so we can go grab the right log file
- if (display_env[0] == ':' &&
- display_env[1] >= '0' && display_env[1] <= '9')
- {
- display_num = display_env[1] - '0';
- }
-
- // *TODO: we could be smarter and see which of Xorg/XFree86 has the
- // freshest time-stamp.
-
- // Try Xorg log first
- fname = x_log_location;
- fname += "Xorg.";
- fname += ('0' + display_num);
- fname += ".log";
- fp = fopen(fname.c_str(), "r");
- if (fp)
- {
- LL_INFOS() << "Looking in " << fname
- << " for VRAM info..." << LL_ENDL;
- rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: ");
- fclose(fp);
- if (0 == rtn)
- {
- fp = fopen(fname.c_str(), "r");
- if (fp)
- {
- rtn = x11_detect_VRAM_kb_fp(fp, ": Video RAM: ");
- fclose(fp);
- if (0 == rtn)
- {
- fp = fopen(fname.c_str(), "r");
- if (fp)
- {
- rtn = x11_detect_VRAM_kb_fp(fp, ": Memory: ");
- fclose(fp);
- }
- }
- }
- }
- }
- else
- {
- LL_INFOS() << "Could not open " << fname
- << " - skipped." << LL_ENDL;
- // Try old XFree86 log otherwise
- fname = x_log_location;
- fname += "XFree86.";
- fname += ('0' + display_num);
- fname += ".log";
- fp = fopen(fname.c_str(), "r");
- if (fp)
- {
- LL_INFOS() << "Looking in " << fname
- << " for VRAM info..." << LL_ENDL;
- rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: ");
- fclose(fp);
- if (0 == rtn)
- {
- fp = fopen(fname.c_str(), "r");
- if (fp)
- {
- rtn = x11_detect_VRAM_kb_fp(fp, ": Memory: ");
- fclose(fp);
- }
- }
- }
- else
- {
- LL_INFOS() << "Could not open " << fname
- << " - skipped." << LL_ENDL;
- }
- }
- return rtn;
-}
-#endif // LL_X11
-
void LLWindowSDL::setTitle(const std::string title)
{
SDL_SetWindowTitle( mWindow, title.c_str() );
@@ -633,8 +247,10 @@ void LLWindowSDL::tryFindFullscreenSize( int &width, int &height )
bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool enable_vsync)
{
- //bool glneedsinit = false;
-
+ if (width == 0)
+ width = 1024;
+ if (height == 0)
+ width = 768;
LL_INFOS() << "createContext, fullscreen=" << fullscreen <<
" size=" << width << "x" << height << LL_ENDL;
@@ -642,54 +258,14 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b
mGrabbyKeyFlags = 0;
mReallyCapturedCount = 0;
- std::initializer_list<std::tuple< char const*, char const * > > hintList =
- {
- {SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR,"0"},
- {SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,"1"},
- {SDL_HINT_IME_INTERNAL_EDITING,"1"}
- };
-
- for( auto hint: hintList )
- {
- SDL_SetHint( std::get<0>(hint), std::get<1>(hint));
- }
-
- std::initializer_list<std::tuple<uint32_t, char const*, bool>> initList=
- { {SDL_INIT_VIDEO,"SDL_INIT_VIDEO", true},
- {SDL_INIT_AUDIO,"SDL_INIT_AUDIO", false},
- {SDL_INIT_GAMECONTROLLER,"SDL_INIT_GAMECONTROLLER", false},
- {SDL_INIT_SENSOR,"SDL_INIT_SENSOR", false}
- };
-
- for( auto subSystem : initList)
- {
- if( SDL_InitSubSystem( std::get<0>(subSystem) ) < 0 )
- {
- LL_WARNS() << "SDL_InitSubSystem for " << std::get<1>(subSystem) << " failed " << SDL_GetError() << LL_ENDL;
-
- if( std::get<2>(subSystem))
- setupFailure("SDL_Init() failure", "error", OSMB_OK);
-
- }
- }
-
- SDL_version c_sdl_version;
- SDL_VERSION(&c_sdl_version);
- LL_INFOS() << "Compiled against SDL "
- << int(c_sdl_version.major) << "."
- << int(c_sdl_version.minor) << "."
- << int(c_sdl_version.patch) << LL_ENDL;
- SDL_version r_sdl_version;
- SDL_GetVersion(&r_sdl_version);
- LL_INFOS() << " Running against SDL "
- << int(r_sdl_version.major) << "."
- << int(r_sdl_version.minor) << "."
- << int(r_sdl_version.patch) << LL_ENDL;
-
if (width == 0)
width = 1024;
if (height == 0)
width = 768;
+ if (x == 0)
+ x = SDL_WINDOWPOS_UNDEFINED;
+ if (y == 0)
+ y = SDL_WINDOWPOS_UNDEFINED;
mFullscreen = fullscreen;
@@ -703,51 +279,54 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b
mSDLFlags = sdlflags;
+ // Setup default backing colors
GLint redBits{8}, greenBits{8}, blueBits{8}, alphaBits{8};
+ GLint depthBits{24}, stencilBits{8};
- GLint depthBits{(bits <= 16) ? 16 : 24}, stencilBits{8};
-
- if (getenv("LL_GL_NO_STENCIL"))
- stencilBits = 0;
-
- SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, alphaBits);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, redBits);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, greenBits);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, blueBits);
- SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, depthBits );
-
- // We need stencil support for a few (minor) things.
- if (stencilBits)
- SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencilBits);
- // *FIX: try to toggle vsync here?
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, alphaBits);
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, depthBits);
+ SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencilBits);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
- if (mFSAASamples > 0)
+ U32 context_flags = 0;
+ if (gDebugGL)
{
- SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
- SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, mFSAASamples);
+ context_flags |= SDL_GL_CONTEXT_DEBUG_FLAG;
}
-
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, context_flags);
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
- mWindow = SDL_CreateWindow( mWindowTitle.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, mSDLFlags );
- if( mWindow )
+ // Create the window
+ mWindow = SDL_CreateWindow(mWindowTitle.c_str(), x, y, width, height, mSDLFlags);
+ if (mWindow == nullptr)
{
- mContext = SDL_GL_CreateContext( mWindow );
+ LL_WARNS() << "Window creation failure. SDL: " << SDL_GetError() << LL_ENDL;
+ setupFailure("Window creation error", "Error", OSMB_OK);
+ return false;
+ }
- if( mContext == 0 )
- {
- LL_WARNS() << "Cannot create GL context " << SDL_GetError() << LL_ENDL;
- setupFailure("GL Context creation error creation error", "Error", OSMB_OK);
- return false;
- }
- // SDL_GL_SetSwapInterval(1);
- mSurface = SDL_GetWindowSurface( mWindow );
+ // Create the context
+ mContext = SDL_GL_CreateContext(mWindow);
+ if(!mContext)
+ {
+ LL_WARNS() << "Cannot create GL context " << SDL_GetError() << LL_ENDL;
+ setupFailure("GL Context creation error", "Error", OSMB_OK);
+ return false;
}
+ if (SDL_GL_MakeCurrent(mWindow, mContext) != 0)
+ {
+ LL_WARNS() << "Failed to make context current. SDL: " << SDL_GetError() << LL_ENDL;
+ setupFailure("GL Context failed to set current failure", "Error", OSMB_OK);
+ return false;
+ }
- if( mFullscreen )
+ mSurface = SDL_GetWindowSurface(mWindow);
+ if(mFullscreen)
{
if (mSurface)
{
@@ -788,40 +367,6 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b
}
}
- // Set the application icon.
- SDL_Surface *bmpsurface;
- bmpsurface = Load_BMP_Resource("ll_icon.BMP");
- if (bmpsurface)
- {
- SDL_SetWindowIcon(mWindow, bmpsurface);
- SDL_FreeSurface(bmpsurface);
- bmpsurface = NULL;
- }
-
- // Detect video memory size.
-# if LL_X11
- gGLManager.mVRAM = x11_detect_VRAM_kb() / 1024;
- if (gGLManager.mVRAM != 0)
- {
- LL_INFOS() << "X11 log-parser detected " << gGLManager.mVRAM << "MB VRAM." << LL_ENDL;
- } else
-# endif // LL_X11
- {
- // fallback to letting SDL detect VRAM.
- // note: I've not seen SDL's detection ever actually find
- // VRAM != 0, but if SDL *does* detect it then that's a bonus.
- gGLManager.mVRAM = 0;
- if (gGLManager.mVRAM != 0)
- {
- LL_INFOS() << "SDL detected " << gGLManager.mVRAM << "MB VRAM." << LL_ENDL;
- }
- }
- // If VRAM is not detected, that is handled later
-
- // *TODO: Now would be an appropriate time to check for some
- // explicitly unsupported cards.
- //const char* RENDERER = (const char*) glGetString(GL_RENDERER);
-
SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &redBits);
SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &greenBits);
SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &blueBits);
@@ -855,6 +400,20 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b
return false;
}
+ LL_PROFILER_GPU_CONTEXT;
+
+ // Enable vertical sync
+ toggleVSync(enable_vsync);
+
+ // Set the application icon.
+ SDL_Surface* bmpsurface = Load_BMP_Resource("ll_icon.BMP");
+ if (bmpsurface)
+ {
+ SDL_SetWindowIcon(mWindow, bmpsurface);
+ SDL_FreeSurface(bmpsurface);
+ bmpsurface = NULL;
+ }
+
#if LL_X11
/* Grab the window manager specific information */
SDL_SysWMinfo info;
@@ -880,7 +439,6 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b
}
#endif // LL_X11
-
SDL_StartTextInput();
//make sure multisampling is disabled by default
glDisable(GL_MULTISAMPLE_ARB);
@@ -889,6 +447,43 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b
return true;
}
+void* LLWindowSDL::createSharedContext()
+{
+ SDL_GLContext pContext = SDL_GL_CreateContext(mWindow);
+ if (pContext)
+ {
+ LL_DEBUGS() << "Creating shared OpenGL context successful!" << LL_ENDL;
+ return (void*)pContext;
+ }
+
+ LL_WARNS() << "Creating shared OpenGL context failed!" << LL_ENDL;
+ return nullptr;
+}
+
+void LLWindowSDL::makeContextCurrent(void* contextPtr)
+{
+ SDL_GL_MakeCurrent(mWindow, contextPtr);
+ LL_PROFILER_GPU_CONTEXT;
+}
+
+void LLWindowSDL::destroySharedContext(void* contextPtr)
+{
+ SDL_GL_DeleteContext(contextPtr);
+}
+
+void LLWindowSDL::toggleVSync(bool enable_vsync)
+{
+ if (!enable_vsync)
+ {
+ LL_INFOS("Window") << "Disabling vertical sync" << LL_ENDL;
+ SDL_GL_SetSwapInterval(0);
+ }
+ else
+ {
+ LL_INFOS("Window") << "Enabling vertical sync" << LL_ENDL;
+ SDL_GL_SetSwapInterval(1);
+ }
+}
// changing fullscreen resolution, or switching between windowed and fullscreen mode.
bool LLWindowSDL::switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp)
@@ -901,7 +496,7 @@ bool LLWindowSDL::switchContext(bool fullscreen, const LLCoordScreen &size, bool
if(needsRebuild)
{
destroyContext();
- result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, enable_vsync);
+ result = createContext(0, 0, size.mX, size.mY, 32, fullscreen, enable_vsync);
if (result)
{
gGLManager.initGL();
@@ -921,7 +516,13 @@ void LLWindowSDL::destroyContext()
{
LL_INFOS() << "destroyContext begins" << LL_ENDL;
+ // Stop unicode input
SDL_StopTextInput();
+
+ // Clean up remaining GL state before blowing away window
+ LL_INFOS() << "shutdownGL begins" << LL_ENDL;
+ gGLManager.shutdownGL();
+
#if LL_X11
mSDL_Display = NULL;
mSDL_XWindowID = None;
@@ -929,18 +530,38 @@ void LLWindowSDL::destroyContext()
Unlock_Display = NULL;
#endif // LL_X11
- // Clean up remaining GL state before blowing away window
- LL_INFOS() << "shutdownGL begins" << LL_ENDL;
- gGLManager.shutdownGL();
+ LL_INFOS() << "Destroying SDL cursors" << LL_ENDL;
+ quitCursors();
+
+ if (mContext)
+ {
+ LL_INFOS() << "Destroying SDL GL Context" << LL_ENDL;
+ SDL_GL_DeleteContext(mContext);
+ mContext = nullptr;
+ }
+ else
+ {
+ LL_INFOS() << "SDL GL Context already destroyed" << LL_ENDL;
+ }
+
+ if (mWindow)
+ {
+ LL_INFOS() << "Destroying SDL Window" << LL_ENDL;
+ SDL_DestroyWindow(mWindow);
+ mWindow = nullptr;
+ }
+ else
+ {
+ LL_INFOS() << "SDL Window already destroyed" << LL_ENDL;
+ }
+ LL_INFOS() << "destroyContext end" << LL_ENDL;
+
LL_INFOS() << "SDL_QuitSS/VID begins" << LL_ENDL;
SDL_QuitSubSystem(SDL_INIT_VIDEO); // *FIX: this might be risky...
-
- mWindow = NULL;
}
LLWindowSDL::~LLWindowSDL()
{
- quitCursors();
destroyContext();
if(mSupportedResolutions != NULL)
@@ -954,27 +575,38 @@ LLWindowSDL::~LLWindowSDL()
void LLWindowSDL::show()
{
- // *FIX: What to do with SDL?
+ if (mWindow)
+ {
+ SDL_ShowWindow(mWindow);
+ }
}
void LLWindowSDL::hide()
{
- // *FIX: What to do with SDL?
+ if (mWindow)
+ {
+ SDL_HideWindow(mWindow);
+ }
}
//virtual
void LLWindowSDL::minimize()
{
- // *FIX: What to do with SDL?
+ if (mWindow)
+ {
+ SDL_MinimizeWindow(mWindow);
+ }
}
//virtual
void LLWindowSDL::restore()
{
- // *FIX: What to do with SDL?
+ if (mWindow)
+ {
+ SDL_RestoreWindow(mWindow);
+ }
}
-
// close() destroys all OS-specific code associated with a window.
// Usually called from LLWindowManager::destroyWindow()
void LLWindowSDL::close()
@@ -997,63 +629,70 @@ bool LLWindowSDL::isValid()
return (mWindow != NULL);
}
-bool LLWindowSDL::getVisible()
+bool LLWindowSDL::getVisible() const
{
bool result = false;
-
- // *FIX: This isn't really right...
- // Then what is?
if (mWindow)
{
- result = true;
+ Uint32 flags = SDL_GetWindowFlags(mWindow);
+ if (flags & SDL_WINDOW_SHOWN)
+ {
+ result = true;
+ }
}
-
- return(result);
+ return result;
}
-bool LLWindowSDL::getMinimized()
+bool LLWindowSDL::getMinimized() const
{
bool result = false;
-
- if (mWindow && (1 == mIsMinimized))
+ if (mWindow)
{
- result = true;
+ Uint32 flags = SDL_GetWindowFlags(mWindow);
+ if (flags & SDL_WINDOW_MINIMIZED)
+ {
+ result = true;
+ }
}
- return(result);
+ return result;
}
-bool LLWindowSDL::getMaximized()
+bool LLWindowSDL::getMaximized() const
{
bool result = false;
-
if (mWindow)
{
- // TODO
+ Uint32 flags = SDL_GetWindowFlags(mWindow);
+ if (flags & SDL_WINDOW_MAXIMIZED)
+ {
+ result = true;
+ }
}
- return(result);
+ return result;
}
bool LLWindowSDL::maximize()
{
- // TODO
+ if (mWindow)
+ {
+ SDL_MaximizeWindow(mWindow);
+ return true;
+ }
return false;
}
-bool LLWindowSDL::getFullscreen()
-{
- return mFullscreen;
-}
-
-bool LLWindowSDL::getPosition(LLCoordScreen *position)
+bool LLWindowSDL::getPosition(LLCoordScreen *position) const
{
- // *FIX: can anything be done with this?
- position->mX = 0;
- position->mY = 0;
- return true;
+ if (mWindow)
+ {
+ SDL_GetWindowPosition(mWindow, &position->mX, &position->mY);
+ return true;
+ }
+ return false;
}
-bool LLWindowSDL::getSize(LLCoordScreen *size)
+bool LLWindowSDL::getSize(LLCoordScreen *size) const
{
if (mSurface)
{
@@ -1065,7 +704,7 @@ bool LLWindowSDL::getSize(LLCoordScreen *size)
return (false);
}
-bool LLWindowSDL::getSize(LLCoordWindow *size)
+bool LLWindowSDL::getSize(LLCoordWindow *size) const
{
if (mSurface)
{
@@ -1079,13 +718,13 @@ bool LLWindowSDL::getSize(LLCoordWindow *size)
bool LLWindowSDL::setPosition(const LLCoordScreen position)
{
- if(mWindow)
+ if (mWindow)
{
- // *FIX: (?)
- //MacMoveWindow(mWindow, position.mX, position.mY, false);
+ SDL_SetWindowPosition(mWindow, position.mX, position.mY);
+ return true;
}
- return true;
+ return false;
}
template< typename T > bool setSizeImpl( const T& newSize, SDL_Window *pWin )
@@ -1126,11 +765,12 @@ void LLWindowSDL::swapBuffers()
{
if (mWindow)
{
- SDL_GL_SwapWindow( mWindow );
+ SDL_GL_SwapWindow(mWindow);
}
+ LL_PROFILER_GPU_COLLECT;
}
-U32 LLWindowSDL::getFSAASamples()
+U32 LLWindowSDL::getFSAASamples() const
{
return mFSAASamples;
}
@@ -1140,24 +780,35 @@ void LLWindowSDL::setFSAASamples(const U32 samples)
mFSAASamples = samples;
}
-F32 LLWindowSDL::getGamma()
+F32 LLWindowSDL::getGamma() const
{
- return 1/mGamma;
+ return 1.f / mGamma;
}
bool LLWindowSDL::restoreGamma()
{
- //CGDisplayRestoreColorSyncSettings();
- // SDL_SetGamma(1.0f, 1.0f, 1.0f);
+ if (mWindow)
+ {
+ Uint16 ramp[256];
+ SDL_CalculateGammaRamp(1.f, ramp);
+ SDL_SetWindowGammaRamp(mWindow, ramp, ramp, ramp);
+ }
return true;
}
bool LLWindowSDL::setGamma(const F32 gamma)
{
- mGamma = gamma;
- if (mGamma == 0) mGamma = 0.1f;
- mGamma = 1/mGamma;
- // SDL_SetGamma(mGamma, mGamma, mGamma);
+ if (mWindow)
+ {
+ Uint16 ramp[256];
+
+ mGamma = gamma;
+ if (mGamma == 0) mGamma = 0.1f;
+ mGamma = 1.f / mGamma;
+
+ SDL_CalculateGammaRamp(mGamma, ramp);
+ SDL_SetWindowGammaRamp(mWindow, ramp, ramp, ramp);
+ }
return true;
}
@@ -1166,10 +817,8 @@ bool LLWindowSDL::isCursorHidden()
return mCursorHidden;
}
-
-
// Constrains the mouse to the window.
-void LLWindowSDL::setMouseClipping( bool b )
+void LLWindowSDL::setMouseClipping(bool b)
{
//SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF);
}
@@ -1179,18 +828,10 @@ void LLWindowSDL::setMinSize(U32 min_width, U32 min_height, bool enforce_immedia
{
LLWindow::setMinSize(min_width, min_height, enforce_immediately);
-#if LL_X11
- // Set the minimum size limits for X11 window
- // so the window manager doesn't allow resizing below those limits.
- XSizeHints* hints = XAllocSizeHints();
- hints->flags |= PMinSize;
- hints->min_width = mMinWindowWidth;
- hints->min_height = mMinWindowHeight;
-
- XSetWMNormalHints(mSDL_Display, mSDL_XWindowID, hints);
-
- XFree(hints);
-#endif
+ if (mWindow && min_width > 0 && min_height > 0)
+ {
+ SDL_SetWindowMinimumSize(mWindow, mMinWindowWidth, mMinWindowHeight);
+ }
}
bool LLWindowSDL::setCursorPosition(const LLCoordWindow position)
@@ -1218,7 +859,6 @@ bool LLWindowSDL::getCursorPosition(LLCoordWindow *position)
//Point cursor_point;
LLCoordScreen screen_pos;
- //GetMouse(&cursor_point);
int x, y;
SDL_GetMouseState(&x, &y);
@@ -1333,85 +973,70 @@ void LLWindowSDL::afterDialog()
}
}
-
-#if LL_X11
-// set/reset the XWMHints flag for 'urgency' that usually makes the icon flash
-void LLWindowSDL::x11_set_urgent(bool urgent)
-{
- if (mSDL_Display && !mFullscreen)
- {
- XWMHints *wm_hints;
-
- LL_INFOS() << "X11 hint for urgency, " << urgent << LL_ENDL;
-
- maybe_lock_display();
- wm_hints = XGetWMHints(mSDL_Display, mSDL_XWindowID);
- if (!wm_hints)
- wm_hints = XAllocWMHints();
-
- if (urgent)
- wm_hints->flags |= XUrgencyHint;
- else
- wm_hints->flags &= ~XUrgencyHint;
-
- XSetWMHints(mSDL_Display, mSDL_XWindowID, wm_hints);
- XFree(wm_hints);
- XSync(mSDL_Display, False);
- maybe_unlock_display();
- }
-}
-#endif // LL_X11
-
void LLWindowSDL::flashIcon(F32 seconds)
{
- if (getMinimized())
- {
-#if !LL_X11
- LL_INFOS() << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL;
-#else
- LL_INFOS() << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL;
+ LL_INFOS() << "LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL;
- F32 remaining_time = mFlashTimer.getRemainingTimeF32();
- if (remaining_time < seconds)
- remaining_time = seconds;
- mFlashTimer.reset();
- mFlashTimer.setTimerExpirySec(remaining_time);
+ F32 remaining_time = mFlashTimer.getRemainingTimeF32();
+ if (remaining_time < seconds)
+ remaining_time = seconds;
+ mFlashTimer.reset();
+ mFlashTimer.setTimerExpirySec(remaining_time);
- x11_set_urgent(true);
- mFlashing = true;
-#endif // LL_X11
- }
+ SDL_FlashWindow(mWindow, SDL_FLASH_UNTIL_FOCUSED);
+ mFlashing = true;
}
bool LLWindowSDL::isClipboardTextAvailable()
{
- return mSDL_Display && XGetSelectionOwner(mSDL_Display, XA_CLIPBOARD) != None;
+ return SDL_HasClipboardText() == SDL_TRUE;
}
bool LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
{
- return getSelectionText(XA_CLIPBOARD, dst);
+ if (isClipboardTextAvailable())
+ {
+ char* data = SDL_GetClipboardText();
+ if (data)
+ {
+ dst = LLWString(utf8str_to_wstring(data));
+ SDL_free(data);
+ return true;
+ }
+ }
+ return false;
}
-bool LLWindowSDL::copyTextToClipboard(const LLWString &s)
+bool LLWindowSDL::copyTextToClipboard(const LLWString& text)
{
- return setSelectionText(XA_CLIPBOARD, s);
+ const std::string utf8 = wstring_to_utf8str(text);
+ return SDL_SetClipboardText(utf8.c_str()) == 0; // success == 0
}
bool LLWindowSDL::isPrimaryTextAvailable()
{
- LLWString text;
- return getSelectionText(XA_PRIMARY, text) && !text.empty();
+ return SDL_HasPrimarySelectionText() == SDL_TRUE;
}
bool LLWindowSDL::pasteTextFromPrimary(LLWString &dst)
{
- return getSelectionText(XA_PRIMARY, dst);
+ if (isPrimaryTextAvailable())
+ {
+ char* data = SDL_GetPrimarySelectionText();
+ if (data)
+ {
+ dst = LLWString(utf8str_to_wstring(data));
+ SDL_free(data);
+ return true;
+ }
+ }
+ return false;
}
-bool LLWindowSDL::copyTextToPrimary(const LLWString &s)
+bool LLWindowSDL::copyTextToPrimary(const LLWString& text)
{
- return setSelectionText(XA_PRIMARY, s);
+ const std::string utf8 = wstring_to_utf8str(text);
+ return SDL_SetPrimarySelectionText(utf8.c_str()) == 0; // success == 0
}
LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions)
@@ -1454,7 +1079,7 @@ LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_reso
return mSupportedResolutions;
}
-bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to)
+bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to) const
{
if (!to)
return false;
@@ -1465,7 +1090,7 @@ bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to)
return true;
}
-bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to)
+bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to) const
{
if (!to)
return false;
@@ -1476,7 +1101,7 @@ bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to)
return true;
}
-bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to)
+bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to) const
{
if (!to)
return false;
@@ -1487,7 +1112,7 @@ bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to)
return (true);
}
-bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to)
+bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to) const
{
if (!to)
return false;
@@ -1498,23 +1123,20 @@ bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to)
return (true);
}
-bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to)
+bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to) const
{
LLCoordWindow window_coord;
return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
}
-bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to)
+bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to) const
{
LLCoordWindow window_coord;
return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
}
-
-
-
void LLWindowSDL::setupFailure(const std::string& text, const std::string& caption, U32 type)
{
destroyContext();
@@ -1549,47 +1171,24 @@ bool LLWindowSDL::SDLReallyCaptureInput(bool capture)
bool newGrab = wantGrab;
-#if LL_X11
if (!mFullscreen) /* only bother if we're windowed anyway */
{
- if (mSDL_Display)
+ int result;
+ if (wantGrab == true)
{
- /* we dirtily mix raw X11 with SDL so that our pointer
- isn't (as often) constrained to the limits of the
- window while grabbed, which feels nicer and
- hopefully eliminates some reported 'sticky pointer'
- problems. We use raw X11 instead of
- SDL_WM_GrabInput() because the latter constrains
- the pointer to the window and also steals all
- *keyboard* input from the window manager, which was
- frustrating users. */
- int result;
- if (wantGrab == true)
- {
- maybe_lock_display();
- result = XGrabPointer(mSDL_Display, mSDL_XWindowID,
- True, 0, GrabModeAsync,
- GrabModeAsync,
- None, None, CurrentTime);
- maybe_unlock_display();
- if (GrabSuccess == result)
- newGrab = true;
- else
- newGrab = false;
- }
+ result = SDL_CaptureMouse(SDL_TRUE);
+ if (0 == result)
+ newGrab = true;
else
- {
newGrab = false;
-
- maybe_lock_display();
- XUngrabPointer(mSDL_Display, CurrentTime);
- // Make sure the ungrab happens RIGHT NOW.
- XSync(mSDL_Display, False);
- maybe_unlock_display();
- }
+ }
+ else
+ {
+ newGrab = false;
+ result = SDL_CaptureMouse(SDL_FALSE);
}
}
-#endif // LL_X11
+
// return boolean success for whether we ended up in the desired state
return capture == newGrab;
}
@@ -1719,7 +1318,7 @@ void check_vm_bloat()
last_rss_size = this_rss_size;
last_vm_size = this_vm_size;
- finally:
+finally:
if (NULL != ptr)
{
free(ptr);
@@ -1753,13 +1352,8 @@ void LLWindowSDL::processMiscNativeEvents()
}
}
-void LLWindowSDL::gatherInput()
+void LLWindowSDL::gatherInput(bool app_has_focus)
{
- const Uint32 CLICK_THRESHOLD = 300; // milliseconds
- static int leftClick = 0;
- static int rightClick = 0;
- static Uint32 lastLeftDown = 0;
- static Uint32 lastRightDown = 0;
SDL_Event event;
// Handle all outstanding SDL events
@@ -1768,13 +1362,21 @@ void LLWindowSDL::gatherInput()
switch (event.type)
{
case SDL_MOUSEWHEEL:
+ {
if( event.wheel.y != 0 )
+ {
mCallbacks->handleScrollWheel(this, -event.wheel.y);
+ }
+ if (event.wheel.x != 0)
+ {
+ mCallbacks->handleScrollHWheel(this, -event.wheel.x);
+ }
break;
+ }
case SDL_MOUSEMOTION:
{
- LLCoordWindow winCoord(event.button.x, event.button.y);
+ LLCoordWindow winCoord(event.motion.x, event.motion.y);
LLCoordGL openGlCoord;
convertCoords(winCoord, &openGlCoord);
MASK mask = gKeyboard->currentMask(true);
@@ -1847,64 +1449,30 @@ void LLWindowSDL::gatherInput()
case SDL_MOUSEBUTTONDOWN:
{
- bool isDoubleClick = false;
LLCoordWindow winCoord(event.button.x, event.button.y);
LLCoordGL openGlCoord;
convertCoords(winCoord, &openGlCoord);
MASK mask = gKeyboard->currentMask(true);
- if (event.button.button == SDL_BUTTON_LEFT) // SDL doesn't manage double clicking...
- {
- Uint32 now = SDL_GetTicks();
- if ((now - lastLeftDown) > CLICK_THRESHOLD)
- leftClick = 1;
- else
- {
- if (++leftClick >= 2)
- {
- leftClick = 0;
- isDoubleClick = true;
- }
- }
- lastLeftDown = now;
- }
- else if (event.button.button == SDL_BUTTON_RIGHT)
- {
- Uint32 now = SDL_GetTicks();
- if ((now - lastRightDown) > CLICK_THRESHOLD)
- rightClick = 1;
- else
- {
- if (++rightClick >= 2)
- {
- rightClick = 0;
- isDoubleClick = true;
- }
- }
- lastRightDown = now;
- }
-
if (event.button.button == SDL_BUTTON_LEFT) // left
{
- if (isDoubleClick)
+ if (event.button.clicks >= 2)
mCallbacks->handleDoubleClick(this, openGlCoord, mask);
else
mCallbacks->handleMouseDown(this, openGlCoord, mask);
}
-
else if (event.button.button == SDL_BUTTON_RIGHT) // right
{
mCallbacks->handleRightMouseDown(this, openGlCoord, mask);
}
-
else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
{
mCallbacks->handleMiddleMouseDown(this, openGlCoord, mask);
}
- else if (event.button.button == 4) // mousewheel up...thanks to X11 for making SDL consider these "buttons".
- mCallbacks->handleScrollWheel(this, -1);
- else if (event.button.button == 5) // mousewheel down...thanks to X11 for making SDL consider these "buttons".
- mCallbacks->handleScrollWheel(this, 1);
+ else
+ {
+ mCallbacks->handleOtherMouseDown(this, openGlCoord, mask, event.button.button);
+ }
break;
}
@@ -1917,64 +1485,65 @@ void LLWindowSDL::gatherInput()
MASK mask = gKeyboard->currentMask(true);
if (event.button.button == SDL_BUTTON_LEFT) // left
+ {
mCallbacks->handleMouseUp(this, openGlCoord, mask);
+ }
else if (event.button.button == SDL_BUTTON_RIGHT) // right
+ {
mCallbacks->handleRightMouseUp(this, openGlCoord, mask);
+ }
else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
+ {
mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask);
- // don't handle mousewheel here...
+ }
+ else
+ {
+ mCallbacks->handleOtherMouseUp(this, openGlCoord, mask, event.button.button);
+ }
break;
}
- case SDL_WINDOWEVENT: // *FIX: handle this?
+ case SDL_WINDOWEVENT:
{
- if( event.window.event == SDL_WINDOWEVENT_RESIZED
- /* || event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED*/ ) // <FS:ND> SDL_WINDOWEVENT_SIZE_CHANGED is followed by SDL_WINDOWEVENT_RESIZED, so handling one shall be enough
- {
- LL_INFOS() << "Handling a resize event: " << event.window.data1 << "x" << event.window.data2 << LL_ENDL;
-
- S32 width = llmax(event.window.data1, (S32)mMinWindowWidth);
- S32 height = llmax(event.window.data2, (S32)mMinWindowHeight);
- mSurface = SDL_GetWindowSurface( mWindow );
-
- // *FIX: I'm not sure this is necessary!
- // <FS:ND> I think is is not
- // SDL_SetWindowSize(mWindow, width, height);
- //
-
- mCallbacks->handleResize(this, width, height);
- }
- else if( event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED ) // <FS:ND> What about SDL_WINDOWEVENT_ENTER (mouse focus)
- {
- // We have to do our own state massaging because SDL
- // can send us two unfocus events in a row for example,
- // which confuses the focus code [SL-24071].
- mHaveInputFocus = true;
-
- mCallbacks->handleFocus(this);
- }
- else if( event.window.event == SDL_WINDOWEVENT_FOCUS_LOST ) // <FS:ND> What about SDL_WINDOWEVENT_LEAVE (mouse focus)
+ switch(event.window.event)
{
- // We have to do our own state massaging because SDL
- // can send us two unfocus events in a row for example,
- // which confuses the focus code [SL-24071].
- mHaveInputFocus = false;
+ //case SDL_WINDOWEVENT_SIZE_CHANGED: <FS:ND> SDL_WINDOWEVENT_SIZE_CHANGED is followed by SDL_WINDOWEVENT_RESIZED, so handling one shall be enough
+ case SDL_WINDOWEVENT_RESIZED:
+ {
+ LL_INFOS() << "Handling a resize event: " << event.window.data1 << "x" << event.window.data2 << LL_ENDL;
+ S32 width = llmax(event.window.data1, (S32)mMinWindowWidth);
+ S32 height = llmax(event.window.data2, (S32)mMinWindowHeight);
- mCallbacks->handleFocusLost(this);
- }
- else if( event.window.event == SDL_WINDOWEVENT_MINIMIZED ||
- event.window.event == SDL_WINDOWEVENT_MAXIMIZED ||
- event.window.event == SDL_WINDOWEVENT_RESTORED ||
- event.window.event == SDL_WINDOWEVENT_EXPOSED ||
- event.window.event == SDL_WINDOWEVENT_SHOWN )
- {
- mIsMinimized = (event.window.event == SDL_WINDOWEVENT_MINIMIZED);
+ mSurface = SDL_GetWindowSurface(mWindow);
+ mCallbacks->handleResize(this, width, height);
+ break;
+ }
+ case SDL_WINDOWEVENT_LEAVE:
+ mCallbacks->handleMouseLeave(this);
+ break;
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ mCallbacks->handleFocus(this);
+ break;
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ mCallbacks->handleFocusLost(this);
+ break;
+ case SDL_WINDOWEVENT_EXPOSED:
+ case SDL_WINDOWEVENT_SHOWN:
+ case SDL_WINDOWEVENT_HIDDEN:
+ case SDL_WINDOWEVENT_MINIMIZED:
+ case SDL_WINDOWEVENT_MAXIMIZED:
+ case SDL_WINDOWEVENT_RESTORED:
+ {
+ Uint32 flags = SDL_GetWindowFlags(mWindow);
+ bool minimized = (flags & SDL_WINDOW_MINIMIZED);
+ bool hidden = (flags & SDL_WINDOW_HIDDEN);
- mCallbacks->handleActivate(this, !mIsMinimized);
- LL_INFOS() << "SDL deiconification state switched to " << mIsMinimized << LL_ENDL;
+ mCallbacks->handleActivate(this, !minimized || !hidden);
+ LL_INFOS() << "SDL deiconification state switched to " << minimized << LL_ENDL;
+ break;
+ }
}
-
break;
}
case SDL_QUIT:
@@ -1986,27 +1555,25 @@ void LLWindowSDL::gatherInput()
}
break;
default:
- //LL_INFOS() << "Unhandled SDL event type " << event.type << LL_ENDL;
+ LLGameControl::handleEvent(event, app_has_focus);
break;
}
}
updateCursor();
-#if LL_X11
// This is a good time to stop flashing the icon if our mFlashTimer has
// expired.
if (mFlashing && mFlashTimer.hasExpired())
{
- x11_set_urgent(false);
+ SDL_FlashWindow(mWindow, SDL_FLASH_CANCEL);
mFlashing = false;
}
-#endif // LL_X11
}
static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty)
{
- SDL_Cursor *sdlcursor = NULL;
+ SDL_Cursor *sdlcursor = nullptr;
SDL_Surface *bmpsurface;
// Load cursor pixel data from BMP file
@@ -2080,12 +1647,6 @@ static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty
void LLWindowSDL::updateCursor()
{
- if (ATIbug) {
- // cursor-updating is very flaky when this bug is
- // present; do nothing.
- return;
- }
-
if (mCurrentCursor != mNextCursor)
{
if (mNextCursor < UI_CURSOR_COUNT)
@@ -2097,10 +1658,13 @@ void LLWindowSDL::updateCursor()
sdlcursor = mSDLCursors[UI_CURSOR_ARROW];
if (sdlcursor)
SDL_SetCursor(sdlcursor);
- } else {
+
+ mCurrentCursor = mNextCursor;
+ }
+ else
+ {
LL_WARNS() << "Tried to set invalid cursor number " << mNextCursor << LL_ENDL;
}
- mCurrentCursor = mNextCursor;
}
}
@@ -2110,24 +1674,24 @@ void LLWindowSDL::initCursors()
// Blank the cursor pointer array for those we may miss.
for (i=0; i<UI_CURSOR_COUNT; ++i)
{
- mSDLCursors[i] = NULL;
+ mSDLCursors[i] = nullptr;
}
// Pre-make an SDL cursor for each of the known cursor types.
// We hardcode the hotspots - to avoid that we'd have to write
// a .cur file loader.
// NOTE: SDL doesn't load RLE-compressed BMP files.
- mSDLCursors[UI_CURSOR_ARROW] = makeSDLCursorFromBMP("llarrow.BMP",0,0);
- mSDLCursors[UI_CURSOR_WAIT] = makeSDLCursorFromBMP("wait.BMP",12,15);
- mSDLCursors[UI_CURSOR_HAND] = makeSDLCursorFromBMP("hand.BMP",7,10);
- mSDLCursors[UI_CURSOR_IBEAM] = makeSDLCursorFromBMP("ibeam.BMP",15,16);
- mSDLCursors[UI_CURSOR_CROSS] = makeSDLCursorFromBMP("cross.BMP",16,14);
- mSDLCursors[UI_CURSOR_SIZENWSE] = makeSDLCursorFromBMP("sizenwse.BMP",14,17);
- mSDLCursors[UI_CURSOR_SIZENESW] = makeSDLCursorFromBMP("sizenesw.BMP",17,17);
- mSDLCursors[UI_CURSOR_SIZEWE] = makeSDLCursorFromBMP("sizewe.BMP",16,14);
- mSDLCursors[UI_CURSOR_SIZENS] = makeSDLCursorFromBMP("sizens.BMP",17,16);
- mSDLCursors[UI_CURSOR_SIZEALL] = makeSDLCursorFromBMP("sizeall.BMP", 17, 17);
- mSDLCursors[UI_CURSOR_NO] = makeSDLCursorFromBMP("llno.BMP",8,8);
- mSDLCursors[UI_CURSOR_WORKING] = makeSDLCursorFromBMP("working.BMP",12,15);
+ mSDLCursors[UI_CURSOR_ARROW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
+ mSDLCursors[UI_CURSOR_WAIT] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
+ mSDLCursors[UI_CURSOR_HAND] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
+ mSDLCursors[UI_CURSOR_IBEAM] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
+ mSDLCursors[UI_CURSOR_CROSS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
+ mSDLCursors[UI_CURSOR_SIZENWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
+ mSDLCursors[UI_CURSOR_SIZENESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
+ mSDLCursors[UI_CURSOR_SIZEWE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
+ mSDLCursors[UI_CURSOR_SIZENS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
+ mSDLCursors[UI_CURSOR_SIZEALL] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
+ mSDLCursors[UI_CURSOR_NO] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
+ mSDLCursors[UI_CURSOR_WORKING] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAITARROW);
mSDLCursors[UI_CURSOR_TOOLGRAB] = makeSDLCursorFromBMP("lltoolgrab.BMP",2,13);
mSDLCursors[UI_CURSOR_TOOLLAND] = makeSDLCursorFromBMP("lltoolland.BMP",1,6);
mSDLCursors[UI_CURSOR_TOOLFOCUS] = makeSDLCursorFromBMP("lltoolfocus.BMP",8,5);
@@ -2160,11 +1724,6 @@ void LLWindowSDL::initCursors()
mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_END] = makeSDLCursorFromBMP("lltoolpathfindingpathend.BMP", 16, 16);
mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD] = makeSDLCursorFromBMP("lltoolpathfindingpathendadd.BMP", 16, 16);
mSDLCursors[UI_CURSOR_TOOLNO] = makeSDLCursorFromBMP("llno.BMP",8,8);
-
- if (getenv("LL_ATI_MOUSE_CURSOR_BUG") != NULL) {
- LL_INFOS() << "Disabling cursor updating due to LL_ATI_MOUSE_CURSOR_BUG" << LL_ENDL;
- ATIbug = true;
- }
}
void LLWindowSDL::quitCursors()
@@ -2214,7 +1773,7 @@ void LLWindowSDL::hideCursor()
// LL_INFOS() << "hideCursor: hiding" << LL_ENDL;
mCursorHidden = true;
mHideCursorPermanent = true;
- SDL_ShowCursor(0);
+ SDL_ShowCursor(SDL_DISABLE);
}
else
{
@@ -2229,7 +1788,7 @@ void LLWindowSDL::showCursor()
// LL_INFOS() << "showCursor: showing" << LL_ENDL;
mCursorHidden = false;
mHideCursorPermanent = false;
- SDL_ShowCursor(1);
+ SDL_ShowCursor(SDL_ENABLE);
}
else
{
@@ -2320,7 +1879,7 @@ bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b)
Make the raw keyboard data available - used to poke through to LLQtWebKit so
that Qt/Webkit has access to the virtual keycodes etc. that it needs
*/
-LLSD LLWindowSDL::getNativeKeyData()
+LLSD LLWindowSDL::getNativeKeyData() const
{
LLSD result = LLSD::emptyMap();
@@ -2347,62 +1906,6 @@ LLSD LLWindowSDL::getNativeKeyData()
return result;
}
-#if LL_LINUX || LL_SOLARIS
-// extracted from spawnWebBrowser for clarity and to eliminate
-// compiler confusion regarding close(int fd) vs. LLWindow::close()
-void exec_cmd(const std::string& cmd, const std::string& arg)
-{
- char* const argv[] = {(char*)cmd.c_str(), (char*)arg.c_str(), NULL};
- fflush(NULL);
- pid_t pid = fork();
- if (pid == 0)
- { // child
- // disconnect from stdin/stdout/stderr, or child will
- // keep our output pipe undesirably alive if it outlives us.
- // close(0);
- // close(1);
- // close(2);
- // <FS:TS> Reopen stdin, stdout, and stderr to /dev/null.
- // It's good practice to always have those file
- // descriptors open to something, lest the exec'd
- // program actually try to use them.
- FILE *result;
- result = freopen("/dev/null","r",stdin);
- if (result == NULL)
- {
- LL_WARNS() << "Error reopening stdin for web browser: "
- << strerror(errno) << LL_ENDL;
- }
- result = freopen("/dev/null","w",stdout);
- if (result == NULL)
- {
- LL_WARNS() << "Error reopening stdout for web browser: "
- << strerror(errno) << LL_ENDL;
- }
- result = freopen("/dev/null","w",stderr);
- if (result == NULL)
- {
- LL_WARNS() << "Error reopening stderr for web browser: "
- << strerror(errno) << LL_ENDL;
- }
- // end ourself by running the command
- execv(cmd.c_str(), argv); /* Flawfinder: ignore */
- // if execv returns at all, there was a problem.
- LL_WARNS() << "execv failure when trying to start " << cmd << LL_ENDL;
- _exit(1); // _exit because we don't want atexit() clean-up!
- } else {
- if (pid > 0)
- {
- // parent - wait for child to die
- int childExitStatus;
- waitpid(pid, &childExitStatus, 0);
- } else {
- LL_WARNS() << "fork failure." << LL_ENDL;
- }
- }
-}
-#endif
-
// Open a URL with the user's default web browser.
// Must begin with protocol identifier.
void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async)
@@ -2426,35 +1929,14 @@ void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async)
LL_INFOS() << "spawn_web_browser: " << escaped_url << LL_ENDL;
-#if LL_LINUX
-# if LL_X11
- if (mSDL_Display)
+ if (SDL_OpenURL(escaped_url.c_str()) != 0)
{
- maybe_lock_display();
- // Just in case - before forking.
- XSync(mSDL_Display, False);
- maybe_unlock_display();
+ LL_WARNS() << "spawn_web_browser failed with error: " << SDL_GetError() << LL_ENDL;
}
-# endif // LL_X11
-
- std::string cmd, arg;
- cmd = gDirUtilp->getAppRODataDir();
- cmd += gDirUtilp->getDirDelimiter();
- cmd += "etc";
- cmd += gDirUtilp->getDirDelimiter();
- cmd += "launch_url.sh";
- arg = escaped_url;
- exec_cmd(cmd, arg);
-#endif // LL_LINUX
LL_INFOS() << "spawn_web_browser returning." << LL_ENDL;
}
-void LLWindowSDL::openFile(const std::string& file_name)
-{
- spawnWebBrowser("file://"+file_name,true);
-}
-
void *LLWindowSDL::getPlatformWindow()
{
return NULL;
@@ -2465,15 +1947,10 @@ void LLWindowSDL::bringToFront()
// This is currently used when we are 'launched' to a specific
// map position externally.
LL_INFOS() << "bringToFront" << LL_ENDL;
-#if LL_X11
- if (mSDL_Display && !mFullscreen)
+ if (mWindow && !mFullscreen)
{
- maybe_lock_display();
- XRaiseWindow(mSDL_Display, mSDL_XWindowID);
- XSync(mSDL_Display, False);
- maybe_unlock_display();
+ SDL_RaiseWindow(mWindow);
}
-#endif // LL_X11
}
//static
@@ -2577,44 +2054,6 @@ std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList()
return rtns;
}
-
-void* LLWindowSDL::createSharedContext()
-{
- auto *pContext = SDL_GL_CreateContext(mWindow);
- if ( pContext)
- {
- SDL_GL_SetSwapInterval(0);
- SDL_GL_MakeCurrent(mWindow, mContext);
-
- LLCoordScreen size;
- if (getSize(&size))
- setSize(size);
-
- LL_DEBUGS() << "Creating shared OpenGL context successful!" << LL_ENDL;
-
- return (void*)pContext;
- }
-
- LL_WARNS() << "Creating shared OpenGL context failed!" << LL_ENDL;
-
- return nullptr;
-}
-
-void LLWindowSDL::makeContextCurrent(void* contextPtr)
-{
- LL_PROFILER_GPU_CONTEXT;
- SDL_GL_MakeCurrent( mWindow, contextPtr );
-}
-
-void LLWindowSDL::destroySharedContext(void* contextPtr)
-{
- SDL_GL_DeleteContext( contextPtr );
-}
-
-void LLWindowSDL::toggleVSync(bool enable_vsync)
-{
-}
-
void LLWindowSDL::setLanguageTextInput(const LLCoordGL& position)
{
LLCoordWindow win_pos;
diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h
index 10769bb3ba..a85b7c11e7 100644
--- a/indra/llwindow/llwindowsdl.h
+++ b/indra/llwindow/llwindowsdl.h
@@ -29,6 +29,7 @@
// Simple Directmedia Layer (http://libsdl.org/) implementation of LLWindow class
+#if LL_LINUX
#include "llwindow.h"
#include "lltimer.h"
@@ -54,11 +55,11 @@ public:
void close() override;
- bool getVisible() override;
+ bool getVisible() const override;
- bool getMinimized() override;
+ bool getMinimized() const override;
- bool getMaximized() override;
+ bool getMaximized() const override;
bool maximize() override;
@@ -66,13 +67,11 @@ public:
void restore() override;
- bool getFullscreen();
+ bool getPosition(LLCoordScreen *position) const override;
- bool getPosition(LLCoordScreen *position) override;
+ bool getSize(LLCoordScreen *size) const override;
- bool getSize(LLCoordScreen *size) override;
-
- bool getSize(LLCoordWindow *size) override;
+ bool getSize(LLCoordWindow *size) const override;
bool setPosition(LLCoordScreen position) override;
@@ -121,19 +120,19 @@ public:
void flashIcon(F32 seconds) override;
- F32 getGamma() override;
+ F32 getGamma() const override;
bool setGamma(const F32 gamma) override; // Set the gamma
- U32 getFSAASamples() override;
+
+ U32 getFSAASamples() const override;
void setFSAASamples(const U32 samples) override;
bool restoreGamma() override; // Restore original gamma table (before updating gamma)
- ESwapMethod getSwapMethod() override { return mSwapMethod; }
void processMiscNativeEvents() override;
- void gatherInput() override;
+ void gatherInput(bool app_has_focus) override;
void swapBuffers() override;
@@ -142,17 +141,17 @@ public:
void delayInputProcessing() override {};
// handy coordinate space conversion routines
- bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override;
+ bool convertCoords(LLCoordScreen from, LLCoordWindow *to) const override;
- bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override;
+ bool convertCoords(LLCoordWindow from, LLCoordScreen *to) const override;
- bool convertCoords(LLCoordWindow from, LLCoordGL *to) override;
+ bool convertCoords(LLCoordWindow from, LLCoordGL *to) const override;
- bool convertCoords(LLCoordGL from, LLCoordWindow *to) override;
+ bool convertCoords(LLCoordGL from, LLCoordWindow *to) const override;
- bool convertCoords(LLCoordScreen from, LLCoordGL *to) override;
+ bool convertCoords(LLCoordScreen from, LLCoordGL *to) const override;
- bool convertCoords(LLCoordGL from, LLCoordScreen *to) override;
+ bool convertCoords(LLCoordGL from, LLCoordScreen *to) const override;
LLWindowResolution *getSupportedResolutions(S32 &num_resolutions) override;
@@ -176,8 +175,6 @@ public:
void spawnWebBrowser(const std::string &escaped_url, bool async) override;
- void openFile(const std::string &file_name);
-
void setTitle(const std::string title) override;
static std::vector<std::string> getDynamicFallbackFontList();
@@ -211,7 +208,7 @@ public:
protected:
LLWindowSDL(LLWindowCallbacks *callbacks,
- const std::string &title, int x, int y, int width, int height, U32 flags,
+ const std::string &title, const std::string& name, int x, int y, int width, int height, U32 flags,
bool fullscreen, bool clearBg, bool enable_vsync, bool use_gl,
bool ignore_pixel_depth, U32 fsaa_samples);
@@ -219,7 +216,7 @@ protected:
bool isValid() override;
- LLSD getNativeKeyData() override;
+ LLSD getNativeKeyData() const override;
void initCursors();
@@ -247,8 +244,6 @@ protected:
void setupFailure(const std::string &text, const std::string &caption, U32 type);
- void fixWindowSize(void);
-
U32 SDLCheckGrabbyKeys(U32 keysym, bool gain);
bool SDLReallyCaptureInput(bool capture);
@@ -280,45 +275,15 @@ protected:
friend class LLWindowManager;
private:
-#if LL_X11
-
- void x11_set_urgent(bool urgent);
-
bool mFlashing;
LLTimer mFlashTimer;
-#endif //LL_X11
-
U32 mKeyVirtualKey;
U32 mKeyModifiers;
std::string mInputType;
-public:
-#if LL_X11
-
- static Display *getSDLDisplay();
-
- LLWString const &getPrimaryText() const { return mPrimaryClipboard; }
-
- LLWString const &getSecondaryText() const { return mSecondaryClipboard; }
-
- void clearPrimaryText() { mPrimaryClipboard.clear(); }
-
- void clearSecondaryText() { mSecondaryClipboard.clear(); }
private:
void tryFindFullscreenSize(int &aWidth, int &aHeight);
-
- void initialiseX11Clipboard();
-
- bool getSelectionText(Atom selection, LLWString &text);
-
- bool getSelectionText(Atom selection, Atom type, LLWString &text);
-
- bool setSelectionText(Atom selection, const LLWString &text);
-
-#endif
- LLWString mPrimaryClipboard;
- LLWString mSecondaryClipboard;
};
class LLSplashScreenSDL : public LLSplashScreen
@@ -334,4 +299,5 @@ public:
S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 type);
+#endif //LL_LINUX
#endif //LL_LLWINDOWSDL_H
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index b19fa13b41..f8294f063f 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -31,6 +31,7 @@
#include "llwindowwin32.h"
// LLWindow library includes
+#include "llgamecontrol.h"
#include "llkeyboardwin32.h"
#include "lldragdropwin32.h"
#include "llpreeditor.h"
@@ -982,17 +983,17 @@ bool LLWindowWin32::isValid()
return (mWindowHandle != NULL);
}
-bool LLWindowWin32::getVisible()
+bool LLWindowWin32::getVisible() const
{
return (mWindowHandle && IsWindowVisible(mWindowHandle));
}
-bool LLWindowWin32::getMinimized()
+bool LLWindowWin32::getMinimized() const
{
return (mWindowHandle && IsIconic(mWindowHandle));
}
-bool LLWindowWin32::getMaximized()
+bool LLWindowWin32::getMaximized() const
{
return (mWindowHandle && IsZoomed(mWindowHandle));
}
@@ -1017,26 +1018,21 @@ bool LLWindowWin32::maximize()
return true;
}
-bool LLWindowWin32::getFullscreen()
-{
- return mFullscreen;
-}
-
-bool LLWindowWin32::getPosition(LLCoordScreen *position)
+bool LLWindowWin32::getPosition(LLCoordScreen *position) const
{
position->mX = mRect.left;
position->mY = mRect.top;
return true;
}
-bool LLWindowWin32::getSize(LLCoordScreen *size)
+bool LLWindowWin32::getSize(LLCoordScreen *size) const
{
size->mX = mRect.right - mRect.left;
size->mY = mRect.bottom - mRect.top;
return true;
}
-bool LLWindowWin32::getSize(LLCoordWindow *size)
+bool LLWindowWin32::getSize(LLCoordWindow *size) const
{
size->mX = mClientRect.right - mClientRect.left;
size->mY = mClientRect.bottom - mClientRect.top;
@@ -1974,7 +1970,7 @@ bool LLWindowWin32::getCursorPosition(LLCoordWindow *position)
return true;
}
-bool LLWindowWin32::getCursorDelta(LLCoordCommon* delta)
+bool LLWindowWin32::getCursorDelta(LLCoordCommon* delta) const
{
if (delta == nullptr)
{
@@ -2162,7 +2158,7 @@ void LLWindowWin32::delayInputProcessing()
}
-void LLWindowWin32::gatherInput()
+void LLWindowWin32::gatherInput(bool app_has_focus)
{
ASSERT_MAIN_THREAD();
LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
@@ -2242,6 +2238,8 @@ void LLWindowWin32::gatherInput()
mInputProcessingPaused = false;
updateCursor();
+
+ LLGameControl::processEvents(app_has_focus);
}
static LLTrace::BlockTimerStatHandle FTM_KEYHANDLER("Handle Keyboard");
@@ -3112,7 +3110,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
return ret;
}
-bool LLWindowWin32::convertCoords(LLCoordGL from, LLCoordWindow *to)
+bool LLWindowWin32::convertCoords(LLCoordGL from, LLCoordWindow *to) const
{
S32 client_height;
RECT client_rect;
@@ -3132,7 +3130,7 @@ bool LLWindowWin32::convertCoords(LLCoordGL from, LLCoordWindow *to)
return true;
}
-bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordGL* to)
+bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordGL* to) const
{
S32 client_height;
RECT client_rect;
@@ -3151,7 +3149,7 @@ bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordGL* to)
return true;
}
-bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordWindow* to)
+bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordWindow* to) const
{
POINT mouse_point;
@@ -3168,7 +3166,7 @@ bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordWindow* to)
return result;
}
-bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordScreen *to)
+bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordScreen *to) const
{
POINT mouse_point;
@@ -3185,7 +3183,7 @@ bool LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordScreen *to)
return result;
}
-bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordGL *to)
+bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordGL *to) const
{
LLCoordWindow window_coord;
@@ -3199,7 +3197,7 @@ bool LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordGL *to)
return true;
}
-bool LLWindowWin32::convertCoords(LLCoordGL from, LLCoordScreen *to)
+bool LLWindowWin32::convertCoords(LLCoordGL from, LLCoordScreen *to) const
{
LLCoordWindow window_coord;
@@ -3318,7 +3316,7 @@ void LLWindowWin32::setMouseClipping( bool b )
}
}
-bool LLWindowWin32::getClientRectInScreenSpace( RECT* rectp )
+bool LLWindowWin32::getClientRectInScreenSpace( RECT* rectp ) const
{
bool success = false;
@@ -3362,7 +3360,7 @@ void LLWindowWin32::flashIcon(F32 seconds)
});
}
-F32 LLWindowWin32::getGamma()
+F32 LLWindowWin32::getGamma() const
{
return mCurrentGamma;
}
@@ -3424,7 +3422,7 @@ void LLWindowWin32::setFSAASamples(const U32 fsaa_samples)
mFSAASamples = fsaa_samples;
}
-U32 LLWindowWin32::getFSAASamples()
+U32 LLWindowWin32::getFSAASamples() const
{
return mFSAASamples;
}
@@ -3787,7 +3785,7 @@ void LLWindowWin32::openFolder(const std::string &path)
Make the raw keyboard data available - used to poke through to LLQtWebKit so
that Qt/Webkit has access to the virtual keycodes etc. that it needs
*/
-LLSD LLWindowWin32::getNativeKeyData()
+LLSD LLWindowWin32::getNativeKeyData() const
{
LLSD result = LLSD::emptyMap();
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index e38cfe7ebc..f4964d064e 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -45,84 +45,82 @@ typedef void (*LLW32MsgCallback)(const MSG &msg);
class LLWindowWin32 : public LLWindow
{
public:
- /*virtual*/ void show();
- /*virtual*/ void hide();
- /*virtual*/ void close();
- /*virtual*/ bool getVisible();
- /*virtual*/ bool getMinimized();
- /*virtual*/ bool getMaximized();
- /*virtual*/ bool maximize();
- /*virtual*/ void minimize();
- /*virtual*/ void restore();
- /*virtual*/ bool getFullscreen();
- /*virtual*/ bool getPosition(LLCoordScreen *position);
- /*virtual*/ bool getSize(LLCoordScreen *size);
- /*virtual*/ bool getSize(LLCoordWindow *size);
- /*virtual*/ bool setPosition(LLCoordScreen position);
- /*virtual*/ bool setSizeImpl(LLCoordScreen size);
- /*virtual*/ bool setSizeImpl(LLCoordWindow size);
- /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL);
- /*virtual*/ void setTitle(const std::string title);
+ void show() override;
+ void hide() override;
+ void close() override;
+ bool getVisible() const override;
+ bool getMinimized() const override;
+ bool getMaximized() const override;
+ bool maximize() override;
+ void minimize() override;
+ void restore() override;
+ bool getPosition(LLCoordScreen *position) const override;
+ bool getSize(LLCoordScreen *size) const override;
+ bool getSize(LLCoordWindow *size) const override;
+ bool setPosition(LLCoordScreen position) override;
+ bool setSizeImpl(LLCoordScreen size) override;
+ bool setSizeImpl(LLCoordWindow size) override;
+ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override;
+ void setTitle(const std::string title) override;
void* createSharedContext() override;
void makeContextCurrent(void* context) override;
void destroySharedContext(void* context) override;
- /*virtual*/ void toggleVSync(bool enable_vsync);
- /*virtual*/ bool setCursorPosition(LLCoordWindow position);
- /*virtual*/ bool getCursorPosition(LLCoordWindow *position);
- /*virtual*/ bool getCursorDelta(LLCoordCommon* delta);
- /*virtual*/ void showCursor();
- /*virtual*/ void hideCursor();
- /*virtual*/ void showCursorFromMouseMove();
- /*virtual*/ void hideCursorUntilMouseMove();
- /*virtual*/ bool isCursorHidden();
- /*virtual*/ void updateCursor();
- /*virtual*/ ECursorType getCursor() const;
- /*virtual*/ void captureMouse();
- /*virtual*/ void releaseMouse();
- /*virtual*/ void setMouseClipping( bool b );
- /*virtual*/ bool isClipboardTextAvailable();
- /*virtual*/ bool pasteTextFromClipboard(LLWString &dst);
- /*virtual*/ bool copyTextToClipboard(const LLWString &src);
- /*virtual*/ void flashIcon(F32 seconds);
- /*virtual*/ F32 getGamma();
- /*virtual*/ bool setGamma(const F32 gamma); // Set the gamma
- /*virtual*/ void setFSAASamples(const U32 fsaa_samples);
- /*virtual*/ U32 getFSAASamples();
- /*virtual*/ bool restoreGamma(); // Restore original gamma table (before updating gamma)
- /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; }
- /*virtual*/ void gatherInput();
- /*virtual*/ void delayInputProcessing();
- /*virtual*/ void swapBuffers();
- /*virtual*/ void restoreGLContext() {};
+ void toggleVSync(bool enable_vsync) override;
+ bool setCursorPosition(LLCoordWindow position) override;
+ bool getCursorPosition(LLCoordWindow *position) override;
+ bool getCursorDelta(LLCoordCommon* delta) const override;
+ void showCursor() override;
+ void hideCursor() override;
+ void showCursorFromMouseMove() override;
+ void hideCursorUntilMouseMove() override;
+ bool isCursorHidden() override;
+ void updateCursor() override;
+ ECursorType getCursor() const override;
+ void captureMouse() override;
+ void releaseMouse() override;
+ void setMouseClipping( bool b ) override;
+ bool isClipboardTextAvailable() override;
+ bool pasteTextFromClipboard(LLWString &dst) override;
+ bool copyTextToClipboard(const LLWString &src) override;
+ void flashIcon(F32 seconds) override;
+ F32 getGamma() const override;
+ bool setGamma(const F32 gamma) override; // Set the gamma
+ void setFSAASamples(const U32 fsaa_samples) override;
+ U32 getFSAASamples() const override;
+ bool restoreGamma() override; // Restore original gamma table (before updating gamma)
+ void gatherInput(bool app_has_focus) override;
+ void delayInputProcessing() override;
+ void swapBuffers() override;
+ void restoreGLContext() {};
// handy coordinate space conversion routines
- /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordWindow *to);
- /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordScreen *to);
- /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordGL *to);
- /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordWindow *to);
- /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordGL *to);
- /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordScreen *to);
-
- /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions);
- /*virtual*/ F32 getNativeAspectRatio();
- /*virtual*/ F32 getPixelAspectRatio();
- /*virtual*/ void setNativeAspectRatio(F32 ratio) { mOverrideAspectRatio = ratio; }
-
- /*virtual*/ bool dialogColorPicker(F32 *r, F32 *g, F32 *b );
-
- /*virtual*/ void *getPlatformWindow();
- /*virtual*/ void bringToFront();
- /*virtual*/ void focusClient();
-
- /*virtual*/ void allowLanguageTextInput(LLPreeditor *preeditor, bool b);
- /*virtual*/ void setLanguageTextInput( const LLCoordGL & pos );
- /*virtual*/ void updateLanguageTextInputArea();
- /*virtual*/ void interruptLanguageTextInput();
- /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async);
+ bool convertCoords(LLCoordScreen from, LLCoordWindow *to) const override;
+ bool convertCoords(LLCoordWindow from, LLCoordScreen *to) const override;
+ bool convertCoords(LLCoordWindow from, LLCoordGL *to) const override;
+ bool convertCoords(LLCoordGL from, LLCoordWindow *to) const override;
+ bool convertCoords(LLCoordScreen from, LLCoordGL *to) const override;
+ bool convertCoords(LLCoordGL from, LLCoordScreen *to) const override;
+
+ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override;
+ F32 getNativeAspectRatio() override;
+ F32 getPixelAspectRatio() override;
+ void setNativeAspectRatio(F32 ratio) override { mOverrideAspectRatio = ratio; }
+
+ bool dialogColorPicker(F32 *r, F32 *g, F32 *b ) override;
+
+ void *getPlatformWindow() override;
+ void bringToFront() override;
+ void focusClient() override;
+
+ void allowLanguageTextInput(LLPreeditor *preeditor, bool b) override;
+ void setLanguageTextInput( const LLCoordGL & pos ) override;
+ void updateLanguageTextInputArea() override;
+ void interruptLanguageTextInput() override;
+ void spawnWebBrowser(const std::string& escaped_url, bool async) override;
void openFolder(const std::string &path) override;
- /*virtual*/ F32 getSystemUISize();
+ F32 getSystemUISize() override;
LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url );
@@ -130,13 +128,13 @@ public:
static std::vector<std::string> getDynamicFallbackFontList();
static void setDPIAwareness();
- /*virtual*/ void* getDirectInput8();
- /*virtual*/ bool getInputDevices(U32 device_type_filter,
+ void* getDirectInput8() override;
+ bool getInputDevices(U32 device_type_filter,
std::function<bool(std::string&, LLSD&, void*)> osx_callback,
void* win_callback,
- void* userdata);
+ void* userdata) override;
- U32 getRawWParam() { return mRawWParam; }
+ U32 getRawWParam() const { return mRawWParam; }
protected:
LLWindowWin32(LLWindowCallbacks* callbacks,
@@ -149,7 +147,7 @@ protected:
HCURSOR loadColorCursor(LPCTSTR name);
bool isValid();
void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size);
- virtual LLSD getNativeKeyData();
+ LLSD getNativeKeyData() const override;
// Changes display resolution. Returns true if successful
bool setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh);
@@ -175,7 +173,7 @@ protected:
// Platform specific methods
//
- bool getClientRectInScreenSpace(RECT* rectp);
+ bool getClientRectInScreenSpace(RECT* rectp) const;
static LRESULT CALLBACK mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param);
@@ -266,9 +264,9 @@ public:
LLSplashScreenWin32();
virtual ~LLSplashScreenWin32();
- /*virtual*/ void showImpl();
- /*virtual*/ void updateImpl(const std::string& mesg);
- /*virtual*/ void hideImpl();
+ void showImpl() override;
+ void updateImpl(const std::string& mesg) override;
+ void hideImpl() override;
#if LL_WINDOWS
static LRESULT CALLBACK windowProc(HWND h_wnd, UINT u_msg,